diff --git a/CMakeLists.txt b/CMakeLists.txt index a9ba436..1b77b3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,12 @@ project(stc_rpncalc C CXX) # 3rd party tools find_package(Qt5 COMPONENTS Widgets Qml Quick REQUIRED) +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") +message(STATUS "using address sanitizer") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls") +link_libraries(asan) +endif() + set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type (for tests debug make sense)") # Compiler warnings diff --git a/Makefile b/Makefile index ef9bb46..ad56bce 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,12 @@ SDCC ?= sdcc STCCODESIZE ?= 13312 -SDCCOPTS ?= --code-size $(STCCODESIZE) --xram-size 256 --idata-loc 0x80 +SDCCOPTS ?= --code-size $(STCCODESIZE) --xram-size 256 --idata-loc 0x70 #SDCCOPTS ?= --code-size $(STCCODESIZE) --xram-size 256 --stack-auto --model-large FLASHFILE ?= main.hex LARGE_LDFLAGS += -L/usr/share/sdcc/lib/large/ +# CFLAGS += -DSTACK_DEBUG # write the stack pointer to P3_4 -SRC = src/lcd.c src/key.c src/utils.c src/decn/decn.c src/calc.c +SRC = src/lcd.c src/key.c src/utils.c src/decn/decn.c src/calc.c src/stack_debug.c OBJ=$(patsubst src%.c,build%.rel, $(SRC)) @@ -13,7 +14,7 @@ all: main build/%.rel: src/%.c src/%.h mkdir -p $(dir $@) - $(SDCC) $(SDCCOPTS) -o $@ -c $< + $(SDCC) $(SDCCOPTS) $(CFLAGS) -o $@ -c $< main: $(OBJ) $(SDCC) -o build/ src/$@.c $(SDCCOPTS) $(CFLAGS) $^ @@ -21,7 +22,7 @@ main: $(OBJ) @ tail -n 5 build/main.mem | head -n 2 @ tail -n 1 build/main.mem cp build/$@.ihx $@.hex - + eeprom: sed -ne '/:..1/ { s/1/0/2; p }' main.hex > eeprom.hex diff --git a/README.md b/README.md index cf75017..c762dbf 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # STC DIY Calculator Firmware -This is a replacement firmware for the [diyleyuan calculator kit](http://www.diyleyuan.com/jc/L8Q.html). The calculator kit is available for purchase for less than $13 shipped from eBay by searching for "diy calculator kit". You will have to solder the kit yourself (see "hardware connections" below). The calculator uses an STC IAP15W413AS microcontroller (an 8051 instruction set-compatible microcontroller) with a built-in serial-port bootloader. See the series [summary](http://www.stcmicro.com/datasheet/STC15W408AS_Features.pdf) and full english [datasheet](https://www.stcmicro.com/datasheet/STC15F2K60S2-en.pdf). This project uses [SDCC](http://sdcc.sourceforge.net/) to compile the C code and [stcgal](https://github.com/grigorig/stcgal) to load the new firmware. +This is a replacement firmware for the [diyleyuan calculator kit](http://www.diyleyuan.com/jc/L8Q.html). The calculator kit is available for purchase for less than $13 shipped from eBay by searching for "diy calculator kit" (price has increased recently, currently closer to $18 shipped). You will have to solder the kit yourself (see "hardware connections" below). The calculator uses an STC IAP15W413AS microcontroller (an 8051 instruction set-compatible microcontroller) with a built-in serial-port bootloader. See the series [summary](http://www.stcmicro.com/datasheet/STC15W408AS_Features.pdf) and full english [datasheet](https://www.stcmicro.com/datasheet/STC15F2K60S2-en.pdf). This project uses [SDCC](http://sdcc.sourceforge.net/) to compile the C code and [stcgal](https://github.com/grigorig/stcgal) to load the new firmware. The replacement firmware supports floating-point calculations (using 18 decimal digits plus exponent for arithmetic) with a 4-level RPN stack. Functions include basic arithmetic as well as log(), exp(), y^x, 1/x and sqrt(), all in floating point. (The original firmware supported only fixed-point calculations in chain mode.) I have not added in the resistor value calculator or the decimal/hexadecimal converter features from the original firmware. @@ -74,15 +74,26 @@ The keys on the *original* calculator map as follows: - 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 +- `mode `: acts as a shift key (press multiple times to toggle between shift up, shift down, and no shift) - `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 - `7 `: acts as y^x when shifted - `8 `: acts as ln(x) when shifted - `9 `: acts as log(x) when shifted +- `÷ `: acts as pi when shifted +- `4 `: acts as roll down when shifted + - acts as roll up when shifted down - `5 `: acts as e^x when shifted - `6 `: acts as 10^x when shifted -- `4 `: acts as roll down when shifted +- `1 `: acts as sin(x) when shifted + - acts as asin(x) when shifted down +- `2 `: acts as cos(x) when shifted + - acts as acos(x) when shifted down +- `3 `: acts as tan(x) when shifted + - acts as atan(x) when shifted down +- all trig functions are currently calculated in degrees +- `- `: acts as to radians when shifted + - acts as to degrees when shifted down - `+ `: acts as LastX when shifted - `0 `: acts as off button when shifted @@ -240,6 +251,8 @@ Disconnected! # Bugs 1. After division by 0, ln(-), over/underflow, or other operations which give an `Error`, it's possible to still do certain operations on `Error`. Many functions do check, and will not operate on `Error`, but not all of them yet. This is somewhat similar to old soviet Elektronika calculators where `Error` is just a number, and there wasn't enough ROM space to check for errors. (There are people who explore the inner-workings of these calculators by manipulating the `Error` "number".) +1. When shifted down, keys which do not have a shifted-down function will instead be interpreted as if there were no shift. +1. Trigonometric functions are extremely slow and inaccurate. 1. There are probably more bugs waiting to be discovered. # Internals @@ -288,10 +301,11 @@ The number `0.135` would be stored the same way, except now the exponent is `0x7 - see `src/decn/proto/exp.cpp` for initial prototyping development work - Powers are calculated using the identity y^x = e^(x*ln(y)) - Square roots are calculated using the identity sqrt(x) = e^(0.5*ln(x)) +- Trigonometric functions are calculated using algorithms similar to the [sinclair scientific](http://files.righto.com/calculator/sinclair_scientific_simulator.html), and are fairly slow and inaccurate. ## 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 `-` for calculating inverse trig functions. +- Trigonometric functions could be implemented with algorithms similar to those used in Valentin Albillo's [implementation](http://www.hpcc.org/datafile/hp12/12c_TrigonometryFunctions.pdf) for the HP 12C, but would take more flash + - These could also use the implementation described in the HP Journal articles "Personal Calculator Algorithms II: Trigonometric Functions" and "Personal Calculator Algorithms III: Inverse Trigonometric Functions", both by William Egbert. This would likely take even more flash though. - 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 @@ -313,7 +327,7 @@ In practice, the keyboard debouncing works much better than the original firmwar # Implementation on an STC 8051 Microcontroller This was my 1st time using an 8051 microcontroller. The architecture is a bit limiting for programming in "high-level" languages such as C compared to more modern architectures -- even compared to other 8-bit architectures such as the AVR (used in the arduino). Most significantly, there is no stack-pointer-relative addressing, which makes C functions takes up a lot of code space, since they have to emulate stack-pointer-relative addressing. Unfortunately, the microcontroller used only has 13K of code space. The compiler used (SDCC) also does not support using a 2nd data pointer, even though STC's implementation of the 8051 has one. -I've avoided relying on the functions being able to be re-entrant, so that they do not depend on having a stack. SDCC is *not* set to use `--stack-auto` to reduce code size (this means functions are not re-entrant). Some "large" local variables are declared as static in functions to save on the code space needed to emulate a stack. I used a lot more globals than I what I would typically like to have used, and a lot less pointers passed to functions, since these are extremely expensive (to account for the 3 different memory types). +I've avoided relying on the functions being able to be re-entrant, so that they do not depend on having a stack. SDCC is *not* set to use `--stack-auto` to reduce code size (this means functions are not re-entrant). Some "large" local variables are declared as static in functions to save on the code space needed to emulate a stack. I used a lot more globals than what I would typically like to have used, and a lot less pointers passed to functions, since these are extremely expensive (to account for the 3 different memory types). Another weird thing about the 8051 is that not all of the memory is addressed the same way. On this microcontroller, there are 512 bytes of ram total, of which: diff --git a/qt_gui.png b/qt_gui.png index 4e829c0..cf2879f 100644 Binary files a/qt_gui.png and b/qt_gui.png differ diff --git a/qt_gui/main.qml b/qt_gui/main.qml index fe0a913..3e5936c 100644 --- a/qt_gui/main.qml +++ b/qt_gui/main.qml @@ -121,7 +121,7 @@ ApplicationWindow horizontalAlignment: Text.AlignHCenter font.pointSize: 16 color: "gray" - text: {getShiftedText(parent.parent.objectName, index) + "

