Brief NPCI HLL Description and Notes


This is a note that I wrote to an interested developer. Has the base description of the NPCI HLL.

The real problem is that I haven't worked up a system for multi-platform development yet. I'm a Linux guy so I've geared everything so far towards Linux. In fact I the only DOS compiler I have access to is the DGJPP DOS GCC cross compiler.

I also left things in a messed up state. I was in the process of implementing parameter passing to procedures when I got bogged down in October [1997]. I had implemented the tokens in the interpreter for this but I hadn't finished working up the changes for the compiler. Now this is flipped as I rewrote the variable access system. So now the compiler generates correct code but the bytecode interpreter hasn't been updated to accomdate it (Dec 1999)

I think the biggest two things that I need are a test in a DOS/Windows environment and a programmer interface for DOS/Windows. Done. It compiles under DGJPP.

Bottom line is that if you want to run it right now immediately you'd have to do it from Linux.Still true for the most part

Here's the 5 cent tour from a README I wrote a couple of years ago. I'll add any new comments in brackets.(And even more current ones in italics.)


NPC (Nano Pseudo C) is a minature High Level Language. The primary purpose of its development is for small microcontroller work and teaching compiler principals. I hope to have NPC 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. [ This was when I was writing a straight compiler. NPCI now uses tokens to represent the program which are loaded into the EEPROM and interpreted by the interpreter on the target.]

This implementation of NPC was developed and runs under the Linux OS.

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

Variable Declarations

Expression Computation

Assigment

Selection

Repetition

Sub-programs

NPC incorporates features from many different High Level Languages:

It's basic structure is from C.

Fortran CALL statements are used for sub-programs.

Modula-2 ENDIF and ENDWHILE constructs are included.

The typical BASIC restriction of one statement per line is enforced.
[ All of the above are still true. I added virtual ports and bit selection ops too. ]

NPC Types and declarations

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

int [;]

char [;]

long [;]

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.

[ Only char is implemented right now. The compiler recognizes int and long but does not yet output correct code for them ]

[ Pointers are not yet implemented ]

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 (32 bits for a 68000, 16 bits for others). A long will always be at least size of an int. Basic C semantics...This now doesn't really make sense anymore. Will need to define a new artificial data size for the bytecode and force the interpreter to implement it.

A 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.

