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.)
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 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. ]
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:
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:
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.
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:
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.
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.
Here is the base layout of the development system:
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.
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
See also:
Expressions
m1 + 3
12 << 2
5 - test2:1 << 2
x_twelve <= -14
portb:7
Selection Statments
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
Repetition Statements
while (<expr>)
/* Compound statements go here */
endwhile
Procedures and Parameters
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 | | |
| | | | | |
------------------- ------------------- ---------------------
Availability and Licensing
; 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.