NPCI (Nano Pseudo C Interpreted) 
                            Byron A. Jeff
                       Last Update: 5/19/2002

NPCI is a minature High Level Language. The primary 
purpose of its development is for small microcontroller work and teaching 
compiler principals. While I started with NPCI with backends for the 
Motorola 68000 family, the Motorola 68HC705 family, the Intel 8051 family, 
and the Microchip PIC 16CXX family of microprocessors and microcontrollers,
I ended up with a bytecode compiler. The bytecode is interpreted by a native
interpreter embedded in the processor memory. Currently the only active 
interpreters is for the PIC 16F877 and 16F628.

This implementation of NPCI was developed and runs under the Linux OS.
At one point in time it was tested with the DJGPP DOS compiler. However I 
make no assertion that it will run under DOS/Windows at this time.

NPCI has only the base elements of High Level Languages. These features include:




NPCI incorporates features from many different High Level Languages:



NPCI Types and declarations
--------------------------

NPCI has only integer types. The C convention of CHAR, INT, and LONG types
are used. In addition Pointers are a part of NPCI but a different nomenclature
is used to remove ambiguity. Arrays and records will probably be added at a later time.
Here are the NPCI type declarations:

- int  <variable list> [;]
- char <variable list> [;]
- long <variable list> [;]

Where a variable list is a comma separated list of variable names. To declare a
pointer variable, an '@' character is placed in front of the name. 

The size of each type is determined by the archtecture of the target machine.
Typically a char will be 8 bits. An int's size is determined by the natural 
integer size of the machine (16 bits for the current interpreter). A long
will always be at least size of an int. Basic C semantics... 

[Note that the long type is not currently implemented.]

A unusual feature of NPCI is the ability to assign a variable to an arbitrary
address. This allows for arbitrary assignment of variables to I/O devices
assigned to memory addresses. This is done by following the variable name
with the '@' character and the address.

[Note that the current compiler/interpreter setup will not properly execute
 this particular feature. BAJ 10/10/01 ]

Also the interpreter predefines some generic special purpose registers. In the
first implementation the following registers are defined: porta, portb, ddra,
ddrb, portsa, ports. The ddra and ddrb define the data direction and are
equivalent to TRISA and TRISB on the PIC. The postsa and ports two registers allow 
for arbitrary special register access. The portsa register represents the address 
of the special register of interest, while ports represents the register pointed 
to by portsa. This is analogous to the FSR/INDF register system on the PIC family of 
processors. Currently all of these registers are 8 bits in length.

Note that the semicolon is optional. C++ style comments are allowed.

Here are some typical declarations:

//---------------------------------------------
// Note that C++ style comments are used for NPCI.

int count, v1
char ch1, command;
long time, length;
int @pointer1, @pointer2
int portd@12,ddrd@11; // This puts portd at address 12 etc.
//---------------------------------------------

Expressions
-----------

NPCI is expression driven. Most of the standard C operators are supported.
The C selection operator is not. NPCI has a new operation for bit manipulation.
The operation : will select only the bit bitnum in the
variable. This expression can be treated as a one bit integer variable. It
can both be value accessed and assigned.

Some sample expressions:

//----------------------------------------------
m1 + 3
12 << 2
5 - test2:1 << 2
x_twelve <= -14
test2:(m1+3) = 1
//----------------------------------------------

Note that the bitnum can be an expression.

Selection Statments
-------------------

Selection statments are a requirement of any high level programming language.
NPCI uses the IF construct for selection. Booleans are represented as in C:
any non-zero value is true, and any 0 value is false regardless of type. 
NPCI has a full complement of relational and logical operators. 
Here is the syntax for the NPCI IF construct:

if (<expr>)
  // Compound statements go here 
elseif (<expr>)
  // Compound statements go here 
elseif (<expr>)
  // Compound statements go here 
  // ... Multiple elseifs are allowed 
else
  // Compound statements go here 
endif
 
Note that the ELSEIF and ELSE constructs are optional.
Also note that the parenthesis around the <expr> are required.

The ENDIF construct is used to remove ambiguity from nested IF's. ENDIF must
be used to end every IF statement in NPC. Also the ENDIF construct causes
grouping of multiple statements without the use of braces (as in C) or
the begin...end (as in pascal).

the ELSEIF construct is used to allow for multiple selection within the context
of a single IF contruct. Its purpose is to allow for multiple selection 
without nesting so an if...elseif...elseif...else...endif statment only
requires one ENDIF to terminate instead of an ENDIF for each IF condidion.


Repetition Statements
---------------------
As with selection, repetition statements are required in any High Level
programming Language. NPCI uses the WHILE construct for repetition. The
same boolean expressions syntax as described in the Selection section
is used. Here is the syntax of the WHILE construct:

while [label] (<expr>)
  // Compound statements go here 
endwhile

As with the IF construct the parenthesis around the <expr> is required.

And again as ENDWHILE construct serves the same purpose as the ENDIF of
the previous section. It must terminate every WHILE construct. It also
performs the same grouping task and removes ambiguity of statements.

As in other languages the statements in the WHILE construct will be executed 
as long as the value of the <expr> is true.

I've implemented break and continue statements. One feature of those 
statements is that they are able to break out of multiple levels
of while loops. An optional label can be attached to a while loop so 
that a break or continue can go to a particular loop instead of only 
the closest enclosing loop.

The format of the break/continue statements allows for an optional
conditional. I've added this simply because every break and
continue must be conditional anyway. So as a preview here is a sample
showing their usage:

