[comp.sys.intel] PLM vs. C for 80286/80386

hd@philtis.UUCP (Henk D. Davids @ PMSN) (06/05/89)

Not trying to start another war, but I'm just looking for
the relative merits of PLM versus C for programming 
our target machines that are based on Intel 80x86 machines.
We use VAX/VMS systems for cross-development. I would
appreciate any input that will help us to make a decision
based on facts, rather than religion ...

Thanks,
	Henk
-- 
-----------------------------------------------------------------
Henk D. Davids  @  PHILIPS Medical Systems B.V.,    Dept. SWE-MTT
Building QA-II  /  PO Box 10000 / 5680 DA  Best / The Netherlands
UUCP: hd@philtis.cft.philips.nl              Voice: +31 40 762255

tneff@bfmny0.UUCP (Tom Neff) (06/06/89)

In article <598@philtis.UUCP> hd@philtis.UUCP (Henk D. Davids @ PMSN) writes:
>Not trying to start another war, but I'm just looking for
>the relative merits of PLM versus C for programming 
>our target machines that are based on Intel 80x86 machines.

"Just looking for the relative merits" is of course exactly how those
wars get started. :-)  Both products exist and work well.  Intel C is
dpANSI conformant (within epsilon, I dunno if it's perfect).  Both
languages can call each other and other Intel HLL's.  C gives you a
choice of "fixed" or "variable" parameter passing conventions.

If you code in Intel C you can maximize portability to other platforms 
should the need arise later on. With PL/M on the other hand you can 
get a little tighter code for some of the classic kinds of systems 
programming constructs. For instance if you want a CASE driven state 
machine in a critical interrupt handler, PL/M implements DO CASE as a 
jump indirect indexed off a short array of offsets, e.g. JMP 
CASETBL[BX]. C on the other hand does 'switch' as a full 
if-then/elseif-then/elseif-then... series. So to get to a late case 
you may have to do a dozen compares and jumps. Unimportant for 
ordinary application code but possibly critical for, say, an A/D 
handler. 

On the other hand if you're bench testing an embedded system using an 
enhanced development platform, you may find the easy access to C's RTL 
(printf etc) to be helpful, although you'll have to #if those out 
before building the embedded target, usually. 

If your memory model needs are exotic or complex PL/M may be the only
way to get it (besides ASMx86).  Its extended segmentation models are
impressively flexible.  Intel C has four basic memory models and
a "far" keyword but doesn't support extended segmentation.

>We use VAX/VMS systems for cross-development. 

All the tools are available as VAX cross products I believe.

						I would
>appreciate any input that will help us to make a decision
>based on facts, rather than religion ...

This presupposes religious arguments are never based on facts. :-)
If your shop can afford VAXen there's no sense in not buying BOTH
Intel C and PL/M and making your own decision.  Whichever you go
with for the main project, the other will probably come in handy
along the way.  The reason arguments like C-vs-PL/M get religious
is that only a fanatic would absolutely recommend one over the
other!  Each product has its strengths and weaknesses and each is
usable in a production environment.  Your own computing needs are
a big part of the answer, so evaluating both is best.
-- 
Tom Neff				UUCP:     ...!uunet!bfmny0!tneff
    "Truisms aren't everything."	Internet: tneff@bfmny0.UU.NET

peter@ficc.uu.net (Peter da Silva) (06/06/89)

In article <14381@bfmny0.UUCP>, tneff@bfmny0.UUCP (Tom Neff) writes:
> CASETBL[BX]. C on the other hand does 'switch' as a full 
> if-then/elseif-then/elseif-then... series.

This must be something specific to intel's C/386, because traditionally
'C' compilers have used a variety of techniques to implement case...
including a jump table for dense case tables (such as PL/M DO CASE
constructs), lookup tables, if-then-else chains, and a combination of
all three. This optimisation existed even in Ritchie's V6 compiler.

> If your memory model needs are exotic or complex PL/M may be the only
> way to get it (besides ASMx86).  Its extended segmentation models are
> impressively flexible.  Intel C has four basic memory models and
> a "far" keyword but doesn't support extended segmentation.

At Ensun, we had a complete polled multitasker written entirely in PL/M.
If we had done it in C there would have had to be some assembly code to
handle the coroutine switching.

(Now is this a balanced article or what?)

If you're married to intel, go for PL/M by all means. If you don't want to
be tied to one processor family, go for C. If you work for the DoD, you didn't
read this far because you're using ADA :->.
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.

Business: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180.
Personal: ...!texbell!sugar!peter, peter@sugar.hackercorp.com.

guy@auspex.auspex.com (Guy Harris) (06/07/89)

>For instance if you want a CASE driven state machine in a critical
>interrupt handler, PL/M implements DO CASE as a jump indirect indexed
>off a short array of offsets, e.g. JMP CASETBL[BX]. C on the other hand
>does 'switch' as a full if-then/elseif-then/elseif-then... series.

I sincerely hope Intel's C compiler does not do so for *all* "switch"
statements; any competent C compiler can and will turn a "dense"
"switch" statement - i.e., one where the cases form a reasonably tight
range - into an indexded jump.  (The definition of "dense" varies from
compiler to compiler.)

tneff@bfmny0.UUCP (Tom Neff) (06/07/89)

In article <1765@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
> [ I had said: ]
>>For instance if you want a CASE driven state machine in a critical
>>interrupt handler, PL/M implements DO CASE as a jump indirect indexed
>>off a short array of offsets, e.g. JMP CASETBL[BX]. C on the other hand
>>does 'switch' as a full if-then/elseif-then/elseif-then... series.
>
>I sincerely hope Intel's C compiler does not do so for *all* "switch"
>statements; any competent C compiler can and will turn a "dense"
>"switch" statement - i.e., one where the cases form a reasonably tight
>range - into an indexded jump.  (The definition of "dense" varies from
>compiler to compiler.)

Not wanting to make rash statements, I retested it.  I'm right.  Here's
the proof (about 70 lines so hit N if you believe me <grin>):

iC-86  COMPILER   JUNK                              05/27/89 11:23:32  PAGE   1

DOS 3.30 (038-N) iC-86 COMPILER V4.0, COMPILATION OF MODULE JUNK
OBJECT MODULE PLACED IN JUNK.OBJ
COMPILER INVOKED BY: Z:\L86\IC86.EXE JUNK.C OPTIMIZE(3) CODE PAGEWIDTH(80)

 line level  incl

    1             main()
    2             {
    3     1           int x;
    4     1
    5     1           switch (x) {
    6     2               case 0: foo(x);
    7     2                       break;
    8     2               case 1: fee(x);
    9     2                       break;
   10     2               case 2: fie(x);
   11     2                       break;
   12     2           }
   13     1       }

iC-86  COMPILER   JUNK                              05/27/89 11:23:32  PAGE   2
                  ASSEMBLY LISTING OF OBJECT CODE

                                                   ; STATEMENT # 2
                           main      PROC NEAR
             0000  56              PUSH    SI
             0001  57              PUSH    DI
             0002  55              PUSH    BP
             0003  8BEC            MOV     BP,SP
             0005  51              PUSH    CX
                                                   ; STATEMENT # 5
             0006  8B5EFE          MOV     BX,[BP].x
             0009  EB19            JMP     @3
                           @4:
                                                   ; STATEMENT # 6
             000B  FF76FE          PUSH    [BP].x  ; 1
             000E  E80000          CALL    foo
             0011  EB0E            JMP     @7
                                                   ; STATEMENT # 7
                           @5:
                                                   ; STATEMENT # 8
             0013  FF76FE          PUSH    [BP].x  ; 1
             0016  E80000          CALL    fee
             0019  EB06            JMP     @7
                                                   ; STATEMENT # 9
                           @6:
                                                   ; STATEMENT # 10
             001B  FF76FE          PUSH    [BP].x  ; 1
             001E  E80000          CALL    fie
                           @7:
             0021  59              POP     CX      ; 1
                                                   ; STATEMENT # 11
             0022  EB0F            JMP     @2
                                                   ; STATEMENT # 12
                           @3:
             0024  83FB00          CMP     BX,0H
             0027  74E2            JZ      @4
             0029  83FB01          CMP     BX,1H
             002C  74E5            JZ      @5
             002E  83FB02          CMP     BX,2H
             0031  74E8            JZ      @6
                           @2:
                                                   ; STATEMENT # 13
             0033  8BE5            MOV     SP,BP
             0035  5D              POP     BP
             0036  5F              POP     DI
             0037  5E              POP     SI
             0038  C3              RET
                           main      ENDP

MODULE INFORMATION:

     CODE AREA SIZE               = 0039H     57D
     CONSTANT AREA SIZE           = 0000H      0D
     DATA AREA SIZE               = 0000H      0D
     MAXIMUM STACK SIZE           = 000CH     12D

iC-86  COMPILER   JUNK                              05/27/89 11:23:32  PAGE   3
                  ASSEMBLY LISTING OF OBJECT CODE

iC-86 COMPILATION COMPLETE.      0 WARNINGS,     0 ERRORS
-- 
You may redistribute this article only to those who may freely do likewise.
--
Tom Neff				UUCP:     ...!uunet!bfmny0!tneff
    "Truisms aren't everything."	Internet: tneff@bfmny0.UU.NET

wht@tridom.uucp (Warren Tucker) (06/08/89)

Having programmed PL/M-86 and -186 for three years and C for
five (not concurrently), I give the vote to C with two minor
caveats.

Pro C:

1.  PL/M-x86 is implemented for Intel 80x86 CPUs.  If you
can imagine you'll EVER want to move the design to another
architecture, C is an infinitely better language to put your
long hours of design, hacking, debugging and strenuous
testing into (medical systems get tested 'strenuously', yes
:-) ?  ).

2.  The tools for C language development abound.  The same
for PL-M are fewer and harder to find.  The same holds for
the availability of subroutine libraries and programs to
extract code and ideas from.

3.  C is a lot less wordy.  Having refused to use a macro
for the PL/M DECLARE keyword, my fingers are a great deal
more tired after finishing a module in PL/M.  EVERY function
or procedure used in a module not resident in it must be
DECLAREd EXTERNALly.  This is probably a weaker argument,
but it counts.

4.  Support.  I may be wrong, but I think there is a great
deal more C programmers out there than PL/M.

Pro PL/M:

5.  PL/M is inherently a strongly typed language.  It is
very hard to screw up the number and type of parameters
supplied to a function, etc.  This might be a good thing for
what you are trying to accomplish.  Passing a variable
number of arguments to some function like printf is nice,
but having the facility all over the language is why
function prototyping and lint were invented.

6.  I haven't seen any C compiler produce such good code for
the Intel CPU.  PL/M built-in functions use REP, LOOPxx,
MOVSB, etc.  in inline code, while C is always calling
external routines for such things (like _blmv for structure
assignment).  Such things are more important for ROM-based
MPU-controlled instruments than for, say, a disk-based
multi-MIP MRI system.

I hope this diatribe has been of some use.
-- 
-------------------------------------------------------------------
Warren Tucker, Tridom Corporation       ...!gatech!emory!tridom!wht 
Sforzando (It., sfohr-tsahn'-doh).  A direction to perform the tone
or chord with special stress, or marked and sudden emphasis.

peter@ficc.uu.net (Peter da Silva) (06/08/89)

So you're saying that iC86 produces worse code than Ritchie's vintage 1974
or so C compiler...
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.

Business: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180.
Personal: ...!texbell!sugar!peter, peter@sugar.hackercorp.com.

karl@ficc.uu.net (karl lehenbauer) (06/08/89)

In article <126@tridom.uucp>, wht@tridom.uucp (Warren Tucker) writes:
> 5.  PL/M is inherently a strongly typed language.  It is
> very hard to screw up the number and type of parameters
> supplied to a function, etc.  This might be a good thing for
> what you are trying to accomplish.  Passing a variable
> number of arguments to some function like printf is nice,
> but having the facility all over the language is why
> function prototyping and lint were invented.

True enough, but as I was just commenting over in comp.realtime where we've
been discussing this as well, unlike in C, PL/M pointers don't have data
types beyond being pointers.  (now quoting from <4423@ficc.uu.net>)

Thus, a lot of improper use of pointers which would cause warnings (at
least) from most C compilers are passed without comment by PL/M and one gets
to find the problem by debugging.  We do a lot of stuff that has to be
FORTRAN-callable, and *all* the parameters for a PL/M routine that's FORTRAN-
callable have to be pointers, so a lot of the benefits of data type checking
by the compiler are lost to us.
-- 
-- uunet!ficc!karl	"Contemptuous lights flashed across the computer's
-- karl@ficc.uu.net	 console."  -- Hitchhiker's Guide

tneff@bfmny0.UUCP (Tom Neff) (06/08/89)

In article <126@tridom.uucp> wht@tridom.uucp (Warren Tucker) writes:
>2.  The tools for C language development abound.  The same
>for PL-M are fewer and harder to find.  The same holds for
>the availability of subroutine libraries and programs to
>extract code and ideas from.

Tool availability unfortunately depends on your choice of platform. If 
you have the (mis)fortune to be developing on an iRMX box, you don't 
have a lot of the goodies UNIX or even MS-DOS based folks do. Some you 
can port yourself, but it's not always trivial to take 200k of BSDish 
code and shoehorn it into ANSI C on an alien box with only sketchy 
equivalents for some of the standard UNIX facilities. 

>3.  C is a lot less wordy.  Having refused to use a macro
>for the PL/M DECLARE keyword, my fingers are a great deal
>more tired after finishing a module in PL/M.  EVERY function
>or procedure used in a module not resident in it must be
>DECLAREd EXTERNALly.  This is probably a weaker argument,
>but it counts.

I would like to play devil's advocate here and say that in my 
experience, the need to spell more things out HELPS program 
reliability and maintainability rather than hinders it. Writing your 
first big PL/M source module from scratch can be a bit daunting. 
Writing the next one can usually be accomplished by boilerplating the 
structure of the first one wholesale in the editor and replacing the 
guts with new stuff. That's what I do. On the other hand I have seen 
my share of write-only C and would not care to have to maintain it for 
a living. 

>4.  Support.  I may be wrong, but I think there is a great
>deal more C programmers out there than PL/M.

The problem with PL/M programmers is that they don't know each other
exist!  That's one of the things comp.realtime can help with, also
comp.sys.intel although everyone seems to use that group to beg for
stepping errata sheets. :-)

