


Electride 1.0
A small interpreter with double precision math, for pic32mx

S. Oliver  2014







0. Contents, preface, aknowledgements

1. Overview, example

2. Getting started : Hello World

3. Variables, datatypes, and arrays

4. Operators, operands and intrinsic functions

5. Subroutines and functions; matrix inversion example

6. Control and flow : Loops, IFs, and GO TOs

7. Input, Output, Formats

8. Ports + Peripherals

9. Appendix : Error messages, setting up, configuration, limits


Preface and acknowledgements

Originally this started as something in assembler, to experiment with writing math routines for pic microcontrollers. Along the way it was moved to C; as the initial project ended, the Microchip math libraries were switched in, and the instructions became more and more like a simple language. So the experiment became, to see how viable a small fortran interpreter for pic32mx was, and what level microcontroller would be needed.

Of course, an interpreted language offering double precision could be seen as over-done. And yet, there is a need for small amounts of precise calculation, a wish for grass-roots development, and concern that education is bypassing quantitive skills...So I hope it is useful.  It is not meant to be a finished work, but is at a reasonable stagepoint at least. 

I have cherry-picked quite a few good ideas, and certainly do not claim to be the originator
of them. The problem is, in an extensive and ill-defined industry, knowing where the ideas came from or where I saw them. Many articles (SC, EPE, Circuit Cellar) with dozens of authors, on using Microchip pics starting with the 16F84...The Microchip documentation...The C language and K&R of course...and the excellent books by Lucio di Jasio. Electride itself is written in C using the Microchip MPLABX and GNU software, with helpful assistance from Microchip support. 

And from IT history, the visionary MONEC system in the mid seventies...running Fortran and Basic jobs for hundreds of students. I recently found out, it ran on a PDP11 with 32k memory. Amazing. 

Iconic examples are included as a traditional salute. This document is provisional  at this stage, however the examples and outputs are cut-and-paste from actual sessions using the same build (with a few slightly abbreviated). A better version will have wait for eyesight and other issues.


Bibliography

Programming 16 bit microcontrollers in C (2007)
Di Jasio, L.

Programming 32 bit microcontrollers in C (2008)
Di Jasio, L.

The C Programming Language, (1978, 1988) second edition (ANSI). 
Kernighan, BW and Ritchie, DM.

and from time
An Introduction to Computer Programming in Fortran (Monecs Fortran) (1976)
Bellamy, CJ (dec) and Whitehouse, LG.



1. Overview, example

Overview

Electride is an embedded fortran interpreter, that runs on PIC32MX chips.

It will load, run and store small programs very similar to Basic, written in simple Fortran.
The programs can easily use high level math and access the chip hardware, for example, to run A/D measurements and display results. Integer (32,16 and 8 bit) and single and double precision (32 and 64) floating point datatypes can be used as required.

It can be connected to a serial terminal or run standalone. Typically a PC with something like TeraTerm and a serial-usb pathway can be used, and/or hardware with a LCD display, button switches and so on. 

At startup a simple menu allows interaction with the user. If a program is already present and there is no serial connection during startup, the stored program will be run automatically.


Example program

      PROGRAM voltz;
C;
      INTEGER*4 i ;
      REAL x, y ;
      FORMAT (A,F5.3,A,/);
      DO 10 i = 1, 100 ;
      CALL delay() ;
      x = FLOAT ( ADX4 ) ;
      x = ( x * 3.260 ) / 1024.0 ;
      WRITE (6,0) "Cell is at ", x, " volts" ;
   10 CONTINUE ;
      END ;
C;
      SUBROUTINE delay ( ) ;
      INTEGER*4 i ;
C wait ;
      DO 10 i = 1, 8000 ;
   10 CONTINUE ;
      RETURN ;
      END ;
\

Cell is at 0.073 volts
Cell is at 0.067 volts
Cell is at 0.054 volts
Cell is at 0.073 volts
Cell is at 0.096 volts
Cell is at 0.073 volts
...




The trial version is intended for entry-level use and has constraints. It runs on a PIC32MX170B sdip ic, using slightly over half of the chip resources. Other versions scale over the pic 32MX range. The appendix has more details.


2. Getting started : Hello World

To start, it is usual to load and run an (iconic) "helloworld" program.

Initially connect the board, switch it on, and run the PC terminal software.

Hitting enter on the keyboard will show the banner and simple menu list, something like :


    ELECTRIDE 1.0   S. Oliver 2014
    with content Microchip and GNU
    Trial PIX32MX170B version 0.9


    (G) Get
    (S) Save
    (R) Run
    (L) List

>


To load a program it is sent from the PC to the board, after firstly setting the 
board to expect it. Hitting G (capital-G) does that, you will see a "Waiting.." prompt
as the board then waits for the program to be sent.

>G
Waiting..

Use the windows menu to send the program. It is the "Send File..." (not Transfer) item in Tera Term.
A file picker will let you navigate to and select the helloworld file.

"OK" is then displayed as feedback that the sending is complete.

>G
Waiting..
OK

The menu list then returns. It is a good idea to select S and save the program.
A "save.." confirmation will be displayed and the menu list returns.

>S
save..

Now select R to run the program.  The program is displayed first, followed by its output as it runs. After it finishes the board (pic) is reset and memory refreshed, and the menu and prompt return.

R

      PROGRAM helloworld;
C;
      WRITE "Hello, world" ;
      END;
\

Hello, world

rrefresh..


Only "r" is displayed by the reset. The "refresh" is displated as the board then restarts and  restores the program from the saved content. 


As an exercise you can alter the WRITE output string a little. (Use a text editor with your own file). 
The input file format is a bit picky, for now keep to the spacing and be careful to finish the line with the semicolon. The description of the input file layout should be read before going much further.


At the start the examples tend to use the WRITE command for output. This simply prints strings and variables in their default format. More specific formatting is detailed in a later section.




The Input file

The program file itself is ordinary text, using a subset of ascii characters.

Keywords and commands in statements are in uppercase, with lowercase used for variables.

Lines are almost exactly in the fortran layout. The first part of a line is set-column, so
the line content usually starts at the seventh column. There are six preliminary characters or spaces.
The first character is reserved for a comment flag (C), which
allows the rest of the line to be used for comment text.
The second and sixth characters are ignored. 
The third, fourth, and fifth character positions on a line can be used for
an identifying statement number (numerical label). 
A slight difference is that a line is finished with a semicolon, 
followed by a carriage return/linefeed combination for readability. 
Spaces following the semicolon will be treated as part of the next line.
Tabs, control characters etc should not be used.

