EmbeddedRelated.com
Forums

Why no Multitasker in C compilers?

Started by grahamgollings June 24, 2003
Hi Graham, another greybeard! I remember my first floppy drive. I got a
pack of 100k floppies thrown in. i wondered how I was ever going to fill
one of them, let alone 5! This of course was in the days when self
modifying code was the only way to go, and not considered a mortal sin,
(which of course it isn't unless you don't know how to use it) with
everything running from a couple of k of RAM, so every byte was
precious. I learnt mainframe programming on a dual mainframe with 8
banks of 128 keys. You literally programmed it in binary. Later on an
'engineering processor' was added that allowed us to use mnemonics. To
this day I still analyse instruction sets and from that try to work out
the internal structures.

I've just received some info regarding the LPC201x series. It looks very
interesting, but from what I can see the tools are way too expensive to
evaluate, and the limited time high priced tools strike me as a rip off.
I don't have an application for it, but if it was able to perform within
certain criteria then I may. As I've stated once before my evaluation of
processing power is related to actual current consumed to perform a
function. 

Al

grahamgollings wrote:
> 
> Hi Al,
> 
> I will keep you guessing agewise! (for now anyway) The 1st micro that
> I got involved with was a CPM machine, It had 32k ram a Z80 (don't
> remember clock speed), a telex machine to edit and punch tape to.
> Writing assembler in binary format. I later replaced the punch tape
> with a cassette tape recorder. Then the final step, I got 2x 8"
> Shutgard floppy drives and upgraded the ram to 128kb ( blistering
> speed) That got me into assembler coding at source level. I will
> never forget how fast the 1st files opened and saved on those 8"
> drives.
> 
> As I got more advanced I ported a Pascal and later helped write a C
> compiler to run under CPM to make embedded applications. No debuggers
> in sight. This must have been 35 years ago........
> 
> Which probably makes me an early pioneer of C in 8bit micro's !
> 
> > Just my old rheumy view of the world.
> Amen.
> 
> Next I got into the 8051 series and discovered Forth, I did
> experiment with C on the 8051 and even bought an expensive compiler
> but just didn't get on with it.
> 
> Presently I use ARM and the 430. Philips have a new chip 128kb Flash
> 60mh/z ARM - LPC2106 - nice.
> 
> Kindest regards
> 
> G.
> 
> 
> .
> 
> 
> 
> ">http://docs.yahoo.com/info/terms/

Beginning Microcontrollers with the MSP430

On Thursday 26 June 2003 13:23 pm, grahamgollings wrote:
[snip]

>
>  As I got more advanced I ported a Pascal and later helped write a C
>  compiler to run under CPM to make embedded applications. No debuggers
>  in sight. This must have been 35 years ago........
>
>  Which probably makes me an early pioneer of C in 8bit micro's !

35 years ago?  I don't think CP/M was around in 1968.  More like late 1975,

early 1976, based on the CPM 1.0 manuals I'm familiar with.  CP/M 2.2 was 
1978, IIRC.  http://sneezy.usu.edu/~ivie/cpm2

	--John


I was happy to continue to this thread until

--- In msp430@msp4..., Jonathan Kirwan <jkirwan@e...> wrote:
> Personally, I'd rather not see Quadravox up
> their price point (or delay lowering it) just because they
> bought themselves into a "feed this guy's children" contract
and
> have to force all of us to carry the load. 

I will not tolerate any derogatory remarks about my children or their
welfare.

Jonathan, consider my input on this thread closed.

--Andrew


	For those wondering about efficiency comparisons between Forth and C, 
basically, Forth has very little overhead, since there are no register saves 
for each function called.  C typically pushes the working registers, unless 
the compiler is in a position to be smart enough to know that a routine is 
static inside a module, and only called from inside (no pointers to the 
static function).  The optimizer, if it's a good one, can determine that 
certain registers are not affected, and avoid pushing them.  These pushings 
are known as prolog and epilog code, and it's what gives C routines their 
callability.

	A typical Forth implementation uses only 3 register pairs.  The system stack 
pointers, the user stack pointer, and the program counter.  Because of the 
nature of Forth, each and every routine doesn't need to save a working 
register set.  Being a stack oriented language, intermediate results are 
saved in the data stack (yes, there are exceptions, <R and >R, etc), all 
registers are implcitly saved between calls, avoiding the majority of prolog 
and epilog code that C requires.

	Forth also has a *very* high code density, assuming the programmer has a 
little common sense.  The first Forth program almost anyone writes takes a 
number on the stack, squares it, and prints the result out.  What's that
code 
look like?

	: SQR DUP * . ;

	: tells the Forth compiler to compile the word.  The word is to be called 
SQR.  The DUP takes the top number of the stack and makes a copy of it (if 
you type "4 SQR", after the DUP, the stack will have "4 4"
on it.  * is just 
like C, in that it's a multiplier.  It takes the top two numbers on the 
stack, multiplies them, and pushes the result back to the stack.  The last 
operation '.' (period, or 'dot') destructively prints the
top number on the 
stack (destructively means that the number is removed from the stack, not 
left in place).

	For a C compiler, gcc produced the following code.  I enforced the variables 
as volatile so that the register loads would be carried out.  We all know the 
compiler will optimize constants, but I didn't want that for this example.

   {
      char buffer [32];
      volatile int i, j, k;
      i = 4;
      j = 4096;
      k = i * j;
      sprintf (buffer, "%d", k);
      puts (buffer);
   }

  b2:   b1 40 01 01 22 00       mov     #257,   34(r1)  ;#0x0101
                j = 4096;
  b8:   b1 40 00 10 24 00       mov     #4096,  36(r1)  ;#0x1000
                k = i * j;
  be:   1f 41 22 00             mov     34(r1), r15     ;
  c2:   1e 41 24 00             mov     36(r1), r14     ;
  c6:   0a 4f                   mov     r15,    r10     ;
  c8:   0c 4e                   mov     r14,    r12     ;
  ca:   b0 12 00 00             call    #0              ;#0x0000
  ce:   81 4e 26 00             mov     r14,    38(r1)  ;
                sprintf (buffer, "%d", k);
  d2:   11 12 28 00             push    40(r1)          ;
  d6:   30 12 00 00             push    #0              ;#0x0000
  da:   03 12                   push    #0              ;subst r3 with As=
  dc:   0b 41                   mov     r1,     r11     ;
  de:   3b 52                   add     #8,     r11     ;subst r2 with As=
  e0:   0b 12                   push    r11             ;
  e2:   b0 12 00 00             call    #0              ;#0x0000
                puts (buffer);
  e6:   0f 4b                   mov     r11,    r15     ;
  e8:   b0 12 00 00             call    #0              ;#0x0000

	I didn't have printf handy, so I printed to a 32 byte buffer, and used
puts.  
We'll knock off 3 instructions for that, or 8 bytes (call takes 4 bytes). 
So 
we're at 14 instructions, and 48 bytes

	In Forth, this function could be implemented in approximately 19 bytes, with 
code looking something like this (actual implementation depends on what 
technique the Forth compiler uses for the headers on words, and whether
it's 
a direct threaded Forth, indirect threaded, or subroutine threaded.  We're 
going to assume a modified direct threaded, where words are presumed to be 
colon definitions unless otherwise instructed) :

		dw 0003
		db 'SQR'
		dw .LIT
		dw 257
		dw .LIT
		dw 4096
		dw .MUL
		dw .DOT
		dw .EXIT
	
	The first DW are some flag bytes (bits depend on the interpeter), and the 
length of the word name (3 bytes).  The next 3 bytes are the word name 
('SQR').  The .LIT says the next 16 bits are a literal word, and get
pushed 
on the stack.  So 257 and 4096 get pushed.  .MUL multiplies the two items on 
the stack, .DOT prints them out, and .EXIT returns from this word.

	If we call SQR in the definition of another word, our total cost is two 
bytes, for the address of the word.  In C, the call setup alone would be 6 to 
10 bytes.  And since one of the programmng goals of Forth is to reuse as many 
words as possible, you get code density that's pretty remarkable. 

	Basically, because of the nature of C, and the fact that almost all 
interpeted languages are denser than a native counterpart (excluding hand 
optimized assembly), you can pack a lot more Forth into the same space.  The 
runtime penalty is small in Forth.  3x is slower than straight assembly is 
possible, with 5x or 6x being more typical.  Compared to C, it *can* be 
fairly comparable, but it's difficult to implement two algorithms the same 
way, since C is not a stack based language and Forth is.  As such, you're 
alsmost always comparing apples to oranges, since algorithm has as much or 
more impact than implementation.  About the fairest comparison I can think of 
is examples involving recursion.

	This has been Forth 101.  Hope you enjoyed it.

	--John


At 03:39 AM 6/27/2003 +0000, aekalman wrote:
>I was happy to continue to this thread until
>
>--- In msp430@msp4..., Jonathan Kirwan <jkirwan@e...> wrote:
> > Personally, I'd rather not see Quadravox up
> > their price point (or delay lowering it) just because they
> > bought themselves into a "feed this guy's children"
contract and
> > have to force all of us to carry the load.
>
>I will not tolerate any derogatory remarks about my children or their
>welfare.
>
>Jonathan, consider my input on this thread closed.
>...

Hey man, I should take some blame for those phrases like that to begin 
with. Sorry about that.

Jonathan, business decision is obviously a complex process. Whether a 
vendor decides to work with another supporter or not depends on a great 
number of factors. In this example, if we were to get involved with Andrew, 
obviously it will be because we think it makes sense for us to do so. 
Whether Andrew uses his income to "feed his children" or to buy a nice

fancy sports car, or whatever, is immaterial. IOW, my decision will solely 
be based on its beneficial factors to me, and not to him. Obviously he will 
make his decision in similar vein. An agreement will only come about when 
both parties think it is beneficial to them in their own ways. Nothing more 
and nothing less.


// richard <http://www.imagecraft.com> 
<http://www.dragonsgate.net/mailman/listinfo> 


How does Forth go a processor like the MSP430 where the stack is
limited? I last used Forth on a 6502 embedded board a long time ago but
it had quite a lot of RAM on it...
--
This email is confidential and intended solely for the use of the individual to
whom it is addressed.  Any views or opinions presented are solely those of the
author and do not necessarily represent those of Nautronix Ltd.

If you are not the intended recipient, you have received this email in error and
use, dissemination, forwarding, printing, or copying of this email is strictly
prohibited.  If you have received this email in error please contact the sender.

Although our computer systems use active virus protection software, and we take
various measures to reduce the risk of viruses being transmitted in e-mail
messages and attachments sent from this company, we cannot guarantee that such
e-mail messages and attachments are free from viruses on receipt.  It is a
condition of our using e-mail to correspond with you, that any and all liability
on our part arising directly or indirectly out of any virus is excluded.
Please ensure that you run virus checking software on all e-mail messages and
attachments before reading them.

Hello,

I've been following this and many threads with interest, I've been 
programming in Forth for many years but have been "forced" by my 
customers to develop systems in C (a language that I've considered 
for many years to be "write only").  I have looked at Forth from FI 
and would appreciate any suggestions for an implementation of Forth 
of some quality for the MSP430 without the high price of FI.  All 
suggestions are appreciated.  Sorry to interrupt the flow of this 
thread but I thought I should appear where my "kind" would be 
attracted.

Regards,
Mark James

--- In msp430@msp4..., "J.C. Wren" <jcwren@j...> wrote:
> 	For those wondering about efficiency comparisons
between 
Forth and C, 
> basically, Forth has very little overhead, since
there are no 
register saves 
> for each function called.  C typically pushes the
working 
registers, unless 
> the compiler is in a position to be smart enough
to know that a 
routine is 
> static inside a module, and only called from
inside (no pointers 
to the 
> static function).  The optimizer, if it's a
good one, can 
determine that 
> certain registers are not affected, and avoid
pushing them.  These 
pushings 
> are known as prolog and epilog code, and it's
what gives C 
routines their 
> callability.
> 
> 	A typical Forth implementation uses only 3 register pairs.  
The system stack 
> pointers, the user stack pointer, and the program
counter.  
Because of the 
> nature of Forth, each and every routine
doesn't need to save a 
working 
> register set.  Being a stack oriented language,
intermediate 
results are 
> saved in the data stack (yes, there are
exceptions, <R and >R, 
etc), all 
> registers are implcitly saved between calls,
avoiding the majority 
of prolog 
> and epilog code that C requires.
> 
> 	Forth also has a *very* high code density, assuming the 
programmer has a 
> little common sense.  The first Forth program
almost anyone writes 
takes a 
> number on the stack, squares it, and prints the
result out.  
What's that code 
> look like?
> 
> 	: SQR DUP * . ;
> 
> 	: tells the Forth compiler to compile the word.  The word is 
to be called 
> SQR.  The DUP takes the top number of the stack
and makes a copy 
of it (if 
> you type "4 SQR", after the DUP, the
stack will have "4 4" on it.  
* is just 
> like C, in that it's a multiplier.  It takes
the top two numbers 
on the 
> stack, multiplies them, and pushes the result back
to the stack.  
The last 
> operation '.' (period, or
'dot') destructively prints the top 
number on the 
> stack (destructively means that the number is
removed from the 
stack, not 
> left in place).
> 
> 	For a C compiler, gcc produced the following code.  I 
enforced the variables 
> as volatile so that the register loads would be
carried out.  We 
all know the 
> compiler will optimize constants, but I
didn't want that for this 
example.
> 
>    {
>       char buffer [32];
>       volatile int i, j, k;
>       i = 4;
>       j = 4096;
>       k = i * j;
>       sprintf (buffer, "%d", k);
>       puts (buffer);
>    }
> 
>   b2:   b1 40 01 01 22 00       mov     #257,   34(r1)  ;#0x0101
>                 j = 4096;
>   b8:   b1 40 00 10 24 00       mov     #4096,  36(r1)  ;#0x1000
>                 k = i * j;
>   be:   1f 41 22 00             mov     34(r1), r15     ;
>   c2:   1e 41 24 00             mov     36(r1), r14     ;
>   c6:   0a 4f                   mov     r15,    r10     ;
>   c8:   0c 4e                   mov     r14,    r12     ;
>   ca:   b0 12 00 00             call    #0              ;#0x0000
>   ce:   81 4e 26 00             mov     r14,    38(r1)  ;
>                 sprintf (buffer, "%d", k);
>   d2:   11 12 28 00             push    40(r1)          ;
>   d6:   30 12 00 00             push    #0              ;#0x0000
>   da:   03 12                   push    #0              ;subst r3 
with As=
>   dc:   0b 41                   mov     r1,    
r11     ;
>   de:   3b 52                   add     #8,     r11     ;subst r2 
with As=
>   e0:   0b 12                   push    r11       
     ;
>   e2:   b0 12 00 00             call    #0              ;#0x0000
>                 puts (buffer);
>   e6:   0f 4b                   mov     r11,    r15     ;
>   e8:   b0 12 00 00             call    #0              ;#0x0000
> 
> 	I didn't have printf handy, so I printed to a 32 byte 
buffer, and used puts.  
> We'll knock off 3 instructions for that, or 8
bytes (call takes 4 
bytes).  So 
> we're at 14 instructions, and 48 bytes
> 
> 	In Forth, this function could be implemented in 
approximately 19 bytes, with 
> code looking something like this (actual
implementation depends on 
what 
> technique the Forth compiler uses for the headers
on words, and 
whether it's 
> a direct threaded Forth, indirect threaded, or
subroutine 
threaded.  We're 
> going to assume a modified direct threaded, where
words are 
presumed to be 
> colon definitions unless otherwise instructed) :
> 
> 		dw 0003
> 		db 'SQR'
> 		dw .LIT
> 		dw 257
> 		dw .LIT
> 		dw 4096
> 		dw .MUL
> 		dw .DOT
> 		dw .EXIT
> 	
> 	The first DW are some flag bytes (bits depend on the 
interpeter), and the 
> length of the word name (3 bytes).  The next 3
bytes are the word 
name 
> ('SQR').  The .LIT says the next 16 bits
are a literal word, and 
get pushed 
> on the stack.  So 257 and 4096 get pushed.  .MUL
multiplies the 
two items on 
> the stack, .DOT prints them out, and .EXIT returns
from this word.
> 
> 	If we call SQR in the definition of another word, our total 
cost is two 
> bytes, for the address of the word.  In C, the
call setup alone 
would be 6 to 
> 10 bytes.  And since one of the programmng goals
of Forth is to 
reuse as many 
> words as possible, you get code density
that's pretty remarkable. 
> 
> 	Basically, because of the nature of C, and the fact that 
almost all 
> interpeted languages are denser than a native
counterpart 
(excluding hand 
> optimized assembly), you can pack a lot more Forth
into the same 
space.  The 
> runtime penalty is small in Forth.  3x is slower
than straight 
assembly is 
> possible, with 5x or 6x being more typical. 
Compared to C, it 
*can* be 
> fairly comparable, but it's difficult to
implement two algorithms 
the same 
> way, since C is not a stack based language and
Forth is.  As such, 
you're 
> alsmost always comparing apples to oranges, since
algorithm has as 
much or 
> more impact than implementation.  About the
fairest comparison I 
can think of 
> is examples involving recursion.
> 
> 	This has been Forth 101.  Hope you enjoyed it.
> 
> 	--John


Hi,

> I've just received some info regarding the
LPC201x series. It 
> looks very interesting, but from what I can see the tools are 
> way too expensive to evaluate, and the limited time high 
> priced tools strike me as a rip off. I don't have an 
> application for it, but if it was able to perform within 
> certain criteria then I may. As I've stated once before my 
> evaluation of processing power is related to actual current 
> consumed to perform a function. 

Has anybody been able to source a board with one of these devices on?
As a company we'd like to support the part with our tools (which would
be straightforward) but are having difficulty finding it in the wild.

-- Paul.

On Thu, 26 Jun 2003 17:23:56 -0000, you wrote:

>I will keep you guessing agewise! (for now anyway)

Younger than me, it looks.

>The 1st micro that 
>I got involved with was a CPM machine,

My first micro was the one I soldered and assembled myself, my
Altair 8800.  My recollection is that I couldn't think of
anything else when I first read the article in Popular
Electronics -- memory tells me that this was in December of
1974.  (I got a new job which started Jan 6th, 1975, which I
expected to have pay for this.)  I think it was a two-parter
article, with the 2nd installation in January, 1975 -- the month
I ordered my about $400 kit.

However, prior to that, I'd been using such computers as the
PDP-8 and the IBM 1130.

My very first computer was the Bell Labs Cardiac, developed in
1969.  It was a "paper computer" and you moved a little
"bug"
around from slot to slot and executed the instructions manually.
My first exposure to the Cardaic was in 1971.

>It had 32k ram

Egads.  It wasn't for many years before I ever got to see
anything with that darned much memory -- my Altair came with 256
bytes (exactly one static ram chip) and the front panel board
had four sockets for them, so three were empty.  I think it was
almost another year before I purchased two 4K dynamic ram cards
from Altair -- and they didn't even work, after building them.
Getting them to work took me more than another month and a lot
of learning.  Just about in time for Altair to send me a nice
little notice telling me about their bad design and the
"patches" I needed to make.  Downside was that I'd already
figured them out and it cost me a lot of time.  Upside was that
I had figured them out and I'd learned a lot about electronics
which would help me.

I think it wasn't until 1979 before I actually owned something
with 32k ram.  And it was "high cotton," to me, believe you me.

>a Z80 (don't remember clock speed),

Yeah, you lucky slob.  You have *no* idea how much easier it was
to design a circuit with the Z80 in it, as compared with the
8080A (or even the 8085.  And the extra instructions were nice.

My first experience with the Z80 was the TRS-80, sometime in
early 1978, where I actually got to have 4k of memory to work
with.  Damn thing was too expensive, though.  I couldn't afford
the price, myself.  So I used someone else's.  I don't actually
remember the cpu speed of the TRS-80, but I do think I remember
the speed of the Z-80's I was hearing about at the time, which
was 2.5MHz.  Could be that was the speed you got to see.  Or the
4MHz, which came out a little later, if memory serves.

If your first foray was into the Z80, that dates it to no
earlier than late 1976, or so.  I don't think I even heard of
the Z80 until sometime in 1977.

You must have been born with a silver spoon in your mouth!

>a telex machine to edit and punch tape to.

Yup.  Silver spoon.  For me?  It was some hard, bat-handled
switches.  I actually got calluses from the Altair front panel.
I practically drooled over the Imsai -- and the reason? --
mostly because the darned thing had these wonderful, wide
plastic switches on the front panel!  Oh, my fingers would love
it.  I still have a box of those switches, which I bought for my
Altair after seeing an Imsai.

And I had to *make* my own paper tape reader!  Punch?  I had to
borrow those, when I could.  There was no way I could afford a
KSR-35 or even an ASR-33, back then.  Much as I wished I could
have.

>Writing assembler in binary format.

Did that in my head.  You memorized the instruction formats and
coded onto the front panel with a deposit switch.  With my 8k
dynamic ram, eventually working with hard work, I would actually
sit there in front of it for hours toggling in code.

>I later replaced the punch tape with a cassette
tape recorder.

Yes, the TRS-80 came with an interface for that.  Nice little
tape recorder I may still have in a junk box somewhere.  That
recorder was actually affordable, back then.  Used one with my
first IBM PC to save programs, too, when I later got one.  The
exact same recorder I'd been using with the TRS-80.

>Then the final step, I got 2x 8" Shutgard

correction...  Shugart, I believe.  Screw drives.  The Persei's
were the first voice-coil floopy drive, if I recall, and a
little more expensive, too.  But Shugart's worked more reliably,
from my experience, although more slowly.

The 8" floppies were 80k, if memory serves.

>floppy drives and upgraded the ram to 128kb (
blistering 
>speed)

I wish I had your money back then!  I didn't see 128kb outside
of some business purchase until... hmm... well, 1984 or 1985, I
think.  I couldn't afford that kind of luxury until the prices
had dropped out of the ram market and Intel began seeing more
money from their cpu business than their memory business, which
transitioned (crossed over) for them in 1985.

>That got me into assembler coding at source level.

Surprisingly, I was using assembler source *long* before that.
I had written a time-shared assembler for the HP 2000F system in
1975.  Completed it in September, that year, and made it
available to our local school system then.  That assembler was
one thing I was very proud of -- I started on a Friday evening
and had it and the linker completely finished by the end of that
Sunday.  Less than three days hard work!

After that, I used various assembler tools (and, of course, Bill
Gate's "paper tape BASIC" on an Altair.)  But mostly on other
people's machines until I actually managed to get something
decent of my own with floppy storage -- which was about
December, 1979.

>I will never forget how fast the 1st files
>opened and saved on those 8" drives.

Yeah.  100's of ms was pretty nice, I suppose.  Especially
compared with my dectape.  But not impressive.  I'd seen faster
and wished I could afford them.  Like the RK05, for example.
But those things were expensive.

>As I got more advanced I ported a Pascal and later
helped write a C 
>compiler to run under CPM to make embedded applications. No debuggers 
>in sight. This must have been 35 years ago........

Hehe.  Not quite.  But it may seem like that.  ;)

><snip>

Jon


On Fri, 27 Jun 2003 03:39:12 -0000, you wrote:

>I was happy to continue to this thread until
>
>--- In msp430@msp4..., Jonathan Kirwan <jkirwan@e...> wrote:
>> Personally, I'd rather not see Quadravox up
>> their price point (or delay lowering it) just because they
>> bought themselves into a "feed this guy's children"
contract and
>> have to force all of us to carry the load. 
>
>I will not tolerate any derogatory remarks about my children or their
>welfare.
>
>Jonathan, consider my input on this thread closed.

Sorry, I was only quoting from someone else, actually.  Nothing
mean meant in it at all, but apparently you are reading more
into it.  You have my most sincere apologies, as best I can give
without understanding your reaction.

Jon