>5.  PL/M is inherently a strongly typed language.  It is
>very hard to screw up the number and type of parameters
>supplied to a function, etc.  

However see another poster's point that once you pass by ADDRESS,
PL/M gives up the ghost, and this can hurt when interfacing with,
say, FORTRAN.  What that poster didn't point out, though, is that
C is no better at interfacing with FORTRAN!  Things like LINT only
help for C-to-C interfaces.  LINK86 is happy to tell you about
"type mismatches" of course - and will do so at *book length* if
you link C and FORTRAN together with the TYPDEF records still in,
even if you did it *right*.

>6.  I haven't seen any C compiler produce such good code for
>the Intel CPU.  PL/M built-in functions use REP, LOOPxx,
>MOVSB, etc.  in inline code, while C is always calling
>external routines for such things (like _blmv for structure
>assignment).  

No - as of iCx86 3.0 and later (the ANSI versions, post-Mark Williams
atrocity) there is something called <inline.h> which tells the compiler
to generate inline code for MOVB type things.  There is a #pragma inline
(a total kludge) that gets tacked onto the special functions.  If
you leave out inline.h, it resolves them to the RTL I believe.
-- 
You may redistribute this article only to those who may freely do likewise.
--
Tom Neff				UUCP:     ...!uunet!bfmny0!tneff
    "Truisms aren't everything."	Internet: tneff@bfmny0.UU.NET

tneff@bfmny0.UUCP (Tom Neff) (06/08/89)

In article <4448@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes:
>So you're saying that iC86 produces worse code than Ritchie's vintage 1974
>or so C compiler...

Actually in response to a mailed suggestion I went back and tried my 
test program with varying numbers of cases. It turns out that if you 
have *6 or more* cases, iC-x86 generates a jump table; 5 or fewer, it 
does it elseif-style. This appears to be more of a space optimization 
than anything else; my experience with critical realtime code is that 
speed optimizations are more important for things like interrupt 
handlers. It would be nice if Intel allowed some programmer control 
over this behavior for those who *really care* (myself among them). 
-- 
You may redistribute this article only to those who may freely do likewise.
--
Tom Neff				UUCP:     ...!uunet!bfmny0!tneff
    "Truisms aren't everything."	Internet: tneff@bfmny0.UU.NET

m5@lynx.uucp (Mike McNally) (06/08/89)

In article <126@tridom.uucp> wht@tridom.uucp (Warren Tucker) writes:
>Having programmed PL/M-86 and -186 for three years ...

You see a lot of people like this lined up at Lourdes...

>6.  I haven't seen any C compiler produce such good code for
>the Intel CPU.  

I think that this speaks ill of the Intel C compiler, rather than
well of PL/M.  Last time I looked at PL/M-86 (admittedly a long long
time ago), the code it generated was OK but not spectacular.  I
feel also that a programmer is much more constrained in terms of
source-level optimizations (particularly in the area of manipulation
of data structures involving pointers).

The claim made that PL/M switch statements are always branch table
based is true, but of course that's easy when the case labels are
*always* 0 through N.  And of course, if the selector is out of range,
the results are undefined (no such thing as default:).  Structures
within structures?  Sorry.  Two dimensional arrays?  Sorry.  Macros
with arguments? Sorry.

My guess is that GCC, well-tuned, would beat the pants off of PL/M.
Of course, GCC doesn't generate object files compatible with LINK86.

I don't want this to sound like an attack on PL/M; it's been used a
lot and it's not all bad.  The original request was for opinions,
after all.

-- 
Mike McNally                                    Lynx Real-Time Systems
uucp: {voder,athsys}!lynx!m5                    phone: 408 370 2233

            Where equal mind and contest equal, go.

maa@nbires.nbi.com (Mark Armbrust) (06/08/89)

In article <126@tridom.uucp> wht@tridom.uucp (Warren Tucker) writes:
>
>5.  PL/M is inherently a strongly typed language.  It is
>very hard to screw up the number and type of parameters
>supplied to a function, etc.  This might be a good thing for
>what you are trying to accomplish.  Passing a variable
>number of arguments to some function like printf is nice,
>but having the facility all over the language is why
>function prototyping and lint were invented.

It's been a while since I've used PL/M, so I may not have the syntax
quite right, but you can implement variable arguments by passing pointers:

call Printf ("format string", @(arg1, arg2, etc));

The biggest thing I missed in PL/M is the pointer op "->".  This op is available
in PL/1 and I always wondered why it was dropped from PL/M.  I wish I had a dime
for every time I wrote:

temp = dir$ptr;
dir$ptr = some$other$ptr;
(do something to dir)
dir$ptr = temp;


-- 

Mark Armbrust
maa@nbires.nbi.com
maa@nbires.UUCP

guy@auspex.auspex.com (Guy Harris) (06/09/89)

>Not wanting to make rash statements, I retested it.  I'm right.

SunOS's PCC-based compiler for the 68K does the same for the "case"
statement you gave; however, if I added one more arm to the "switch",
with a "case" value of 3, it generated an indexed jump.

In the worst case, for the if/then/elseif/then...  version (i.e., the
value is the last value against which you compare - assuming the value
is within range) of the 3-arm "switch", the indexed jump is better on a
68K.  In the best case (i.e., the value is the first value against which
you compare), the indexed jump is *worse* on a 68K (you do one
comparison and one successful conditional jump).  If the value is the
one in the middle, the indexed jump is *still* worse.