The main program optionally starts with a PROGRAM statement, 
and finishes with an END statement. Any subroutine and function 
definitions then follow, each starting with the
name declaration and finishing with an END statement. 

A backslash (\) is used to signal the end of the file. 
It is suggested that a file extension ".f4" be used to differentiate the file from other text files.

The examples used are included as files and are a good starting point. 

3. Variables, datatypes, and arrays

A variable name is comprised of lowercase ascii characters, 
with the first six characters used as the unique identifier. 
Variables must be declared, usually at the start of the program or 
subprogram involved. The declaration specifies the datatype to use,
followed by the variable name(s). 


Eg for an integer

      PROGRAM integer4;
C;
      INTEGER*4 i ;
      i = 1222333444 ;
      WRITE i ;
      END;
\

1222333444


Up to 32 variables and arrays can be defined in each subprogram. 
They are not automatic or implied. Note that the name of a variable has to be 
interpreted every time it is used, so short names are more efficient. 

[There is a foible to watch out for, the last variable name in a declaration must be followed by a space.]  

The datatype associated with a variable determines how that variable's data is 
processed and held in memory. The datatypes provided allow the use of integer, floating point or character variables. 
 
Integer data is processed using the intrinsic 32-bit word size and integer arithmetic, and uses memory according to the specified byte count, eg INTEGER*4 uses 4 bytes of memory. 


      PROGRAM temperature;
C;
      INTEGER*4 i, fahr, celsius ;
      fahr = 0;
      DO 10 i = 1, 11 ;
      fahr = fahr + 20 ;
      celsius = 5 * ( fahr - 32 ) / 9 ;
      WRITE fahr, " ", celsius ;
   10 CONTINUE ;
      END;
\

20 -6
40 4
60 15
80 26
100 37
120 48
140 60
160 71
180 82
200 93
220 104

To conserve memory INTEGER*1 and INTEGER*2 can be used, which require 1 and 2 bytes
respectively. INTEGER*8 may be added in future.
Character data is processed as integers and stored as single bytes. 
At input or output, the data is treated as ascii instead of numeric values, 
and the single byte storage conserves memory in arrays.


Floating point data is processed using either one word (datatype REAL: single precision) or two words (datatype DOUBLE: double precision). REAL provides precision of six significant figures and is held in 4 bytes of memory, DOUBLE gives twelve significant figures and uses 8 bytes.


Eg :

>R

      PROGRAM real;
C;
      REAL a ;
      a = 1.123456 ;
      WRITE "a = ", a ;
      END;
\

a = 1.123456

      PROGRAM double;
C;
      DOUBLE a ;
      a = 1.12345678 ;
      WRITE "a = ", a ;
      END;
\

a = 1.12345678


The datatype also determines the default reporting format. Integers are reported with as many digits as required. Six (rounded) decimal places are presented by default for REALs, and eight for DOUBLEs.

      PROGRAM realish;
C;
      REAL a ;
      a = 1000.20 ;
      WRITE "a = ", a ;
      END;
\

a = 1000.200012
 
Definite formatting can be used to be more realistic, for example, measurment of a battery voltage may have resolution of 0.01 volts at best.


      PROGRAM realishfmt;
C;
      REAL a ;
      a = 12.534976 ;
      FORMAT (F5.2) ;
      WRITE (6,0) a ;
      END;
\

12.53

For clarity the examples will often use default reporting until specific formatting is covered later.
 

Arrays

Arrays are declared much the same way as single variables, with the declation also
specifying the dimension extents.  The total number of elements possible is set by 
the available memory. There is a maximum of three dimensions.

An array element is identified by an index of either an integer value or 
by a simple variable that has a valid integer value. Index numbering starts at 1.
Variables used as an index must be datatype integer*4.  
Index values outside the declared extent are trapped as errors at runtime.
Memory is reserved by each declaration line in four-byte chunks, so an array declaration 
is more efficient than multiple lines of variables.

      PROGRAM birdlife;
C;
      INTEGER*4 birds(3) ;
      birds(1) = 123456 ;
      birds(2) = 314153 ;
      birds(3) = 987654 ;
      WRITE "There are ", birds(1), " buzzards" ;
      WRITE "There are ", birds(2), " eagles" ;
      WRITE "There are ", birds(3), " turkeys" ;
      END;
\

There are 123456 buzzards
There are 314153 eagles
There are 987654 turkeys

Note that the array identifier is used as one token , ie without spaces.


Using variables

Care should be taken to avoid incorrect techniques.  An example is when a large value is used as a baseline, and a much smaller value is added. The summation may not have the available range to utilise many significant figures of the smaller number. A similar example is subtracting two almost identical numbers. The result will have lost most of the significant digits, even if integers are being used to replace floating-point. 
  
In standard 32 bit IT systems, single-precision floating point values are accurate to about one part in 16 million - ie a little under seven significant figures for positive values. In many cases this is not enough or is at least inconvenient. As an illustration it is interesting to try adding the fraction one ten-thousandth, 10000 times, to a starting point of 2500.0, on your favorite single-precision system...the returned answer will almost certainly be 2500.0, not 2501.0 as expected.

Double precision can help in some situations. It is accurate to twelve significant figures, and takes a little extra time to process.


      PROGRAM doubleish;
C;
      DOUBLE a ;
      a = 1000.20000000 ;
      WRITE "a = ", a ;
      END;
\

a = 1000.20000000


The following program simulates (not very well) float-charging a cell for three hours. The cell already has 10 amp-hours stored, and there is a milliamp charging. The summation fails with single-precision but can be rescued with DOUBLE ( as a quick fix before a rethink.. ).


C;
      PROGRAM battery ;
      INTEGER*4 sec ;
      DOUBLE start, stored, current, gain ;
C Initial state in amp-seconds;
      start = 36000.0 ;
      stored = start ;
C Three hours adding at one milliamp ;
      current = 0.001 ;
      DO 10 sec = 1, 10800 ;
      stored = stored + current ;
   10 CONTINUE ;
      gain = stored - start ;
      FORMAT (A,F5.3,/) ;
      WRITE (6,0) "amp-seconds gained : ", gain ;
      END ;
\

amp-seconds gained : 10.800

Often, as in this case, a need for double precision can be avoided by a suitable change in methodology, but sometimes it is vital.

* Do not over charge lithium cells !!


4. Operators, operands and intrinsic functions

The standard operators (=, -, *, / and **)  are used as normal. 
A "negate" ( - ) operator is included. Double precision math is used by default.
The operators ( <, <=, =, !=, >=, > amd & or | ) used for comparison logic 
are covered in the flow section. 


Operands can be simple numbers, variable identifiers, array identifiers (with
subscripts of either simple integers or integer variable identifiers), or  
the resultants of other operations in the normal math precedance. If required, 
brackets can be used to enforce evaluation sequence.
Inadverdently mixing integer and floating point arithmetic is trapped as an 
error, unless the datatype is deliberately overridden.

In general the intrinsic functions SIN, COS, TAN, ASIN, ACOS, ATAN, SQRT, ABS, RAND, IFIX, FLOAT, DBLE, SNGL, ALOG, and EXP are used as normal, with the proviso that uppercase is used. They can be used as operators, ie, brackets are optional.
Complex formulae should be broken down into sections to fit.

Math functions and output

SIN 	Sin of input value (radians)
COS 	Cos of input value (radians)
TAN 	Tan of input value (radians)

ASIN	Arcsin in radians of input value (double)
ACOS	Arccos in radians of input value (double)
ATAN	Arctan in radians of input value (double)

SQRT	Square root of input value
ABS	Absolute value
RAND	Random integer, 0 - 65565. 

IFIX	Integer value of floatinpg point input
FLOAT	Floating point (single precision) equivalent of integer
DBLE	Double precision equivalent of input
SNGL	Single precision equivalent of double

ALOG	Natural logarithm
EXP	e to the power x.

The RAND function requires a dummy integer*4 argument. 

Intrinsic functions are also used for reading a digitized analog value via the A-D converter (ADxx) and reading input pin states (BRxx). They return integer*2 values and are covered in a later section.


Simple math expressions showing precedence

      PROGRAM mathexp;
C;
      INTEGER*4 a, b, c ;
      REAL x, y, z ;
      FORMAT (A,F4.2,A,/) ;
      a = 1 ;
      WRITE "a = ", a ;
      b = 1 + 2 ;
      WRITE "b = ", b ;
      c = b + b ;
      WRITE "c = ", c ;
      a = 1 * 2 + 3 ;
      WRITE "a = ", a ;
      a = 1 + 2 * 3 ;
      WRITE "a = ", a ;
      a = 1 + b * 3 ;
      WRITE "a = ", a ;
      a = ( 1 + b ) * 3 ;
      WRITE "a = ", a ;
      x = 0.500 ;
      y = SIN ( x ) + COS ( x ) ;
      WRITE "y = ", y ;
      z = FLOAT ( ADC5 ) ;
      z = ( z * 3.260 ) / 1024.0 ;
      WRITE "z = ", z , " volts";
      WRITE (6,0) "z = ", z , " volts";
      END ;
\

a = 1
b = 3
c = 6
a = 5
a = 7
a = 10
a = 12
y = 1.357008
z = 0.133711 volts
z = 0.13 volts




5. Subroutines and functions

User subroutines and functions can be defined and used as necessary. The definitions are placed after the main program. The interpreter sees the definitions in a pre-pass.

The definitions start with the SUBROUTINE or FUNCTION statement,
and finish with an END statement. A RETURN statement must be present. 
Functions must cite the required datatype. 

A subroutine is instigated by a CALL statement in the main program.
In general variables are declared as normal and are local to the subroutine 
invocation.  Control returns to the calling program with the RETURN statement. 

      PROGRAM startup;
C;
      INTEGER*1 j ;
      j = 9 ;
      WRITE "OK to start" ;
      CALL righto ( ) ;
      WRITE "j = " , j ;
      END;
C;
      SUBROUTINE righto ( ) ;
      INTEGER*1 j ;
      j = 42 ;
      WRITE "Righto" ;
      WRITE "sub j = " , j ;
      RETURN ;
      END ;
\

OK to start
Righto
sub j = 42
j = 9

The subroutine definition can also include formal arguments, that is, parameters 
that are the variables used to pass data. Calls to invoke the subroutine must then also
include the matching actual arguments, to a maximum of eight. 

Parameters that are variable identifiers are passed as call-by-reference, that is, 
the memory locations used by the actual variables in the call are then 
also used by the correlating formal arguments set in the subroutine sefinition. 
When the subroutine finishes the actual variables retain the values. This uses memory efficiently.


      PROGRAM subaddten;
C;
      WRITE "Start" ;
      INTEGER*2 x ;
      x = 7 ;
      WRITE "x = ", x ;
      CALL addten ( x );
      WRITE "x now = ", x ;
      END ;
C;
      SUBROUTINE addten ( a ) ;
      WRITE "start subroutine" ;
      a = a + 10 ;
      WRITE "end subroutine" ;
      RETURN ;
      END ;
\

Start
x = 7
start subroutine
end subroutine
x now = 17

If a call cites a numerical value instead of a variable, it is passed as 
call-by-value, that is, the value will be passed to the subroutine as the 
value of the corresponding formal variable, and the data discarded when the subroutine finishes.  

When an array is passed as a parameter, the current attributes of the array are 
used in the subroutine and so re-specifying the array dimensions is not  
necessary. However the array can be redeclared in the subroutine as a safeguard,
in keeping with common practice. 

Within a subroutine, dimension extents can be dynamically specified using 
simple integer variables from the argument list. This is useful as the subroutine can then be used again in other programs without having to edit it each time.

Subroutines can be nested four deep and called recursively. They must not have 
the same name as an array.


Functions are a specialised form of subroutine and defined much the same way, with
the addition of specifying the datatype.
When executed, the function has the use of a variable of the same name and datatype, 
with the contents being returned to the calling statement.

Note that functions have been implemented with some concessions. 
Functions are evaluated left-to-right before the rest of the calling statement 
is processed, and a total of four can be called in one statement. 
Also, arguments for subroutines must be separated by spaces, but arguments 
in function calls are separated with commas only. 


Here the subroutine above is implemented as a function.

     PROGRAM funcaddten;
C;
      WRITE "Start" ;
      INTEGER*2 x, y ;
      x = 7 ;
      y = 0 ;
      WRITE "x = ", x, " y = ", y ;
      y = addten(x) ;
      WRITE "x = ", x, " y = ", y ;
      END ;
C;
      INTEGER*2 FUNCTION addten ( a ) ;
      WRITE "start function" ;
      addten = a + 10 ;
      WRITE "end function" ;
      RETURN ;
      END ;
\

Start
x = 7 y = 0
start function
end function
x = 7 y = 17




Example

This example was originally on another system (MONECS) to show in-situ matrix inversion using single precision, and is used here with kind permission of the author LG Whitehouse.  The subroutine calculates the inverse of a 5x5 array (matrix), as used in solving simultaneous linear equations. The product is calculated as a check, and subroutines are also used to load data and print the input, output and product matrices. There are some superficial changes to suit, the use here is as an example of using subroutines.


C;
      PROGRAM lineqform;
      DOUBLE a(5,5), b(5,5), c(5,5) ;
      DOUBLE s, det ;
      INTEGER*4 i, j, k, idim ;
      idim = 5 ;
C;
      CALL loadmatrix ( a ) ;
      WRITE ;
      WRITE "Input matrix" ;
      WRITE ;
      CALL printmatrix ( a, idim ) ;
      DO 10 i = 1, idim ;
      DO 10 j = 1, idim ;
      b(i,j) = a(i,j) ;
   10 CONTINUE ;
C;
      CALL invert ( b, idim, det ) ;
C;
      DO 20 i = 1, idim ;
      DO 20 j = 1, idim ;
      s = 0.0 ;
      DO 15 k = 1, idim ;
   15 s = s + a(i,k) * b(k,j) ;
   20 c(i,j) = s ;
      WRITE ;
      WRITE "Results" ;
      WRITE ;
      WRITE "determinant = ", det ;
      WRITE ;
      WRITE "Inverse" ;
      WRITE ;
      CALL printmatrix ( b, idim ) ;
      WRITE ;
      WRITE "Product" ;
      WRITE ;
      CALL printmatrix ( c, idim ) ;
      END ;
C;
      SUBROUTINE invert ( a, idim, det ) ;
      DOUBLE a(idim,idim), d, amax, x, g ;
      DOUBLE t ;
      INTEGER*4 i, j, m, n, irow, jcol ;
      INTEGER*4 pivot(20), row(20), col(20) ;
      det = 0.0 ;
      d = 1.0 ;
      n = idim ;
      DO 10 i = 1, n ;
   10 pivot(i) = 1 ;
C;
      DO 50 m = 1, n ;
C;
      amax = 0.0 ;
      DO 20 j = 1, n ;
      IF ( pivot(j) = 0 ) GO TO 20 ;
      DO 20 i = 1, n ;
      IF ( pivot(i) = 0 ) GO TO 20 ;
      g = ABS a(i,j) ;
      IF ( amax >= g ) GO TO 20 ;
      amax = g ;
      irow = i ;
      jcol = j ;
   20 CONTINUE ;
      IF ( amax < 0.00000001 ) RETURN ;
C;
      pivot(jcol) = 0 ;
      IF ( irow = jcol ) GO TO 30 ;
C;
      d = - d ;
      DO 25 j = 1, n ;
      x = a(irow,j) ;
      a(irow,j) = a(jcol,j) ;
   25 a(jcol,j) = x ;
   30 row(m) = irow ;
      col(m) = jcol ;
      amax = a(jcol,jcol) ;
      d = d * amax ;
      t = ABS d ;
      IF ( t < 0.00000001 ) RETURN ;
C;
      amax = 1.0 / amax ;
      a(jcol,jcol) = 1.0 ;
      DO 40 j = 1, n ;
   40 a(jcol,j) = a(jcol,j) * amax ;
C;
      DO 50 i = 1, n ;
      IF ( i = jcol ) GO TO 50 ;
C;
      x = a(i,jcol) ;
      a(i,jcol) = 0.0 ;
      DO 45 j = 1, n ;
   45 a(i,j) = a(i,j) - x * a(jcol,j) ;
   50 CONTINUE ;
      det = d ;
C;
      m = n ;
   51 irow = row(m) ;
      jcol = col(m) ;
      IF ( irow = jcol ) GO TO 60 ;
      DO 55 i = 1, n ;
      x = a(i,irow) ;
      a(i,irow) = a(i,jcol) ;
   55 a(i,jcol) = x ;
   60 m = m - 1 ;
      IF ( m > 0.0 ) GO TO 51 ;
      RETURN ;
      END ;
C;
C;
      SUBROUTINE loadmatrix ( a ) ;
      a(1,1) = 0.041668 ;
      ...
      ...
      a(5,5) = 0.395988 ;
      RETURN ;
      END ;
C;
C;
      SUBROUTINE printmatrix ( r, idim ) ;
      INTEGER*4 j, p ;
      FORMAT (5F12.8,/) ;
      DO 10 j = 1, 5 ;
      WRITE (6,0)  ( r(j,p), p = 1, 5 ) ;
   10 CONTINUE ;
      RETURN ;
      END ;
\


Input matrix

  0.04166800  0.19445100  0.19912000  0.73498000  0.67651100
  0.06158700  0.84717000  0.03664000  0.98924200  0.79252400
  0.11768000  0.11054000  0.22253600  0.58110700  0.16348900
  0.79508200  0.47063000  0.81649900  0.30149100  0.18643400
  0.80432700  0.70817000  0.13764400  0.26360700  0.39598800

Results

determinant = 0.07822067

Inverse

  0.44987731 -1.42634679  1.40131425 -0.74054601  1.85619416
 -1.73032787  1.82195217 -1.00791263  0.71814048 -0.61228664
  0.19153324  0.48494498 -1.25432256  1.77010047 -1.61329093
 -0.52919504 -0.11617152  2.80560573 -0.68076180  0.29876273
  2.46637257 -0.45235910 -2.47550111  0.05779336  0.21192517

Product

  1.00000000  0.00000000  0.00000000 -0.00000000 -0.00000000
  0.00000000  1.00000000  0.00000000 -0.00000000 -0.00000000
 -0.00000000  0.00000000  1.00000000  0.00000000 -0.00000000
 -0.00000000  0.00000000  0.00000000  1.00000000 -0.00000000
 -0.00000000  0.00000000  0.00000000  0.00000000  1.00000000


