PC: Programmers Calculator

This is a simple programmers calculator which operates on 32-bit integer
values in Binary, Octal, Decimal and Hexidecimal number bases.

It supports:

Number types:
  % = Binary                    eg: %1010
  @ = Octal                     eg: @127
  . = Decimal                   eg: .1234
  $ = Hexidecimal               eg: $3FF
 0x = Hexidecimal               eg: 0x3FF
'.' = Character constant        eg: '"'     (max 4 charcters)
"." = Character constant        eg: "'"     (max 4 characters)

Unary operators:
  - = Negation                  eg: -12
  ~ = Bitwise compliment        eg: ~0x80
  ! = Logical compliment        eg: !(VALUE==1)

Binary operators:
  + = Addition                  eg: 1234+5
  - = Subtraction               eg: 1234-5
  * = Multiplication            eg: 1234*5
  / = Division                  eg: 1234/5
  % = Modulus (remainder)       eg: 1234%5
  & = Bitwise AND               eg: $3FF&2
  | = Bitwise OR                eg: $100|2
  ^ = Bitwise exclusive-OR      eg: 0xFF^1
 == = Test for equality         eg: VALUE==1
 != = Test for inequality       eg: VALUE!=1
  < = Test for less-than        eg: VALUE<1
 <= = Test for less-or-equal    eg: VALUE<=2
  > = Test for greater-than     eg: VALUE>3
 >= = Test for greater-or-equal eg: VALUE>=4
 << = Shift-left                eg: 1<<10
 >> = Shift-right               eg: $8000>>4
 && = Logical AND               eg: A&&B    (1 of both A and B are != 0)
 || = Logical OR                eg: A||B    (1 if either A or B is != 0)

 Operators are performed left to right with no precedence. Precedence
 can be forced with brackets, eg: 1+5*2 = 12,  1+(5*2) = 11

Symbol operations:

 SYMBOL = value     = Assign simple value to symbol

 #SYMBOL text       = Assign function to symbol
   use '\' as last character on line to continue function definition to
   next line.
   NOTE: extra whitespace including blank lines is automatically removed when
   the function is stored in memory - keep this in mind when interpreting
   line numbers of errors occuring within functions.

NOTE: Value symbols can be reassigned (with '=') as many times as you
      like with no loss of memory.
      Function '#' symbols can be redefined, however the memory occupied
      by the original definition will be lost until you CLEAR/exit/restart.

 SYMBOL: ...        = Assign line label
   SYNBOL is local to current function and takes precedence over any other
   symbols by the same name - value of symbol is the line number within the
   function. Line label symbols are destroyed when the function terminates.

 Symbols can be accessed simply by using their name as a value element
 within an expression.
 eg:    VALUE = 123
        VALUE + 10      [= 133]

 Function symbols can have parameters which are passed using (..,..)
 eg:    FUNCTION(10)
        FUNCTION(1,VALUE,2)

 [n] can be used to access function parameters, with [0] being the first.
 eg:    #ADD10  [0]+10
        ADD10(123)      [=133]

 [n] when used interactively, accesses the numeric result of the interactive
 calculation n where n is the calculation number shown in the prompt.
 NOTE: PC only keeps the last 256 calculation results.

 [] can be used to access the last interactive calculation result.

Keyword statements:

  BASE [v]      = display/set the default number base (10 until changed)

  BIN [v]       = show value in binary with bit positions
    If no value is specified, last interactive result is displayed.

  CLEAR         = clear to fresh workspace (erases all symbols & stacks)

  GOTO v        = transfer execution to line within function

  IF v : ..     = conditional '..' is executed only if v is !=0

  MEM           = show memory usage

  PRINT {f}v,.. = formatted print: {f} = {[-][0][width]B|O|D|U|H}
    {..} indicates special format (otherwise free-form in default number base)
    -   = Left-justify output
    0   = Fill with '0' (otherwise SPACE)
    w   = Width of output (always specified in decimal)
    B   = Binary output
    O   = Octal output
    D   = signed Decimal output
    U   = Unsigned decimal output
    H   = Hexidecimal output
    ,   = Multiple values can be output if seperated by ','
          If line ends with ',', no newline is output at the end
    "'  = Character strings can be output "as is" (any length).
          If no string between values, single space seperates.
    eg: PRINT {032B}value,"-",{H}value

  QUIT          = Terminate PC and exit

  RETURN [v]    = return from function [with value]
    If no value is given, result of function is indeterminate
    If function ends without a RETURN statement, result of function will be
    that of calculation on last line.

  SAVEA file    = save all symbols to named file

  SAVEC file    = save only changed/added symbols to named file

When you start PC, you can specify one or more files on the command line.
These files will be processed as PC commands before the interactive prompt
is issued, and provide a convienent way to preload value and function
symbols.

You can also provide expressions to be evaluated on the command line in
which case PC evaluates the expressions, displays the results and then
terminates (no interactive session). Please note the following:
  - If a command-line expression contains spaces, you must surround the
    whole expression in double quotes (").
  - If a command-line expression contains the characters <, >, | or &
    you must surround the whole expression in double quotes (").

;
; Sample PC function defintions - save this section to a file and then
; start PC with 'PC file' to make them available.
;

; Return selected byte0-byte3 of 32-bit value: byte0(value)
#BYTE0 [0]&255
#BYTE1 [0]>>8&255
#BYTE2 [0]>>16&255
#BYTE3 [0]>>24

; Swap upper and lower 16-bit chunks in value: swap16(value)
#SWAP16 ([0]>>16)|([0]<<16)

; Swap upper and lower bytes in 16-bit halfwords: swap8(value)
#SWAP8  ([0]&$FF<<8)|([0]&$FF00>>8)|([0]&$FF0000<<8)|([0]&$FF000000>>8)

; Change endian of value (msb<>lsb): endian(value)
#ENDIAN ([0]&$FF<<24)|([0]&$FF00<<8)|([0]&$FF0000>>8)|([0]&$FF000000>>24)

; Rotate left with carry: rol(value, count)
#ROL    a = [0]\
        b = [1]\
loop:   if !b : return a\
        a = (a<<1)|(a>>31)\
        b = b - 1\
        goto loop;

; Rotate right with carry: rol(value, count)
#ROR    a = [0]\
        b = [1]\
loop:   if !b : return a\
        a = (a>>1)|(a<<31)\
        b = b - 1\
        goto loop

; Fast rotate byte right and left with carry over: rolb(value)
#ROLB ([0]<<8)|([0]>>24)
#RORB ([0]>>8)|([0]<<24)

; Calculate exponent: exp(value, power)
#exp    a = 1\
        b = [1]\
loop:   if !b : return a\
        a = a * [0]\
        b = b - 1\
        goto loop

; Calculate factorial: fact(value)
#fact   if [0] > 12 : 0/0\  ; Error if reqest too high
        a = 1\
        b = 1\
loop:   b = b * a\
        a = a + 1\
        if a <= [0] : goto loop\
        return b