The numbers may be different for an 80*86, but there may still be a
turn-over point; below some number of comparisions, the
if/then/elseif/then... chain may be better, and above that number an
indexed jump may be better.  If so, then if the number of cases in the
"switch" statement is small enough, assuming a reasonably even
distribution of the values "switch"ed on generating an
if/then/elseif/then... chain may be the right thing.

However, if you're switching amongst, say, 64 different values, it's
probably the wrong thing.  Given that, I hope Intel's C compiler doesn't
*always* generate an if/then/elseif/then... chain for a "switch"
statement; if the switch statement is dense and has sufficiently many
arms, I would hope it would generate an indexed jump (I find it
difficult to imagine that an indexed jump would *never* be better).

Try a few more arms; if you make it up to, say, 32 arms, and it *still*
generates an if/then/elseif/then... chain, the compiler is probably
being dumb (but check the cycle counts anyway, just for fun...).  If
there is an inflection point, check the cycle counts, etc. to see
whether an if/then/elseif/then... chain makes sense for a number of
arms below the inflection point.  If so, perhaps the PL/M compiler is
the wrong one....

tneff@bfmny0.UUCP (Tom Neff) (06/09/89)

[Talking about jump tables versus elseif's for dense 'case' constructs.]

In article <1782@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
>Try a few more arms; if you make it up to, say, 32 arms, and it *still*
>generates an if/then/elseif/then... chain, the compiler is probably
>being dumb (but check the cycle counts anyway, just for fun...).  If
>there is an inflection point, check the cycle counts, etc. to see
>whether an if/then/elseif/then... chain makes sense for a number of
>arms below the inflection point.  If so, perhaps the PL/M compiler is
>the wrong one....

[Side note: in a separate posting I confirm that iC-x86 does change
to a jump table at the "inflection point" of 6 or more cases.  I had
tried 3 and 5 before retesting and just plain missed it.]

I would make the claim that in critical realtime code it's more
important to have *consistent* code generation for things like 4- and
5- and 6- case state machines than it is to try and shave the extra
cycle by using a completely different algorithm below (undocumented) N
number of cases. That's the kind of thing that riseth up to smite
the harried sysprog at JUST the wrong time, like when the boss screaming
at you from an overseas conference room where Something Went Wrong. :-)

The thing is, if you want an elseif construct you can always CODE ONE
YOURSELF in any language.  The whole point of DO CASE (PL/M) or switch
(C) for dense case lists should be to use something different, i.e.
a jump table.   PL/M does this perforce because its DO CASE is like
'switch' without 'case' - each simple or compound statement within
the DO CASE scope is assumed to handle the next integer value of the
control expression starting at 0 -- thus:

	DO CASE state;

		;		/*  case 0 - shouldn't happen  */


		DO;		/*  case 1 - do something  */
			masterflags.busy = TRUE;
			val = get$something(x, y, @status);
			CALL OUTWORD(that$port, val);
			state = 2;
			END;

		DO;		/*  case 2 - something else  */
			val = INWORD(this$port);
			CALL put$something(val, 0122H, @status);
			state = 3;
			END;

		DO;
			masterflags.busy = FALSE;
			state = 1;
			END;

		END;		/*  end case  */

Thus in PL/M you have the responsibility for coding your own RANGE
CHECKS in an example like the above.  Let state = 5 and the above
code branches off into never-never land.  Unfortunately as you maintain
a project and add new states to a machine there is no good automated
way to compute MAX_STATE for range check purposes.  I tend to put
padding cases at the end of my DO CASE structures just in "case":

		;;;;;;;;;;;	/*  insurance  */
-- 
You may redistribute this article only to those who may freely do likewise.
--
Tom Neff				UUCP:     ...!uunet!bfmny0!tneff
    "Truisms aren't everything."	Internet: tneff@bfmny0.UU.NET

tneff@bfmny0.UUCP (Tom Neff) (06/09/89)

In article <4454@ficc.uu.net> karl@ficc.uu.net (karl lehenbauer) writes:
>                              ...  We do a lot of stuff that has to be
>FORTRAN-callable, and *all* the parameters for a PL/M routine that's FORTRAN-
>callable have to be pointers, so a lot of the benefits of data type checking
>by the compiler are lost to us.

This is not actually true.  Intel FORT86 supports the %VAL construct as
defined in F77, so you can say

	SUBROUTINE MYSUB(%VAL(X),%VAL(I),%VAL(J))

	REAL*8 X
	INTEGER I,J

	...

	END

	...

	SUBROUTINE MAIN(RCODE)

	...

	CALL MYSUB(%VAL(OURX),%VAL(2),%VAL(35))

	...

and the stuff will get passed by value. If MYSUB or MAIN were in PL/M 
it would handle its end of the interface with pass-by-value. 

The one annoyance is LINK86's overzealous TYPDEF frenzy. If you pass 
%VAL(INTEGER*2) you had better declare your PL/M routine as expecting 
INTEGER not WORD or you'll get TYPE MISMATCH. For 32 bit quantities 
there's no escape, except by compiling one or both modules with 
$NOTYPE. 

As I point out in comp.realtime (hmm, sometimes crossposting IS a good 
idea <grin>), *nobody* does a good job of parameter type checking with 
Intel FORTRAN because it's all done at link time. Only FORTRAN-FORTRAN 
and C-C and PL/M-PL/M are guaranteed to give meaningful results on the 
TYPDEF level. (I think PASCAL-FORTRAN did OK the one time I checked. 
Lotta projects with THAT combo I'll bet! <grin>) So for interfacing 
with FORTRAN, C is no better a choice than PL/M -- UNLESS you work out 
an automated way of generating <fortstuff.h> extern declarations from 
parsing the FORTRAN source code, which seems like a good tool but a 
lot of work. 
-- 
You may redistribute this article only to those who may freely do likewise.
--
Tom Neff				UUCP:     ...!uunet!bfmny0!tneff
    "Truisms aren't everything."	Internet: tneff@bfmny0.UU.NET

tneff@bfmny0.UUCP (Tom Neff) (06/09/89)

In article <409@nbires.nbi.com> maa@nbires.UUCP (Mark Armbrust) writes:
>It's been a while since I've used PL/M, so I may not have the syntax
>quite right, but you can implement variable arguments by passing pointers:
>
>call Printf ("format string", @(arg1, arg2, etc));

Uh, no.  Strings are passed by @address, and the only legal way to
put things in a @(x, y, z...) container is if x and y and z are simple
8-bit constants.

If you want to make an array or structure of pointers to various
things you can do that, e.g.,

	DECLARE
		plist	(100) POINTER;

		...

	plist(0) = @word1;
	plist(1) = @realval;
	plist(2) = @string1;
	plist(3) = NIL;

	CALL printf(@('W1 = %d, R1 = %f, S1 = %s', 0DH, 0AH, 0), @plist);

you could do it, provided someone wrote a printf-style interpreter that
could deal with it (you don't get one with PL/M).

Someone put together a PL/M interface with the iC-86 1.0 (== awful Mark
Williams C) RTL but it didn't strike me as very usable when I tried
it out.
-- 
You may redistribute this article only to those who may freely do likewise.
--
Tom Neff				UUCP:     ...!uunet!bfmny0!tneff
    "Truisms aren't everything."	Internet: tneff@bfmny0.UU.NET

tneff@bfmny0.UUCP (Tom Neff) (06/09/89)

In article <5711@lynx.UUCP> m5@lynx.UUCP (Mike McNally) writes:
>The claim made that PL/M switch statements are always branch table
>based is true, but of course that's easy when the case labels are
>*always* 0 through N.  And of course, if the selector is out of range,
>the results are undefined (no such thing as default:).  Structures
>within structures?  Sorry.  Two dimensional arrays?  Sorry.  Macros
>with arguments? Sorry.

Truth Squad Dept.: 

 * The original 'switch' claim had to do with iC, not PL/M, and the
   claim was that elseif was used when a jump table should have been.
   PL/M certainly uses jump tables on DO CASE -- this wasn't a claim,
   it's just a fact.

 * PL/M doesn't range check DO CASE for you automatically, it's your
   responsibility.  This demands programmer responsibility but allows
   for extra speed in the case where the o/r *truly* "can't happen".

 * Structures within structures - available since PL/M 2.7 (1987).

 * 2-d arrays aren't available in the sense of A(i,j) but you have
   always been able to do this elegantly with structure arrays:

	DECLARE
		table (20) STRUCTURE(
			col	(50) WORD);

	table(i).col(j) = x+y*5;

   Now with nested structures (for the last two years) you can generalize
   this to N-dimensional arrays if the mood strikes.

 * Macros with arguments are sadly missing from PL/M (all you get is the
   simple-replacement LITERALLY facility), however one is at liberty to 
   preprocess PL/M source with either MPL86 or (now) the iC-x86 
   preprocessor before compiling.  In fact I suspect the latter will
   become popular - you can have all the #defines you want and still
   get valid PL/M to compile.  I use MPL86 based "macro PL/M" for a
   number of projects - it's the best way to automatically generate
   count precedent string constants, for instance.

-- 
You may redistribute this article only to those who may freely do likewise.
--
Tom Neff				UUCP:     ...!uunet!bfmny0!tneff
    "Truisms aren't everything."	Internet: tneff@bfmny0.UU.NET

peter@ficc.uu.net (Peter da Silva) (06/09/89)

One more point to make before I leave this particular subject. The PL/M
do-case construct is a ripe source of bugs:

In article <14391@bfmny0.UUCP>, tneff@bfmny0.UUCP (Tom Neff) writes:
[explanation of do-case]
> I tend to put padding cases at the end of my DO CASE structures just in
> "case":

> 		;;;;;;;;;;;	/*  insurance  */

I rest my case.
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.

Business: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180.
Personal: ...!texbell!sugar!peter, peter@sugar.hackercorp.com.

vjs@calcite.UUCP (Vernon Schryver) (06/11/89)

In article <5711@lynx.UUCP>, m5@lynx.uucp (Mike McNally) writes:
> 
> ...  Last time I looked at PL/M-86 (admittedly a long long
> time ago), the code it generated was OK but not spectacular. ...

I first saw PL/M-86 as part of MDS-311, in 1978.  By 1981, it was ok and
reasonably bug free.  The current PL/M-386, version 1.3, generates what
looks like the same stuff, modified for the 386.  Today, it is not
comparable to the output of a good C compiler (not pcc).  I try to limit
looks at the result of '$CODE', because it always makes me want to chuck it
and write in ASMx86, regardless of whether it is worthwhile.  Current C
compilers rarely do that to me.

If I had to use BND386 and BLD386, perhaps to use ICE386, I would stick
with PL/M.  From the manuals, C386 is a new idea at INTEL.  There are lots
of caveats about C calling sequences, externals vs. common, and so on.
Some day, one can expect C386 to be preferred to PL/M.  However, unless you
work for INTEL, other frontiers look more profitable to pioneer.  Recalling
my stack of PTR's on early PL/M-86, I took my own advice, and so do not
have first hand experience with C386.

No one has mentioned INTEL's new policy on their binary format.  They treat
their relocatable binary format as a top trade secret.  In the old 8086
days, it was proprietary, but with enough non-disclosures, they they would
tell you about it.  This year, I've been told by F.E.'s that even the
absolute format is secret, though some of it is documented in the 1.3
manuals.  The official policy handed out from the Oregon hotline seems to
be that one does not need any PROM programmer, system debugger, or
operating system that is not suppied by INTEL.  If you do not like that,
there is the new HEX386.  I guess RMX386 is finally good enough for all
possible purposes, and we should look for it to replace AIX as the be
all and end all.


Vernon Schryver
vjs@calcite.uucp

m5@lynx.uucp (Mike McNally) (06/13/89)

In article <14395@bfmny0.UUCP> tneff@bfmny0.UUCP (Tom Neff) writes:
>In article <5711@lynx.UUCP> m5@lynx.UUCP (Mike McNally) writes:
>> [ things about PL/M, not entirely positive ]
>
>Truth Squad Dept.: 
>
> [ corrections to some of my claims ]

Point taken.  I did say that it's been a while since I used PL/M; I know
that 2.7 sounds like a kinda big version number.

I think a lot of the bad emotional feelings I have about PL/M stem from
having to use it on an old System III ("Blue Box") development system
with 8" diskette drives.  I  t    w  a  s    v  e  r  y    s  l  o  w .
I can't say I ever got too fond of ISIS either.  If I had PL/M available
under UNIX or something, I'd probably not mind using it at all.

-- 
Mike McNally                                    Lynx Real-Time Systems
uucp: {voder,athsys}!lynx!m5                    phone: 408 370 2233

            Where equal mind and contest equal, go.

tneff@bfmny0.UUCP (Tom Neff) (06/14/89)

In article <5732@lynx.UUCP> m5@lynx.UUCP (Mike McNally) writes:
>I think a lot of the bad emotional feelings I have about PL/M stem from
>having to use it on an old System III ("Blue Box") development system
>with 8" diskette drives.  I  t    w  a  s    v  e  r  y    s  l  o  w .
>I can't say I ever got too fond of ISIS either.  If I had PL/M available
>under UNIX or something, I'd probably not mind using it at all.

ISIS was certainly disgusting.  The MDS was a ridiculous tower of
iron, but it's brought a lot of embedded controllers to life!

PL/M is currently platformed to iRMX I and II, VAX/VMS, MS-DOS and 
(via the iRMK386 Developers' Kit) to UNIX/386. The latter port 
requires a kernel hack which was developed for Bell Tech's port and 
may or may not work with other AT&T derived 386 UNIXen. (Certainly not 
with Xenix or AIX or whatever.) 
-- 
You may redistribute this article only to those who may freely do likewise.
--
Tom Neff				UUCP:     ...!uunet!bfmny0!tneff
    "Truisms aren't everything."	Internet: tneff@bfmny0.UU.NET

peter@ficc.uu.net (Peter da Silva) (06/14/89)

In article <14395@bfmny0.UUCP>, tneff@bfmny0.UUCP (Tom Neff) writes:
>  * PL/M doesn't range check DO CASE for you automatically, it's your
>    responsibility.  This demands programmer responsibility but allows
>    for extra speed in the case where the o/r *truly* "can't happen".

Come one, Tom. You're usually right on, but it's not fair to tout the
PL/M case statement as a benefit over C's switch statement after you've
already admitted that PL/M's case statement is badly flawed.
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.

Business: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180.
Personal: ...!texbell!sugar!peter, peter@sugar.hackercorp.com.

alfred@guardian.UUCP (alfred) (06/15/89)

> It's been a while since I've used PL/M, so I may not have the syntax
> quite right, but you can implement variable arguments by passing pointers:
> 
> call Printf ("format string", @(arg1, arg2, etc));

For your information, you no longer need to do that with PL/M-n86 V3.3,
under this version, you can call any C-interfaced function with variable
parameters (similar to ANSI-C function prototype with ellipses):

    $interface (C=printf)
    foo: do;
            printf: procedure (p) external;
                    declare p pointer;
            end;
 
            proc: procedure (a,b) public;
                  declare (a,b) word;
                  call printf(@('A = %d, B = %d'), a, b); /* LEGAL NOW */
            end;
    end;

 In this case, type checking will be carried out for the first
 argument only, extra arguments in the actual call will not be checked.
 The extreme case will be declaring a C external function with no
 argument, then you can call that function with any number of parameter.

alfred@guardian.UUCP (alfred) (06/15/89)

> >I sincerely hope Intel's C compiler does not do so for *all* "switch"
> >statements; any competent C compiler can and will turn a "dense"
> >"switch" statement - i.e., one where the cases form a reasonably tight
> >range - into an indexded jump.  (The definition of "dense" varies from
> >compiler to compiler.)

Intel iC-n86 compilers are capable of generating three different kind of
code for the "switch" statement. Normally, we generate simple if-then-else
code for a dense switch statement with less than six cases, above that
an indexed jump will be generated. Moreover, we generate binary search code
for those sparsely dense switch statements. And in conformance to ANSI,
32-bit "long" data type can be used as case values.

mac@uvacs.cs.Virginia.EDU (Alex Colvin) (06/20/89)

I had limited experience with PLM on iRMX86.  I've used iNtel's old iC86 on
iRMX and Turbo C on DOS.

PLM does a nice job in the code generation department.  A construct such as:
	call Fred(); do while (UnDone); call Fred(); end;
has the branches rearranged into
	RepeatL: call Fred(); if (UnDone) goto RepeatL;
thereby overcoming some if the inexpressiveness of PLM.

iC86, by contrast, has a naive code generator.  This isn't normally a
problem, unless you're really tight on microseconds.  Since it's not an
ANSI C compiler, it doesn't give you a lot of control over parameter
passing (you keep generating code to widen char to int), signed-ness, etc.
The libraries are pretty antiquated.

I was trying to maintain compatible sources for a network controller across
the two systems.  The differrence between PC-bus and Multibus was handled
by linking in different libraries.  The big problems were incompatibilities
in C language & (non-)standard libraries.

PLM isn't compatible with anything else, so this won't bother you.  It's
related to PL/I only in syntax.  Their implementation of BASED storage ties
data structures to a particular pointer, making it hard to write reentrant
code.  You wind up having to re-declare your structures all over the place,
making the code hard to maintain.  Macros are a clumsy way to get around
this.  At least C permits you to re-use a STRUCT declaration.

PLM does give you access to selectors (tokens) and other peculiarities of
the architecture and OS, where C will require lots of casting.  PLM has
argument type checking, where iC86 doesn't.  PL/M pointers are totally
untyped, where iC86 will cast.

If the rumor of an ANSI-type C compiler is true, I'd recommend that.  The
(optional) stronger typing is likely to save you a lot of headaches.  You
stand a greater chance of stealing code from other applications.

nusip@maccs.McMaster.CA (Mike Borza) (06/21/89)

In article <231@guardian.UUCP> alfred@.UUCP (Alfred Huang) writes:
>Intel iC-n86 compilers are capable of generating three different kind of
>code for the "switch" statement. Normally, we generate simple if-then-else

Great.  Was this compiler written by the same bozos who wrote the MS-DOS
C cross-compiler for the '196?  That one gracefully generates syntax
errors for `unsigned int foo();' within  function bodies.  It fares
better for `unsigned int bar;' within a function; it saves its errors
for incorrect code generation, conveniently throwing away the desired
result of an assignment and saving garbage instead.  Beauty, eh?

mike borza, Antel Optronics Inc.
-- 
Michael Borza                       work: <antel!mike@maccs.McMaster.CA>
Antel Optronics Inc.                home: <boopsy!mike@maccs.McMaster.CA>
(416)335-5507                       occasionally: <nusip@maccs.McMaster.CA>
3325B Mainway, Burlington, Ont., Canada  L7M 1A6

tneff@bfmny0.UUCP (Tom Neff) (06/28/89)

In article <190@uvacs.cs.Virginia.EDU> mac@uvacs.cs.Virginia.EDU (Alex Colvin) writes:
>PLM does a nice job in the code generation department...

>iC86, by contrast, has a naive code generator.  This isn't normally a
>problem, unless you're really tight on microseconds.  Since it's not an
>ANSI C compiler, it doesn't give you a lot of control over parameter
>passing (you keep generating code to widen char to int), signed-ness, etc.
>The libraries are pretty antiquated.

Once again, since a lot of people seem to be unaware of this: The iC86
Alex is talking about is the bad, old Mark Williams-derived unusable
atrocity.  (Versions 3.1 and earlier.)  The shiny, new iC86 4.0, out for
well over a year now, is no relation!

Intel wrote a dpANS compatible front end and hitched it to the good PL/M
code generator Alex alludes to above.  The libraries are fine, and available
in all memory models.  You even have builtins available (optionally) for
hardware specific things like OUT and MOVB, so you can use iC86 to write
high performance embedded stuff.  The code is ROMable and interfaces well
with the other Intel languages - PL/M, ASMx86 of course, Pascal, Fortran.

Remember, old C bad, new C good.
-- 
You may not redistribute this article for profit without written permission.
--
Tom Neff				UUCP:     ...!uunet!bfmny0!tneff
    "Truisms aren't everything."	Internet: tneff@bfmny0.UU.NET

clay@uci.UUCP (News Administrator) (07/06/89)

I have actually used the latest (as of Fall, '88) version of Intel C with
a specific eye towards reasonable code and PL/M compatibility.  I was pleased
to see both.  This is version 3.x (maybe 4.x, I can't remember).  This was NOT
the original hack job that was too awful for words.

With regards to PL/M compatibility, there is no way to say "SELECTOR" in C such
that LINK86 will not complain of type mismatches with PL/M modules, but the
code works fine.

Generated code looks very much like that of PL/M, the default parameter-passing
mechanism is "fixed param" which is like MSC "pascal".  Interrupt procedures
work just like PL/M.  Now don't send flames to me if you don't like PL/M's code
I'm just saying it looks similar if not identical!

The debugger that I Beta -tested looks like Intel licenced it from the same
place Microsquishy got Codeview.  Seemed to work OK for simple things --
I didn't have a lot of time to work with it, as I was doing an embedded
system, not DOS.

This work was done when employed by Intercim Corp. in Burnsville, MN.  I will
forward email to them, as they are not quit on the net yet.

-- 
Clayton Haapala                ...!mmm!dicome!uci!clay
Unified Communications Inc.    "Declare your Point of Entry, Space Wanderer!"
3001 Metro Drive - Suite 500   "Canarsi -- where everyone looks the same."
Bloomington, MN  55425          	-- zappa

tneff@bfmny0.UUCP (Tom Neff) (07/07/89)

In article <95@uci.UUCP> clay@uci.UUCP (News Administrator) writes:
>I have actually used the latest (as of Fall, '88) version of Intel C with
>a specific eye towards reasonable code and PL/M compatibility.  I was pleased
>to see both.  This is version 3.x (maybe 4.x, I can't remember).  This was NOT
>the original hack job that was too awful for words.

Definitely 4.0 or 4.1.  Anything from Intel marked 3.1 or earlier is the
Mark Williams C hack job, which was an atrocity.

(Parenthetically (he says, in parentheses (hehe)), I want readers of this
newsgroup to know that although I know a lot about Intel products and will
defend them against ignorant slanders, I also give Intel unmerciful heat
about their shortcomings.  Ask any Intel FAE who's had to deal with "Neff"
at one time or another. [:-)] )

>With regards to PL/M compatibility, there is no way to say "SELECTOR" in C such
>that LINK86 will not complain of type mismatches with PL/M modules, but the
>code works fine.

This is a case where you have to either compile with NOTYPE or eat the type
mismatch with a smile.  I might add that LINK86 v2.7 hemorrhages TMM's compared
to 2.5.  If you have ever delved into the Byzantine workings of OMF-86 and
tried to figure out TYPDEF records you will see that there about 850 ways to
interpret any pair of candidate PUBDEF/EXTDEF symbols in terms of how they
express their applicable types.  2.7 seems stricter, and in the cross language
world that's a disaster.

I make it a habit to strip TYPDEF's before linking cross language jobs.
It just ain't worth it - the few legitimate mismatches you might catch are
inundated in the bogus ones.

>Generated code looks very much like that of PL/M, the default parameter-passing
>mechanism is "fixed param" which is like MSC "pascal".  

I have my qualms about this decision on Intel's part.  They support two
parameter passing conventions in iC-x86: "varparams," corresponding to
the traditional C implementation on 86 architectures, wherein the caller
pushes all arguments onto the stack in reverse order, then a count, then
calls the subprogram, then cleans up the stack itself; and "fixparams,"
corresponding to the way PL/M-86, FORTRAN-86 and Pascal-86 work, wherein
the caller pushes all arguments on the stack in forward order, then
calls the subprogram which is expected to agree on the number and type
of arguments and perform its own stack cleanup on return with the RET
<NN> 86 opcode.  The latter method is faster for the majority of cases
where in fact the caller and callee expect a fixed list of parameters.
The former method is slower to deal with but permits variable parameter
lists a la "printf".  Intel iC-x86 allows functions to be
declared/defined with one or another parameter passing convention via a
#pragma (functions of either convention may be mixed within a program);
so far so good.  What I don't like is that Intel has lately selected
"fixedparams" as the DEFAULT convention unless you specifically override.
That makes me a bit queasy.  I doubt Doug Gwyn reads this newsgroup but
if he does I bet it would make him a bit queasy too.
-- 
"My God, Thiokol, when do you     \\	Tom Neff
want me to launch -- next April?"  \\	uunet!bfmny0!tneff

peter@guardian.UUCP (peter) (07/11/89)

In article <95@uci.UUCP> clay@uci.UUCP (News Administrator) writes:
>
>I have actually used the latest (as of Fall, '88) version of Intel C with
>a specific eye towards reasonable code and PL/M compatibility.  I was pleased
>to see both.  This is version 3.x (maybe 4.x, I can't remember).  This was NOT
>the original hack job that was too awful for words.

That was iC-86 V4.0.

>With regards to PL/M compatibility, there is no way to say "SELECTOR" in C such
>that LINK86 will not complain of type mismatches with PL/M modules, but the

iC-86 V4.1 includes the "selector" data type, which is compatibile with
the PL/M selector.  iC-86 V4.1 is available on DOS now, on iRMX(tm) I
(aka iRMX-86) in 3-4 weeks.  iC-286 V4.1 is also available on DOS now,
on iRMX II (aka iRMX 286) in 3-4 weeks.  People who suffered through the
V3.x compilers on iRMX can upgrade to V4.1 free of charge!

>code works fine.
>

On this matter, I _DO_ speak for Intel!
-------------------------------------------------------------------------------
Peter Plamondon, Intel Corp, 5200 NE Elam Young Pkwy, Hillsboro, OR  97124-6497
Internet: peter@langlab1.hf.intel.com                           +1 503-696-5219
UUNET   : uunet!intelhf!langlab1!peter     "I speak for myself, as best I can."
UUCP    : tektronix!psu-cs!foobar!langlab1.hf.intel.com!peter    
-------------------------------------------------------------------------------

vjs@calcite.UUCP (Vernon Schryver) (07/12/89)

There has been some discussion about PLM386 vs. C in this newsgroup.  Some
have said that PLM386 does a reasonable job, and so does C.  Tonight, I've
had occassion to look at the output of PLM386 with optimize(3).  It is
ugly.  If iC386 is as bad, then one would be wise to talk to Motorola, AMD,
MIPS, or the SPARK clones.  It appears that in the 8 years since I stared
closely at large gobs of PL/M86 output, INTEL has not improved the
"optimizer".  They still do not know the relevance of "peep hole." Because
I wish to share my disgust, included below is a small function, and
PLM386's best effort.  The function is not coded wonderfully, but that is
no excuse for the slop the compiler emits.

This is a small chunk of a disgustingly large file, from the days when it
had to be compiled "large" and multiple files meant extra segment register
usage and lots more code, and it had to fit in 2716's

The lower case text in the code output are my comments.

              $code
1192   1          hex$arith: procedure reentrant;
1193   2            declare ptr     pointer;
1194   2            call read$char;
1195   2            call get$addr(@ptr,selector(0));	/* takes ptr&selector */
1196   2            call put$string(@(' = ',0));	/* takes a ptr */
1197   2            call put$ptr(ptr,0);		/* takes ptr */
1198   2            if selector(ptr)<>data_seg		/* a selector */
1199   2              then do;				/* '&&' would be nice */
1200   3                if not ck$ptr(ptr,1)		/* takes ptr & word */
1201   3                  then do;
1202   4                    call put$word(flatten(ptr),0);
1203   4                    call put$string(@(' base=',0));
1204   4                  call put$word(flatten(build$ptr(selector(ptr),0)),0);
1205   4                    call put$string(@(' limit=',0));
1206   4                    call put$word(get$segment$limit(selector(ptr)),0);
1207   4                    end;
1208   3                end;
1209   2            end hex$arith;
              $nocode

					 ; STATEMENT # 1192
		 HEXARITH      PROC NEAR
000028DC  55              PUSH    EBP
000028DD  8BEC            MOV     EBP,ESP
000028DF  51              PUSH    ECX		? dense but slow since it
000028E0  51              PUSH    ECX		? makes extra memory traffic
						? why not ENTER 8 ?
					 ; STATEMENT # 1194
000028E1  E8F6DFFFFF      CALL    READCHAR
					 ; STATEMENT # 1195
000028E6  8D45FA          LEA     EAX,[EBP].PTR
000028E9  16              PUSH    SS      ; 1
000028EA  50              PUSH    EAX     ; 2
000028EB  B800000000      MOV     EAX,0H	? what's wrong with XOR EAX,EAX
						? 2 bytes instead of 5
000028F0  50              PUSH    EAX     ; 3	? what's wrong with PUSH 0?
000028F1  E856E8FFFF      CALL    GETADDR
					 ; STATEMENT # 1196
000028F6  B8C5020000      MOV     EAX,OFFSET(@@LONG$CONSTANT$02C5H)
000028FB  0E              PUSH    CS      ; 1
000028FC  50              PUSH    EAX     ; 2
000028FD  0E              PUSH    CS		? what's wrong with long calls?
000028FE  E815E2FFFF      CALL    PUTSTRING 
					 ; STATEMENT # 1197
00002903  FF75FE          PUSH    [EBP].PTR+4H; 1
00002906  FF75FA          PUSH    [EBP].PTR; 2
00002909  6A00            PUSH    0H		!see, it knows about PUSH 0
0000290B  E8B4E1FFFF      CALL    PUTPTR
					 ; STATEMENT # 1198
00002910  668B55FE        MOV     DX,[EBP].PTR+4H
00002914  8B45FA          MOV     EAX,[EBP].PTR		?why? Completely dead!?
00002917  662E3B1500000000CMP     DX,CS:DATA_SEG	?why not CMP [EBP].PTR..
0000291F  7467            JZ      @284
					 ; STATEMENT # 1200
00002921  FF75FE          PUSH    [EBP].PTR+4H; 1
00002924  FF75FA          PUSH    [EBP].PTR; 2
00002927  6A01            PUSH    1H
00002929  E852E6FFFF      CALL    CKPTR
0000292E  A801            TEST    AL,1H
00002930  7556            JNZ     @284
					 ; STATEMENT # 1202
00002932  FF75FE          PUSH    [EBP].PTR+4H; 1
00002935  FF75FA          PUSH    [EBP].PTR; 2
00002938  0E              PUSH    CS			?why not a far call
00002939  E800000000      CALL    FLATTEN
0000293E  50              PUSH    EAX     ; 1
0000293F  6A00            PUSH    0H
00002941  E85EE1FFFF      CALL    PUTWORD
					 ; STATEMENT # 1203
00002946  B8C9020000      MOV     EAX,OFFSET(@@LONG$CONSTANT$02C9H)
0000294B  0E              PUSH    CS      ; 1
0000294C  50              PUSH    EAX     ; 2
0000294D  0E              PUSH    CS
0000294E  E8C5E1FFFF      CALL    PUTSTRING
					 ; STATEMENT # 1204
00002953  668B45FE        MOV     AX,[EBP].PTR+4H	?why not PUSH [EBP]....
00002957  B900000000      MOV     ECX,0H		?why not PUSH 0 ?
0000295C  50              PUSH    EAX     ; 1
0000295D  51              PUSH    ECX     ; 2
0000295E  0E              PUSH    CS
0000295F  E800000000      CALL    FLATTEN
00002964  50              PUSH    EAX     ; 1
00002965  6A00            PUSH    0H
00002967  E838E1FFFF      CALL    PUTWORD
					 ; STATEMENT # 1205
0000296C  B8D0020000      MOV     EAX,OFFSET(@@LONG$CONSTANT$02D0H)
00002971  0E              PUSH    CS      ; 1
00002972  50              PUSH    EAX     ; 2
00002973  0E              PUSH    CS
00002974  E89FE1FFFF      CALL    PUTSTRING
					 ; STATEMENT # 1206
00002979  668B45FE        MOV     AX,[EBP].PTR+4H
0000297D  0F03C0          LSL     EAX,EAX
00002980  50              PUSH    EAX     ; 1
00002981  6A00            PUSH    0H
00002983  E81CE1FFFF      CALL    PUTWORD
					 ; STATEMENT # 1208
					 ; STATEMENT # 1209
		 @284:
00002988  C9              LEAVE
00002989  C3              RET
		 HEXARITH      ENDP


Vernon Schryver
vjs@calcite.uucp