"} + text: {getShiftedUpText(parent.parent.objectName, index) + "

"} textFormat: Text.RichText anchors.centerIn: parent } @@ -132,6 +132,14 @@ ApplicationWindow textFormat: Text.RichText anchors.centerIn: parent } + Text { + horizontalAlignment: Text.AlignHCenter + font.pointSize: 16 + color: "gray" + text: {"

" + getShiftedDownText(parent.parent.objectName, index)} + textFormat: Text.RichText + anchors.centerIn: parent + } MouseArea { //get row/column onClicked: _calculator.buttonClicked(parent.parent.objectName + "," + index) @@ -155,18 +163,30 @@ ApplicationWindow return "" + keys[row][col] + "" } - function getShiftedText(row, col) { + function getShiftedUpText(row, col) { var shifted_keys = [ ["Shift", "1/x", " √x ", "CLx"], - ["yx ", "ln(x)", "log(x)", ""], - ["🔃", "ex", "10x", ""], - ["", "", "", ""], + ["yx ", "ln(x)", "log(x)", "π"], + ["R▼", "ex", "10x", ""], + ["sin(x)", "cos(x)", "tan(x)", "►rad"], ["off", "STO", "RCL", "LASTx"] ] return "" + shifted_keys[row][col] + "" } + function getShiftedDownText(row, col) { + var shifted_keys = [ + ["", "", "", ""], + ["", "", "", ""], + ["R▲", "", "", ""], + ["asin(x)", "acos(x)", "atan(x)", "►deg"], + ["", "", "", ""] + ] + + return "" + shifted_keys[row][col] + "" + } + function getBackgroundColor(row, col) { var background_color = [ ["white", "white", "white", "#ff9494"], diff --git a/src/calc.c b/src/calc.c index a89e446..fe82a0b 100644 --- a/src/calc.c +++ b/src/calc.c @@ -21,6 +21,11 @@ #include "utils.h" #include "calc.h" +#include "stack_debug.h" + +#ifdef DESKTOP +#include +#endif __xdata dec80 StoredDecn; __xdata dec80 LastX; @@ -33,7 +38,8 @@ __xdata dec80 LastX; #define STACK_T 3 uint8_t NoLift = 0; -__bit IsShifted = 0; +__bit IsShiftedUp = 0; +__bit IsShiftedDown = 0; //stack "grows" towards 0 __xdata dec80 Stack[STACK_SIZE]; //0->x, 1->y, 2->z, 3->t initially @@ -87,10 +93,6 @@ static void do_unary_op(void (*f_ptr)(void)){ } } -static void toggle_shifted(void){ - IsShifted ^= 1; -} - void process_cmd(char cmd){ //turn off backlight before start of processing backlight_off(); @@ -98,12 +100,11 @@ void process_cmd(char cmd){ switch(cmd){ ////////// case '+':{ - if (IsShifted){ // LastX + if (IsShiftedUp){ // LastX if (NoLift != 1){ StackPtr--; } copy_decn(&stack(STACK_X), &LastX); - IsShifted = 0; } else { // + do_binary_op(add_decn); } @@ -114,22 +115,33 @@ void process_cmd(char cmd){ } break; ////////// case '-':{ - negate_decn(&stack(STACK_X)); - do_binary_op(add_decn); - negate_decn(&LastX); //stored LastX was after negation of X + if (IsShiftedUp) { + do_unary_op(to_radian_decn); + } else if (IsShiftedDown) { + do_unary_op(to_degree_decn); + } else { + negate_decn(&stack(STACK_X)); + do_binary_op(add_decn); + negate_decn(&LastX); //stored LastX was after negation of X + } } break; ////////// case '/':{ - do_binary_op(div_decn); + if (IsShiftedUp){ + StackPtr--; + pi_decn(); + copy_decn(&stack(STACK_X), &AccDecn); + } else { + do_binary_op(div_decn); + } } break; ////////// case '=':{ - if (IsShifted){ //RCL + if (IsShiftedUp){ //RCL if (NoLift != 1){ StackPtr--; } copy_decn(&stack(STACK_X), &StoredDecn); - IsShifted = 0; } else { //Enter if (!decn_is_nan(&stack(STACK_X))){ StackPtr--; @@ -139,9 +151,8 @@ void process_cmd(char cmd){ } break; ////////// case '.':{ - if (IsShifted){ //STO + if (IsShiftedUp){ //STO copy_decn(&StoredDecn, &stack(STACK_X)); - IsShifted = 0; } } break; ////////// @@ -150,23 +161,8 @@ void process_cmd(char cmd){ } break; ////////// case '<':{ //use as +/- and sqrt - if (IsShifted){ //take sqrt - IsShifted = 0; - if (decn_is_zero(&stack(STACK_X))){ - //sqrt(0) = 0 - } else 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)); - break; - } - //b = 0.5 - set_dec80_zero(&BDecn); - BDecn.lsu[0] = 5; - pow_decn(); - copy_decn(&stack(STACK_X), &AccDecn); - } + if (IsShiftedUp){ //take sqrt + do_unary_op(sqrt_decn); } else { // +/- if (!decn_is_nan(&stack(STACK_X))){ negate_decn(&stack(STACK_X)); @@ -175,55 +171,84 @@ void process_cmd(char cmd){ } break; ////////// case 'r':{ //use as swap and 1/x - if (IsShifted){ //take 1/x - IsShifted = 0; + if (IsShiftedUp){ //take 1/x do_unary_op(recip_decn); } else { // swap if (!decn_is_nan(&stack(STACK_X))){ - dec80 tmp; - copy_decn(&tmp, &stack(STACK_X)); + copy_decn(&AccDecn, &stack(STACK_X)); copy_decn(&stack(STACK_X), &stack(STACK_Y)); - copy_decn(&stack(STACK_Y), &tmp); + copy_decn(&stack(STACK_Y), &AccDecn); } } } break; ////////// case 'm':{ //use as shift - toggle_shifted(); + if (IsShiftedUp) { + IsShiftedUp = 0; + IsShiftedDown = 1; + } else if (IsShiftedDown) { + IsShiftedUp = 0; + IsShiftedDown = 0; + } else { + IsShiftedUp = 1; + IsShiftedDown = 0; + } + return; + } break; + ////////// + case '1':{ + if (IsShiftedUp){ + do_unary_op(sin_decn); + } else if (IsShiftedDown){ + do_unary_op(arcsin_decn); + } + } break; + ////////// + case '2':{ + if (IsShiftedUp){ + do_unary_op(cos_decn); + } else if (IsShiftedDown){ + do_unary_op(arccos_decn); + } + } break; + ////////// + case '3':{ + if (IsShiftedUp){ + do_unary_op(tan_decn); + } else if (IsShiftedDown){ + do_unary_op(arctan_decn); + } } break; ////////// case '4':{ - if (IsShifted){ //roll down + if (IsShiftedUp){ //roll down StackPtr++; - IsShifted = 0; + } else if (IsShiftedDown){ //roll up + StackPtr--; } } break; ////////// case '5':{ - if (IsShifted){ //e^x + if (IsShiftedUp){ //e^x do_unary_op(exp_decn); - IsShifted = 0; } } break; ////////// case '6':{ - if (IsShifted){ //10^x + if (IsShiftedUp){ //10^x do_unary_op(exp10_decn); - IsShifted = 0; } } break; ////////// case '9':{ - if (IsShifted){ //log10(x) + if (IsShiftedUp){ //log10(x) do_unary_op(log10_decn); - IsShifted = 0; } } break; ////////// case '8':{ - if (IsShifted){ //ln(x) + if (IsShiftedUp){ //ln(x) do_unary_op(ln_decn); - IsShifted = 0; } } break; ////////// @@ -238,10 +263,14 @@ void process_cmd(char cmd){ copy_decn(&stack(STACK_Y), &AccDecn); } pop(); - IsShifted = 0; } break; ////////// } //switch(cmd) + IsShiftedUp = 0; + IsShiftedDown = 0; +#ifdef DESKTOP + assert(TmpStackPtr == 0); // there should be no items on the temporaries stack after one global operation +#endif } diff --git a/src/calc.h b/src/calc.h index 2c0c987..ca25d63 100644 --- a/src/calc.h +++ b/src/calc.h @@ -32,7 +32,8 @@ void process_cmd(char cmd); //push_decn is equivalent to "set_x()" if no_lift is true void push_decn(__xdata const char* signif_str, __xdata exp_t exponent); extern uint8_t NoLift; -extern __bit IsShifted; +extern __bit IsShiftedUp; +extern __bit IsShiftedDown; void clear_x(void); __xdata dec80* get_x(void); diff --git a/src/decn/CMakeLists.txt b/src/decn/CMakeLists.txt index 246c619..77ef315 100644 --- a/src/decn/CMakeLists.txt +++ b/src/decn/CMakeLists.txt @@ -12,7 +12,7 @@ target_compile_options(coverage_config INTERFACE -O0 -g --coverage) target_link_libraries(coverage_config INTERFACE --coverage) # decn library -add_library(decn decn.c) +add_library(decn decn.c ../utils.c) # decn library with coverage add_library(decn_cover decn.c) @@ -21,7 +21,7 @@ target_link_libraries(decn_cover PUBLIC coverage_config) add_executable(decn_test decn_test.c ../utils.c) target_link_libraries(decn_test decn_cover coverage_config Catch) -add_executable(decn_tests catch_main.cpp decn_tests.cpp ../utils.c) +add_executable(decn_tests catch_main.cpp decn_tests.cpp decn_tests_trig.cpp ../utils.c) target_link_libraries(decn_tests decn_cover coverage_config mpfr Catch) # decn prototyping diff --git a/src/decn/decn.c b/src/decn/decn.c index 8c09786..dbbada1 100644 --- a/src/decn/decn.c +++ b/src/decn/decn.c @@ -19,6 +19,7 @@ */ #include "../utils.h" +#include "../stack_debug.h" #include "decn.h" @@ -66,10 +67,14 @@ static const uint8_t num_digits_display = 16; dec80 AccDecn; __idata dec80 BDecn; -__idata dec80 TmpDecn; //used by add_decn() and mult_decn() -__idata dec80 Tmp2Decn; //used by recip_decn() and ln_decn() -__idata dec80 Tmp3Decn; //used by recip_decn() and ln_decn() -__xdata dec80 Tmp4Decn; //used by div_decn() and pow_decn() +__idata dec80 TmpDecn; //used by add_decn() and mult_decn() and sqrt_decn() +__idata dec80 Tmp2Decn; //used by recip_decn() and ln_decn() and sincos_decn() and sqrt_decn() +__xdata dec80 Tmp3Decn; //used by recip_decn() and ln_decn() and sincos_decn() and sqrt_decn() +__xdata dec80 Tmp4Decn; //used by sincos_decn() + +__xdata dec80 TmpStackDecn[4]; +#define TMP_STACK_SIZE (sizeof TmpStackDecn / sizeof TmpStackDecn[0]) +__idata uint8_t TmpStackPtr; __xdata char Buf[DECN_BUF_SIZE]; @@ -83,9 +88,45 @@ const dec80 DECN_LN_10 = { 0, {23, 2, 58, 50, 92, 99, 40, 45, 68} }; +// 2 pi +const dec80 DECN_2PI = { + 0, {62, 83, 18, 53, 7, 17, 95, 86, 48} +}; + +// pi/2 +const dec80 DECN_PI2 = { + 0, {15, 70, 79, 63, 26, 79, 48, 96, 62} +}; + +// 180/pi = 1rad in degree +const dec80 DECN_1RAD = { + 1, {57, 29, 57, 79, 51, 30, 82, 32, 9} +}; + +void st_push_decn(const dec80 * const src) +{ + copy_decn(&TmpStackDecn[TmpStackPtr], src); + TmpStackPtr++; + assert(TmpStackPtr < TMP_STACK_SIZE); +} + +void st_pop_decn(dec80 * const dst) +{ + assert(TmpStackPtr >= 1); + TmpStackPtr--; + if (dst) copy_decn(dst, &TmpStackDecn[TmpStackPtr]); +} + +void st_load_decn(dec80 * const dst) +{ + assert(TmpStackPtr >= 1); + copy_decn(dst, &TmpStackDecn[TmpStackPtr - 1]); +} void copy_decn(dec80* const dest, const dec80* const src){ uint8_t i; + + stack_debug(0x01); dest->exponent = src->exponent; //copy nibbles @@ -163,6 +204,7 @@ static void remove_leading_zeros(dec80* x){ uint8_t is_negative = (x->exponent < 0); exp_t exponent = get_exponent(x); + stack_debug(0x02); //find first non-zero digit100 for (digit100 = 0; digit100 < DEC80_NUM_LSU; digit100++){ if (x->lsu[digit100] != 0){ @@ -586,6 +628,7 @@ void add_decn(void){ return; } //save b for restoring later + //n.b. don't use TmpStackDecn here, it is called quite often. So you'd need to increase TMP_STACK_SIZE copy_decn(&TmpDecn, &BDecn); //handle cases where signs differ if (AccDecn.exponent < 0 && BDecn.exponent >= 0){ @@ -792,7 +835,6 @@ void mult_decn(void){ void recip_decn(void){ #define CURR_RECIP Tmp2Decn //copy of x, holds current 1/x estimate -#define X_COPY Tmp3Decn //holds copy of original x uint8_t i; exp_t initial_exp; //check divide by zero @@ -808,7 +850,7 @@ void recip_decn(void){ //normalize remove_leading_zeros(&AccDecn); //store copy of x - copy_decn(&X_COPY, &AccDecn); + st_push_decn(&AccDecn); //get initial exponent of estimate for 1/x initial_exp = get_exponent(&AccDecn); #ifdef DEBUG_DIV @@ -841,7 +883,7 @@ void recip_decn(void){ printf("%2d: %s\n", i, Buf); #endif //Accum *= x_copy - copy_decn(&BDecn, &X_COPY); + st_load_decn(&BDecn); mult_decn(); #ifdef DEBUG_DIV decn_to_str_complete(&AccDecn); @@ -868,24 +910,20 @@ void recip_decn(void){ //new_est(Accum) = recip + (1 - recip*x)*recip, where recip is current recip estimate copy_decn(&CURR_RECIP, &AccDecn); } + st_pop_decn(0); //try not to pollute namespace #undef CURR_RECIP -#undef X_COPY } -inline void div_decn(void){ -#define ACC_COPY Tmp4Decn //holds copy of original acc +void div_decn(void){ //store copy of acc for final multiply by 1/x - copy_decn(&ACC_COPY, &AccDecn); + st_push_decn(&AccDecn); copy_decn(&AccDecn, &BDecn); recip_decn(); //Accum now holds 1/x, multiply by original acc to complete division - copy_decn(&BDecn, &ACC_COPY); + st_pop_decn(&BDecn); mult_decn(); - -//try not to pollute namespace -#undef ACC_COPY } @@ -1056,7 +1094,7 @@ void ln_decn(void){ #undef NUM_TIMES } -inline void log10_decn(void){ +void log10_decn(void){ ln_decn(); copy_decn(&BDecn, &DECN_LN_10); div_decn(); @@ -1226,14 +1264,14 @@ void exp_decn(void){ #undef NUM_TIMES } -inline void exp10_decn(void){ +void exp10_decn(void){ //exp10_decn() = exp_decn(AccDecn * ln(10)) copy_decn(&BDecn, &DECN_LN_10); mult_decn(); exp_decn(); } -inline void pow_decn(void) { +void pow_decn(void) { if (decn_is_zero(&BDecn)) { copy_decn(&AccDecn, &DECN_1); return; @@ -1243,13 +1281,189 @@ inline void pow_decn(void) { return; } //calculate AccDecn = AccDecn ^ BDecn - copy_decn(&Tmp4Decn, &BDecn); //save b + st_push_decn(&BDecn); ln_decn(); - copy_decn(&BDecn, &Tmp4Decn); //restore b + st_pop_decn(&BDecn); mult_decn(); //accum = b*ln(accum) exp_decn(); } +// see W.E. Egbert, "Personal Calculator Algorithms II: Trigonometric functions" +void project_decn_into_0_2pi(void) { + const uint8_t is_negative = (AccDecn.exponent < 0); + exp_t exponent; + + remove_leading_zeros(&AccDecn); + if (is_negative) { + negate_decn(&AccDecn); + } + exponent = get_exponent(&AccDecn); + copy_decn(&BDecn, &DECN_2PI); + if (compare_magn() > 0) { + do { + do { + copy_decn(&BDecn, &DECN_2PI); + BDecn.exponent = exponent; + if (compare_magn() >= 0) { + negate_decn(&BDecn); + add_decn(); + } else { + break; + } + } while (1); + exponent--; + } while (exponent >= 0); + } + + if (is_negative) { + negate_decn(&AccDecn); + copy_decn(&BDecn, &DECN_2PI); + add_decn(); + } +} + +// K. Shirriff, "Reversing Sinclair's amazing 1974 calculator hack - half the ROM of the HP-35" +// http://files.righto.com/calculator/sinclair_scientific_simulator.html +#define SIN Tmp2Decn +#define COS Tmp3Decn +#define THETA Tmp4Decn +void sincos_decn(const uint8_t sincos_arctan) { + const uint8_t is_negative = AccDecn.exponent < 0; + if (sincos_arctan) { + set_dec80_zero(&THETA); + if (is_negative) negate_decn(&AccDecn); + copy_decn(&COS, &AccDecn); + copy_decn(&SIN, &DECN_1); + } else { + project_decn_into_0_2pi(); + copy_decn(&THETA, &AccDecn); + copy_decn(&COS, &DECN_1); + set_dec80_zero(&SIN); + // 0.0 00 5 + SIN.lsu[2] = 50; + negate_decn(&SIN); + } + do { + if (sincos_arctan) { + // THETA is in AccDecn from previous iteration + if (COS.exponent < 0) { + if (is_negative) negate_decn(&AccDecn); + break; + } + } else { + if (THETA.exponent < 0) { + break; + } + } + // COS = COS - SIN / 1000 + copy_decn(&AccDecn, &COS); + copy_decn(&BDecn, &SIN); + shift_right(&BDecn); + shift_right(&BDecn); + shift_right(&BDecn); + negate_decn(&BDecn); + add_decn(); + copy_decn(&COS, &AccDecn); + // SIN = SIN + COS / 1000 + copy_decn(&AccDecn, &SIN); + copy_decn(&BDecn, &COS); + shift_right(&BDecn); + shift_right(&BDecn); + shift_right(&BDecn); + add_decn(); + copy_decn(&SIN, &AccDecn); + // THETA = THETA -/+ 0.0 01 + copy_decn(&AccDecn, &THETA); + set_dec80_zero(&BDecn); + BDecn.lsu[1] = 1; + if (!sincos_arctan) negate_decn(&BDecn); + add_decn(); + copy_decn(&THETA, &AccDecn); + } while (1); +} + +void sin_decn(void) { + sincos_decn(0); + copy_decn(&AccDecn, &SIN); +} + +void cos_decn(void) { + sincos_decn(0); + copy_decn(&AccDecn, &COS); +} + +void tan_decn(void) { + sincos_decn(0); + copy_decn(&AccDecn, &SIN); + copy_decn(&BDecn, &COS); + div_decn(); +} + +void arctan_decn(void) { + sincos_decn(1); +} + +// see W.E. Egbert, "Personal Calculator Algorithms III: Inverse Trigonometric Functions" +void arcsin_decn(void) { + st_push_decn(&AccDecn); + copy_decn(&BDecn, &AccDecn); + mult_decn(); + negate_decn(&AccDecn); + copy_decn(&BDecn, &DECN_1); + add_decn(); + sqrt_decn(); + recip_decn(); + st_pop_decn(&BDecn); + mult_decn(); + + sincos_decn(1); +} + +void arccos_decn(void) { + arcsin_decn(); + negate_decn(&AccDecn); + copy_decn(&BDecn, &DECN_PI2); + add_decn(); +} +#undef SIN +#undef COS +#undef THETA + +void to_degree_decn(void) { + copy_decn(&BDecn, &DECN_1RAD); + mult_decn(); +} + +void to_radian_decn(void) { + copy_decn(&BDecn, &DECN_1RAD); + div_decn(); +} + +void pi_decn(void) { + set_dec80_zero(&BDecn); + BDecn.lsu[0] = 5; // 0.5 00 .. + copy_decn(&AccDecn, &DECN_2PI); + mult_decn(); +} + +void sqrt_decn(void) { + if (decn_is_zero(&AccDecn)) { + return; + } + if (decn_is_nan(&AccDecn)) { + return; + } + if (AccDecn.exponent < 0){ //negative + set_dec80_NaN(&AccDecn); + return; + } + st_push_decn(&BDecn); // sqrt should behave like an unary operation + //b = 0.5 + set_dec80_zero(&BDecn); + BDecn.lsu[0] = 5; + pow_decn(); + st_pop_decn(&BDecn); +} static void set_str_error(void){ Buf[0] = 'E'; @@ -1444,4 +1658,3 @@ void build_decn_at(dec80* dest, const char* signif_str, exp_t exponent){ #endif //DESKTOP - diff --git a/src/decn/decn.h b/src/decn/decn.h index d551477..ae04654 100644 --- a/src/decn/decn.h +++ b/src/decn/decn.h @@ -64,7 +64,7 @@ void copy_decn(dec80* const dest, const dec80* const src); extern dec80 AccDecn; extern __idata dec80 BDecn; -extern __xdata dec80 Tmp4Decn; +extern __idata uint8_t TmpStackPtr; void build_dec80(__xdata const char* signif_str, __xdata exp_t exponent); @@ -85,6 +85,17 @@ void log10_decn(void); void exp_decn(void); void exp10_decn(void); void pow_decn(void); +void sqrt_decn(void); + +void sin_decn(void); +void cos_decn(void); +void tan_decn(void); +void arctan_decn(void); +void arcsin_decn(void); +void arccos_decn(void); +void to_degree_decn(void); +void to_radian_decn(void); +void pi_decn(void); //Buf should hold at least 18 + 4 + 5 + 1 = 28 #define DECN_BUF_SIZE 28 @@ -103,6 +114,17 @@ void decn_to_str_complete(const dec80* x); void build_decn_at(dec80* dest, const char* signif_str, exp_t exponent); #endif +#ifdef DESKTOP +#define PRINT_DEC80(n, v) \ + printf(n " %d %5d: ", v.exponent < 0, get_exponent(&v)); \ + for (int i = 0; i < DEC80_NUM_LSU; i++) { \ + printf("%02d ", v.lsu[i]); \ + } \ + fputc('\n', stdout); +#else +#define PRINT_DEC80(n, v) +#endif + #ifdef __cplusplus } #endif diff --git a/src/decn/decn_test.c b/src/decn/decn_test.c index 41de63a..5b424ce 100644 --- a/src/decn/decn_test.c +++ b/src/decn/decn_test.c @@ -20,9 +20,6 @@ #include #include "decn.h" -extern char Buf[DECN_BUF_SIZE]; - - static dec80 diff; //diff = (acc - diff) / diff diff --git a/src/decn/decn_tests.cpp b/src/decn/decn_tests.cpp index 353af31..11b9240 100644 --- a/src/decn/decn_tests.cpp +++ b/src/decn/decn_tests.cpp @@ -31,10 +31,6 @@ namespace bmp = boost::multiprecision; using Catch::Matchers::Equals; - -extern char Buf[DECN_BUF_SIZE]; - - TEST_CASE("build decn"){ build_dec80("0.0009234567890123456", 7); decn_to_str_complete(&AccDecn); diff --git a/src/decn/decn_tests_trig.cpp b/src/decn/decn_tests_trig.cpp new file mode 100644 index 0000000..534e7bf --- /dev/null +++ b/src/decn/decn_tests_trig.cpp @@ -0,0 +1,203 @@ + +#include +#include +#include +#include "decn.h" +namespace bmp = boost::multiprecision; +using Catch::Matchers::Equals; + + +static void trig_test(void (*operation)(void), bmp::mpfr_float (*mpfr_operation)(bmp::mpfr_float x), + const char* a_str, int a_exp, double rtol, double atol) +{ + CAPTURE(a_str); CAPTURE(a_exp); + build_dec80(a_str, a_exp); + operation(); + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); + + bmp::mpfr_float::default_precision(50); + std::string a_full_str(a_str); + a_full_str += "e" + std::to_string(a_exp); + + bmp::mpfr_float a_actual(a_full_str); + a_actual = mpfr_operation(a_actual); + CAPTURE(a_actual); + + bmp::mpfr_float calculated(Buf); + if (rtol >= 0) { + bmp::mpfr_float rel_diff = abs((a_actual - calculated) / a_actual); + CHECK(rel_diff < rtol); + } else { + bmp::mpfr_float diff = abs(a_actual - calculated); + CHECK(diff < atol); + } + +} + +static void sin_test( + const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) +{ + trig_test(sin_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return sin(x);}, a_str, a_exp, rtol, atol); +} + +static void cos_test( + const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) +{ + trig_test(cos_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return cos(x);}, a_str, a_exp, rtol, atol); +} + +static void tan_test( + const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) +{ + trig_test(tan_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return tan(x);}, a_str, a_exp, rtol, atol); +} + +static void atan_test( + const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) +{ + trig_test(arctan_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return atan(x);}, a_str, a_exp, rtol, atol); +} + +static void asin_test( + const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) +{ + trig_test(arcsin_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return asin(x);}, a_str, a_exp, rtol, atol); +} + +static void acos_test( + const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) +{ + trig_test(arccos_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return acos(x);}, a_str, a_exp, rtol, atol); +} + + +const char * const pi = "3.141592653589793239"; +const char * const pi_threequarters = "2.356194490192344929"; +const char * const pi_halfed = "1.570796326794896619"; +const char * const pi_quarted = ".7853981633974483096"; + + +TEST_CASE("sin") { + sin_test("0.1", 0); + sin_test("0.05", 0, 1e-2); + sin_test("0.01", 0, -1); + sin_test("0.001", 0, -1); + sin_test("0.0001", 0, -1); + sin_test("0.00001", 0, -1); + sin_test("0.000001", 0, -1); + sin_test("0.0", 0, -1); + sin_test("0.2", 0); + sin_test("0.3", 0); + sin_test("0.4", 0); + sin_test("0.9", 0); + sin_test("1.5", 0); + sin_test("2.0", 0); + sin_test("2.5", 0); + sin_test("3.0", 0); + sin_test(pi, 0, -1); + sin_test(pi_quarted, 0); + sin_test(pi_halfed, 0); + sin_test(pi_threequarters, 0); + sin_test("1000.0", 0); + sin_test("-0.5", 0); + sin_test("-1.5", 0); + sin_test("-2.0", 0); + sin_test("-2.5", 0); + sin_test("-3.0", 0); + sin_test("-9.0", 0); + sin_test("-18.0", 0); + sin_test("-27.0", 0); + sin_test("-1000.0", 0); +} + +TEST_CASE("cos") { + cos_test("0.1", 0); + cos_test("0.05", 0); + cos_test("0.01", 0); + cos_test("0.001", 0); + cos_test("0.0001", 0); + cos_test("0.00001", 0); + cos_test("0.000001", 0); + cos_test("0.0", 0); + cos_test("0.2", 0); + cos_test("0.3", 0); + cos_test("0.4", 0); + cos_test("0.9", 0); + cos_test("1.5", 0); + cos_test("2.0", 0); + cos_test("2.5", 0); + cos_test("3.0", 0); + cos_test(pi, 0); + cos_test(pi_quarted, 0); + cos_test(pi_halfed, 0, -1); + cos_test(pi_threequarters, 0); + cos_test("1000.0", 0); + cos_test("-0.5", 0); + cos_test("-1.5", 0); + cos_test("-2.0", 0); + cos_test("-2.5", 0); + cos_test("-3.0", 0); + cos_test("-9.0", 0); + cos_test("-18.0", 0); + cos_test("-27.0", 0); + cos_test("-1000.0", 0); +} + + +TEST_CASE("tan") { + tan_test("0.1", 0); + tan_test("0.05", 0, 1e-2); + tan_test("0.01", 0, 5e-2); + tan_test("0.001", 0, -1); + tan_test("0.0001", 0, -1); + tan_test("0.00001", 0, -1); + tan_test("0.000001", 0, -1); + tan_test("0.0", 0, -1); + tan_test("0.2", 0); + tan_test("0.3", 0); + tan_test("0.4", 0); + tan_test("0.9", 0); + tan_test("1.5", 0); + tan_test("2.0", 0); + tan_test("2.5", 0); + tan_test("3.0", 0); +} + +TEST_CASE("arctan") { + atan_test("0.001", 0, -1, 2e-3); + atan_test("-0.001", 0, -1, 2e-3); + atan_test("0.7", 0); + atan_test("-0.7", 0); + atan_test("0.1", 0); + atan_test("-0.1", 0); + atan_test("1.0", 0); + atan_test("-1.0", 0); + atan_test("2.0", 0); + atan_test("-2.0", 0); + atan_test("3.0", 0); + atan_test("-3.0", 0); + atan_test("0", 0, -1); +} + +TEST_CASE("arcsin") { + asin_test("0.001", 0, -1); + asin_test("-0.001", 0, -1); + asin_test("0.7", 0); + asin_test("-0.7", 0); + asin_test("0.1", 0, 1e-2); + asin_test("-0.1", 0, 1e-2); + asin_test("0.9", 0); + asin_test("-0.9", 0); +} + +TEST_CASE("arccos") { + acos_test("0.001", 0); + acos_test("-0.001", 0); + acos_test("0.7", 0); + acos_test("-0.7", 0); + acos_test("0.1", 0); + acos_test("-0.1", 0); + acos_test("0.9", 0); + acos_test("-0.9", 0); +} diff --git a/src/lcd.c b/src/lcd.c index e2253c9..278df08 100644 --- a/src/lcd.c +++ b/src/lcd.c @@ -155,9 +155,15 @@ void LCD_Open(void) { //P2 entire port P2M1 = 0; P2M0 = 0xff; +#ifdef STACK_DEBUG + // P3_4 is special + P3M1 &= ~(0xe0); + P3M0 |= (0xe0); +#else //P3 pins 7:4 P3M1 &= ~(0xf0); P3M0 |= (0xf0); +#endif _delay_ms(30); // to allow LCD powerup outCsrBlindNibble(0x03); // (DL=1 8-bit mode) @@ -195,6 +201,13 @@ void LCD_Open(void) { LCD_OutChar(0x10); LCD_OutChar(0x1c); } + //program shift down sign + for (i = 0; i < 5; i++){ + LCD_OutChar(0x0); + } + LCD_OutChar(0x1F); + LCD_OutChar(0x0E); + LCD_OutChar(0x04); //clear display LCD_Clear(); @@ -279,3 +292,15 @@ void LCD_Clear() { col = 0; } +void TERMIO_PrintU8(uint8_t x) { + uint8_t i; + for (i = 2; i; i--) { + const uint8_t upper_nibble = (x & 0xf0) >> 4; + if (upper_nibble <= 9) { + TERMIO_PutChar(upper_nibble + '0'); + } else { + TERMIO_PutChar((upper_nibble - 0x0a) + 'A'); + } + x <<= 4; + } +} diff --git a/src/lcd.h b/src/lcd.h index 8002d1f..6eb8965 100644 --- a/src/lcd.h +++ b/src/lcd.h @@ -31,12 +31,14 @@ void LCD_GoTo(uint8_t row, uint8_t col); void LCD_OutString(__xdata const char* string, uint8_t max_chars); void LCD_OutString_Initial(__code const char* string); short TERMIO_PutChar(unsigned char letter); +void TERMIO_PrintU8(uint8_t x); void LCD_OutNibble(uint8_t x); void LCD_ClearToEnd(uint8_t curr_row); //CGRAM character address #define CGRAM_EXP 0 #define CGRAM_EXP_NEG 1 +#define CGRAM_DOWN 2 #include "utils.h" #ifdef DESKTOP diff --git a/src/lcd_emulator.c b/src/lcd_emulator.c index 1b05520..a9bf642 100644 --- a/src/lcd_emulator.c +++ b/src/lcd_emulator.c @@ -131,6 +131,8 @@ short TERMIO_PutChar(unsigned char letter) { lcd_buf[lcd_row][lcd_col] = 'E'; } else if (letter == CGRAM_EXP_NEG) { lcd_buf[lcd_row][lcd_col] = '-'; + } else if (letter == CGRAM_DOWN) { + lcd_buf[lcd_row][lcd_col] = 'V'; } else { lcd_buf[lcd_row][lcd_col] = letter; } diff --git a/src/main.c b/src/main.c index 961fc1c..2840d6f 100644 --- a/src/main.c +++ b/src/main.c @@ -28,6 +28,7 @@ #else #include "stc15.h" #endif +#include "stack_debug.h" #define FOSC 11583000 @@ -139,7 +140,7 @@ static void latch_on(void) __xdata char EntryBuf[MAX_CHARS_PER_LINE + 1]; __xdata uint8_t ExpBuf[2]; -__code const char VER_STR[32+1] = "STC RPN Calculator v1.10"; +__code const char VER_STR[32+1] = "STC RPN Calculator v1.11"; enum { @@ -225,7 +226,9 @@ int main() LCD_Open(); KeyInit(); Timer0Init(); //for reading keyboard - BACKLIGHT_ON(); //turn on led backlight + backlight_on(); //turn on led backlight + stack_debug_init(); + stack_debug(0xfe); ExpBuf[0] = 0; ExpBuf[1] = 0; @@ -304,7 +307,7 @@ int main() switch(KEY_MAP[I_Key]){ ////////// case '0': { - if (IsShifted){ + if (IsShiftedUp || IsShiftedDown){ //off TURN_OFF(); } else { @@ -340,7 +343,7 @@ int main() case '7': //fallthrough case '8': //fallthrough case '9': { - if (IsShifted){ + if (IsShiftedUp || IsShiftedDown){ finish_process_entry(); } else if ( EnteringExp >= ENTERING_EXP){ if ( Exp_i == 0){ @@ -365,7 +368,7 @@ int main() } break; ////////// case '.': { - if (IsShifted){ + if (IsShiftedUp || IsShiftedDown){ //STO finish_process_entry(); } else { @@ -388,7 +391,7 @@ int main() } break; ////////// case '=': { - if (IsShifted){ //RCL + if (IsShiftedUp || IsShiftedDown){ //RCL finish_process_entry(); } else { //Enter //track stack lift @@ -398,9 +401,10 @@ int main() } break; ////////// case 'c': { - if (IsShifted || is_entering_done()){ + if (IsShiftedUp || IsShiftedDown || is_entering_done()){ //clear - IsShifted = 0; + IsShiftedUp = 0; + IsShiftedDown = 0; NoLift = 1; entering_done(); EnteringExp = ENTERING_DONE_CLEARED; @@ -518,8 +522,15 @@ int main() LCD_ClearToEnd(1); //print shifted status - if (IsShifted){ + if (IsShiftedUp){ TERMIO_PutChar('^'); +#if defined(STACK_DEBUG) && defined(SHOW_STACK) + TERMIO_PutChar(' '); + TERMIO_PrintU8(stack_max); + TERMIO_PutChar(' '); +#endif + } else if (IsShiftedDown){ + TERMIO_PutChar(CGRAM_DOWN); } #ifdef DESKTOP @@ -529,7 +540,7 @@ int main() LcdAvailable.release(); #endif //turn backlight back on - BACKLIGHT_ON(); + backlight_on(); } //while (1) } /* ------------------------------------------------------------------------- */ diff --git a/src/stack_debug.c b/src/stack_debug.c new file mode 100644 index 0000000..ff6139e --- /dev/null +++ b/src/stack_debug.c @@ -0,0 +1,98 @@ +// 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 3 of the License, 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. If not, see . + +#include +#include "stack_debug.h" + +#if defined(STACK_DEBUG) + +inline static void HIGH() { + P3_4 = 1; +} + +inline static void LOW() { + P3_4 = 1; +} + +void stack_debug_init() { + P3M1 &= ~(0x10); + P3M0 |= 0x10; + LOW(); + stack_debug_write(0x55); + stack_debug_write(0xAA); + stack_debug_write(0x55); + stack_debug_write(0xAA); + stack_debug_write(0x80); + stack_debug_write(0x10); + stack_debug_write(0x08); + stack_debug_write(0x01); +} + +/* + for frequency of 12.000 MHz (12.005 adjusted) this gives a rate of 1'190'000 Baud + negative polarity, MSB + */ + +void stack_debug_write(uint8_t value) __naked { + value; +__asm + .macro HIGH + setb _P3_4 + .endm + + .macro LOW + clr _P3_4 + .endm + + mov dph,r0 + mov a,dpl + mov r0,#8 + ; START + HIGH ; 1 + nop ; 1 + nop ; 1 + nop ; 1 +stack_debug_1$: + rlc a ; 1 + jc stack_debug_2$ ; 3 + HIGH ; 1 + djnz r0,stack_debug_1$ ; 4 + sjmp stack_debug_3$ ; 3 +stack_debug_2$: + LOW ; 1 + djnz r0,stack_debug_1$ ; 4 + sjmp stack_debug_3$ ; 3 +stack_debug_3$: + nop ; 1 + ; STOP + LOW ; 1 + nop ; 1 + nop ; 1 + + mov r0,dph ; 3 + ret ; 4 +__endasm; +} + +#if defined(STACK_DEBUG) && defined(SHOW_STACK) +__xdata uint8_t stack_max = 0x00; +#endif + +void stack_debug(uint8_t marker) { +#ifdef SHOW_STACK + if (SP > stack_max) stack_max = SP; +#endif + stack_debug_write(marker); + stack_debug_write(SP); +} +#endif // defined(STACK_DEBUG) diff --git a/src/stack_debug.h b/src/stack_debug.h new file mode 100644 index 0000000..ef4adcb --- /dev/null +++ b/src/stack_debug.h @@ -0,0 +1,58 @@ +// 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 3 of the License, 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. If not, see . +#ifndef SRC_STACK_DEBUG_H_ +#define SRC_STACK_DEBUG_H_ + +#include + +#if !defined(DESKTOP) +#include "stc15.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// P3_4 is connected to the cathode and the only pin that is not toggled normally +// unfortunately, the hardware uart cannot be mapped to TX = P3_4 + +#if defined(STACK_DEBUG) +void stack_debug_init(void); +void stack_debug(uint8_t marker); +void stack_debug_write(uint8_t value) __naked; +#else +#define stack_debug_init() +#define stack_debug(marker) +#endif + +#if defined(STACK_DEBUG) && defined(SHOW_STACK) +extern __xdata uint8_t stack_max; +#endif + +#if defined(DESKTOP) || defined(STACK_DEBUG) +#define backlight_on() +#define backlight_off() +#else +inline void backlight_on(void) { + P3_4 = 0; +} +inline void backlight_off(void) { + P3_4 = 1; +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/utils.c b/src/utils.c index 38680ed..f92f994 100644 --- a/src/utils.c +++ b/src/utils.c @@ -29,7 +29,6 @@ void _delay_us(uint8_t us) (void) us; } #endif -void backlight_off(void){ } #else //!DESKTOP void _delay_ms(uint8_t ms) { @@ -69,10 +68,6 @@ void _delay_us(uint8_t us) } #endif -void backlight_off(void){ - P3_4 = 1; -} - #endif //ifdef desktop #ifdef DESKTOP diff --git a/src/utils.h b/src/utils.h index 192dc5e..c4373bf 100644 --- a/src/utils.h +++ b/src/utils.h @@ -35,9 +35,6 @@ void _delay_us(uint8_t us); #define _delay_us(x) _delay_ms(1) #endif -void backlight_off(void); - - #ifdef __linux__ #define DESKTOP @@ -58,11 +55,9 @@ char* u32str(uint32_t x, char* buf, uint8_t base); #define __at uint8_t* #define SDCC_ISR(isr, reg) #define __using(x) -#define BACKLIGHT_ON() #define TURN_OFF() #else #define SDCC_ISR(isr, reg) __interrupt (isr) __using (reg) -#define BACKLIGHT_ON() P3_4 = 0 #define TURN_OFF() P3_2 = 0 #endif