Function example

This example calculates distance between map points using a function. 

The latitude and longitude of the origin and destination are passed to the function, 
and the result is returned. The formula used is the arc distance formula

Distance = acos( sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(long2 - long1) )

and has been constituted over several lines. The input values in degrees are converted to radians and back again for the output. It is included only to show a function using four parameters.

      PROGRAM inpgpsdist;
C;
      DOUBLE xo, yo, xd, yd, r ;
      DOUBLE qf ;
      WRITE "Start" ;
      r = 57.29577951 ;
      WRITE "What is the Origin latitude ?" ;
      READ xo ;
      WRITE ;
      WRITE "What is the Origin longitude ?" ;
      READ yo ;
      WRITE ;
      WRITE "Destination latitude ?" ;
      READ xd ;
      WRITE ;
      WRITE "Destination longitude ?" ;
      READ yd ;
      WRITE ;
      WRITE ;
      WRITE "Origin latitude is ", xo ;
      WRITE "Origin longitude is ", yo ;
      WRITE "Destination latitude is ", xd ;
      WRITE "Destination longitude is ", yd ;
      xo = xo / r ;
      yo = yo / r ;
      xd = xd / r ;
      yd = yd / r ;
      qf = dist(xo,yo,xd,yd) ;
      FORMAT (A,F6.1,A,/) ;
      WRITE (6,0) "Distance is ", qf, " km" ;
      END ;
C;
      DOUBLE FUNCTION dist ( a, b, c, e ) ;
      DOUBLE l, m, z, t, r ;
      r = 57.29577951 ;
      m = SIN a * SIN c ;
      l = ( b - e );
      l = COS l ;
      t = COS a * COS c * l ;
      z = m + t ;
      z = ACOS z ;
      z = z * 1.852 * 60.0 * r ;
      dist = z ;
      RETURN ;
      END ;
\

Start
What is the Origin latitude ?
-37.814107

What is the Origin longitude ?
144.96328

Destination latitude ?
-33.8674869

Destination longitude ?
151.2069902


Origin latitude is -37.81410700
Origin longitude is 144.96328000
Destination latitude is -33.86748690
Destination longitude is 151.20699020
Distance is  712.9 km




6. Control and flow : Loops, IFs, and GO TOs

Apart from subroutine calls and functions, processing sequence and flow 
is controlled by loops, conditional tests and branching.

DO loops are used as normal. The initial DO statement specifies the loop end 
statement, the variable to use as a counter,  the start count, the end count,
and optionally the increment value. The loop end statement is identified by a 
numerical label and must be within the program or subroutine. The count parameters 
can be numbers or integer variables (four byte), and the increment value 
defaults to 1 if not set.


      PROGRAM doloopfin;
C;
      INTEGER*4 count, start, finish ;
      WRITE "do loop" ;
      start = 4 ;
      finish = 8 ;
      DO 10 count = start, finish ;
      WRITE "count = ", count ;
   10 CONTINUE ;
      END ;
\

do loop
count = 4
count = 5
count = 6
count = 7
count = 8
 
Loops can be nested in multiple levels, with a limit set by the software version.
The same loop end statement can be used by multiple loop levels.  The loop end 
must not be a branching statement and it is strongly recommended that
a CONTINUE is used as a convention.


      PROGRAM doloops ;
C;
      INTEGER*4 i, j, k ;
      WRITE "Yo!  Loops! " ;
      DO 10 i = 1, 2 ;
      DO 10 j = 1, 2 ;
      DO 10 k = 1, 2 ;
      WRITE "Like..  i=", i," j=", j," k=", k ;
   10 CONTINUE ;
      END ;
\

Yo!  Loops!
Like..  i=1 j=1 k=1
Like..  i=1 j=1 k=2
Like..  i=1 j=2 k=1
Like..  i=1 j=2 k=2
Like..  i=2 j=1 k=1
Like..  i=2 j=1 k=2
Like..  i=2 j=2 k=1
Like..  i=2 j=2 k=2

Conditional tests

Conditional testing is implemented with IF statements, which have a test (condition) before the rest of the line. If the test evaluates to true, the rest of the line is processed, otherwise it is ignored. 
The rest of the line is usually a simple assignment, a call to a subroutine 
or a return, a command to set a port pin state, or a GO TO. 

The tests are for conditions of 

	equal-to 			( = ),
	not-equal-to 			( != ),
	less-than 			( < ), 
	less-than-or-equal-to 		( <= ),
	greater-than 			( > ), 
	greater-than-or-equal-to 	( >= ). 

The statement must follow the set layout, with one test and simple operands,  
however two arithmetic conditions can be included using a logical operator :

	and 	( & ) 
	or 	( | ).

      PROGRAM ifasgn;
C;
      INTEGER*4 altitude, stage ;
      WRITE "Ignition..." ;
      WRITE "Lift off !" ;
      stage = 1 ;
      DO 10 altitude = 0, 100, 10 ;
      IF ( altitude > 20 ) stage = 2 ;
      IF ( altitude > 70 ) stage = 3 ;
      WRITE "Stage ", stage, " and altitude is ", altitude ;
   10 CONTINUE ;
      END;
\

Ignition...
Lift off !
Stage 1 and altitude is 0
Stage 1 and altitude is 10
Stage 1 and altitude is 20
Stage 2 and altitude is 30
Stage 2 and altitude is 40
Stage 2 and altitude is 50
Stage 2 and altitude is 60
Stage 2 and altitude is 70
Stage 3 and altitude is 80
Stage 3 and altitude is 90
Stage 3 and altitude is 100


Unconditional branching 

A direct CALL or RETURN will instigate or return from a subroutine as already discussed.
A GO TO command transfers program flow to the destination statement, which must have a numerical label id. The destination must be within the subroutine. 
 
      PROGRAM goto;
C;
      WRITE "Dig it !" ;
      GO TO 10 ;
      WRITE "Austin who ?" ;
   10 WRITE "Yeah, baby !" ;
      END ;
\

Dig it !
Yeah, baby !