[ This is still in place but with the rise of virtual ports it really isn't necessary much anymore ]

Note that the semicolon is optional. Required for multiple statements per line

Here are some typical declarations:

int count, v1

char ch1, command;

long time, length;

int @pointer1, @pointer2

int portb@12,ddrb@11; /* This puts portb at address 12 etc. */

[ I also added a table construct so that table data can be loaded into the EEPROM along with the program. ]

Expressions

NPC is expression driven. Most of the standard C operators are supported. The selection operator is not. NPC has a new operation for bit manipulation. The operation <variable>:<bitnum> will select only the bit bitnum in the variable. This expression can be treated as a one bit integer variable. Some sample expressions:

m1 + 3
12 << 2
5 - test2:1 << 2
x_twelve <= -14
portb:7

Selection Statments

Selection statments are a requirement of any high level programming language. NPC 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. NPC has a full complement of relational and logical operators. Here is the syntax for the NPC IF construct:

if (<expr>)
  /* Compound statements go here */
elseif (<expr>)
  /* Compound statements go here */
elseif (<expr>)
  /* Compound statements go here */
/* ... Multiple elseif's 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. It's 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. NPC 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 (<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 C the statements in the WHILE construct will be executed as long as the value of the <&expr> is true.

Procedures and Parameters

I didn't write about procedures and parameters. They are typical except that unlike C the default parameter type is call by reference. I like this because it'll generate an error if you attempt to pass an expression where a variable is required. Also it obviates the immediate need for pointers like C must have just to pass data between program and subroutine.

NPCI also has an assembly language routine interface that'll call a high speed assembly routine that's rolled into the interpreter. It'll be useful for timed stuff like serial routines, microsecond timing, and special tasks that would take too long to execute in the interpreter.

Development System

Here is the base layout of the development system:

-------------------         -------------------      ---------------------
|                 |         |                 |      |                   |
|      PC.        |         |     EEPROM      |      |    TARGET NPCI    |
|                 |         |  PROGRAMMER     |      |E   INTERPRETER    |
| Compiler        |         |                 |      |E                  |
| Loader          |  Serial | Receives program|EEPROM|P  Has interpreter |
|                 |<------->| From PC. Writes |<---->|R  That reads      |
|                 |   Port  | and verifies to |Cable |O  program tokens  |
|                 |         | EEPROM on target|      |M  from EEPROM and |
|                 |         | and passes      |      |   executes them.  |
|                 |         | debug info from |      |                   |
|                 |         | Target to PC    |      |                   |
|                 |         |                 |      |                   |
-------------------         -------------------      ---------------------

The EEPROM programmer isn't a strict requirement. It can be done using a parallel port. But this base system will work in any environment because every computer has a serial port, but not all have parallel ports.

Availability and Licensing

So take a read and let me know what you think. I can package up the source for:

The compiler.

The Linux serial loader program

The EEPROM program program written in 16CXX assembly. I run it on a 16C84 and I have two versions: a lazy 2400 BPS version that I probably won't release and a much better 38400 version that's much better.

The NPCI interpreter. I've tested it with 16C71 and 16C84's. There's no reason that it won't work with somethine like a 16C74 Or a 16F87X.

As for licensing it's kind of touchy. I want NPCI to be free for non-commercial use. So for development and hobby work it shouldn't cost anything. The problem occurs once folks want to sell items with the interpreter in it. Any program that runs on top of the interpreter is derived because it can't run without the interpreter. If I make the interpreter non-sellable, then folks will just give the interpreter away and sell the program that runs on top of it. NPCI is over 5 years of work. The businessman in me tell me that it's only fair to get a cut if NPCI is sold. The free software guy in me tell me that NPCI will go much further if free development is available because just like you if more folks work on it, it can only get better. I really don't know how to resolve the two yet.

Anyway take a read and I'll slap a non-commercial license only to the distribution. Then you can tell me what you think.

BAJ

; This part of the code file shows each of the possible combinations of lines
; that are available in an assembly language program

label1:   opcode   operand   ;  a comment
# comment comment comment Note that a comment can start with either # or ;
labelonly:
; comment
label: opcodeonly
opcode andoperand
justopcode

; Here are the NPCI opcodes along with their value and a brief description
; Most of these operands work with the stack so terms like TOS (top of stack)
; and 2nd, 3rd etc are used to desribe the elements in the stack. Also in
; NPCI any elements in the stack used by the instruction are always removed
; and sometimes new stuff is added. This operation is called a replace
; Since the original stack elements are always removed, there is no need to
; further reference this fact the descriptions below. Note that OW is used as
; an abbreviation for OtherWise. Also with so many binary operators that RTTE
; is used to represent the action of Replacing the Top Two Elements of the 
;stack.

	assign		; Assigns the value in the 2nd to the address in TOS
	add		; RTTE with sum
	equal		; RTTE with 1 if equal, 0 OW
	notequal	; RTTE with 1 if not equal, 0 OW 
	sub		; Subtracts TOS from 2nd replacing with result
	and		; RTTE with bitwise and 
	or		; RTTE with bitwise or
	xor		; RTTE with bitwise xor
	lor		; RTTE with logical or, 1 if either TTE true, 0 OW
	land		; RTTE with logical and, 1 if both TTE true, 0 OW
	return
	call
	shiftl		; RTTE with 2nd shifted TOS bits to the left
	shiftr		; RTTE with  2nd shifted TOS bits to the right
	greater		; RTTE with 1 if 2nd > TOS, 0 OW
	less		; RTTE with 1 if 2nd < TOS, 0 OW
	greateq		; RTTE with 1 if 2nd >= TOS, 0 OW
	lesseq		; RTTE with 1 if 2nd <= TOS, 0 OW
	bitval
	bitassign
	not		; Replace TOS with 1 if TOS is 0, 0 OW
	table		; Get a table entry from program memory address in TOS
	getval		; Replace TOS with value located at address in TOS
	addfp		; Adds the value of the frame pointer (FP) to TOS
	intmode		; Switches stack to 16 bit mode
	bytemode	; Switches stack to 8 bit mode
	link		; Pushes old FP on stack and points FP to TOS
	unlink		; Restores old FP by pulling FP from TOS
	movetos		; Add the value of TOS to the stack pointer moving TOS
	callasm		; Calls the assmebly routine whose index is at TOS
	push		; Pushes a value from 0-62 onto the stack
	pushbyte	; Pushed a value from 63-255 onto the stack
	cjump		; Conditional Jump. Jumps to operand address if TOS=0
	jump		; Jump. Jumps to operand address
Here's a couple of examples in NPCI:

int i; // Declares a word sized variable
char c; // Declares a byte sized variable
i=0; // pushes a 0 byte. Assign notes that the expression result is a byte,
     // issues a sign extend, puts the stack in word mode, does the assign
     // popping the top word off the stack

i=i+1 // Notes that i in an int, switches to word mode, pushes i, the 1 will
      // push the word 1, and the add is a word add, as is the assign

c=0  // Selects byte mode. Pushes a 0 byte. assigns a zero. 

c=c+1 // Byte mode. Push c and 1, add, assign

i=i+c // Word mode, pushes i, pushes the byte for c and sign extends, adds,
      // assign


Hope this gives you the idea. What I didn't want to do was double the size of
the instruction set simply to accomodate operands of different sizes on the
stack. If for example I wanted to add longs to the mix, instead of having a
addl, subl, andl, orl, xorl, and all the rest, just add a longmode bytecode
and from then on all the original add, sub, and, etc will work on longs.

Since NPCI is a
microcontroller language, direct bit manipulation is a requirement. So instead
of all the bit masks, NPCI has operators that access and manipulate bits
directly. And yes the bit number is variable. So if you wanted to count 
the odd bits are set you could do something like:

odd=1
set=0
while(odd < 8)
   set = set + target:odd
   odd = odd + 2
endwhile

And access the bits of target directly instead of using a mask.


See also: