home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 18 REXX
/
18-REXX.zip
/
rxportio.zip
/
RXPORTIO.INF
(
.txt
)
< prev
next >
Wrap
OS/2 Help File
|
1998-06-22
|
26KB
|
726 lines
ΓòÉΓòÉΓòÉ 1. Description ΓòÉΓòÉΓòÉ
rxPortIO ver. 1.01 - is a REXX-callable DLL and provides hardware I/O read and
write services to OS/2 Warp 3.0 and later REXX programs. With rxPortIO you can
easily access hardware like digital I/O, data acquisition and other boards that
require I/O read and writes. rxPortIO is ideal for prototyping hardware for
both, the Professional and the Hobbyist. rxPortIO is copyrighted freeware.
ΓòÉΓòÉΓòÉ 2. Introduction ΓòÉΓòÉΓòÉ
Have you been looking for a way to do port I/O in OS/2 without having to
program in C? Well, here is rxPortIO. It is a dynamic link library that allows
REXX programs to access anything from digital I/O and data acquisition boards
to volume controls of sound cards, printer ports and even plug-in radio cards.
rxPortIO also includes a 1-1000 msec delay and bit manipulation routines that
are useful with digital I/O manipulation.
Together with REXX or a visual REXX application development tool like
Vispro/REXX, you can easily and quickly put together a program that supports
the mentioned hardware.
I wrote an initial version of rxPortIO for accessing my computer FM radio card
with a native OS/2 program written with Vispro/REXX. A few of you have written
me, asking how I access I/O with REXX, so I decided to spiff-up the code a bit
an release it for everybody's use. If you write a program with rxPortIO, return
the favor and consider making it available to the OS/2 community as well!
rxPortIO uses TESTCFG.SYS, a generic device driver standard to every OS/2 Warp
3.0 and 4.0 system. Installing and using rxPortIO does in most cases not
require to reboot the computer as TESTCFG.SYS is installed by default on every
Warp system.
rxPortIO includes two sample programs, one is a standard REXX program, the
other is a compiled Vispro/REXX program to show how to use rxPortIO.
I have used and tested rxPortIO with my plug-in radio, sound card and a relay
board and found it to work without any problems.
I gathered most information for rxPortIO from the internet and if you would
like additional information on port I/O or REXX programming, check the section
on References in this file.
If you will be using rxPortIO with VX-REXX or Vispro/REXX, please read the
section Important Notes for a possible problem and the fix for it.
Before you go ahead and make plans to use rxPortIO consider these limitations:
- rxPortIO is a 32-bit DLL but is designed to read 8-bit I/O only.
- rxPortIO does not support interrupt driven devices.
- The cheapest way to do digital I/O is using a printer
or serial adapter. The problem is that rxPortIO and
TESTCFG.SYS do not allow to write to printer or serial
ports that have been loaded by the system. The only
way I found to access LPT ports with rxPortIO, for example,
is to set the address of the port to some non-standard value
if that is possible, or set it to 278hex and remark out the
line BASEDEV=PRINT01.SYS in the CONFIG.SYS. This will
remove printer support from your system, but allows to
access the LPT port to be used as digital I/O by rxPortIO.
If you know of a better way, let me know.
- Digital I/O and data acquisition boards usually include
documentation on how to access the cards registers.
Sound card manufacturers also list that data, so it is easy
to write an application for these devices. Other devices,
like radio cards for example, do not come with that
documentation. So before you decide to write any program
for such a device, you must know how to access the registers.
The hardware manufacturers will typically not provide you
with that information.
If you have any suggestions about rxPortIO, please let me know.
ΓòÉΓòÉΓòÉ 3. Performance Considerations ΓòÉΓòÉΓòÉ
REXX PortIO Functions PIORead and PIOWrite:
Doing I/O using rxPortIO with REXX programs is not going to set speed records.
It is not possible to do high speed data acquisition, the REXX overhead is too
high.
Here is an informal tests I did on a 100MHz Pentium:
Using rxPortIO.DLL to access I/O through TESTCFG.SYS:
10,000 readings in 17.8 sec or 1.8 msec per reading
Using a C program to access I/O through TESTCFG.SYS:
10,000 readings in 2.3 sec or 0.23 msec per reading
The C program is over seven times faster.
Despite the slow speed of I/O access using rxPortIO, it is still useful for
many applications. Any digital I/O cards, especially relay cards can be
controlled without any problems as the controls attached to these devices are
usually much slower. Relays have typical response times of 100 msec or slower
and pose no timing problems to rxPortIO. rxPortIO is also being used to control
computer radio cards and can be used to control sound card volume and balance
as well.
Bit Manipulation Functions:
Using the supplied bit manipulation functions makes it convenient to do bit
operations in REXX and from looking at the code it appears that it will execute
much faster as well. Not so. The call to the DLL puts quite an overhead on to
the program and can take up to 20 times longer than using several of the
built-in REXX functions to do the same operation. The native REXX function
example below will execute much faster than the call to rxPortIO using
PIOBitClear. Both samples achieve the same result.
/* Native REXX Functions D2C and BITAND - MUCH FASTER */
BitVal = D2C('255')
BitResult = BITAND(BitVal, '1'x)
/* Call to rxPortIO - SLOWER */
BitVal = '255'
BitResult = PIOBitClear(BitVal, '0')
If your program executes the bit manipulation routines many times in a loop,
you may consider using native REXX functions to perform this operation.
Time Delay PIODelay:
The PIODelay function is included for convenience and provides delays for a
fraction of a second, something the SysSleep function of REXXUTIL.DLL is not
able to do. If you are using VX-REXX or Vispro/REXX to write your program, I
suggest to use the Timer events of these programs whenever possible. This will
make your programs more responsive, especially if time delays are fairly long.
PIODelay will essentially wait for the delay to time-out and your program will
not respond during that time. Using the Timer events of the visual REXX
programs will consume less CPU cycles and it will allow you to terminate the
delay through a dedicated button for example.
ΓòÉΓòÉΓòÉ 4. rxPortIO Function Description ΓòÉΓòÉΓòÉ
Describes all functions available in rxPortIO.DLL and how to use them.
PIOLoadFuncs
PIODropFuncs
PIORead
PIOWrite
PIODelay
PIOBitPick
PIOBitSet
PIOBitClear
ΓòÉΓòÉΓòÉ 4.1. PIOLoadFuncs ΓòÉΓòÉΓòÉ
This function loads rxPortIO.DLL to be used within your REXX program. A REXX
program using rxPortIO must contain the following two lines to access other
functions inside rxPortIO:
CALL RXFuncAdd 'PIOLoadFuncs', 'RXPORTIO', 'PIOLoadFuncs'
CALL PIOLoadFuncs
Standard REXX errors are returned for this function, for example: Error 43 -
Routine not found if rxPortIO.DLL can't be found.
This function also opens TESTCFG.SYS for read and write access. No errors are
returned by this function if opening TESTCFG.SYS fails. For errors returned see
the PIORead or PIOWrite sections.
ΓòÉΓòÉΓòÉ 4.2. PIODropFuncs ΓòÉΓòÉΓòÉ
This function drops rxPortIO.DLL from your REXX program and also closes access
to TESTCFG.SYS. Use it in your REXX program as shown below.
CALL PIODropFuncs
ΓòÉΓòÉΓòÉ 4.3. PIORead ΓòÉΓòÉΓòÉ
This function reads port_Address and returns the data in return_data. The
function format is as follows:
return_data = PIORead( port_Address )
The value of port_Address must be a decimal number in the range of 256 to 65535
which corresponds to the I/O range of the IBM Personal Computer.
If the PIORead was successful, the return_data contains the following string:
Addr: xxx Data: yyy
where xxx is the port_Address and yyy is the data of the port read. All values
are in decimal form. The range of the data read is 0 to 255.
If the PIORead was unsuccessful, the following errors are returned:
Addr: xx Data: yy
xx = 10 TESTCFG.SYS could not be opened during PIOLoadFuncs.
yy = rc Return code of DOSOpen.
xx = 11 Unsuccessful read.
yy = rc Return code of DosDevIOCtl.
For error codes of DosOpen and DosDevIOCtl see the section on Error Codes.
Examples:
The code below reads the port at location 768 (300 hex) and results in the
following output:
port_Address = X2D('300')
return_data = PIORead( port_Address )
SAY 'Data read is ' || return_data
Output:
Data read is Addr: 768 Data: 255
The code below reads the port at location 888 (378 hex) which is reserved for
LPT1 and tied up by PRINT01.SYS. It results in the following read error output:
port_Address = X2D('378')
return_data = PIORead( port_Address )
SAY 'Data read is ' || return_data
Output:
Data read is Addr: 11 Data: 19
The error (19) given here is not listed under the return codes of DosDevIOCtl
of the Control Programming documentation. Error 19 is listed as
ERROR_WRITE_PROTECT under the disk access functions. Go figure.
The port_Address range is not directly checked by this function, however,
TESTCFG.SYS does not allow to read or write to ranges below 256 (100 hex) as
these contain critical system parameters. Also, it does not allow to read or
write to ports that are controlled by other drivers, like the printer (LPT1) or
serial ports (COM1, COM2).
ΓòÉΓòÉΓòÉ 4.4. PIOWrite ΓòÉΓòÉΓòÉ
If you find that PIOWrite is not working in a VX-REXX or Vispro/REXX program,
please read the section Important Notes for details.
This function writes data_Value to port_Address. The function format is as
follows:
return_data = PIOWrite( port_Address, data_Value )
The value of port_Address must be a decimal number in the range of 256 to 65535
which corresponds to the I/O range of the IBM Personal Computer. The value of
data_Value must be in decimal and have a range of 0 to 255.
If the PIOWrite was successful, the return_data contains the following string:
Addr: xxx Data: yyy
where xxx is the port_Address and yyy is the data just written to the port. All
values are in decimal form.
If the PIOWrite was unsuccessful, the following errors are returned:
Addr: xx Data: yy
xx = 10 TESTCFG.SYS could not be opened during PIOLoadFuncs.
yy = rc Return code of DOSOpen.
xx = 12 Unsuccessful write.
yy = rc Return code of DosDevIOCtl.
For error codes of DosOpen and DosDevIOCtl see the section on Error Codes.
Example:
The code below writes to the port at location 768 (300 hex) and results in the
following output:
port_Address = X2D('300')
data_Value = 255
return_data = PIOWrite( port_Address, data_Value )
SAY 'Data written is ' || return_data
Output:
Data written is Addr: 768 Data: 255
The port_Address range is not directly checked by this function, however,
TESTCFG.SYS does not allow to read or write to ranges below 256 (100 hex) as
these contain critical system parameters. Also, it does not allow to read or
write to ports that are controlled by other drivers, like the printer (LPT1) or
serial ports (COM1, COM2).
ΓòÉΓòÉΓòÉ 4.5. PIODelay ΓòÉΓòÉΓòÉ
This function provides time delay services to your REXX application. It does
not read or write to any I/O. REXX provides time delay in one second increments
only with the function SysSleep in REXXUTIL.DLL. The PIODelay function provides
time delay in the range of 1 to 1000 milliseconds and is useful if some I/O
hardware needs this short delay for proper operation. Note: This time delay is
rounded up to the next system clock tick and is therefore not accurate to the
millisecond. The format of the function is:
return_data = PIODelay( delay_value )
The delay_value is in the range of 1 to 1000 milliseconds.
return_data returns the following values:
0 No errors.
1 Invalid time delay range.
322 ERROR_TS_WAKEUP (Error given by DosSleep API function).
Example:
The code below illustrates the time delay function and shows how to verify the
delay using the REXX function TIME(). Note that TIME() has a resolution of only
10 milliseconds.
delay_value = 500
SAY 'Start delay of ' || delay_value || ' msec...'
verify_delay = TIME('R')
return_data = PIODelay( delay_value )
verify_delay = TIME('E')
SAY "... End Delay. rc = " || return_data
SAY 'elapsed time = ' || verify_delay
Output:
Start delay of 500 msec...
┬╖┬╖┬╖ End Delay. rc = 0
elapsed time = 0.520000
ΓòÉΓòÉΓòÉ 4.6. PIOBitPick ΓòÉΓòÉΓòÉ
This function provides an easy way to pick a certain bit from a value. It does
not access any I/O, but rather provides a much easier way to pick a bit than
standard REXX functions do. The format of the function is:
return_bit_value = PIOBitPick( value, bit_position )
The value is in decimal with a range of 0 to 65535.
bit_position is the position of the bit which value is to be extracted. The
right-most position (and therefore the least significant bit) is 0, the
left-most (or most significant bit) position is 15.
return_bit_value is the value returned and can only be 0 or 1.
There is no variable range checking, so you must insure that the right values
are passed along in the function.
Example:
The code below illustrates the PIOBitPick function.
SAY 'The bit position 0 of the value 254 is ' || PIOBitPick( '254', '0' )
SAY 'The bit position 1 of the value 254 is ' || PIOBitPick( '254', '1' )
Output:
The bit position 0 of the value 254 is 0
The bit position 1 of the value 254 is 1
ΓòÉΓòÉΓòÉ 4.7. PIOBitSet ΓòÉΓòÉΓòÉ
This function provides an easy way to set a certain bit in a value. It does not
access any I/O, but rather provides a much easier way to set a bit than
standard REXX functions do. The format of the function is:
return_value = PIOBitSet( value, bit_position )
The value is in decimal with a range of 0 to 65535.
bit_position is the position of the bit which value is to be set. The
right-most position (and therefore the least significant bit) is 0, the
left-most (or most significant bit) position is 15.
return_value is the value returned after the bit has been set. Range is 1 to
65535.
There is no variable range checking, so you must insure that the right values
are passed along in the function.
Example:
The code below illustrates the PIOBitSet function.
SAY 'Setting bit position 0 of the value 254 results in ' || PIOBitSet( '254', '0' )
SAY 'Setting bit position 1 of the value 0 results in ' || PIOBitSet( '0', '1' )
Output:
Setting bit position 0 of the value 254 results in 255
Setting bit position 1 of the value 0 results in 2
ΓòÉΓòÉΓòÉ 4.8. PIOBitClear ΓòÉΓòÉΓòÉ
This function provides an easy way to clear a certain bit in a value. It does
not access any I/O, but rather provides a much easier way to clear a bit than
standard REXX functions do. The format of the function is:
return_value = PIOBitClear( value, bit_position )
The value is in decimal with a range of 0 to 65535.
bit_position is the position of the bit which value is to be cleared. The
right-most position (and therefore the least significant bit) is 0, the
left-most (or most significant bit) position is 15.
return_value is the value returned after the bit has been cleared. Range is 0
to 65535.
There is no variable range checking, so you must insure that the right values
are passed along in the function.
Example:
The code below illustrates the PIOBitClear function.
SAY 'Clearing bit position 0 of the value 255 results in ' || PIOBitClear( '255', '0' )
SAY 'Clearing bit position 1 of the value 65535 results in ' || PIOBitClear( '65535', '1' )
Output:
Clearing bit position 0 of the value 255 results in 254
Clearing bit position 1 of the value 65535 results in 65533
ΓòÉΓòÉΓòÉ 5. Error Codes ΓòÉΓòÉΓòÉ
Error Codes Returned:
If the port address is not returned by the PIORead and PIOWrite functions, then
the data portion of the returned value indicates the type of error that
occurred.
DosOpen returns one of the following values as rc:
0 NO_ERROR
2 ERROR_FILE_NOT_FOUND
3 ERROR_PATH_NOT_FOUND
4 ERROR_TOO_MANY_OPEN_FILES
5 ERROR_ACCESS_DENIED
12 ERROR_INVALID_ACCESS
26 ERROR_NOT_DOS_DISK
32 ERROR_SHARING_VIOLATION
36 ERROR_SHARING_BUFFER_EXCEEDED
82 ERROR_CANNOT_MAKE
87 ERROR_INVALID_PARAMETER
99 ERROR_DEVICE_IN_USE
108 ERROR_DRIVE_LOCKED
110 ERROR_OPEN_FAILED
112 ERROR_DISK_FULL
206 ERROR_FILENAME_EXCED_RANGE
231 ERROR_PIPE_BUSY
DosDevIOCtl returns one of the following values as rc:
0 NO_ERROR
1 ERROR_INVALID_FUNCTION
6 ERROR_INVALID_HANDLE
15 ERROR_INVALID_DRIVE
31 ERROR_GEN_FAILURE
87 ERROR_INVALID_PARAMETER
111 ERROR_BUFFER_OVERFLOW
115 ERROR_PROTECTION_VIOLATION
117 ERROR_INVALID_CATEGORY
119 ERROR_BAD_DRIVER_LEVEL
163 ERROR_UNCERTAIN_MEDIA
165 ERROR_MONITORS_NOT_SUPPORTED
ΓòÉΓòÉΓòÉ 6. Important Notes ΓòÉΓòÉΓòÉ
It has been reported that the PIOWrite function will sometimes not write to the
selected port when using VX-REXX or Vispro/REXX based programs. This especially
is the case when the PIOWrite function is the last statement in an event. To
fix this problem, simply add any other statement after the PIOWrite function
call. A simple NOP (no operation) right after the PIOWrite call will fix the
problem.
This problem does not occur with classical REXX programs.
I have observed this behaviour in the past but can not duplicate it anymore. I
have tested this on a relay card, by actually checking if the correct outputs
are turned on and I also have tested it with my FM radio card by changing
frequency and volume. It appears to work fine.
I would suspect that the reason could be in how VX-REXX and Vispro/REXX poll
keyboard and mouse events. If anyone has any suggestions why this happens and
how to fix it, please let me know.
ΓòÉΓòÉΓòÉ 7. Example REXX Program ΓòÉΓòÉΓòÉ
The program below shows how to use rxPortIO.
/* RXPORTIO.CMD */
/* Sample REXX Program to access I/O ports using TESTCFG.SYS */
/* Load RXPORTIO.DLL */
CALL RXFuncAdd 'PIOLoadFuncs', 'RXPORTIO', 'PIOLoadFuncs'
CALL PIOLoadFuncs
SAY ''
SAY '--- Sample REXX Program to access I/O ports using TESTCFG.SYS.---'
SAY ''
SAY 'CAUTION: This program is capable of writing to I/O directly.'
SAY ' Make sure the port_Address is set to a value'
SAY ' that will not cause harm to your system.'
SAY ' Port_Address is set to 768 (300hex) by default.'
SAY ''
port_Address = X2D('300') /* Port_address is converted from hex to decimal */
data_value = 0 /* initialize I/O data_value to zero */
SAY 'Address is set to ' || port_Address || ' decimal.'
keyhit = ""
DO FOREVER
SAY ""
SAY "Press w to write 0x55, r to Read; d for 0.5 sec delay;"
SAY "b for bit manipulation; x for eXit..."
SAY ""
PARSE PULL keyhit
SELECT
when keyhit = "r" | keyhit = "R" then /* read */
do
return_data = PIORead( port_Address )
SAY 'Read returned ' || return_data
PARSE VAR return_data . ': ' port1 . ': ' data1
IF port1 <> port_Address then
SAY 'error reading port: ' || port_Address || ' rc = ' || data1
ELSE
SAY "READ. Address: " || D2X(port1) || "h Data: " || D2X(data1) || "h = " || X2B(D2X(data1)) || " bin"
keyhit = ""
end
when keyhit = "w" | keyhit = "W" then /* write */
do
data_value = X2D('55') /* data to write */
return_data = PIOWrite( port_Address, data_value )
SAY 'Write returned ' || return_data
PARSE VAR return_data . ': ' port1 . ': ' data1
SAY "WRITE. Address: " || D2X(port1) || "h Data: " || D2X(data1) || "h = " || X2B(D2X(data1)) || " bin"
keyhit = ""
end
when keyhit = "d" | keyhit = "D" then /* delay */
do
delay_value = 500 /* delay 1 to 1000 msec max. */
SAY 'Start delay of ' || delay_value || ' msec...'
delay = TIME('R') /* delay verification only */
return_data = PIODelay( delay_value )
delay = TIME('E')
SAY "... End Delay (0 = successfull, 322 = error). rc = " || return_data
SAY 'elapsed time = ' || delay
keyhit = ""
end
when keyhit = "b" | keyhit = "B" then /* bit manipulations */
do
value = X2D('FF') /* init value to 255 (FFhex) */
SAY 'BitPick. Bit 0 of ' || value || ' is ' || PIOBitPick(value, '0')
SAY 'BitClear. Bit 1 cleared of ' || value || ' is ' || PIOBitClear(value, '1')
SAY 'BitSet. Bit 15 set of 0 is ' || PIOBitSet('0', '15')
keyhit = ""
end
otherwise
DO
IF keyhit = "x" | keyhit = "X" THEN
DO
CALL PIODropFuncs
SAY 'RXPORTIO dropped.'
EXIT
END
ELSE
SAY "Invalid Input"
END
end /* select */
end /* do */
EXIT
ΓòÉΓòÉΓòÉ 8. Installation ΓòÉΓòÉΓòÉ
Note: REXX must be installed on your system for this program to work. REXX is
installed by default, but if you did not install it, then run Warp installation
again and selectively install REXX support.
rxPortIO.DLL requires the presence of the following statements in the
CONFIG.SYS:
IOPL=YES,FXPRINT
DEVICE=D:\OS2\BOOT\TESTCFG.SYS
These statements are present by default on any OS/2 Warp 3.0 or Warp 4.0
systems.
If you write your own REXX programs, only rxPortIO.DLL is required to access
the I/O. I suggest to keep rxPortIO.DLL in the same directory as your REXX
program so it will be able to find it when rxPortIO.DLL is loaded.
As an alternative, put rxPortIO.DLL into a directory called out by the LIBPATH
in the CONFIG.SYS. A reboot is required for this. Any program loading
rxPortIO.DLL will then automatically find it.
The following files belong to this program:
rxPortIO.DLL REXX-callable Dynamic Link Library for I/O read and
write.
rxPortIO.INF Documentation for rxPortIO.
IOTEST.EXE Sample Vispro/REXX program to show how to use
rxPortIO.DLL.
IOTSTSRC.ZIP Sample Vispro/REXX program source. UNZIP (preserve
paths) in your visprorx subdirectory to recreate
project.
VPOBJ.DLL DLL required by IOTEST.EXE.
rxPortIO.CMD Sample REXX program to show how to use rxPortIO.DLL.
README.TXT Short program description.
ΓòÉΓòÉΓòÉ 9. References ΓòÉΓòÉΓòÉ
To get more information on programming for OS/2, especially for hardware I/O,
visit the sites listed below:
www.edm2.com
Look through back issues of articles published on hardware I/O and device drivers.
Also offers online classes for programming for OS/2.
http://avenger.mri.psu.edu/os2page.html
Information and source code on how to access I/O ports in OS/2.
http://femto.ssp.ameslab.gov
Contains OS/2 Warp Programming information for digital I/O
and GPIB devices.
To get information on interfacing to a PC or to get information on digital I/O
products, visit the sites listed below:
http://shell.rmi.net/~hisys/parport.html
IBM Parallel Port FAQ/Tutorial. Useful if you want to use rxPortIO to
control a device attached to the parallel port.
http://www.senet.com.au/~cpeacock/
Craig Peacock's Interfacing the PC. Online tutorial to interface any
device to the parallel or serial ports.
Many additional links to other sites about interfacing to computers.
http://www.industry.net/indcompsrc
Industrial Computer Source. Industrial computers and data acquisition products.
http://www.natinst.com
National Instruments. Digital I/O and data acquisition products.
http://www.acces-usa.com
Acces I/O Products. Carries low-cost digital I/O products.
To get more information on programming for OS/2 using REXX visit the sites
listed below:
http://rexx.hursley.ibm.com/rexx/
Main REXX site.
http://www.vispro.com/
Visual programming for OS/2 using REXX. Hockware's Vispro/REXX site.
Helpful books about programming for OS/2 using REXX:
Teach Yourself REXX in 21 Days. Schindler&Schindler, SAMS, ISBN 0-672-30529-1
REXX Reference Summary Handbook, Dick Goran, CFS Nevada, ISBN 0-9639854-3-4
http://www.cfsrexx.com
OS/2 REXX, From Bark to Byte, Document Number GG24-4199-00, read online at:
http://publib.boulder.ibm.com/cgi-bin/bookmgr/BOOKS/GG244199/contents
ΓòÉΓòÉΓòÉ 10. The Legal Stuff ΓòÉΓòÉΓòÉ
rxPortIO.DLL is free, however the author retains the copyright to the source
code. It may be distributed electronically at no fee or a minimum fee to cover
media and distribution costs as long as all files are kept together.
DISCLAIMER
rxPortIO has been successfully tested on a variety of systems and with a
variety of I/O hardware. Use this software at your own risk. The author of
rxPortIO is in no way responsible for any damage this program may cause to
computer equipment or other property by running this software on it.
ΓòÉΓòÉΓòÉ 11. Contact ΓòÉΓòÉΓòÉ
For comments and questions on this program, you can contact me directly via
e-mail.
My e-mail address:
ASchw@worldnet.att.net
Visit my home page, "The Warped Code Cellar" at
http://home.att.net/~ASchw
for other information and applications for OS/2 like HOUSE/2, a home automation
program using X10 devices, Memory Game and Leave One, two
speech-navigation-enabled games.