Note that, the original standard requires that a GO TO statement within a loop can 
transfer to a statement that is apparently outside the loop, (past the loop end point) and then jump back again with the loop state still remaining active. To avoid unstructured code it is recommended that GO TOs be avoided if possible, or at least kept local and within any loop.



7. Input, Output, Formats

Often I/O will be at a low level that uses specific peripheral modules, and the defined commands used are covered in the next section. Otherwise READ (input) and WRITE (output) commands are used as a generic mechanism. A modified subset of Fortran 90 is used as a guide.
  
At a basic level, the READ and WRITE commands use defaults for details and data formatting. For more specific control, optional information is used to set the required parameters.

The basic commands using the defaults are described first. The I/O is assumed to use the chip uart, connected to a serial terminal (console).


WRITE

A WRITE sends the values of variables to the console. The datatype of each variable determines the format used. Quoted text strings can be included. Asterisks signify default values
eg

      WRITE (*,*) "a = ", a ; 

A shorter version can be used for convenience.

      WRITE "a = ", a ;


READ

A basic READ command reads keypresses from the keyboard and ends on a carriage return typed by the user. The input is then converted to the datatype of the variable specified. If a negative sign is required it must be the first character input.

eg
      READ a ;


      PROGRAM defaultformat;
C;
      INTEGER*4 a ;
      REAL b ;
      DOUBLE c ;
      CHARACTER*1 d ;
      a = 1222333444 ;
      b = 3.12345678 ;
      c = 45.12345678 ;
      d = 'T' ;
      WRITE "start" ;
      WRITE "a = ", a ;
      WRITE "b = ", b ;
      WRITE "c = ", c ;
      WRITE "d = ", d ;
      WRITE "Now change them.." ;
      WRITE "What integer is a ?";
      READ a ;
      WRITE "What real number is b ?";
      READ b ;
      WRITE "What precise number is c ?";
      READ c ;
      WRITE "What character is d ?";
      READ d ;
      WRITE "a = ", a ;
      WRITE "b = ", b ;
      WRITE "c = ", c ;
      WRITE "d = ", d ;
      WRITE "finish" ;
      END;
\

start
a = 1222333444
b = 3.123457
c = 45.12345678
d = T
Now change them..
What integer is a ?
54
What real number is b ?
3.44
What precise number is c ?
3.14159265
What character is d ?
W
a = 54
b = 3.440000
c = 3.14159265
d = W
finish


Using the parameters for specific formatting.

Formatted I/O requires a format descriptor to use for each variable. The format decriptors are stored as a sequential list using a FORMAT statement. When the WRITE command is processed, each variable is examined and output in turn, using the next format descriptor in the list. If the format list finishes, it is re-used as necessary.

The subsequent WRITE command then includes two extra parameters to identify in turn the hardware or file type (eg, a user terminal or disk), and the format list, before the variables to use. 

The first parameter, the number identifying the file/hardware type, can be left as '*' or '6' (terminal) at this stage. 

The second specifies which format list to use. In this version a zero ( ie '0' instead of the asterisk ) is used to simply point to the most recent format statement. More extensive versions can juggle multiple format lists.

Format descriptors are short strings citing I (integer), F (floating point), and A (alphanumeric). The letter is followed by numbers for the number of digits and decimal places. 

Eg

	I6	Integer of six digits.
	F6.2	Real number with six digits and two decimal places.
	A1	One ASCII character.
	A	Text string.
 

A number before the descriptor will repeat it. Other characters in a format descriptor are passed to the underlying C format command so some experimentation is possible, eg, E will produce exponential (scientific) format. If necessary the descriptors can be separated by simple actions to some extent, eg, an X will insert a space. A forward slash (/) specifies an end-of-record and will reinitialise the list, and output a carriage return/newline. The numeric output fields are right-justified with blanked leading zeros.

Text strings (to 32 characters) can be included in the variable list and must have a matching single A format.


The example has a few outputs.

      PROGRAM specformat;
C;
      INTEGER*4 a ;
      REAL b ;
      DOUBLE c ;
      CHARACTER*1 d, e, f, g ;
      a = 1222333444 ;
      b = 3.12345678 ;
      c = 45.12345678 ;
      d = 'T' ;
      e = 'H' ;
      f = 'E' ;
      g = ' ' ;
      WRITE "start" ;
C;
      FORMAT (I10,/) ;
      WRITE (6,0) a ;
C;
      a = 1234 ;
      WRITE (6,0) a ;
      WRITE (6,0) a, a, a ;
C;
      FORMAT (3I10,/) ;
      WRITE (6,0) a, a, a ;
C;
      FORMAT (F8.6,/) ;
      WRITE (6,0) b ;
      FORMAT (F8.2,/) ;
      WRITE (6,0) b ;
      FORMAT (F10.8,/) ;
      WRITE (6,0) c ;
      FORMAT (A,F5.3,A,/) ;
      WRITE (6,0) "The value of c is ", c, " degrees. " ;
C;
      FORMAT (E10.8,/) ;
      WRITE (6,0) c ;
      FORMAT (E20.4,/) ;
      WRITE (6,0) c ;
C;
      FORMAT (A1) ;
      WRITE (6,0) d, e, f, g ;
      WRITE "finish" ;
      END;
\

start
1222333444
      1234
      1234
      1234
      1234
      1234      1234      1234
3.123457
    3.12
45.12345678
The value of c is 45.123 degrees.
4.51234568e+01
          4.5123e+01
THE finish

Repetition using implied loop

A WRITE statement can acess array elements using an implied loop. This is useful
with repeat descriptors.

Eg 

      FORMAT (64A1,/) ;
      WRITE (6,0) ( page(x,y), x = 1, 64 ) ;


Here the A1 descriptor is used 64 times before the next line, as the index variable x increments from 1 through 64. So, this will print 64 characters from the array and then start the next line.

Note that with an implied loop the list must be enclosed in brackets.  Currently there is a limit of one implied loop per write statement. It must follow non-loop variables.


The example subroutine presents a pre-filled array row by row.

      ...
C;
      SUBROUTINE plott ( page );
      INTEGER*4 x, y ;
      FORMAT (64A1,/) ;
      DO 10 x = 1, 64 ;
      page(x,1) =  '-' ;
      page(x,11) =  '-' ;
   10 page(x,21) =  '-' ;
      DO 30 y = 1, 21 ;
      page(1,y) =  '!' ;
   30 page(64,y) =  '!' ;
      DO 40 y = 1, 21 ;
      WRITE (6,0) ( page(x,y), x = 1, 64 ) ;
   40 CONTINUE ;
      RETURN ;
      END;
