Merge branch 'srtlg/development-trigonometric'
This commit is contained in:
commit
5f6a375c06
@ -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
|
||||
|
7
Makefile
7
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) $^
|
||||
|
26
README.md
26
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:
|
||||
|
||||
|
BIN
qt_gui.png
BIN
qt_gui.png
Binary file not shown.
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 67 KiB |
@ -121,7 +121,7 @@ ApplicationWindow
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pointSize: 16
|
||||
color: "gray"
|
||||
text: {getShiftedText(parent.parent.objectName, index) + "<br><br>"}
|
||||
text: {getShiftedUpText(parent.parent.objectName, index) + "<br><br>"}
|
||||
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: {"<br><br>" + 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 "<b>" + keys[row][col] + "</b>"
|
||||
}
|
||||
|
||||
function getShiftedText(row, col) {
|
||||
function getShiftedUpText(row, col) {
|
||||
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>", ""],
|
||||
["", "", "", ""],
|
||||
["y<sup>x</sup> ", "ln(x)", "log(x)", "π"],
|
||||
["R▼", "e<sup>x</sup>", "10<sup>x</sup>", ""],
|
||||
["sin(x)", "cos(x)", "tan(x)", "►rad"],
|
||||
["off", "STO", "RCL", "LAST<i>x</i>"]
|
||||
]
|
||||
|
||||
return "<small>" + shifted_keys[row][col] + "</small>"
|
||||
}
|
||||
|
||||
function getShiftedDownText(row, col) {
|
||||
var shifted_keys = [
|
||||
["", "", "", ""],
|
||||
["", "", "", ""],
|
||||
["R▲", "", "", ""],
|
||||
["asin(x)", "acos(x)", "atan(x)", "►deg"],
|
||||
["", "", "", ""]
|
||||
]
|
||||
|
||||
return "<small>" + shifted_keys[row][col] + "</small>"
|
||||
}
|
||||
|
||||
function getBackgroundColor(row, col) {
|
||||
var background_color = [
|
||||
["white", "white", "white", "#ff9494"],
|
||||
|
127
src/calc.c
127
src/calc.c
@ -21,6 +21,11 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include "calc.h"
|
||||
#include "stack_debug.h"
|
||||
|
||||
#ifdef DESKTOP
|
||||
#include <assert.h>
|
||||
#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
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
255
src/decn/decn.c
255
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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -20,9 +20,6 @@
|
||||
#include <stdio.h>
|
||||
#include "decn.h"
|
||||
|
||||
extern char Buf[DECN_BUF_SIZE];
|
||||
|
||||
|
||||
static dec80 diff;
|
||||
|
||||
//diff = (acc - diff) / diff
|
||||
|
@ -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);
|
||||
|
203
src/decn/decn_tests_trig.cpp
Normal file
203
src/decn/decn_tests_trig.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
|
||||
#include <string>
|
||||
#include <boost/multiprecision/mpfr.hpp>
|
||||
#include <catch.hpp>
|
||||
#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);
|
||||
}
|
25
src/lcd.c
25
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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
31
src/main.c
31
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)
|
||||
}
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
98
src/stack_debug.c
Normal file
98
src/stack_debug.c
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <stdint.h>
|
||||
#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)
|
58
src/stack_debug.h
Normal file
58
src/stack_debug.h
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
#ifndef SRC_STACK_DEBUG_H_
|
||||
#define SRC_STACK_DEBUG_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user