add STO, RCL, lastX, and rotate; do not require holding shift while

pressing 0 to turn off
This commit is contained in:
Jeff Wang 2019-10-21 02:30:58 -04:00
parent f184c6dc18
commit c05a402b72
4 changed files with 110 additions and 48 deletions

View File

@ -6,7 +6,7 @@ The replacement firmware supports floating-point calculations (using 18 decimal
Note that once you change the firmware on the calculator, there's no way to go back to the original firmware (the original firmware isn't posted for download anywhere). STC's bootloader on the microcontroller deliberately prevents readback of the microcontroller's content, and STC considers this to be a "feature".
Here's a picture of the assembled calculator kit running the new firmware (it's impossible to keep the glossy black acrylic clean):
Here's a picture of the assembled calculator kit running the new firmware:
![calculator](./calc_small.jpg)
@ -38,6 +38,7 @@ Some of the keys have slightly different functions, see the picture of the emula
The keys on the *original* calculator map as follows:
- `= `: Enter
- acts as RCL when shifted (there is only 1 memory register)
- `<- `: Negate (+/-: change sign)
- Note: for implementation simplicity, this is a postfix operator.
- Pressing this key will immediately terminate digit entry and negate the number.
@ -48,6 +49,7 @@ The keys on the *original* calculator map as follows:
- The 1st press inserts a decimal point.
- The 2nd press begins exponent entry.
- The 3rd and subsequent presses negates the current exponent being entered.
- Acts as STO when shifted (there is only 1 memory register)
- `mode `: acts as a shift key
- `ON/AC`: acts as a backspace key during digit entry, acts as `Clear X` when digit entry is finished (e.g. after an operator key is pressed)
- acts as `Clear X` when shifted
@ -56,6 +58,9 @@ The keys on the *original* calculator map as follows:
- `9 `: acts as log(x) when shifted
- `5 `: acts as e^x when shifted
- `6 `: acts as 10^x when shifted
- `4 `: acts as roll down when shifted
- `+ `: acts as LastX when shifted
- `0 `: acts as off button when shifted
## Floating Point
@ -65,7 +70,7 @@ The calculator internally calculates with an 18 digit significand for better pre
Internally, the calculator dedicates 15 bits for representing the signed exponent, so exponents up to +/- 16,383 can be represented (see the internals section below for more information). This is to ensure that intermediate parts of certain calculations (mainly taking the reciprocal of a number) do not prematurely cause overflow or underflow, even when the result is fully representable with just 2 digits. You can do calculations with greater than 2 digits in the exponent, but only 2 digits will be displayed. For larger exponents, a 10 in the ten's place of the exponent will be displayed as a '`:`'. (This just so happens to be the next character after '`9`' in the 1602 LCD's character map).
## Turning off
Hold `Shift` (the `mode` key on the physical calculator) and `0` *at the same time* to turn off. NOTE: There is no auto power off.
Press `Shift` (the `mode` key on the physical calculator) and then `0` to turn off. On older stc_rpncalc firmwares, or if the calculator is unresponsive, hold `Shift` (the `mode` key on the physical calculator) and `0` *at the same time* to turn off. NOTE: There is no auto power off.
# Building
@ -221,11 +226,7 @@ The number `0.135` would be stored the same way, except now the exponent is `0x7
## TODO
- Trigonometric functions could be implemented with algorithms similar to those described in the HP Journal articles "Personal Calculator Algorithms II: Trigonometric Functions" and "Personal Calculator Algorithms III: Inverse Trigonometric Functions", both by William Egbert.
- will probably assign to the shifted `1`, `2`, and `3` keys, and `0` for calculating inverse trig functions.
- The stack rotate function is currently unimplemented
- will probably assign to the shifted `4` key
- STO and RCL are currently unimplemented
- will probably assign to the shifted `.` and `Enter` (`=`) keys
- will probably assign to the shifted `1`, `2`, and `3` keys, and `-` for calculating inverse trig functions.
- Special cases, such as taking the logarithms of numbers near 1, negative number raised to integer powers, etc. could be implemented separately, similar to what is described in the HP Journal note "The New Accuracy: Making 2^3 = 8" by Dennis Harms.
- The display blanking for trailing 0s assumes that 16 digits will actually be displayed, but this might not be the case if the negative sign, decimal point, or exponents are displayed
- Would be nice to have the `hex <=> dec` converter from the original firmware if there is more flash space

View File

@ -99,9 +99,9 @@ ApplicationWindow
var shifted_keys = [
["Shift", "1/x", " √<span style=\"text-decoration: overline\">x</span> ", "CL<i>x</i>"],
["y<sup>x</sup> ", "ln(x)", "log(x)", ""],
["", "e<sup>x</sup>", "10<sup>x</sup>", ""],
["🔃", "e<sup>x</sup>", "10<sup>x</sup>", ""],
["", "", "", ""],
["off", "", "", ""]
["off", "STO", "RCL", "LAST<i>x</i>"]
]
return "<small>" + shifted_keys[row][col] + "</small>"

View File

@ -9,6 +9,9 @@
#include "calc.h"
__xdata dec80 StoredDecn;
__xdata dec80 LastX;
#define STACK_SIZE 4 //must be a power of 2
#define STACK_X 0
@ -53,6 +56,7 @@ static void do_binary_op(void (*f_ptr)(void)){
if (decn_is_nan(&stack(STACK_Y)) || decn_is_nan(&stack(STACK_X))){
set_dec80_NaN(&stack(STACK_Y));
} else {
copy_decn(&LastX, &stack(STACK_X)); //save LastX
copy_decn(&AccDecn, &stack(STACK_Y));
copy_decn(&BDecn, &stack(STACK_X));
f_ptr();
@ -63,6 +67,7 @@ static void do_binary_op(void (*f_ptr)(void)){
static void do_unary_op(void (*f_ptr)(void)){
if (!decn_is_nan(&stack(STACK_X))){
copy_decn(&LastX, &stack(STACK_X)); //save LastX
copy_decn(&AccDecn, &stack(STACK_X));
f_ptr();
copy_decn(&stack(STACK_X), &AccDecn);
@ -80,7 +85,15 @@ void process_cmd(char cmd){
switch(cmd){
//////////
case '+':{
do_binary_op(add_decn);
if (IsShifted){ // LastX
if (NoLift == 2){
StackPtr--;
}
copy_decn(&stack(STACK_X), &LastX);
IsShifted = 0;
} else { // +
do_binary_op(add_decn);
}
} break;
//////////
case '*':{
@ -90,6 +103,7 @@ void process_cmd(char cmd){
case '-':{
negate_decn(&stack(STACK_X));
do_binary_op(add_decn);
negate_decn(&LastX); //stored LastX was after negation of X
} break;
//////////
case '/':{
@ -97,9 +111,24 @@ void process_cmd(char cmd){
} break;
//////////
case '=':{
if (!decn_is_nan(&stack(STACK_X))){
StackPtr--;
copy_decn(&stack(STACK_X), &stack(STACK_Y));
if (IsShifted){ //RCL
if (NoLift == 2){
StackPtr--;
}
copy_decn(&stack(STACK_X), &StoredDecn);
IsShifted = 0;
} else { //Enter
if (!decn_is_nan(&stack(STACK_X))){
StackPtr--;
copy_decn(&stack(STACK_X), &stack(STACK_Y));
}
}
} break;
//////////
case '.':{
if (IsShifted){ //STO
copy_decn(&StoredDecn, &stack(STACK_X));
IsShifted = 0;
}
} break;
//////////
@ -111,6 +140,7 @@ void process_cmd(char cmd){
if (IsShifted){ //take sqrt
IsShifted = 0;
if (!decn_is_nan(&stack(STACK_X))){
copy_decn(&LastX, &stack(STACK_X)); //save LastX
copy_decn(&AccDecn, &stack(STACK_X));
if (AccDecn.exponent < 0){ //negative
set_dec80_NaN(&stack(STACK_X));
@ -146,6 +176,13 @@ void process_cmd(char cmd){
toggle_shifted();
} break;
//////////
case '4':{
if (IsShifted){ //roll down
StackPtr++;
IsShifted = 0;
}
} break;
//////////
case '5':{
if (IsShifted){ //e^x
do_unary_op(exp_decn);
@ -174,10 +211,11 @@ void process_cmd(char cmd){
}
} break;
//////////
case '7':{
case '7':{ //y^x
if (decn_is_nan(&stack(STACK_Y)) || decn_is_nan(&stack(STACK_X))){
set_dec80_NaN(&stack(STACK_Y));
} else {
copy_decn(&LastX, &stack(STACK_X)); //save LastX
copy_decn(&AccDecn, &stack(STACK_Y));
copy_decn(&BDecn, &stack(STACK_X));
pow_decn();

View File

@ -125,7 +125,7 @@ static void latch_on(void)
__xdata char EntryBuf[MAX_CHARS_PER_LINE + 1];
__xdata uint8_t ExpBuf[2];
__xdata const char VER_STR[32+1] = "STC RPN Calculator v1.05";
__xdata const char VER_STR[32+1] = "STC RPN Calculator v1.06";
enum {
@ -165,10 +165,18 @@ static inline void finish_process_entry(void){
push_decn(EntryBuf, exponent);
//reset to done
entering_done();
//track entry for RCL and lastX
if (NoLift){
#ifdef DESKTOP
printf("no lift==2\n");
#endif
NoLift++;
}
}
//process cmd
process_cmd(KEY_MAP[I_Key]);
EnteringExp = ENTERING_DONE;
NoLift = 0;
}
#ifdef DESKTOP
@ -221,6 +229,9 @@ int main()
{
//turn off?
if (Keys[0] == 8 && Keys[4] == 8){
//check if both shift (mode) and 0 key are held to turn off
//(should work even if rest of calculator is in inifite loop,
// since this is checked within ISR)
TURN_OFF();
}
#ifdef DESKTOP
@ -279,25 +290,30 @@ int main()
switch(KEY_MAP[I_Key]){
//////////
case '0': {
if ( EnteringExp >= ENTERING_EXP){
if ( Exp_i == 0){
ExpBuf[0] = 0;
Exp_i = 1;
} else {
ExpBuf[1] = ExpBuf[0];
ExpBuf[0] = 0;
Exp_i++;
if ( Exp_i > 2){
if (IsShifted){
//off
TURN_OFF();
} else {
if ( EnteringExp >= ENTERING_EXP){
if ( Exp_i == 0){
ExpBuf[0] = 0;
Exp_i = 1;
} else {
ExpBuf[1] = ExpBuf[0];
ExpBuf[0] = 0;
Exp_i++;
if ( Exp_i > 2){
Exp_i = 1;
}
}
} else if (is_entering_done()){
EnteringExp = ENTERING_SIGNIF;
EntryBuf[Entry_i] = KEY_MAP[I_Key];
//do not increment entry_i from 0, until first non-0 entry
} else if ( Entry_i != 0 && Entry_i < MAX_CHARS_PER_LINE - 1 + 1){
EntryBuf[Entry_i] = KEY_MAP[I_Key];
Entry_i++;
}
} else if (is_entering_done()){
EnteringExp = ENTERING_SIGNIF;
EntryBuf[Entry_i] = KEY_MAP[I_Key];
//do not increment entry_i from 0, until first non-0 entry
} else if ( Entry_i != 0 && Entry_i < MAX_CHARS_PER_LINE - 1 + 1){
EntryBuf[Entry_i] = KEY_MAP[I_Key];
Entry_i++;
}
} break;
//////////
@ -312,7 +328,6 @@ int main()
case '9': {
if (IsShifted){
finish_process_entry();
NoLift = 0;
} else if ( EnteringExp >= ENTERING_EXP){
if ( Exp_i == 0){
ExpBuf[0] = KEY_MAP[I_Key] - '0';
@ -336,27 +351,36 @@ int main()
} break;
//////////
case '.': {
if (is_entering_done()){
EntryBuf[Entry_i++] = '0';
EntryBuf[Entry_i++] = '.';
EnteringExp = ENTERING_FRAC;
} else if ( EnteringExp == ENTERING_SIGNIF){
if ( Entry_i == 0){
if (IsShifted){
//STO
finish_process_entry();
} else {
if (is_entering_done()){
EntryBuf[Entry_i++] = '0';
EntryBuf[Entry_i++] = '.';
EnteringExp = ENTERING_FRAC;
} else if ( EnteringExp == ENTERING_SIGNIF){
if ( Entry_i == 0){
EntryBuf[Entry_i++] = '0';
}
EntryBuf[Entry_i++] = '.';
EnteringExp = ENTERING_FRAC;
} else if ( EnteringExp <= ENTERING_EXP) {
EnteringExp++;
} else { //entering_exp == ENTERING_EXP_NEG
EnteringExp = ENTERING_EXP;
}
EntryBuf[Entry_i++] = '.';
EnteringExp = ENTERING_FRAC;
} else if ( EnteringExp <= ENTERING_EXP) {
EnteringExp++;
} else { //entering_exp == ENTERING_EXP_NEG
EnteringExp = ENTERING_EXP;
}
} break;
//////////
case '=': {
//track stack lift
finish_process_entry();
NoLift = 1;
if (IsShifted){ //RCL
finish_process_entry();
} else { //Enter
//track stack lift
finish_process_entry();
NoLift = 1;
}
} break;
//////////
case 'c': {
@ -389,7 +413,6 @@ int main()
case '<': //fallthrough //use as +/-
case 'r': { //use as swap
finish_process_entry();
NoLift = 0;
} break;
//////////
default: process_cmd(KEY_MAP[I_Key]);