\


!--------------------------------------------------------------!
!             ****                                             !
!          ***    ***                                          !
!        **          **                                        !
!      **              **                                      !
!     *                  *                                     !
!    *                    **                                   !
!  **                       *                                  !
! *                          *                                 !
!*                            *                                !
!--------------------------------------------------------------!
!                               **                            *!
!                                 *                         ** !
!                                  *                       *   !
!                                   *                     *    !
!                                    **                 **     !
!                                      *               *       !
!                                       **           **        !
!                                         ****    ***          !
!                                             ****             !
!--------------------------------------------------------------!



8. Ports + peripherals

Low level port pin access, LCD panel, A-D value acquisition and optionally keypad I/O 
is provided. Higher level access to peripherals, files etc will also use the READ/WRITE I/O already discussed. The crystal and SPI pins are provisionally reserved.


Port pins are read or set with the Bxxx command keywords. 

To set a port pin state on or off the relevent bit is set up or down :

BDXY	Bit Down on port X pin Y
BUXY	Bit Up on port X pin Y

eg
 	BDB5 set bit low on port B bit 5.
and	BUB5 will set it high.


      PROGRAM pinblipd;
C;
   10 CONTINUE ;
      BUB5 ;
      BDB5 ;
      GO TO 10 ;
      END ;
\



Similarly a port pin state is read using R instead. The command acts as a function returning an integer value.

wg	
	n = BRB5 ;

will read port B pin 5 and return 1 or 0 as appropriate.


      PROGRAM pinled;
C;
   10 CONTINUE ;
      IF ( BRB8 = 1 ) BUB5 ;
      IF ( BRB8 = 0 ) BDB5 ;
      GO TO 10 ;
      END ;
\



In the '170 version port B pins are accessable. The port 'B' identifier is automatically set so the B is ignored and a generic X can be used instead.


To read a 4x4 keypad, it is envisaged that a written function scans and reads port B lines and returns the key value.


ADC

Analog-digital readings are taken with the chip 10-bit A-D converter. In this version it runs contiuously in the background, and measures the potential on pin 6 (AN4, RB2) and 7 (AN5, RB3) in alternation. 

The results are stored in a continuously updating buffer as standard two-byte integers in the range 0 - 1023. They are accessed using the ADxx command which refers to the analog input # eg

      PROGRAM readport;
C;
      INTEGER*2 a, b, c ;
      INTEGER*4 i, j ;
      WRITE "start" ;
      DO 20 i = 1, 50 ;
      DO 10 j = 1, 3000 ;
   10 CONTINUE ;
      a = ADX4  ;
      b = ADX5  ;
      c = BRX5 ;
      WRITE "a = ", a, " b = ", b, " c = ", c ;
   20 CONTINUE ;
      END;
\

start
a = 15 b = 16 c = 1
a = 21 b = 21 c = 1
a = 22 b = 22 c = 1
a = 28 b = 27 c = 1
a = 24 b = 23 c = 1
a = 22 b = 24 c = 1
...

reads AN4, AN5 and the logic state of RB5. 

The ADC minimum and maximum reference levels are set to the ADC power supply, which should be filtered to prevent noise. If filtering resistors are used, the current drawn by the ADC (a few milliamps) and consequent voltage drop will cause a small voltage offset from the main power levels. 

In this version/ic the analog pins have initial precedence as ANxx. The third character id for larger analog input id's and is redundant here, it is ignored and a generic C or X can be used.
 
Note that using a digital port command (eg BDB2) on a pin used for analog input will then set that pin configuration to be digital and clobber the analog sampling. 

The ADc has a range of 1024 points so for a 3.2 volt ceiling the step size (quantum) will be a little over 3 millivolts. If nothing is connected to the input pin it is normal to see noise and the potential float around a bit. It is important to safeguard the analog inputs with overvoltage and noise filters.


      PROGRAM voltz;
C;
      INTEGER*4 i ;
      REAL x, y ;
      FORMAT (A,F5.3,A,/);
      DO 10 i = 1, 100 ;
      CALL delay() ;
      x = FLOAT ( ADX4 ) ;
      x = ( x * 3.260 ) / 1024.0 ;
      WRITE (6,0) "Cell is at ", x, " volts" ;
   10 CONTINUE ;
      END ;
C;
      SUBROUTINE delay ( ) ;
      INTEGER*4 i ;
C wait ;
      DO 10 i = 1, 8000 ;
   10 CONTINUE ;
      RETURN ;
      END ;
\

Cell is at 0.073 volts
Cell is at 0.067 volts
Cell is at 0.054 volts
Cell is at 0.073 volts
Cell is at 0.096 volts
Cell is at 0.073 volts
...
...


LCD

LCD output is set up for the standard Hitachi-compatible 16x2 LCD modules.

To use the module, firstly it is initialized with the LCDSTRT command, then
command LCDPRNT will write to the display. The LCDPRNT is very similar to the
WRITE using default formats and is used the same way. 

      PROGRAM lcdhello;
C;
      LCDSTRT ;
      LCDPRNT "Hello, world    "  ;
      END;
\

Floating point numbers are displayed with a conservative format with two decimal places (REAL) and three decimal places (DOUBLE) respectively.

At the finish of the command the display cursor returns to the start of the first display line. To move to the next line, the command LCDNWLN is used, and the following LCDPRNT then writes to the next line.

      PROGRAM lcdhello2line;
C;
      LCDSTRT ;
      LCDPRNT "Hello, world    " ;
      LCDNWLN ;
      LCDPRNT "Yeah, baby !!   "  ;
      END;
\


The updates are only as long as the new line content, so it can be a good idea to include extra space characters, to overwrite and refresh the full line. The modules are fairly slow to update so a delay is advisable for repetition.




9. Appendix

Error messages

Error #1 - incorrect datatype

	The variable type was not consistent with the action. Use FLOAT/DBLE/IFIX etc if needed.

Error #2 - array bounds error

	An array index value was zero or larger than the declared array range.
        
Error #3 - variable max exceeded

	Tha value was larger than the datatype maximum.
        
