diff --git a/firmware/application/external/calculator/ivt.hpp b/firmware/application/external/calculator/ivt.hpp new file mode 100644 index 00000000..f258bb0b --- /dev/null +++ b/firmware/application/external/calculator/ivt.hpp @@ -0,0 +1,2072 @@ +/* + +## Copyright 2021, zooxo/deetee +All rights reserverd + +## The 3-Clause BSD License + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + IVT (IVEE-TINY) - A FORTH-programable Scientific RPN Calculator + that fits in 8 kilobytes (Arduino, ATTINY85) + + ____________________ + + PREAMBLE + ____________________ + + IVT (IVEE-TINY) is the smallest member of the IV-calculator series (FORTH-like + programable calculators). The name Ivee or IV stands for the roman number 4, + which was the basis for naming FORTH (4th generation programming language). + + The hardware is simple: + - AVR ATTINY85 microcontroller (8192/512/512 bytes of FLASH/RAM/EEPROM) + - OLED-display (128x32, I2C, SSD1306) + - 16 keys touch sensitive keypad (TTP229-BSF, 2-wire) + - CR2032 battery + + IVT was made without making compromises to offer a complete FORTH-like + programming environment and a maximum of mathematical functions (mostly + written in FORTH itself). And it's amazing, how much calculating power fits + into 8 kilobytes: + - 80 intrinsic functions based on FORTH + - A wide range of mathematical and scientific commands + (DUP DROP SWAP ROT OVER / * - + PI SQRT POWER INV INT + EXP LN SIN COS TAN ASIN ACOS ATAN GAMMA P>R R>P nPr nCr) + - Statistics, line best fit and normal distibution (CDF/PDF) + - Present value calculations + - Programming: Up to 16 user definable programs with a total of 440 steps + (< = <> > IF ELSE THEN BEGIN UNTIL) + - A solver to find roots of user defined functions + - A dictionary of all commands, words and programs + - An user definable menu for fast access to all commands, words and programs + - Storing of 10 numbers/constants (permanently) + - Adjustable brightness of the display + + Have fun! + deetee + + ____________________ + + COMPILING + ____________________ + + As IVT consumes 8190 bytes of flash memory (maximal 8192 bytes possible) + compiling with proper settings is essential. Use the Arduino IDE, load the + right library for the ATTINY85 and use the following settings: + + - Library: "attiny by Davis A. Mellis" + - Board: "ATtiny25/45/85 (No bootloader)" + - Chip: "ATtiny85" + - Clock: "8 MHz (internal)" + - B.O.D. Level: B.O.D. Disabled + - Save EEPROM: "EEPROM retained" + - Timer 1 Clock: "CPU (CPU frequency)" + - LTO: "Enabled" + - millis()/micros(): "Disabled" + + ____________________ + + KEYBOARD + ____________________ + + F(MENU) 7(SUM+) 8(PRG) 9(/) + E(SWAP) 4(DICT) 5(USR) 6(*) + N(ROT) 1(RCL) 2(STO) 3(-) + C(CA) 0(PI) .(INT) D(+) + + ____________________ + + LIMITS + ____________________ + + As a microprocessor is primarily not made to do such complex things like + performing a powerful calculator there are some limits in performance and + resources. + Most obvious is the limited precision of the intrinsic float format (IEEE + 754, 32 bit). As four bytes only are used to represent a float respective + double number the decimal digits of precision are limited to 6...7. + + In addition the resources of a microcontroller are limited like the FLASH + memory (holds the executable program code), the RAM memory (holds variables + and data while running) and the EEPROM (holds permanent data like settings + or user programs). Due to the target of maximal calculating power IVT lacks + on many "features of comfort". For example there is no error control - a + division by zero results in a "non interpretable" display. + + However IVT tries to offer a maximum of features, comfort and performance + with a minimum of required resources. + + LIMITS: + 24 ... Maximal data stack size + 7 ... Maximum number of displayed significant digits of a number + 10 ... Maximal amount of numbers saved permanently (0~9) + 16 ... Maximal number of user programs + 440 ... Maximal size (steps) of all user programs + 32 ... Maximal definable command slots of user menu + + ____________________ + + BROWSING MENUS + ____________________ + + To navigate through the menu of some functions (MENU, DICT or USR) + all selectable items are divided into four sections. Every section has its + own up and down key (section I: E/N, section II: 4/1, section III: 5/2 and + section IV: 6/3). + To select one of the four presented items use the appropriate function key + (F/7/8/9) or escape the menu with "C". + There is some kind of hidden feature: If you leave the menu with the D-key + the brightness of the display will be set (0~255, not permanent!). + + ____________________ + + COMMANDS + ____________________ + + BASIC KEYS: + 0~9. ... Digits and decimal point + EE N ... 10-exponent (actually Y*10^X) and negate (change sign) + D ... DUP (push stack) or complete number input + C ... DROP top of stack or clear entry when in number input + F ... Shift to select function keys or double press for user menu + + FUNCTION KEYS: + + - * / ... Basic operations + MENU ... Browse (and select) the user menu + SUM+ ... Enter X-data for statistics or X,Y-data for linear regression + PRG ... Edit program (00~15) - enter program number first + SWAP ... Swap the two top numbers of stack (1 2 -> 2 1) + DICT ... Browse (and select) the complete dictionary + USR ... Set dictionary entry to user menu + ROT ... Rotate stack (1 2 3 -> 2 3 1) + STO RCL ... Store/recall number - enter memory number first (0~9) + CA ... Clear stack and memories for statistics (5~7) + PI ... Push PI to stack + INT ... Integer value + + DICTIONARY (4 sections): + F 7 8 9 ... F-key, numbers + EE 4 5 6 ... 10-exponent, numbers + N 1 2 3 ... NEGATE, numbers + C 0 . D ... Clear/DROP, number, dot, DUP + M S+ PR / ... MENU, SUM+, PRG edit, divide + + >< DC US * ... SWAP, DICT, set USER menu, multiply + RT RC ST - ... ROT, RCL, STO, subtract + CA PI IN + ... CA (clear all), PI, INT, add + IF EL TH PC ... IF, ELSE, THEN, nPr/nCr + < = <> > ... LESSTHAN, EQUAL, NOTEQUAL, GREATERTHAN + + BE UN SO I ... BEGIN, UNTIL, SOLVE, INV (1/X) + c t- E LN ... COS, ATAN, EXP, LN + s t s- c- ... SIN, TAN, ASIN, ACOS + OV SQ yx !L ... OVER, SQRT, POW (y^x), ln(GAMMA) + PV ND P> R> ... Present value, normal distribution, P->R, R->P + + S+ Sc x- LR ... SUM+, SUMclr (clears memories 5~9), MEAN/STDEV, L.R. + 00 01 02 03 ... User programs + 04 05 06 07 ... User programs + 08 09 10 11 ... User programs + 12 13 14 15 ... User programs + + ____________________ + + PV, ND, P<>R, STAT + ____________________ + + PV ... Present value of given interest rate (ie. 0.08) and periods + ND ... PDF (X) and CDF (Y) of standard normal distribution + P> R> ... Polar/Rectangular conversion + STAT ... Mean value (X) and standard deviation (Y). + Note that the memories 5~9 (see RCL/STO) are used as statistic + registers (Sxx, Sxy, n, Sx, Sy). + LR ... Line best fit (y = X * x + Y) + + ____________________ + + PROGRAMMING + ____________________ + + IVT is able to deal with up to 16 user programs (named 00~15) with a total + number of 440 steps/commands. The maximal size per user program rises from + 20 steps (program 00) to 35 steps (program 15). + + To edit a program, enter the program number (00~15) followed by PRG (F-8). + The display shows P (for program), the program number (00~15), the program + step number (vary with cursor keys E and N) and the command of this step. + To insert a program step + - press a key (number or DUP), + - press a shifted key (press F twice to toggle) or + - press DICT (F-4) to select a command from the dictionary. + To delete a program step press ".". + Leave and save the program with "C". + To execute a program select the appropriate program number/name from DICT. + Please note that the first user program (00) will be used by the solver. + + ____________________ + + SOLVER + ____________________ + + To find a root of a function (programmed in user program 00) enter an + appropriate start value and select the command SO from the dictionary. + + ____________________ + + POWER CONSUMPTION + ____________________ + + As IVT provides a maximum of calculating power there where less resources + for a good power management (i.e. idle, screen saver, auto power off) left. + Merely the not needed timer1 and AD-convertes are set off to save + approximately 0.9 mA. + The power consumption of IVT depends mainly on the usage of the display. + That's why per default the brightness of the display is set to minimum. + Please note that you can (not permanently) set the brightness of the display + with pressing D when in any menu (takes brightness value from stack). + In total IVT consumes approximately 9 mA - so a a single battery (CR2032) + which has a capacity of approximately 200 mAh should theoretically work at + least 20 hours. + Electrical current drawn by device (approximately): + - ATTINY85 ... 5 mA + - Keypad ... 2 mA + - Display ... 1~4 mA (promt ~ display full of 8's) + + ____________________ + + PROGRAM EXAMPLES + ____________________ + + ABS: DUP 0 LT IF NEG THEN + FRAC: DUP INT - + SINH: EXP DUP INV NEG + 2 / ... sinh=(exp(x)-exp(-x))/2 + COSH: EXP DUP INV + 2 / ... cosh=(exp(x)+exp(-x))/2 + TANH: 2 * EXP DUP 1 - SWAP 1 + / ... tanh=(exp(2*x)-1)/(exp(2*x)+1) + ASINH: DUP DUP * 1 + SQRT + LN ... asinh(x)=ln(x+sqrt(x*x+1)) + ACOSH: DUP DUP * 1 - SQRT + LN ... acosh(x)=ln(x+sqrt(x*x-1)) + ATANH: DUP 1 + SWAP NEG 1 + / SQRT LN ... atanh(x)=ln(sqrt((1+x)/(1-x))) + POW10: 1 SWAP EE + LOG: LN 1 0 LN / ... log(x)=ln(x)/ln(10) + %: OVER / 1 0 0 * ... %=x/B*100% + CHG%: OVER - OVER / 1 0 0 * ... chg%=(x-B)/B*100% + + QE: OVER 2 / DUP * SWAP - SQRT SWAP 2 / NEG + SWAP OVER OVER - ROT ROT + ... x12=-p/2+-sqrt(p*p/4-q) + + DEG<>RAD: DUP PI * 1 8 0 / SWAP 1 8 0-* PI / + C<>F: DUP 1 . 8 * 3 2 + SWAP 3 2 - 1 . 8 / + KM<>MI: DUP 1 . 6 0 9 3 4 4 DUP DUP ROT SWAP / ROT ROT * + M<>FT: DUP 3 . 3 7 0 0 7 9 DUP DUP ROT * ROT ROT / + CM<>IN: DUP 2 . 5 4 DUP DUP ROT SWAP / ROT ROT * + KG<>LBS: DUP 2 . 2 0 4 6 2 3 DUP DUP ROT * ROT ROT / + L<>GAL: DUP 3. 7 8 5 4 1 2 DUP DUP ROT SWAP / ROT ROT * + + HMS2H: DOT 0 0 0 0 0 1 ADD // Round up to prevent leaps + DUP DUP INT SWAP OVER - 1 0 0 * INT // hh mm + ROT 3 PICK SUB 1 0 0 MULT OVER SUB 1 0 0 MULT, // ss + 3 6 0 0 / SWAP 6 0 / + + // ->s ->h + + H2HMS: DUP 3 6 0 0 * DUP ROT INT // h->s + SWAP OVER 3 6 0 0 * - 60 DIV INT, // hh mm + ROT OVER 6 0 MULT SUB 3 PICK 3 6 0 0 * - // ss + 1 0 0 0 0 / SWAP 1 0 0 / + + // hh.mmss + + nPr = n!/(n-r)! : OVER ROT ROT - 1 ROT ROT SWAP + BEGIN SWAP ROT 1 ROT + DUP ROT * SWAP ROT OVER OVER SWAP LT UNTIL DROP DROP + + nCr = n!/(n-r)!/r! = nPr/r! : DUP ROT SWAP PERM 1 ROT + BEGIN ROT ROT DUP ROT SWAP / ROT ROT 1 + SWAP OVER 1 - OVER SWAP LT UNTIL DROP DROP + + ____________________ + + ATTINY85 PINS + ____________________ + + _____ + Analog0/Reset P5 H1|* U |H8 Vcc + Analog2 P3 H2| |H7 P2 SCK/Analog1 + Analog3 P4 H3| |H6 P1 PWM1/MISO + GND H4|_____|H5 P0 PWM0/AREF/SDA/MOSI + + ____________________ + + CIRCUIT DIAGRAM + ____________________ + + _________________ + | OLED-DISPLAY | + | 128x32 SSD1306 | + |_GND_VCC_SCK_SDA_| + | | | | + | | + __|___|___|___|__ + | GND VCC P2 P0 | + | AVR ATTINY85 | + |_________P3__P1__| + | | + | | + __|___|___|___|__ + | VCC GND SCL SDO | + |Keypad TTP229-BSF| + |-----------------| + | O O O O | + | | + | O O O O | + | O O O O | + | | |... Solder to enable + | O O O O | 16-key-mode (TP2=0) + | | + + +*/ + +// ***** I N C L U D E S + +// #include // I2C wire communication with display +// #include // Needed for (simple) power management +// #include // For saving data to EEPROM + +// ***** F O N T (4x8) + +// Compatibility +#define PROGMEM +typedef uint8_t byte; +typedef bool boolean; +uint8_t dummy = 0; + +class A { + public: + void write(uint32_t ee, uint8_t p) { + (void)ee; + (void)p; + } + uint8_t read(uint32_t ee) { + (void)ee; + return 0; + } + uint8_t& operator[](uint32_t ee) { + (void)ee; + return dummy; + } + size_t length() { + return 0; + } +} EEPROM; + +static void _keyf(void); +static void _num(void); +static void _e(void); +static void _neg(void); +static void _drop(void); +static void _dot(void); +static void _dup(void); +static void _menu(void); +static void _sumadd(void); +static void _prgedit(void); +static void _div(void); // 16 F-keys + +static void _swap(void); +static void _dict(void); +static void _usrset(void); +static void _mul(void); +static void _rot(void); +static void _mrcl(void); +static void _msto(void); +static void _sub(void); +static void _clr(void); +static void _pi(void); +static void _int(void); +static void _add(void); +static void _condif(void); +static void _condelse(void); +static void _condthen(void); +static void _permcomb(void); // 32 Intrinsic functions +static void _condlt(void); +static void _condeq(void); +static void _condne(void); +static void _condgt(void); + +static void _begin(void); +static void _until(void); +static void _solve(void); +static void _inv(void); +static void _cos(void); +static void _atan(void); +static void _exp(void); +static void _ln(void); +static void _sin(void); +static void _tan(void); +static void _asin(void); +static void _acos(void); // 48 Builtin functions +static void _over(void); +static void _sqrt(void); +static void _pow(void); +static void _gammaln(void); +static void _pv(void); +static void _nd(void); +static void _pol2rect(void); +static void _rect2pol(void); + +static void _sumadd(void); +static void _sumclr(void); +static void _sumstat(void); +static void _sumlr(void); + +#undef __I +#undef __N +#undef __O +#undef __P + +#define PI 3.141592653589793 + +static void _numinput(byte k); +static double dpush(double d); +static double dpop(void); +static void seekmem(byte n); +static double dpush(double d); +static void apush(int addr); +static int apop(void); +static void _condseek(void); +static int eeudist(byte i); +static void _mstorcl(boolean issto); + +template +void EEwrite(int ee, const T& value); + +template +void EEread(int ee, T& value); + +#define pgm_read_byte(_x) (*(_x)) + +// Font table +#define __0 0 +#define __1 1 +#define __2 2 +#define __3 3 +#define __4 4 +#define __5 5 +#define __6 6 +#define __7 7 +#define __8 8 +#define __9 9 +#define __A 10 +#define __B 11 +#define __C 12 +#define __D 13 +#define __E 14 +#define __F 15 +#define __H 16 +#define __I 17 +#define __L 18 +#define __M 19 +#define __N 20 +#define __O 21 +#define __P 22 +#define __R 23 +#define __S 24 +#define __T 25 +#define __U 26 +#define __V 27 +#define __c 28 +#define __s 29 +#define __t 30 +#define ___ 31 // space +#define __DOT 32 +#define __MULT 33 +#define __ADD 34 +#define __SUB 35 +#define __DIV 36 +#define __EM 37 // ! +#define __LT 38 +#define __EQ 39 +#define __GT 40 +#define __ARROW 41 +#define __FINV 42 +#define __POW1 43 +#define __PI 44 +#define __SQRT 45 +#define __MEAN 46 +#define __SWAP 47 + +#define FONTWIDTH 4 // Font width +const byte font[] PROGMEM = { + 0xff, 0x81, 0x81, 0xff, // 0 + 0x00, 0x02, 0xff, 0x00, // 1 + 0xf9, 0x89, 0x89, 0x8f, // 2 + 0x81, 0x89, 0x89, 0xff, // 3 + 0x0f, 0x08, 0x08, 0xff, // 4 + 0x8f, 0x89, 0x89, 0xf9, // 5 + 0xff, 0x89, 0x89, 0xf8, // 6 + 0x03, 0x01, 0x01, 0xff, // 7 + 0xff, 0x89, 0x89, 0xff, // 8 + 0x0f, 0x89, 0x89, 0xff, // 9 + 0xff, 0x09, 0x09, 0xff, // A + 0xff, 0x89, 0x8f, 0xf8, // B + 0xff, 0x81, 0x81, 0x80, // C + 0x81, 0xfF, 0x81, 0xfF, // D + 0xfF, 0x89, 0x89, 0x81, // E + 0xfF, 0x09, 0x09, 0x01, // F + 0xfF, 0x08, 0x08, 0xfF, // H + 0x81, 0xff, 0x81, 0x80, // I + 0xfF, 0x80, 0x80, 0x80, // L + 0xfF, 0x06, 0x06, 0xfF, // M + 0xfF, 0x0c, 0x18, 0xfF, // N + 0xff, 0x81, 0x81, 0xff, // O + 0xfF, 0x09, 0x09, 0x0f, // P + 0xfF, 0x09, 0xf9, 0x8f, // R + 0x8f, 0x89, 0x89, 0xf8, // S + 0x01, 0xff, 0x01, 0x01, // T + 0xfF, 0x80, 0x80, 0xfF, // U + 0x1F, 0xf0, 0xf0, 0x1F, // V + 0xfc, 0x84, 0x84, 0x84, // c + 0x9c, 0x94, 0x94, 0xf4, // s + 0x04, 0xff, 0x84, 0x80, // t + 0x00, 0x00, 0x00, 0x00, // space + 0xc0, 0xc0, 0x00, 0x00, // . + 0x24, 0x18, 0x18, 0x24, // * + 0x10, 0x38, 0x10, 0x00, // + + 0x08, 0x08, 0x08, 0x08, // - + 0xc0, 0x30, 0x0c, 0x03, // / + 0x00, 0xbf, 0x00, 0x00, // ! + 0x08, 0x14, 0x22, 0x41, // < + 0x14, 0x14, 0x14, 0x14, // = + 0x41, 0x22, 0x14, 0x08, // > + 0x7f, 0x3e, 0x1c, 0x08, // arrow + 0xff, 0xc1, 0xf5, 0xff, // inverted F + 0x08, 0x08, 0x02, 0x1f, // ^-1 + 0x0c, 0xfc, 0x04, 0xfc, // PI + 0x10, 0xff, 0x01, 0x01, // sqrt + 0xda, 0x22, 0x22, 0xda, // mean + 0x22, 0x72, 0x27, 0x22, // swap +}; + +// ***** D I S P L A Y + +// DEFINES +#define CONTRAST 0x00 // Initial contrast/brightness +#define DADDRESS 0x3C // I2C slave address +#define DPAGES 4 // Lines of screen +#define DCOMMAND 0x00 // Command byte +#define DDATA 0x40 // Data byte +#define SCREENWIDTH 128 // Screen width in pixel +#define MAXSTRBUF 10 // Maximal length of string buffer sbuf[] +#define CHARW 3 // Character width (3 x FONTWIDTH) +#define CHARH 4 // Character height (4 lines) +#define DIGITS 8 // Number of digits when printing a number +#define ALMOSTZERO 1e-37 // Limits to decide if sci or fix +#define FIXMIN 1e-3 // Limits for fix display guarantee maximal +#define FIXMAX 1e7 // number of significant digits +#define PRINTNUMBER 9 // Digits to print number (small space for '.') - printsbuf +#define PRINTMENU 8 // Digits to print menu (pairs of two) - printsbuf + +// VARIABLES +// static byte renderram = 0xB0, drawram = 0x40; // Masks to address GDDRAM of display +static byte sbuf[MAXSTRBUF]; // Holds string to print +static boolean isnewnumber = true; // True if stack has to be lifted before entering a new number +static byte decimals = 0; // Number of decimals entered (input after decimal dot) +static boolean isdot = false; // True if dot was pressed and decimals will be entered + +// MACROS +#define _abs(x) ((x < 0) ? (-x) : (x)) // abs()-substitute macro +#define _ones(x) ((x) % 10) // Calculates ones unit +#define _tens(x) (((x) / 10) % 10) // Calculates tens unit +#define _huns(x) (((x) / 100) % 10) // Calculates hundreds unit +#define _tsds(x) (((x) / 1000) % 10) // Calculates thousands unit + +// SUBPROGRAMS +// static void dbegin(void) { // Initialize communication +// TinyWireM.begin(); +// } +// static void dsendstart(void) { // Start communication +// TinyWireM.beginTransmission(DADDRESS); +// } +// static bool dsendbyte(byte b) { // Send byte +// return (TinyWireM.write(b)); +// } +// static void dsendstop(void) { // Stop communication +// TinyWireM.endTransmission(); +// } +// static void dsenddatastart(void) { // Start data transfer +// dsendstart(); +// dsendbyte(DDATA); +// } +// static void dsenddatabyte(byte b) { // Send data byte +// if (!dsendbyte(b)) { +// dsendstop(); +// dsenddatastart(); +// dsendbyte(b); +// } +// } +// static void dsendcmdstart(void) { // Start command transfer +// dsendstart(); +// dsendbyte(DCOMMAND); +// } +// static void dsendcmd(byte cmd) { // Send command +// dsendcmdstart(); +// dsendbyte(cmd); +// dsendstop(); +// } + +// static const byte initscreen[] PROGMEM = { +// // Initialization sequence +// 0xC8, // Set scan direction (C0 scan from COM0 to COM[N-1] or C8 mirroring) +// 0xA1, // Set segment remap (A0 regular or A1 flip) +// 0xA8, 0x1F, // Set mux ratio (N+1) where: 14> 4)); +// dsendbyte(x & 0x0f); +// dsendstop(); +// } +// static void dfill(byte b) { // Fill screen with byte/pattern b +// dsetcursor(0, 0); +// dsenddatastart(); +// for (int i = 0; i < SCREENWIDTH * DPAGES; i++) dsenddatabyte(b); +// dsendstop(); +// } + +// static byte expand2bit(byte b) { // Expand 2 bits 000000ab +// b = (b | (b << 3)) & 0x11; // 000a000b +// for (byte i = 0; i < 3; i++) b |= (b << 1); // aaaabbbb +// return (b); +// } + +static double pow10(int8_t e) { // Calculates 10 raised to the power of e + double f = 1.0F; + if (e > 0) + while (e--) f *= 10.0F; + else + while (e++) f /= 10.0F; + return (f); +} + +// static void printcat(byte c, byte x) { // Print char c at position x +// for (byte y = 0; y < CHARH; y++) { // Lines (4) +// dsetcursor(x, y); // Set cursor +// for (byte j = 0; j < FONTWIDTH; j++) { // Fontbyte - shifted one pixel down +// byte bits = pgm_read_byte(&font[FONTWIDTH * c + j]); // Fontbyte +// bits = expand2bit((bits >> (y * 2)) & 0x03); // Expand 000000ab +// dsenddatastart(); +// for (byte i = 0; i < CHARW; i++) dsenddatabyte(bits); +// dsendstop(); +// } +// } +// } + +static void printsbuf(byte digits) { // Print "digits" elements of sbuf[] (shrink ".") + int8_t dotx = 0; + for (byte i = 0; i < digits; i++) { + printcat(sbuf[i], /*(FONTWIDTH + 1) * CHARW * i + dotx*/ i); + if (digits == PRINTMENU) { + if (i != 0 && i % 2) dotx += 3; // Menu - group in pairs + } else if (sbuf[i] == __DOT) + dotx = -6; // Number - small space for dot + } +} + +static void sbufclr(void) { // Clear sbuf + for (byte i = 0; i < sizeof(sbuf); i++) sbuf[i] = ___; +} + +static void printnum(double f) { // Print number + int8_t ee = 0; // Fixed format + int8_t e = 1; // Exponent + long m; // Mantissa + sbufclr(); + if (f < 0.0F) { // # Manage sign + f = -f; + sbuf[0] = __SUB; + } + if (f >= ALMOSTZERO && (f < FIXMIN || f >= FIXMAX)) { // # SCI format + ee = log10(f); // Exponent + if (ee < 0) ee--; + f /= pow10(ee); + } + if (f >= 1.0F) e = log10(f) + 1; // Calculate new exponent if (f !< 1) + double a = pow10(7 - e); // # Calculate pre dot + double d = (f * a + 0.5) / a; // Rounding + m = d; + for (byte i = e; i > 0; i--) { + sbuf[i] = _ones(m); + m /= 10; + } + sbuf[e + 1] = __DOT; + if ((long)f >= (long)d) d = f; // # Calculate after dot (and suppress trailing zeros) + m = (d - (long)d) * a + 0.5; + boolean istrail = true; + for (byte i = DIGITS; i > e + 1; i--) { + byte one = _ones(m); + if (!istrail || ((isnewnumber || i - e - 1 <= decimals) && (!isnewnumber || one != 0))) { + sbuf[i] = one; // Assign digit + istrail = false; // End of trailing zeros + } + m /= 10L; + } + if (ee) { // # Scientific exponent if applicable + sbuf[6] = (ee < 0) ? __SUB : ___; + if (ee < 0) ee = -ee; + sbuf[8] = _ones(ee); + sbuf[7] = _tens(ee); + } + printsbuf(PRINTNUMBER); +} + +// ***** K E Y B O A R D + +// Defines +#define KCLOCK 3 // SCL - Keyboard clock pin +#define KDATA 1 // SDO - Keyboard data pin + +#define PREENDKEY 254 // Only evaluate keys smaller +#define ENDKEY 255 // Evaluate keys smaller + +// Variables +static byte key = PREENDKEY, oldkey = PREENDKEY; // Holds entered and old key (prevent keyrepeat) + +// static byte getkey(void) { // Read keypad (TTP229-BSF) +// byte k; +// for (byte i = 0; i < 16; i++) { +// digitalWrite(KCLOCK, LOW); +// k = digitalRead(KDATA); +// digitalWrite(KCLOCK, HIGH); +// if (!k) return (i); +// } +// return (ENDKEY); +// } + +static byte key2nr(byte k) { // Convert key to number on keypad + if (k >= 1 && k <= 3) + return (k + 6); + else if (k >= 5 && k <= 7) + return (k - 1); + else if (k >= 9 && k <= 11) + return (k - 8); + else + return (0); +} + +// ***** A P P L I C A T I O N + +#define RAD (180.0F / PI) // 180/PI ... used for _atan and _cos +#define MAXCMDI 48 // Number of commands of intrinsic functions +#define MAXCMDB 64 // 120 End of builtin commands +#define MAXCMDU 80 // 160 End of user commands +#define ISF 1 // F-key demanded +#define MAXPRG 16 // Maximal number of user programs +#define MAXPRGBUF 36 // Maximal size of prgbuf +#define MEDIMENU 2 // Number of menu entry lines +#define MEDIDICT 5 // Number of dict entry lines + +// EEPROM dimensions and addresses +#define MEMSTO 10 // Number of memories +#define EESTO 0 // Memories (MEMSTOx4 bytes) +#define MENUITEMS 32 // Number of selectable user menu items +#define EEMENU 40 // User menu (MENUITEMS bytes) +#define EEUSTART 72 // User programs +#define EEUEND EEPROM.length() +#define EEU (EEUEND - EEUSTART) // Available user memory +#define UL0 20 // Length of first user program +#define UN 16 // Number of user programs + +// VARIABLES +static boolean isprintscreen = true; // True, if screen should be printed +// static unsigned int mp; // MEMPOINTER (builtin and user functions) +static byte setfgm = 0; // F-key variables +static byte select; // Selected options +static byte medi = 0; // MEnu and DIctionary + +static boolean issetusr = false; // True if dict is used for setting usr menu +static boolean isprgdict = false; // To select dict entry for program +static byte setusrselect; // Stores selected cmd value to store +static boolean isprgedit = false; // True, if program is edited +static byte prgnr; // Number of program edited +static int prgaddr; // Address of recent program in EEPROM (includes EEUSTART) +static byte prgpos; // Position in edited program +static byte prglength; // Size of program in program buffer +static byte prgbuf[MAXPRGBUF]; // Program buffer + +#define DELTAX 1E-4 // Delta for solver +static byte runs; // Solver cycle runs +static boolean issolve = false; // True if solving is demanded + +#define DATASTACKSIZE 24 // DATA STACK +double ds[DATASTACKSIZE]; +static byte dp = 0; + +#define ADDRSTACKSIZE 24 // ADDRESS STACK +static int as[ADDRSTACKSIZE]; +static byte ap = 0; + +byte cl = 0; // CONDITIONAL LEVEL + +// Command code defines +#define _7 1 // Intrinsic commands +#define _8 2 +#define _9 3 +#define _4 5 +#define _5 6 +#define _6 7 +#define _NEG 8 +#define _1 9 +#define _2 10 +#define _3 11 +#define _DROP 12 +#define _0 13 +#define _DOT 14 +#define _DUP 15 +#define _DIV 19 +#define _SWAP 20 +#define _MULT 23 +#define _RCL 25 +#define _STO 26 +#define _ROT 24 +#define _SUB 27 +#define _PI 29 +#define _ADD 31 +#define _IF 32 +#define _ELSE 33 +#define _THEN 34 +#define _LT 36 +#define _NE 38 +#define _INV 43 +#define _BEGIN 40 +#define _UNTIL 41 +#define _COS 44 +#define _ATAN 45 +#define _EXP 46 +#define _LN 47 +#define _SIN MAXCMDI + 0 // Builtin commands (mem) +#define _TAN MAXCMDI + 1 +#define _ASIN MAXCMDI + 2 +#define _ACOS MAXCMDI + 3 +#define _OVER MAXCMDI + 4 +#define _SQRT MAXCMDI + 5 +#define _POW MAXCMDI + 6 +#define _PV MAXCMDI + 7 +#define _SUMADD MAXCMDI + 8 +#define _SUMCLR MAXCMDI + 9 +#define _SUMLR MAXCMDI + 10 +#define _SUMSTAT MAXCMDI + 11 +#define _GAMMALN MAXCMDI + 12 +#define _POL2RECT MAXCMDI + 13 +#define _RECT2POL MAXCMDI + 14 +#define _ND MAXCMDI + 15 +#define _PERMCOMB MAXCMDI + 16 +#define _END 255 // Function delimiter + +// Builtin functions (mem) +const byte mem[] PROGMEM = { + _END, // Necessary to prevent function starting with mp = 0 + _9, + _0, + _SWAP, + _SUB, + _COS, + _END, // 0 SIN =cos(90-x) + _DUP, + _SIN, + _SWAP, + _COS, + _DIV, + _END, // 1 TAN =sin/cos + _DUP, + _MULT, + _INV, + _1, + _SUB, + _SQRT, + _INV, + _ATAN, + _END, // 2 ASIN =atan(1/(sqrt(1/x/x-1)) + _DUP, + _MULT, + _INV, + _1, + _SUB, + _SQRT, + _ATAN, + _END, // 3 ACOS =atan(sqrt(1/x/x-1)) + _SWAP, + _DUP, + _ROT, + _ROT, + _END, // 4 OVER + _DUP, + _0, + _NE, + _IF, + _LN, + _2, + _DIV, + _EXP, + _THEN, + _END, // 5 SQRT =exp(ln(x)/2) + _SWAP, + _LN, + _MULT, + _EXP, + _END, // 6 POW a^b=exp(b*ln(a)) + _OVER, + _1, + _ADD, + _SWAP, + _POW, + _DUP, + _1, + _SUB, + _SWAP, + _DIV, + _SWAP, + _DIV, + _END, // 7 PV PV(i,n)=((1+i)^n-1)/(1+i)^n/i + + _7, + _RCL, + _1, + _ADD, + _7, + _STO, // 8 SUMADD - n + _DUP, + _8, + _RCL, + _ADD, + _8, + _STO, // X + _DUP, + _DUP, + _MULT, + _5, + _RCL, + _ADD, + _5, + _STO, // XX + _OVER, + _MULT, + _6, + _RCL, + _ADD, + _6, + _STO, // XY + _9, + _RCL, + _ADD, + _9, + _STO, + _7, + _RCL, + _END, // Y push(n) + + _0, + _DUP, + _DUP, + _DUP, + _DUP, + _DUP, // 9 CLRSUMCLR + _5, + _STO, + _6, + _STO, + _7, + _STO, + _8, + _STO, + _9, + _STO, + _END, + + _6, + _RCL, + _7, + _RCL, + _MULT, + _8, + _RCL, + _9, + _RCL, + _MULT, + _SUB, // 10 SUMLR - a + _5, + _RCL, + _7, + _RCL, + _MULT, + _8, + _RCL, + _DUP, + _MULT, + _SUB, + _DIV, + _DUP, + _8, + _RCL, + _MULT, + _NEG, + _9, + _RCL, + _ADD, + _7, + _RCL, + _DIV, + _SWAP, + _END, // b + + _8, + _RCL, + _7, + _RCL, + _DIV, // 11 STAT - mean (X/n) + _DUP, + _DUP, + _MULT, + _7, + _RCL, + _MULT, + _NEG, + _5, + _RCL, + _ADD, // stddev (XX-n*m^2)/(n-1) + _7, + _RCL, + _1, + _SUB, + _DIV, + _SQRT, + _SWAP, + _END, + + _1, + _ADD, + _DUP, + _DUP, + _DUP, + _DUP, + _1, + _2, + _MULT, // 12 GAMMALN: ln!=(ln(2*PI)-ln(x))/2+x*(ln(x+1/(12*x-1/10/x))-1) + _SWAP, + _1, + _0, + _MULT, + _INV, + _SUB, + _INV, + _ADD, + _LN, + _1, + _SUB, + _MULT, + _SWAP, + _LN, + _NEG, + _2, + _PI, + _MULT, + _LN, + _ADD, + _2, + _DIV, + _ADD, + _END, + + _DUP, + _ROT, + _DUP, + _COS, + _SWAP, + _SIN, + _ROT, + _MULT, + _ROT, + _ROT, + _MULT, + _END, // 13 P>R y=r*sin(a) x=r*cos(a) + + _DUP, + _MULT, + _SWAP, + _DUP, + _MULT, + _DUP, + _ROT, + _DUP, + _ROT, + _ADD, + _SQRT, // 14 R>P r=sqrt(x*x+y*y) a=atan(y/x) + _ROT, + _ROT, + _DIV, + _SQRT, + _ATAN, + _SWAP, + _END, + + _DUP, + _DUP, + _DUP, + _DUP, + _MULT, + _MULT, + _DOT, + _0, + _7, + _MULT, // 15 ND + _SWAP, + _1, + _DOT, + _6, + _MULT, + _NEG, + _ADD, + _EXP, + _1, + _ADD, + _INV, + _SWAP, // CDF ~ 1/(1+exp(-0.07*x^3-1.6*x)) + _DUP, + _MULT, + _NEG, + _2, + _DIV, + _EXP, + _2, + _PI, + _MULT, + _SQRT, + _INV, + _MULT, + _END, // PDF = exp(-x*x/2)/sqrt(2*PI) + + _DUP, + _ROT, + _SWAP, // 16 PERM COMB + _OVER, + _ROT, + _ROT, + _SUB, + _1, + _ROT, + _ROT, + _SWAP, // PERM + _BEGIN, + _SWAP, + _ROT, + _1, + _ROT, + _ADD, + _DUP, + _ROT, + _MULT, + _SWAP, + _ROT, + _OVER, + _OVER, + _SWAP, + _LT, + _UNTIL, + _DROP, + _DROP, + _DUP, + _ROT, + _1, + _SWAP, // COMB + _BEGIN, + _ROT, + _ROT, + _DUP, + _ROT, + _SWAP, + _DIV, + _ROT, + _ROT, + _1, + _ADD, + _SWAP, + _OVER, + _1, + _SUB, + _OVER, + _SWAP, + _LT, + _UNTIL, + _DROP, + _DROP, + _END, + +}; + +// Command names +static const byte cmd[] PROGMEM = { + __FINV, + ___, + ___, + __7, + ___, + __8, + ___, + __9, // 0 Primary keys + __E, + __E, + ___, + __4, + ___, + __5, + ___, + __6, + __N, + ___, + ___, + __1, + ___, + __2, + ___, + __3, + __C, + ___, + ___, + __0, + ___, + __DOT, + ___, + __D, + __M, + ___, + __S, + __ADD, + __P, + __R, + ___, + __DIV, // 16 F-keys + + __SWAP, + ___, + __D, + __C, + __U, + __S, + ___, + __MULT, + __R, + __T, + __R, + __C, + __S, + __T, + ___, + __SUB, + __C, + __A, + __PI, + ___, + __I, + __N, + ___, + __ADD, + __I, + __F, + __E, + __L, + __T, + __H, + __P, + __C, // 32 Intrinsic functions + __LT, + ___, + __EQ, + ___, + __LT, + __GT, + ___, + __GT, + + __B, + __E, + __U, + __N, + __S, + __O, + __I, + ___, + __c, + ___, + __t, + __POW1, + __E, + ___, + __L, + __N, + __s, + ___, + __t, + ___, + __s, + __POW1, + __c, + __POW1, // 48 Builtin functions + __O, + __V, + __SQRT, + ___, + __P, + ___, + __EM, + __L, + __P, + __V, + __N, + __D, + __P, + __ARROW, + __R, + __ARROW, + + __S, + __ADD, + __S, + __c, + __MEAN, + ___, + __L, + __R, + __0, + __0, + __0, + __1, + __0, + __2, + __0, + __3, // 64 User functions + __0, + __4, + __0, + __5, + __0, + __6, + __0, + __7, + __0, + __8, + __0, + __9, + __1, + __0, + __1, + __1, + __1, + __2, + __1, + __3, + __1, + __4, + __1, + __5, +}; + +// FUNCTION POINTER ARRAY +static void (*dispatch[])(void) = { + // Function pointer array + &_keyf, + &_num, + &_num, + &_num, // 00 Primary keys + &_e, + &_num, + &_num, + &_num, + &_neg, + &_num, + &_num, + &_num, + &_drop, + &_num, + &_dot, + &_dup, + &_menu, + &_sumadd, + &_prgedit, + &_div, // 16 F-keys + + &_swap, + &_dict, + &_usrset, + &_mul, + &_rot, + &_mrcl, + &_msto, + &_sub, + &_clr, + &_pi, + &_int, + &_add, + &_condif, + &_condelse, + &_condthen, + &_permcomb, // 32 Intrinsic functions + &_condlt, + &_condeq, + &_condne, + &_condgt, + + &_begin, + &_until, + &_solve, + &_inv, + &_cos, + &_atan, + &_exp, + &_ln, + &_sin, + &_tan, + &_asin, + &_acos, // 48 Builtin functions + &_over, + &_sqrt, + &_pow, + &_gammaln, + &_pv, + &_nd, + &_pol2rect, + &_rect2pol, + + &_sumadd, + &_sumclr, + &_sumstat, + &_sumlr, +}; +// static void _nop(void) {} // NOP - no operation +static void _num(void) { // Insert number + _numinput(key2nr(key)); +} +static void _add(void) { // ADD + + dpush(dpop() + dpop()); +} +static void _acos(void) { // ACOS + seekmem(_ACOS); +} +static void _asin(void) { // ASIN + seekmem(_ASIN); +} +static void _atan(void) { // ATAN + dpush(atan(dpop()) * RAD); +} +static void _begin(void) { // BEGIN + apush(mp); +} +static void _ce(void) { // CE + if (isdot) { + if (decimals) { + decimals--; + double a = pow10(decimals); + dpush(((long)(dpop() * a) / a)); + } else + isdot = false; + } else { + long a = dpop() / 10.0F; + if (!a) + isnewnumber = true; + else + dpush(a); + } +} +static void _clr(void) { // CLR + dp = 0; + _sumclr(); +} +static void _condelse(void) { // CONDITION ELSE + _condseek(); // Seek next THEN + cl--; +} +static void _condeq(void) { // CONDITION = + dpush(dpop() == dpop()); +} +static void _condgt(void) { // CONDITION > + dpush(dpop() < dpop()); +} +static void _condif(void) { // CONDITION IF + cl++; // Increment conditional level + if (!dpop()) _condseek(); // FALSE-Clause - seek next ELSE or THEN +} +static void _condlt(void) { // CONDITION < + _condgt(); + dpush(!dpop()); +} +static void _condne(void) { // CONDITION <> + _condeq(); + dpush(!dpop()); +} +static void _condseek(void) { // CONDITION - seek next ELSE or THEN + boolean isloop = true; + byte cltmp = 0; // Local conditional level + while (isloop) { + byte c = 0; + if (mp < sizeof(mem)) + c = pgm_read_byte(mem + mp++); // Builtin + else if (mp < sizeof(mem) + EEU) + c = EEPROM[mp++ - sizeof(mem) + EEUSTART]; + if (mp >= sizeof(mem) + EEU) + isloop = false; // No corresponding ELSE or THEN + else if (c == _IF) + cltmp++; // Nested IF found + else if (cltmp && c == _THEN) + cltmp--; // Nested IF ended + else if (!cltmp && (c == _ELSE || c == _THEN)) + isloop = false; + } +} +static void _condthen(void) { // CONDITION THEN + cl--; // Decrement conditional level +} +static void _cos(void) { // COS + dpush(cos(dpop() / RAD)); +} +static void _dict(void) { // DICT + select = 0; + medi = MEDIDICT; +} +static void _div(void) { // DIV / + _inv(); + _mul(); +} +static void _dot(void) { // DOT . + if (isnewnumber) { + dpush(0.0F); // Start new number with 0 + decimals = 0; + isnewnumber = false; + } + isdot = true; +} +static void _drop(void) { // DROP + if (!isnewnumber) + _ce(); // Clear entry + else if (dp) + dp--; // Clear TOS +} +static void _dup(void) { // DUP + if (isnewnumber && dp) dpush(ds[dp - 1]); +} +static void _e(void) { // E + dpush(pow10(dpop())); + _mul(); +} +static void _exp(void) { // EXP + boolean isneg = false; // True for negative x + if (dpush(dpop()) < 0.0F) { // Taylor series ist weak for negative x ... calculate exp(-x)=1/exp(x) + _neg(); + isneg = true; + } + dpush(1.0F); // Stack: x res + for (byte i = 255; i; i--) { + _swap(); + _dup(); + _rot(); // Duplicate x (TOS-1) + dpush(i); + _div(); + _mul(); + dpush(1.0F); + _add(); // res = 1.0 + x * res / i; + } + if (isneg) _inv(); // Negative argument + _swap(); + _drop(); // Remove x (TOS-1) +} +static void _gammaln(void) { // GAMMALN + seekmem(_GAMMALN); +} +static void _int(void) { // INT + dpush((long)dpop()); +} +static void _inv(void) { // INV + dpush(1.0F / dpop()); +} +static void _keyf(void) { // KEY-F + fgm = ISF; + setfgm = 0; +} +static void _ln(void) { // LN + dpush(log(dpop())); +} +static void _menu(void) { // MENU + select = 0; + medi = MEDIMENU; +} +static void _mrcl(void) { // MRCL + _mstorcl(false); +} +static void _msto(void) { // MSTO + _mstorcl(true); +} +static void _mstorcl(boolean issto) { // MRCL + byte nr = dpop(); + byte addr = EESTO + nr * sizeof(double); // Has to be <255 (byte) + if (nr < MEMSTO) { + if (issto) + EEwrite(addr, dpop()); + else { + double a; + EEread(addr, a); + dpush(a); + } + } +} +static void _mul(void) { // MULT * + dpush(dpop() * dpop()); +} +/*static void _nand(void) { // NAND + long b = dpop(); + dpush(~((long)dpop() & b)); + }*/ +static void _nd(void) { // ND + seekmem(_ND); +} +static void _neg(void) { // NEGATE + dpush(-dpop()); +} +static void _numinput(byte k) { // NUM Numeric input (0...9) + if (isdot) { // Append decimal + dpush(k); + dpush(pow10(++decimals)); + _div(); + _add(); + } else if (isnewnumber) + dpush((double)k); // Push new numeral + else { // Append numeral + dpush(10.0F); + _mul(); + dpush(k); + _add(); + } + isnewnumber = false; +} +static void _over(void) { // OVER + seekmem(_OVER); +} +void _permcomb(void) { // PERM COMB + seekmem(_PERMCOMB); +} +static void _pi(void) { // PI + dpush(PI); +} +void _pol2rect(void) { // POL2RECT + seekmem(_POL2RECT); +} +void _pow(void) { // POWER + seekmem(_POW); +} +void _pv(void) { // PRESENT VALUE + seekmem(_PV); +} +static void _prgedit(void) { // PRGEDIT + isprgedit = true; + if ((prgnr = dpop()) >= MAXPRG) prgnr = 0; + prgpos = prglength = 0; + prgaddr = EEUSTART + eeudist(prgnr); + byte len = prgnr + UL0, prgstep = EEPROM[prgaddr]; // Max program length and first step + while (prglength < len && prgstep != _END) { + prgbuf[prglength] = prgstep; // Load step from EEPROM to prgbuf + prgstep = EEPROM[prgaddr + ++prglength]; // Next step + } +} +void _rect2pol(void) { // RECT2POL + seekmem(_RECT2POL); +} +static void _rot(void) { // ROT + if (dp > 2) { + double a = dpop(), b = dpop(), c = dpop(); + dpush(b); + dpush(a); + dpush(c); + } +} +static void _sin(void) { // SIN + seekmem(_SIN); +} +static void _solve(void) { // SOLVE + _dup(); + _dup(); // 3 x0 on stack + runs = 0; + issolve = true; +} +static void _sqrt(void) { // SQRT + seekmem(_SQRT); +} +static void _sub(void) { // SUB - + _neg(); + _add(); +} +static void _sumadd(void) { // SUM+ + seekmem(_SUMADD); +} +static void _sumclr(void) { // SUMclr + seekmem(_SUMCLR); +} +static void _sumlr(void) { // SUM LR + seekmem(_SUMLR); +} +static void _sumstat(void) { // SUM STAT + seekmem(_SUMSTAT); +} +static void _swap(void) { // SWAP + if (dp > 1) { + double a = dpop(), b = dpop(); + dpush(a); + dpush(b); + } +} +static void _tan(void) { // TAN + seekmem(_TAN); +} +static void _until(void) { // UNTIL + if (!ap) + ; // No BEGIN for this UNTIL + else if (dpop()) + apop(); // Go on (delete return address) + else + apush(mp = apop()); // Go back to BEGIN +} +static void _usrset(void) { // USR + select = 0; + medi = MEDIDICT; + issetusr = true; +} + +// SUBPROGRAMS + +static void delayshort(byte ms) { // Delay in ms (8MHz) + for (long k = 0; k < ms * 8000L; k++) __asm__ __volatile__("nop\n\t"); +} + +static int eeudist(byte i) { // Distance of user program number i from EEUSTART + return ((2 * UL0 + i - 1) * i / 2); +} + +static void execute(byte cmd) { // Execute command + if (cmd < MAXCMDB) + (*dispatch[cmd])(); // Dispatch intrinsic/builtin command + else if (cmd < MAXCMDU) + mp = eeudist(cmd - MAXCMDB) + sizeof(mem); // Execute user command + if ((cmd % 4 == 0 && cmd != 12) || cmd > 14) { // New number - except: 0-9. DROP + decimals = 0; + isdot = false; + isnewnumber = true; + } + if (fgm && setfgm) fgm = setfgm = 0; // Hold demanded F-key status one cycle + setfgm = 1; +} + +static void prgstepins(byte c) { // Insert step (character c in prgbuf at prgeditstart) + for (byte i = prglength; i > prgpos; i--) prgbuf[i] = prgbuf[i - 1]; + prgbuf[prgpos + 1] = c; + prglength++; + prgpos++; +} + +template +void EEwrite(int ee, const T& value) { // Write any datatype to EEPROM + const byte* p = (const byte*)(const void*)&value; + for (byte i = 0; i < sizeof(value); i++) EEPROM.write(ee++, *p++); +} +template +void EEread(int ee, T& value) { // Read any datatype from EEPROM + byte* p = (byte*)(void*)&value; + for (byte i = 0; i < sizeof(value); i++) *p++ = EEPROM.read(ee++); +} + +static void upn(byte n, byte l) { // Selection up l lines + if (select > n * l && select <= (n + 1) * l - 1) + select--; + else + select = (n + 1) * l - 1; +} +static void downn(byte n, byte l) { // Selection down l lines + if (select >= n * l && select < (n + 1) * l - 1) + select++; + else + select = n * l; +} +static byte menuselect(byte lines) { // Selection (1 line = 16 items) + char k = key; + if (k <= 3) + return (select * 4 + k); // Execute + else if (k >= 4 && k <= 7) + upn(k - 4, lines); // # Up + else if (k >= 8 && k <= 11) + downn(k - 8, lines); // # Down0 + if (k == 12) return (ENDKEY); // Escape + /*if (k == 13) { // Hidden feature: Display off + doff(); + return (ENDKEY); + }*/ + if (k == 15) { // Hidden feature: Set contrast + // dcontrast(dpop()); + return (ENDKEY); + } + return (PREENDKEY); +} + +// *** S T A C K + +static void floatstack() { + memcpy(ds, &ds[1], (DATASTACKSIZE - 1) * sizeof(double)); + dp--; +} + +static double dpush(double d) { // Push complex number to data-stack + if (dp >= DATASTACKSIZE) floatstack(); // Float stack + return (ds[dp++] = d); +} +static double dpop(void) { // Pop real number from data-stack + return (dp ? ds[--dp] : 0.0F); +} + +static void apush(int addr) { // Push address (int) to address-stack + as[ap++] = addr; +} +static int apop(void) { // Pop address (int) from address-stack + return (ap ? as[--ap] : NULL); +} + +static void seekmem(byte n) { // Find run-address (mp) of n-th builtin function + mp = 0; + while (n + 1 - MAXCMDI) + if (pgm_read_byte(&mem[mp++]) == _END) n--; +} + +// ***** P R I N T I N G + +static boolean printscreen(void) { // Print screen due to state + // dfill(0x00); + + if (medi) { // MEnu or DIct + for (byte i = 0; i < 4; i++) { + byte nr = select * 4 + i; + if (medi == MEDIMENU) nr = EEPROM[EEMENU + nr]; + for (byte j = 0; j < 2; j++) sbuf[2 * i + j] = pgm_read_byte(&cmd[2 * nr + j]); + } + printsbuf(PRINTMENU); + } + + else if (isprgedit) { // # Edit program + sbuf[0] = ___; + sbuf[1] = __P; + sbuf[3] = _ones(prgnr); + sbuf[2] = _tens(prgnr); // Program number + sbuf[5] = _ones(prgpos); + sbuf[4] = _tens(prgpos); // Program pos + for (byte i = 0; i < 2; i++) sbuf[6 + i] = pgm_read_byte(&cmd[2 * prgbuf[prgpos] + i]); // Command + printsbuf(PRINTMENU); + } + + else if (dp) + printnum(ds[dp - 1]); // Stack + + else + printcat(__ARROW, 0); // Empty stack prompt + + if (!isnewnumber) printcat(__ARROW, 0); // Num input indicator + + if (fgm) printcat(__FINV, 0); // F indicator + + // ddisplay(); + return (false); // To determine isprintscreen +} + +// ***** S E T U P A N D L O O P + +// void setup() { +// dinit(); // Init display +// drender(); // Render current half of GDDRAM +// pinMode(KCLOCK, OUTPUT); +// pinMode(KDATA, INPUT); // Init keyboard pins +// // power_timer0_disable(); // Power off timer0 (saves 0.1 mA) +// power_timer1_disable(); // Power off timer1 (saves 0.6 mA) +// ADCSRA &= ~bit(ADEN); // Power off AD converter (saves 0.4 mA) +// } + +void loop() { + if (isprintscreen) isprintscreen = printscreen(); // Print screen + + if (mp) { // *** Execute/run code + if (mp < sizeof(mem)) + key = pgm_read_byte(&mem[mp++]); // Builtin + else if (mp < sizeof(mem) + EEU) + key = EEPROM[mp++ - sizeof(mem) + EEUSTART]; // User + else + mp = 0; // cmd > MAXCMDU + if (key >= MAXCMDI && key != _END) apush(mp); // Subroutine detected - branch + if (key == _END) { // _END reached + if (ap) + mp = apop(); // End of subroutine - return + else { // End of run + mp = 0; + if (!issolve) isprintscreen = true; // Print screen if not in solver + } + } else + execute(key); + } + + else { // *** No run - get key + key = getkey(); // READ KEY + delayshort(1); // Give keypad some time to settle + + if (issolve) { // # SOLVE + if (++runs < 3) { + if (runs == 2) { // Second run - f(x0+dx) + _swap(); + dpush(DELTAX); + _add(); // x0+DELTAX ... Prepare new x-value + } + execute(MAXCMDB); // Execute first user program + } else { // Third run - x1 + _swap(); + _div(); + dpush(-1.0); + _add(); // f1/f0-1 + dpush(DELTAX); + _swap(); + _div(); // diffx=DELTAX/(f1/f0-1) + double diffx = dpush(dpop()); // Rescue diffx for exit condition + _sub(); // x1=x0-diffx ... improved x-value + runs = 0; + if (diffx < DELTAX && diffx > -DELTAX) { // Exit + isnewnumber = isprintscreen = true; + issolve = false; + } else { // 3 x1 on stack + _dup(); + _dup(); + } + } + } + + if (key != oldkey) { + oldkey = key; // New valid key is old key + if (key < ENDKEY) { + // don(); // Wake up display (in case of doff) + + if (medi) { // ### Menu or Dict + byte sel = menuselect(medi); + if (sel < PREENDKEY) { // Item selected + if (medi == MEDIMENU) { // # MENU + if (issetusr) { // Save user menu entry + EEPROM[EEMENU + sel] = setusrselect; + issetusr = false; + } else + execute(EEPROM[EEMENU + sel]); // Execute selected command + medi = 0; + } else { // # DICT + if (issetusr) { // Go on to menu + setusrselect = sel; + select = 0; + medi = MEDIMENU; + } else if (isprgdict) { // Go back to prgedit + prgstepins(sel); + isprgdict = false; + isprgedit = true; + medi = 0; + } else { + execute(sel); // Execute command directly + medi = 0; + } + } + } else if (sel == ENDKEY) { + issetusr = false; // Escape + medi = 0; + } + } + + else if (isprgedit) { // ### Program edit + if (key == 0) + fgm = fgm ? 0 : ISF; // # F-key toggle + else if (key == 5 && fgm == ISF) { // # F-4 ... insert step via dict + select = fgm = 0; + medi = MEDIDICT; + isprgdict = true; + isprgedit = false; + } else if (fgm == ISF) { // # Insert F-shifted key + prgstepins(key + fgm * 16); + fgm = 0; + } else if (key == 4) { // # Up + if (prgpos) prgpos--; /// else if (prglength) prgpos = prglength - 1; + } else if (key == 8) { // # Down + if (prgpos < prglength - 1) prgpos++; /// else prgpos = 0; + } else if (key == 12) { // # Escape and save program to EEPROM + for (byte i = 0; i < prglength; i++) EEPROM[prgaddr + i] = prgbuf[i]; + EEPROM[prgaddr + prglength] = _END; + isprgedit = false; + } else if (key == 14) { // # Dot - Delete step + if (prglength) { + for (byte i = prgpos; i < prglength; i++) prgbuf[i] = prgbuf[i + 1]; + prglength--; + } + } else + prgstepins(key); // # Insert direct key + } + + else + execute(key + fgm * 16); // ### Execute key + + isprintscreen = true; + } + } + } // End of evaluating key +} diff --git a/firmware/application/external/calculator/main.cpp b/firmware/application/external/calculator/main.cpp new file mode 100644 index 00000000..0a9ce0c3 --- /dev/null +++ b/firmware/application/external/calculator/main.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui.hpp" +#include "ui_calculator.hpp" +#include "ui_navigation.hpp" +#include "external_app.hpp" + +namespace ui::external_app::calculator { +void initialize_app(ui::NavigationView& nav) { + nav.push(); +} +} // namespace ui::external_app::calculator + +extern "C" { + +__attribute__((section(".external_app.app_calculator.application_information"), used)) application_information_t _application_information_calculator = { + /*.memory_location = */ (uint8_t*)0x00000000, + /*.externalAppEntry = */ ui::external_app::calculator::initialize_app, + /*.header_version = */ CURRENT_HEADER_VERSION, + /*.app_version = */ VERSION_MD5, + + /*.app_name = */ "Calculator", + /*.bitmap_data = */ { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0xF8, + 0x1F, + 0xF8, + 0x1F, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + }, + /*.icon_color = */ ui::Color::yellow().v, + /*.menu_location = */ app_location_t::UTILITIES, + + /*.m4_app_tag = portapack::spi_flash::image_tag_noop */ {'\0', '\0', '\0', '\0'}, // optional + /*.m4_app_offset = */ 0x00000000, // will be filled at compile time +}; +} diff --git a/firmware/application/external/calculator/ui_calculator.cpp b/firmware/application/external/calculator/ui_calculator.cpp new file mode 100644 index 00000000..e8cb8d9b --- /dev/null +++ b/firmware/application/external/calculator/ui_calculator.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui_calculator.hpp" + +using namespace ui; + +namespace ui::external_app::calculator { + +uint8_t current_key = 255; +char display_string[10]; +uint8_t fgm = 0; + +// interface to external code +unsigned int mp; + +char CHARMAP[] = { + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'H', + 'I', + 'L', + 'M', + 'N', + 'O', + 'P', + 'R', + 'S', + 'T', + 'U', + 'V', + 'c', + 's', + 't', + ' ', + '.', + '*', + '+', + '-', + '/', + '!', + '<', + '=', + '>', + '^', + 'f', + 'n', + 'p', + 's', + 'm', + 'w', + +}; + +void printcat(uint8_t c, uint8_t x) { // Print char c at position x + display_string[x] = CHARMAP[c]; +} +uint8_t getkey(void) { // Read keypad + return current_key; +} + +#include "ivt.hpp" + +void step() { + loop(); +} + +void CalculatorView::focus() { + button_F.focus(); +} + +CalculatorView::CalculatorView(NavigationView& nav) + : nav_{nav} { + add_children({&button_F, + &button_7, + &button_8, + &button_9, + &button_E, + &button_4, + &button_5, + &button_6, + &button_N, + &button_1, + &button_2, + &button_3, + &button_C, + &button_0, + &button_P, + &button_D, + &console}); + + button_F.on_select = [&nav, this](Button&) { on_button_press(0); }; + button_7.on_select = [&nav, this](Button&) { on_button_press(1); }; + button_8.on_select = [&nav, this](Button&) { on_button_press(2); }; + button_9.on_select = [&nav, this](Button&) { on_button_press(3); }; + + button_E.on_select = [&nav, this](Button&) { on_button_press(4); }; + button_4.on_select = [&nav, this](Button&) { on_button_press(5); }; + button_5.on_select = [&nav, this](Button&) { on_button_press(6); }; + button_6.on_select = [&nav, this](Button&) { on_button_press(7); }; + + button_N.on_select = [&nav, this](Button&) { on_button_press(8); }; + button_1.on_select = [&nav, this](Button&) { on_button_press(9); }; + button_2.on_select = [&nav, this](Button&) { on_button_press(10); }; + button_3.on_select = [&nav, this](Button&) { on_button_press(11); }; + + button_C.on_select = [&nav, this](Button&) { on_button_press(12); }; + button_0.on_select = [&nav, this](Button&) { on_button_press(13); }; + button_P.on_select = [&nav, this](Button&) { on_button_press(14); }; + button_D.on_select = [&nav, this](Button&) { on_button_press(15); }; +} + +void CalculatorView::on_button_press(uint8_t button) { + auto pre_fgm = fgm; + for (int i = 0; i < 10; i++) + display_string[i] = ' '; + + current_key = button; + step(); + + do { + current_key = 255; + step(); + } while (mp); + + std::string d(&display_string[0], 10); + + if (pre_fgm && button != 0) { + console.write(" "); + switch (button) { + case 1: + console.write("SUM+"); + break; + case 2: + console.write("PRG"); + break; + case 3: + console.write("/"); + break; + + case 4: + console.write("SWAP"); + break; + case 5: + console.write("DICT"); + break; + case 6: + console.write("USR"); + break; + case 7: + console.write("*"); + break; + + case 8: + console.write("ROT"); + break; + case 9: + console.write("RCL"); + break; + case 10: + console.write("STO"); + break; + case 11: + console.write("-"); + break; + + case 12: + console.write("CA"); + break; + case 13: + console.write("PI"); + break; + case 14: + console.write("INT"); + break; + case 15: + console.write("+"); + break; + } + console.writeln(d); + } else if (button == 15) { + console.write("\r"); + console.writeln(d); + } else { + console.write("\r"); + console.write(d); + } + + update_button_labels(); +} + +void CalculatorView::update_button_labels() { + if (fgm) { + button_F.set_text("MENU"); + button_7.set_text("SUM+"); + button_8.set_text("PRG"); + button_9.set_text("/"); + + button_E.set_text("SWAP"); + button_4.set_text("DICT"); + button_5.set_text("USR"); + button_6.set_text("*"); + + button_N.set_text("ROT"); + button_1.set_text("RCL"); + button_2.set_text("STO"); + button_3.set_text("-"); + + button_C.set_text("CA"); + button_0.set_text("PI"); + button_P.set_text("INT"); + button_D.set_text("+"); + + } else { + button_F.set_text("F"); + button_7.set_text("7"); + button_8.set_text("8"); + button_9.set_text("9"); + + button_E.set_text("E"); + button_4.set_text("4"); + button_5.set_text("5"); + button_6.set_text("6"); + + button_N.set_text("N"); + button_1.set_text("1"); + button_2.set_text("2"); + button_3.set_text("3"); + + button_C.set_text("C"); + button_0.set_text("0"); + button_P.set_text("."); + button_D.set_text("D"); + } +} + +} // namespace ui::external_app::calculator diff --git a/firmware/application/external/calculator/ui_calculator.hpp b/firmware/application/external/calculator/ui_calculator.hpp new file mode 100644 index 00000000..a4cdaba9 --- /dev/null +++ b/firmware/application/external/calculator/ui_calculator.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2017 Furrtek + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __UI_calculator_H__ +#define __UI_calculator_H__ + +#include "ui.hpp" +#include "ui_navigation.hpp" +#include "ui_receiver.hpp" +#include "ui_freq_field.hpp" +#include "ui_record_view.hpp" +#include "app_settings.hpp" +#include "radio_state.hpp" +#include "log_file.hpp" +#include "utility.hpp" + +using namespace ui; + +namespace ui::external_app::calculator { + +class CalculatorView : public View { + public: + CalculatorView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "Calculator"; }; + + private: + NavigationView& nav_; + + Button button_F{{0 * 8, 11 * 16, 6 * 8, 16 + 8}, "F"}; + Button button_7{{8 * 8, 11 * 16, 6 * 8, 16 + 8}, "7"}; + Button button_8{{16 * 8, 11 * 16, 6 * 8, 16 + 8}, "8"}; + Button button_9{{24 * 8, 11 * 16, 6 * 8, 16 + 8}, "9"}; + Button button_E{{0 * 8, 13 * 16, 6 * 8, 16 + 8}, "E"}; + Button button_4{{8 * 8, 13 * 16, 6 * 8, 16 + 8}, "4"}; + Button button_5{{16 * 8, 13 * 16, 6 * 8, 16 + 8}, "5"}; + Button button_6{{24 * 8, 13 * 16, 6 * 8, 16 + 8}, "6"}; + Button button_N{{0 * 8, 15 * 16, 6 * 8, 16 + 8}, "N"}; + Button button_1{{8 * 8, 15 * 16, 6 * 8, 16 + 8}, "1"}; + Button button_2{{16 * 8, 15 * 16, 6 * 8, 16 + 8}, "2"}; + Button button_3{{24 * 8, 15 * 16, 6 * 8, 16 + 8}, "3"}; + Button button_C{{0 * 8, 17 * 16, 6 * 8, 16 + 8}, "C"}; + Button button_0{{8 * 8, 17 * 16, 6 * 8, 16 + 8}, "0"}; + Button button_P{{16 * 8, 17 * 16, 6 * 8, 16 + 8}, "."}; + Button button_D{{24 * 8, 17 * 16, 6 * 8, 16 + 8}, "D"}; + + Console console{ + {0 * 8, 0 * 16, screen_width, 10 * 16}}; + + void on_button_press(uint8_t button); + void update_button_labels(); +}; + +} // namespace ui::external_app::calculator + +#endif /*__UI_calculator_H__*/ diff --git a/firmware/application/external/external.cmake b/firmware/application/external/external.cmake index b4f52f30..1dd3d696 100644 --- a/firmware/application/external/external.cmake +++ b/firmware/application/external/external.cmake @@ -7,9 +7,14 @@ set(EXTCPPSRC #afsk_rx external/afsk_rx/main.cpp external/afsk_rx/ui_afsk_rx.cpp + + #calculator + external/calculator/main.cpp + external/calculator/ui_calculator.cpp ) set(EXTAPPLIST pacman afsk_rx + calculator ) diff --git a/firmware/application/external/external.ld b/firmware/application/external/external.ld index 80604fb6..0e129d72 100644 --- a/firmware/application/external/external.ld +++ b/firmware/application/external/external.ld @@ -17,21 +17,28 @@ MEMORY { /* external apps: regions can't overlap so addresses are corrected after build */ - ram_external_app_pacman (rwx) : org = 0xEEE90000, len = 40k - ram_external_app_afsk_rx (rwx) : org = 0xEEEA0000, len = 40k + ram_external_app_pacman (rwx) : org = 0xEEE90000, len = 32k + ram_external_app_afsk_rx (rwx) : org = 0xEEEA0000, len = 32k + ram_external_app_calculator (rwx) : org = 0xEEEB0000, len = 32k } SECTIONS { - .external_app_pacman : ALIGN(16) SUBALIGN(16) + .external_app_pacman : ALIGN(4) SUBALIGN(4) { KEEP(*(.external_app.app_pacman.application_information)); *(*ui*external_app*pacman*); } > ram_external_app_pacman - .external_app_afsk_rx : ALIGN(16) SUBALIGN(16) + .external_app_afsk_rx : ALIGN(4) SUBALIGN(4) { KEEP(*(.external_app.app_afsk_rx.application_information)); *(*ui*external_app*afsk_rx*); } > ram_external_app_afsk_rx + + .external_app_calculator : ALIGN(4) SUBALIGN(4) + { + KEEP(*(.external_app.app_calculator.application_information)); + *(*ui*external_app*calculator*); + } > ram_external_app_calculator } diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 31b8eac1..434c4cc4 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -626,6 +626,8 @@ void Console::write(std::string message) { } else { if (c == '\n') { crlf(); + } else if (c == '\r') { + pos = {0, pos.y()}; } else if (c == '\x1B') { escape = true; } else { diff --git a/firmware/tools/export_external_apps.py b/firmware/tools/export_external_apps.py index 068038c4..228d82e6 100755 --- a/firmware/tools/export_external_apps.py +++ b/firmware/tools/export_external_apps.py @@ -34,7 +34,7 @@ See firmware/application/CMakeLists.txt > COMMAND ${EXPORT_EXTERNAL_APP_IMAGES} Usage: """ -maximum_application_size = 40*1024 +maximum_application_size = 32*1024 if len(sys.argv) < 4: print(usage_message) @@ -124,7 +124,7 @@ for external_image_prefix in sys.argv[4:]: external_application_image[m4_app_offset_header_position:m4_app_offset_header_position+4] = app_image_len.to_bytes(4, byteorder='little') if (len(external_application_image) > maximum_application_size) != 0: - print("application {} can not exceed 40kb: {} bytes used".format(external_image_prefix, len(external_application_image))) + print("application {} can not exceed 32kb: {} bytes used".format(external_image_prefix, len(external_application_image))) sys.exit(-1) # write .ppma (portapack mayhem application)