[comp.sys.amiga] Need help with signals

dillon@CORY.BERKELEY.EDU (Matt Dillon) (01/03/88)

	Since you indicated a relative newness to programming on the Amiga
I won't rattle you on your programming style.  There are a couple of things
wrong here...

	(*) Wait() clears all waited on signals before it returns.   Wait also
	    returns a mask of all requested signals which OCCURED... this is
	    usually used before doing the more expensive GetMsg() or CheckIO()
	    calls.

	You seem to be assumming that only one signal will come back at a 
	time.  In actuality, any one, two, or all the signals may come back
	at the same time.  So if an intuition message occurs at the same time
	the serial completes, your code will never get to the serial due to
	your "ELSE IF" ... "if intuition message then to intuition stuff but
	DO NOT do serial stuff (even if serial message)" is what your code is
	doing.  It gets even worse because your console handling section is
	also on the tail end of an ELSE... 

	Fix #1:  Make each IF independant, not dangling on the previous IF's
		 else.

	Now, assumming that SerRead and ConRead are each message ports the 
	rest of the code looks correct (at least what you've given us).  In
	fact, your use of GetMsg() is one of the more interesting ways to
	check for IO completion (and remove the message from the reply port
	if the IO has completed).  In the case of the code below, it only 
	works if each request has its own reply port, which yours do.

	Most advanced programmers use only one reply port for all their 
	requests and use CheckIO()/WaitIO() instead of GetMsg().

	One addition you might want to make to the fragment below is to
	use the returned signal mask from Wait() as another IF level before
	making the more expensive GetMsg() calls:

	mask = Wait(blah)

	if ((mask & (1 << SerRead->mp_SigBit)) &&  GetMsg(SerRead)) {
	    blah
	}

					-Matt

:	while(1){
:		 Wait((1L << MyWindow->UserPort->mp_SigBit) |
:		     (1L << SerRead->mp_SigBit) |
:		     (1L << ConRead->mp_SigBit));  
:		if(WinMsg = (struct IntuiMessage *)GetMsg(MyWindow->UserPort)){
:			mclass = WinMsg->Class;
:			mcode = WinMsg->Code;
:			ReplyMsg(WinMsg);
:			if(mclass == CLOSEWINDOW){
:				AbortIO(ser_in); 
:				CloseSerial();	
:				cleanup("Exiting", 0);
:				break;
:			}
:		}
:		else if(GetMsg(SerRead)){	 <<<<<<<<<<<<<--- remove else
:			data_in = SerGetChar();
:			PutChar(data_in);
:		} 	
:		else if(GetMsg(ConRead)){	<<<<<<<<<<<<<<-- remove else
:			data_out = GetChar();
:			SerPutChar(data_out);
:		}
:	}

jesup@pawl22.pawl.rpi.edu (Randell E. Jesup) (01/03/88)

In article <8801030156.AA04670@cory.Berkeley.EDU> dillon@CORY.BERKELEY.EDU (Matt Dillon) writes:
>	Now, assumming that SerRead and ConRead are each message ports the 
>	rest of the code looks correct (at least what you've given us).  In
>	fact, your use of GetMsg() is one of the more interesting ways to
>	check for IO completion (and remove the message from the reply port
>	if the IO has completed).  In the case of the code below, it only 
>	works if each request has its own reply port, which yours do.
>
>	Most advanced programmers use only one reply port for all their 
>	requests and use CheckIO()/WaitIO() instead of GetMsg().

	One thing you forgot, Matt:  for the userport (or any port where
multiple messages may accumulate) the code should look like this:

	while(whatever) {
		mask = Wait(...);
		if (mask & (1L << UserPortSigBit))
			while ((msg = GetMsg(UserPort)) != NULL)
			{
				process message
			}
	}

Note that the 'while' is what I'm refering to.  You MUST clear out all
messages from the port when you get woken up, as the signal bit has been
cleared.  If you only clear one per awakening, and you are ever sent two,
you'll always be one behind.  If you are SURE that the port can only have
one message in it, ok.  Even then I like to use the while loop anyways.

     //	Randell Jesup			Lunge Software Development
    //	Dedicated Amiga Programmer	13 Frear Ave, Troy, NY 12180
 \\//	lunge!jesup@beowulf.UUCP	(518) 272-2942
  \/    (uunet!steinmetz!beowulf!lunge!jesup)

jesup@pawl22.pawl.rpi.edu (Randell E. Jesup) (01/03/88)

In article <22374@ucbvax.BERKELEY.EDU> bryce@hoser.berkeley.edu (Bryce Nesbitt) writes:
...
>Also, since you are opening the serial.device, be sure to give the user the
>capability to open *any* unit number.  Unit zero is the built-in serial
>port.  Higher unit numbers represent additional ports a user may have 
>configured. **
>
>|\ /|  . Ack! (NAK, SOH, EOT)
>{o O} . bryce@hoser.berkeley.EDU -or- ucbvax!hoser!bryce (or try "cogsci")

Bryce -
	I posted this message on Bix, in response to one of your messages
about serial port expansion.  Since you don't get on there directly, and 
because others in the Usenet amiga community will be interested, I am reposting
my response here.  (All text with > is Bryce, >> is me).

==============================================================================
	I'm glad you (bryce) got the responses posted here.  I suggest you try
to get direct access to bix, it can be invaluable for things like this.
	Now to specific comments:

> *Nobody* opens random numbers.  I checked a lot of programs, they all open
> zero.

	I know of at least one that opens random units (one of the early
simple kermits).  However, just handling it cleanly is all that's needed.
It can happen because of uninitialized variables used for unit numbers.
But yes, it's rare.

> For those programs that open zero they will talk to the built-in
> port.  A utility will be available so users can map old programs to use any
> unit.  Software will quickly catch up to open *any* unit because it is so
> simple.

	That utility MUST be available.  And does it have to be a pre-emptive
action, or can it be interactive (requester)?  New software will, but a LOT
of old software will never be updated, for example PBM (play by modem) games.
Even if the company releases a new version, most people won't upgrade.  The
same (or even more so) goes for PD/PA stuff.
	Best of all would be a requester that popped up and asked for a
name-binding for serial port 0, with an option to pass it through to the
internal serial port.

>  IN THIS CASE, THERE IS NO NEED TO CORRUPT THE LOW LEVER DRIVER WITH
>  BACKWARDS PATCHES FOR OLDER PROGRAMS.
>
> I already have 4 publicly available (PA) communications programs that will
> open any unit.  2 other PA authors have agreed to add the capability. 1
> commercial package will have the capability in under two months and one
> more author (of A-Talk Gold) has not yet committed.  I have yet to contact
> Aegis (makers of Diga!).

	Who ever said the low level driver had to be patched?  All you need
is a unit mapper that maps opens to any device/unit combo.  BTW, if you're
going to try to create a standard, I wish you'd posted first, and gotten people
to convert second.

> There is no requirement the driver be loaded at boot.  There is a virtual
> requirement that the drivers be "bound" at boot time, just like the
> autoconfig spec demands.  However, the driver code is still passed
> "OpenDevice" requests... the code can initialized and/or loaded then.
>
> If the serial board has autoconfig ROMs, then there is no disk access
> problem.  If the files are on disk, there is.  Assuming a person has serial
> boards from 5 manufacturer in the machine, my system would require that 5
> files be opened and read in.  Is there a better way?  Let me know.

	Even 'binding' them at boot time will slow it down.  And playing
such tricks to allow binding then loading most of it later will increase
the complexity of the drivers.  Most people won't bother.
	I also doubt boards will have autoconfig roms.  They add expense and
freeze your software early.  They may exist, however.
	Also, your implication was that this new serial driver of yours would
create a message port for devices to register themselves.  When does this
get created, and what happens if a driver is initialized (for example, from
autoconfig roms or expansion directory) before this serial driver of yours
is loaded from disk?

> > You end with the old dip-switch problem, except this time in software:
> > how are serial devices assigned unit numbers?  The dip- switch problem is
> > the reason we have auto-config.
> 
> No problem.  I did not mention it before, but there is a simple name
> binding protocol available.  The user can ask to link up to "Accounting's
> LaserWriter" or "Telebit Modem 6" with no problems.  He can also link up to
> any "class" of object, ie "*modem*", or "*laserwriter*".

	Ah.  Do you think you could tell us anything else in this design
that you left out (like the messages to the message port and how they work)?
And also, you miss my point.  The unit numbers have no meaning, as they
can change from boot to boot, so you MUST use the name-binding protocol.
(see my comments at the end of this message.)  The dip-switch problem was the
old 'what address (read unit) do I put this new piece of hardware at?'
The solution is to assign it automatically, and make it invisible to the
user.

> The physical ports are bound to names via the tooltypes of the driver icon.
> (Remember... you drop an icon into the expansion drawer to install a new
> serial port).  The tooltypes could look like this:

	That brings up a point: does the expansion drawer require icons, or
just files?  Also, remember that people using this may not realize how to
set up tooltypes, they are a bit arcane and hidden from most people.

> 	UNIT 1=Avatex 1200 baud modem
> 	UNIT 2=Hayes modem 1
> 	UNIT 3=Larry's LaserWriter|Accounting's LaserWriter
> 	UNIT 4=MIDI
> 
> This also points out a disadvantage of Autoboot ROMs and my name binding...
> the user would still need to have an expansion drawer entry to assign the
> names.

	What about name bindings for the original serial port?

> I strongly suspect that the name binding protocol I'm building in will be
> hidden underneath the higher level protocol (AppleTalk, TOPS, etc.).
> 
> The serial.device just a transport layer for most applications.

	Care to elaborate?  By the way, how does a program USE this naming
protocol, and does it affect the serial device being EXACTLY like the old
one?

> > The next thing is the fact that the program isn't really opening the serial
> > device he's talking to, but your serial device.  Thus the user can't call
> > any extended vectors, just the standard ones.
> 
> That is correct.  Of course the current serial.device does not have or
> strictly need any extended vectors.

	Of course it doesn't.  But someone else's advanced super-speed serial
port might need (or want) such.  If the capability can be preserved without
making things harder, why not preserve it?  Who knows, maybe there will be
more driver entries in some future version of exec.

> Nope!  I link in ahead of the old serial.device and dole out requests to
> it.  The magic of priorities.  There is no requirement for the old
> serial.device to be re-written.
> 
> As for Commodore's involvement, they already are.  amgia!bart (Manager of
> Amiga ROM Software) not only approves this approach, but came up with the
> initial suggestion.

	And what if the serial.device priority changes?  Again, I really
wish bart would sign on here!!!!!!!!  (Someone at C= want to jog him?
Please?)

	If you are going to create a seperate device, how can you call it
the serial.device?  Anyone writing (or re-writing) their program to use it
will be able the change the name easily, and it might avoid a lot of
confusion.  I also dread a bunch of programs coming out that have a menu
item (or requester) that says "Enter serial port unit number:".  I suspect
they'll just put in a way to enter the unit, not any name-binding stuff, from
the way you phrased that stuff above about the people who would have the
multiple unit stuff in their programs.

	Ok, now for some additional comments:

1.  I think you'll end up with all drivers loaded on boot, and permanently
resident thereafter.  It could eat memory that would otherwise ( if the open
was passed on to the real driver) be released when the driver was closed.

2.  How do you link at a higher priority than the serial.device (which is
loaded from disk)?  You can't call your device serial.device, as it would
conflict with the serial.device in the devs directory (the old one).  Running
a program afterwards that creates a device works, but it is only a debugging
technique, and can't be used for production.

3.  We should consider what application this name-binding/etc has to 
other types of expansion (such as parallel device, printers, etc).

MOST IMPORTANT:
4.  As far as I can see, the only thing needed is a name-binding protocol.
Nothing should care about unit numbers that may change from boot to boot.
Therefor, EVERYTHING would HAVE to use the name-binding, and that could just
as easily return device and unit, instead of unit only.  If you return device
and unit, you get all the advantages of loadable/purgable device drivers,
that require nothing done at boot to slow the system down.  Have you told
these developers that the unit numbers can't be relied upon from boot to
boot, and therefor the user won't know the number either?

	How about this:  a name-binder (device, I assume) that understands
how to bind names to devices.  You tell it you want something that matches
a string, and it tells you the device-name/unit to open.  This is a bit
simplistic, so how about making it open the device for you, with flags
allowing trying other devices that match if the first open fails.  This
solves the 'get first free modem' problem.  You would pass it something
like this:

	struct IOExtMap {
		struct IORequest IOMap;
		/* IOMap has flags, if more needed add here */
		UBYTE *MapName;
		struct IOExtSer *SerRequest;
	};

or you could just extend the IOExtSer with a couple of fields for flags/
whatever.  One of the flags should allow a force of a specific device (and
optionally a specific unit as well).  This is for programs that MUST talk
to a specific piece of hardware.

	I'm not sure wildcards are the way to go.  I think multiple names
might be less confusing and prone to error.  Lets discuss it.
(ex: MBSerial Unit 1 : Hayes_2400 | Modem | Modem_2400
     MBSerial Unit 2 : Laserwriter | Printer | Postscript_Printer).

	Please don't take my sometimes harsh comments negatively, I'm just
trying to make sure the final result is the best possible.  Also, last I heard,
Bix was the place these discussions were supposed to be (and were) going
on.  It can really muddy issues when these things go on independantly without
contact.  Hopefully, now that we know of what you've been doing, we can all
cooperate on this.  (Your initial message implied that no one else anywhere
was doing anything about it, whereas it's been being discussed here for
months.)

May I ask: are you involved in a hardware expansion product?  If not, I'd
suggest holding off on things until a consensus is reached.  If so, can you
give us some idea what your deadlines are like?

	Once again, I thank you for your fresh perspective and your bringing
your ideas to the attention of all the developers interested in this here
on Bix.  I welcome any and all criticism of my ideas and comments.

	Randell Jesup
============================================================================

Remember, that message was posted on bix, in amiga.dev/main.

     //	Randell Jesup			Lunge Software Development
    //	Dedicated Amiga Programmer	13 Frear Ave, Troy, NY 12180
 \\//	lunge!jesup@beowulf.UUCP	(518) 272-2942
  \/    (uunet!steinmetz!beowulf!lunge!jesup)

dillon@CORY.BERKELEY.EDU (Matt Dillon) (01/03/88)

:>	Most advanced programmers use only one reply port for all their 
:>	requests and use CheckIO()/WaitIO() instead of GetMsg().
:
:	One thing you forgot, Matt:  for the userport (or any port where
:multiple messages may accumulate) the code should look like this:
:
:	(code using while to clear all messages from intuition port)

	Right...  Also when I said CheckIO()/WaitIO() I was refering to
IO requests, not intuition messages, which you always GetMsg().  And when
I mentioned sharing reply ports, I was refering to sharing among IO requests,
not sharing with the intuition port...

				-Matt

carolyn@cbmvax.UUCP (Carolyn Scheppner CATS) (01/04/88)

In article <8801030156.AA04670@cory.Berkeley.EDU> dillon@CORY.BERKELEY.EDU (Matt Dillon) writes:
>
>:	while(1){
>:		 Wait((1L << MyWindow->UserPort->mp_SigBit) |
>:		     (1L << SerRead->mp_SigBit) |
>:		     (1L << ConRead->mp_SigBit));  
>:		if(WinMsg = (struct IntuiMessage *)GetMsg(MyWindow->UserPort)){
>:			mclass = WinMsg->Class;
>:			mcode = WinMsg->Code;
>:			ReplyMsg(WinMsg);
>:			if(mclass == CLOSEWINDOW){
>:				AbortIO(ser_in); 
>:				CloseSerial();	
>:				cleanup("Exiting", 0);
>:				break;
>:			}
>:		}
>:		else if(GetMsg(SerRead)){	 <<<<<<<<<<<<<--- remove else
>:			data_in = SerGetChar();
>:			PutChar(data_in);
>:		} 	
>:		else if(GetMsg(ConRead)){	<<<<<<<<<<<<<<-- remove else
>:			data_out = GetChar();
>:			SerPutChar(data_out);
>:		}
>:	}

   I would add that for any port where multiple messages might come in 
(like the window's UserPort), your GetMsg line should not be an "if" but a 
"while".  A set signal bit does not mean there is only ONE message at the port.
If you don't handle them in a while loop, your program can easily get behind,
especially with things like rawmouse or multiple item menu selection.

-- 
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Carolyn Scheppner -- CATS   >>Commodore Amiga Technical Support<<
                     UUCP  ...{allegra,ihnp4,rutgers}!cbmvax!carolyn 
                     PHONE 215-431-9180
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=