Error #4 - invalid hardware unit

	A formatted I/O has tried to use an illegal hardware type.
        
Error #5 - variable not found

	An expression has used a variable that has not been declared.
 
Error #%d - variable already specified

	A variable has been declared already.
        
Error #6 - statement not found

	An identified statement has been cited by id, but no statement with that id 	exists.    
  
Error #8 - variable datatype unknown

	A variable's datatype was not found. 
          
Error #9 - variable not defined  

        The variable is unknown.

Error #10 - expected array

	A variable that is not an array has been used instead of an array.
        
Error #11 - invalid option

	The option used is not available.
        
Error #12 - mismatched datatype

	An accidental attempt was made to mix integer and floating point math.
 
Error #13 - divide by zero

	A division by zero was attempted.
        
Error #14 - memory limit exceeded

	The variables declared use too much memory.
          
Error #15 - expected INTEGER*4

	Variables used as loop counters and array indexes must be INTEGER*4.
        
Error #16 - subroutine level exceeded

	Too many subroutines called.
         
Error #17 - subroutine definitions exceeded

	Too many subroutines defined.
        
Error #18 - loop nesting level exceeded

	Too many loops called.        

Error #19 - loop range past integer*4

	A loop variable will exceed the integer*4 limit.

Error #20 - illegal character detected near ...

	The input file has an illegal character eg tab.
        
Error #21 - syntax error 

	A misformed command.
       
Error #22 - line length exceeded

	The input line has too many characters.
      
Error #23 - file length exceeded  

	The input file (program) is too long.      

Error #24 - dimension error

	An array has been referenced with incorrect number of dimensions.
        
Error #26 - last line end/start space

	A space character was detected after the end of a line.
         
Error #27 - check input line

	The input line has a misformed sequence or typo.    

Error #28 - negative root     

	Attempt to find the square root of a negative number.   

Error #29 - numbr of variables exceeded

	A program/subroutine has tried to define too many variables.
        
Error #30 - subroutine not found  

	The subroutine called does not exist.
         
Error #34 - id'd lines exceeded

	A program/subroutine has tried to set too many lines with id number (labels).

Error #36 - missing quote

	A closing quotation mark was not found within range.

Error #37 - space expected 

	A space was expected, eg 'WRITE (...)' not 'WRITE(...)' .
          
Error #38 - not implemented in this  version

	Feature is not available.

Error #40 - eeprom not erased

	The eeprom was not cleared so a save is not possible. It usually means a power supply problem (likely), or that the eeprom is worn out (unlikely).
	

Error - unspecified 

	Generic error message.



Setting up

The IC containing Electride will probably be on a circuit board or hardware 
with the required power supply and serial/usb connection. 

Pin 11		RB4		Serial (TTL RS232) out to terminal
Pin 12		RA4		Serial (TTL RS232) in from terminal

As well, a remote serial terminal will be needed, probably a PC running something like Tera Term. The PC usb/serial bridge may require setting up with the vendors usb .inf file. 
 
The serial connection then uses a PC COMx serial port, eg COM5, and the parameters should be set to 38400 bps, 8 bits, no parity, 1 stop bit, and no flow control/handshake. 

Configuration

Trial pic32mx170b SDIP version, configuration B :

IC pin		Used as		Connected to

01		/MCLR		Reset
02		nc
03		nc
04		RB0		Digital 0 / LCD D7
05		RB1		Digital 1 / LCD D6
06		RB2/AN4		Digital 2 / Analog ADC4
07		RB3/AN5		Digital 3 / Analog ADC5
08		Vss		Ground
09		nc		Reserved for crystal
10		nc		Reserved for crystal
11		RB4		Serial out to terminal / Digital 4
12		RA4		Serial in from terminal / Run mode
13		Vdd		Power 3.3v Tank+bypass capacitors
14		RB5		Digital 5
15		RB6		Digital 6 / LCD D5
16		RB7		Digital 7 / LCD D4
17		RB8		Digital 8 / LCD Enable
18		RB9		Digital 9 / LCD RegSelect
19		Vss		Ground
20		Vcap		Capacitor 10uF low esr
21		RB10		Digital A / LCD ReadWrite
22		RB11		Digital B 
23		RB12		Digital C
24		RB13		Digital D
25		RB14		Digital E
26		RB15		Digital F
27		AVss		ADC ground -
28		AVdd		ADC power +, filter+bypass capacitors


Version limits

Trial version :

program length			4k
memory for variables		8k
subroutine definitions		8
subroutine nest limit		4
variables / sub			32
array dimensions 		3
id'd lines / sub		20
loop nest depth			7


These are arbitrary values designed to allow small archetype programs to run. The scale-enabling algorithms are removed. Just over half the IC (PIC32MX170B) resources are used.

The main version runs on a larger PIC32MX795/695 IC at nearly double the speed and has much larger limits. The software should also port to a PIC32MZ easily.



Differences from mainstream.

The scope is very much smaller than the traditional areas of fortran. So Electride is not meant to be a fast number cruncher, run economic or weather modelling, support Cape Canaveral, run a spreadsheet, pilot a UAV or run audio and video codecs!. The memory limits are small, and in particular it is an interpreted system which means the execution speed is much slower than compiled code. Only a basic subset of the language is intended.

Inevitably, for an interpreter to be shoehorned into a microcontroller there are some tradeoffs. As the scope is small several capabilities of larger systems are not particularly needed and so are not present or still pending. 
There are also arbitrary limits set for the intended use and version. On the positive side, several features of later software are included to replace deprecated or obsolete habits. There are also cosmetic differences left in place for differentiation.


The following are not present.

COMMON
EQUIVALENCE
Multiple (continuation) records
Complex numbers
Hollerith strings

To be included :

END DO
DATA 
FILE I/O


The following are updated/changed features.

IMPLICIT NONE is permanently set.
Default-format reporting is included. (F77)
Quoted (double quotes) text string literals to be used in output. (F90)
Lower case is used for variable names.
Variable names can be over six characters. (F77)
Standard comparison operators eg "<" instead of ".LT." (F77)
Format statements are used on a most-recent basis.


The cosmetic differences.

Lines are terminated with semicolons.
A WRITE command has a space separator before the first content (as in F90).


Known errors 

#1. A repeated declaration of a variable will overwrite the first declaration; it should report an error, at least for non-arrays.