//----------------------------------------------
// Sample of while labels, break and continue. 
char i,j

i = 0
while bigloop (i < 15)
   continue if (i == 16) // Will never execute but shows conditional continue
   j=3
   while innerloop (j < 22)
      break bigloop if (i == 4 && j > 12) // Break completely
      continue bigloop if (i > 5) // Continue to the outside loop
      continue if (j < 2) // Continues the current loop
      j=j+2
   endwhile // innerloop
   i = i + 5
endwhile // bigloop
//----------------------------------------------

The format of 

break/continue [label] [if (<expr>)] 
     
Takes care of every possible loop exit situation that will arise.

UPDATE 10/20/01: These statements and labeled while loops are implemented.

Procedures
----------
NPCI implements two types of procedures. Normal procedures are defined with
a "proc" statement and ended with an "endproc". Assembly procedures which are
embedded with the interpreter. They are used to implement high performance or
time sensitive activies. Both types of procedures must be defined before
they are used. Both types of procedures can be defined with a prototype 
statement. The following code annotates all of the issues with NPCI
procedures:

//======================================================================
// Here is my prototype/call/parameter testing program.
// It illustrates all of the interfaces for subroutines
// Haven't figured out how to implement functions yet - will probably leave out

// Here's the rationale for the parameter convention I chose. I rejected:
// 
// C - value only - requires pointers, requires change to every instance to 
//                  switch between value to variable parameters. It stinks!
// FORTRAN - reference only - Too confusing and error prone. Can change
//                            constants and expressions! Yuck!
// PASCAL - val default with reference - Error prone because the default val
//                                       parameter is legal with all expressions
//                                       including variable. Many times program
//                                       errors resulted from missing var in
//                                       parameter declaration. A mistake not
//                                       worth repeating.
// 
// So NPCI does the opposite of PASCAL: reference default with value as option.
// The keyword 'copy' defines a value parameter. It doesn't require any changes
// to the code to switch from one to the other, and if you attempt to pass a
// non variable as a reference parameter, you get an error! The default is
// that changes in the procedure are passed back to the calling routine. This
// obviates the need for pointers initially too.
// 
// It works in theory. I'm sure that it'll need some excercise in practice.
// 

// OK here's the first one. Prototypes are necessary for all asm procedures
// because they are never defined in the NPCI program. Prototypes or 
// a defined procedure is required before the call. The 'copy' keyword allows
// parameter 'out' to be an expression. Note that the number and type of
// parameters must match on the call. Have not dealt with promotion because
// CHARS are the only legal type for now.

prototype asm serout(copy char out);

// Next is a typical prototype. If a prototype and a procedure definition
// occur in the same program, the parameter lists must match except for the
// name. If names exist for both then the defined procedure names will
// superceed the prototype ones. Note how the last parameter of the prototype
// doesn't have a name (nor copy for that matter. The first parameter is
// a value one, while the last two are reference and will retain whatever
// values are assigned within the procedure.

prototype proc testme(copy char input,char output,char);

// Some global variables. As soon as parameters are working OK I'll add
// local procedure variables to the mix. Update: 5/19/20028 bit local variable work fine. Note that locals will be allocated
// on the stack while globals aren't. May affect the size of each depending
// where they are allocated. For example on a 32K RAM based NPCI board, both
// could be assigned to the RAM or the stack could be in the RAM while the
// globals are assigned to the PIC's memory.
char i,one,two,three,four,five,six;

// A procedure. Note that the parameter types match the prototype parameters
// exactly. Also all parameters must have names in the actual declaration.
proc testme(copy char input,char output,char another);

// The compiler code to access parameters is completed. Assigning to them
// is also finished. Parameters are completely done.
   i = input+output;
endproc

// Note that every NPCI program must have a main procedure where execution
// starts. And unlike C an empty parameter list means that no parameters
// are allowed.

proc main ()
// Example call. Note that the last two parameters must be actual variables.
// The first can be any expression. Each parameter (or the address of a var)
// is pushed on the stack and a LINK command creates a new frame. Parameters
// Can be accessed directly from the stack.
   call testme(4+3,i,one)
endproc
//======================================================================

INSTALLATION & USAGE
--------------------

Just type make. It creates the file npci. You can then run npci with an
npci source file (use extension ".npc"). It produces assembly output in
the file npciout.asm. In addition NPCI produces a Motorola S-Record file
with the code.

BUGS and QUIRKS
---------------

- The compiler code generator is complete except for local variables. However 
  the interpreter doesn't do any of the 16 bit integers operations.Update: May 19,2002: 16 bit integers are complete except
for local variable. The compiler properly computes all 16 bit global ops and properly mixes and matches 8 and 16 bit operations.
- I need to generate a description of the virtual machine. It's not too 
  complicated, consisting of a stack, instruction pointer, and a frame pointer
  for procedures. Global variables are allocated from the bottom of memory
  while the stack grows down from the top of memory. The stack contains all
  local variables, parameters, return addresses, and subexpression values.
  All computations are done on the top of the stack. Opcodes are 1 byte 
  consisting of basic operations, push of integers, push of variable values,
  conditional and unconditional goto. Opcodes can have up to 2 following value
  bytes. More to come...

-------------------------------------------------- Paid Advertisement Links Below --------------------------------------------------
Checks By Phone | Verify a Check | Reorder Checks | Routing Number | Check Verification
| BIN Database |
| Recycle Food Waste Boston | Check By Phone Software Reviews | Short Term Furnished Allston | SWIFT Code Database |