Compare commits

..

82 Commits

Author SHA1 Message Date
jjj11x
a8ada6b7d6
Merge pull request #7 from jjj11x/jdw/pi_fixup
Jdw/pi fixup
2022-04-26 22:45:38 -04:00
Jeff Wang
33cd262674 bump version to 1.14 for pi fixes 2022-04-26 21:47:46 -04:00
toml12953
9770525dba Pressing PI (Shift ÷) should only lift the stack if the previous operation would allow it.
fixes #4
2022-04-26 21:45:52 -04:00
toml12953
c6901dc442 A call to mult_decn is not needed and prevents PI from being loaded into the X register.
fixes #3
2022-04-26 21:35:55 -04:00
Jeff Wang
5bc3b881a1 update catch2 version/link (there's probably a better way to do this) 2022-04-26 21:19:00 -04:00
Jeff Wang
086f814e7b run CI checks on pull requests also 2022-04-26 21:13:09 -04:00
Jeff Wang
25db3835e0 Merge branch 'jdw/degree_default' 2021-01-31 17:21:30 -05:00
Jeff Wang
78cb37bf54 whitespace only changes to readme
format as one sentence/phrase per line to make futuer diffs smaller
2021-01-30 22:38:41 -05:00
Jeff Wang
2a301e71a9 update readme (default degrees, update outdated stuff), bump version 2021-01-30 22:38:41 -05:00
Jeff Wang
380b90d434 resize/remove unused variables in lcd.c 2021-01-30 22:38:13 -05:00
Jeff Wang
774be02e1f use ->deg as default shifted function now that trig functions operate in degrees
(->rad is down-shifted function now)
2021-01-30 19:00:37 -05:00
Jeff Wang
5bcfb8a371 do all trig calculations in degrees 2021-01-30 18:45:44 -05:00
Jeff Wang
1e6d786482 Merge branch 'jjj11x/parallel_random_test'
add lots of random tests for decn library
2021-01-27 21:40:49 -05:00
Jeff Wang
c6ac1e5e2c add random tests for inverse trigonometric functions 2021-01-27 21:11:32 -05:00
Jeff Wang
f15790f252 add random sin/cos/tan trig tests (accuracy is fairly bad) 2020-12-10 22:54:45 -05:00
Jeff Wang
81253d8934 separate out tests into separate files 2020-10-26 22:16:26 -04:00
Jeff Wang
540d9e282c add random y^x tests 2020-10-19 21:36:18 -04:00
Jeff Wang
d3cddc1326 update readme 2020-10-13 22:11:03 -04:00
Jeff Wang
714cd69b9c add random exp() tests 2020-10-13 22:11:03 -04:00
Jeff Wang
46851eab9f use ctest to run unit tests in parallel (requires newer catch2) 2020-10-13 22:11:03 -04:00
Jeff Wang
08f1d199c1 add random logarithm tests, extra tests near log(1.0) 2020-10-13 22:11:00 -04:00
Jeff Wang
89f442cae8 add random division/reciprocal tests 2020-10-13 00:52:00 -04:00
Jeff Wang
18692d9baf add random sqrt tests 2020-10-13 00:51:57 -04:00
Jeff Wang
0a552c9f61 Merge branch 'jjj11x/ci' 2020-09-14 10:05:03 -04:00
Jeff Wang
bc8c3aab8e upload build artifacts 2020-09-14 10:00:22 -04:00
Jeff Wang
bef903b20a initial attempt at CI build 2020-09-14 08:51:19 -04:00
Jeff Wang
233f347ecc Merge branch 'sqrt_decn' 2020-09-13 23:00:56 -04:00
Jeff Wang
10ec4efed3 remove checked in binaries 2020-09-13 23:00:02 -04:00
Jeff Wang
a5e64e89d4 free up some flash space 2020-09-13 22:50:19 -04:00
Jeff Wang
273e2f5742 add fast reciprocal sqrt() implementation 2020-09-13 22:49:38 -04:00
Jeff Wang
5f6a375c06 Merge branch 'srtlg/development-trigonometric' 2020-09-13 02:06:33 -04:00
Jeff Wang
b43f0b9480 update readme 2020-09-13 01:55:32 -04:00
Jeff Wang
64754395be add shift down sign, change GUI keys, bump version 2020-09-13 01:55:00 -04:00
Jeff Wang
d8d7f98663 clear IsShiftedUp/Down at end of switch case, allow turning off with either shift 2020-09-13 01:19:22 -04:00
Mirko Scholz
2b915a3e59 decreased accuracy so that the real hardware is useful at 33 MHz 2020-09-10 19:59:57 +02:00
Mirko Scholz
5f0bd3886a cleanup 2020-09-10 19:23:58 +02:00
Mirko Scholz
2903579efd added arcsin and arccos
added a stack for temporaries, otherwise it became to tedious to keep
track which function uses which temporary for later
2020-09-10 18:49:47 +02:00
Mirko Scholz
b56523fadf updated keyboard of Qt GUI 2020-09-10 16:56:58 +02:00
Mirko Scholz
370df33a6c implemented roll up 2020-09-10 16:42:32 +02:00
Mirko Scholz
9deae8128d freed unused internal ram 2020-09-10 16:40:40 +02:00
Mirko Scholz
0b5e011017 made new functions accessible to the calculator 2020-09-10 16:37:23 +02:00
Mirko Scholz
e4ad37623b implemented arctan 2020-09-10 15:38:26 +02:00
Mirko Scholz
3e5648de87 implemented sin, cos, theta for all real numbers 2020-09-10 14:30:53 +02:00
Mirko Scholz
2caedc9a45 re-structured tests 2020-09-10 11:40:26 +02:00
Mirko Scholz
f95aa9ab82 fixed tests 2020-09-09 23:52:30 +02:00
Mirko Scholz
13d26e7b75 simple trigonometric functions as in Sinclair scientific 2020-09-09 23:37:46 +02:00
Mirko Scholz
28243c267d enable address sanitizer for g++ 2020-09-09 19:06:49 +02:00
Mirko Scholz
a491ab0461 print the maximal stack pointer value when pressing shift 2020-09-09 09:40:26 +02:00
Mirko Scholz
2a9801e676 superfluous 2020-09-09 08:47:51 +02:00
Mirko Scholz
4316f928f8 added facility to debug the stack pointer on P3_4 (LCD LED cathode)
roughly 139 bytes
functions were chosen by manually traversing pow_decn to the bottom
2020-09-08 23:46:41 +02:00
Mirko Scholz
7593260487 freed some space for the stack 2020-09-08 16:57:25 +02:00
Mirko Scholz
3f7dfe460b declaration exists already in decn.h 2020-09-08 16:56:50 +02:00
Jeff Wang
6147b39ffc v1.10: handle special cases for power, extra checks in multiply for Error 2020-09-07 23:14:04 -04:00
jjj11x
7a733cea0b
Merge pull request #1 from srtlg/development-compile-sdcc350
- fixes build with sdcc 3.5.0
- adds a few more checks for special cases, and prevents some operations on `Error`
2020-09-07 23:10:24 -04:00
jjj11x
0add7eb813
bump version 2020-09-07 23:05:31 -04:00
Mirko Scholz
be1033274a remove unused definitions
(the sanitizer does not work like this, it would need also linker flags)
2020-09-05 11:01:04 +02:00
Mirko Scholz
96f70c9474 use debug per default 2020-09-05 10:55:54 +02:00
Mirko Scholz
51c2d2e2e7 handle truncation in entering numbers properly 2020-09-05 10:55:33 +02:00
Mirko Scholz
6ab4cfb714 extra checks for multiply 2020-09-05 10:35:45 +02:00
Mirko Scholz
6487fbde6e move constants out of the header
(as far as my C knowledge goes, this may cause problems with the linker)
2020-09-05 10:25:35 +02:00
Mirko Scholz
1075e6d103 truly take advantage of __code pointer 2020-09-05 09:59:08 +02:00
Mirko Scholz
c18c0dabfd handle special cases for power function 2020-09-05 01:08:09 +02:00
Mirko Scholz
5ea2818fe3 move pow_decn from header to source file 2020-09-05 00:18:47 +02:00
Mirko Scholz
1fb0a1fd50 row/col should be always less than 16 2020-09-04 23:02:46 +02:00
Mirko Scholz
a8cf1a881b put two flags into bit addressable ram 2020-09-04 22:55:16 +02:00
Mirko Scholz
fb76fac249 move VER_STR into flash 2020-09-04 22:23:35 +02:00
Mirko Scholz
9464360014 compiles under gcc 10 2020-09-04 21:16:46 +02:00
Mirko Scholz
ae9beb120a handle common install paths for catch.hpp 2020-09-04 21:13:26 +02:00
Mirko Scholz
bb7deb53cb address cmake warning 2020-09-04 20:54:39 +02:00
Mirko Scholz
d3314a9de9 gcc 10 refuses to link otherwise 2020-09-04 20:48:09 +02:00
Mirko Scholz
eea645f401 compiles again under sdcc-3.5.0 2020-09-04 20:45:29 +02:00
Jeff Wang
fc0fe7c327 initial reciprocal square root prototyping 2019-12-11 02:27:27 -05:00
Jeff Wang
b04a95e420 increase test coverage 2019-11-20 01:54:04 -05:00
Jeff Wang
38c4e4678c update readme 2019-11-19 00:04:58 -05:00
Jeff Wang
218cb67ea7 more automated unit tests 2019-11-19 00:00:18 -05:00
Jeff Wang
e907f4571d start converting testbench to Catch unit tests 2019-11-18 04:30:20 -05:00
Jeff Wang
816c3d7ec6 update readme 2019-11-14 04:14:18 -05:00
Jeff Wang
d532f2cf44 put license notice in all files, use GPLv3+ 2019-11-14 03:45:52 -05:00
Jeff Wang
9402bd512f keyboard support for Qt GUI 2019-11-14 03:32:07 -05:00
Jeff Wang
f70510e46a use cmake everywhere for desktop build, fix build warnings 2019-11-14 02:44:07 -05:00
Jeff Wang
f25a71b74c v1.09: bugfix for taking log/ln of a number with an exponent of 99 2019-11-14 02:15:34 -05:00
Jeff Wang
47459f5253 v1.08 bugfix: make clearX actually clear X instead of just clearing number entry 2019-10-22 20:17:30 -04:00
58 changed files with 3540 additions and 719 deletions

1
.github/actions/stc_docker/Dockerfile vendored Symbolic link
View File

@ -0,0 +1 @@
../../../Dockerfile

1
.github/actions/stc_docker/action.yml vendored Symbolic link
View File

@ -0,0 +1 @@
../../../action.yml

31
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,31 @@
on: [push, pull_request]
jobs:
stc_rpncalc_ci_job:
runs-on: ubuntu-latest
name: Test building
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Docker layer cache
uses: satackey/action-docker-layer-caching@v0.0.8
- name: Docker
run: $GITHUB_WORKSPACE/steps/compose_build.sh
- name: Desktop build/check
run: $GITHUB_WORKSPACE/steps/compose_run.sh desktop_build_check.sh --rebuild
- name: Calc build
run: $GITHUB_WORKSPACE/steps/compose_run.sh build_calc.sh
- name: ls
run: ls $GITHUB_WORKSPACE
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: stc_rpncalc_artifacts
path: |
${{ github.workspace }}/build/
${{ github.workspace }}/build_qt/lcov/
${{ github.workspace }}/build_qt/decn.c.gcov
${{ github.workspace }}/build_qt/Testing/
${{ github.workspace }}/main.hex
if-no-files-found: error

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
*.swp
*.hex
*.bak
CMakeLists.txt.user

View File

@ -1,8 +1,25 @@
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.1)
project(stc_rpncalc C CXX)
# 3rd party tools
find_package(Qt5 COMPONENTS Widgets Qml Quick REQUIRED)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11 -fsanitize=address,undefined")
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
if(MSVC)
add_compile_options(/W4 /WX)
else()
add_compile_options(-Wall -Wextra -pedantic)
endif()
# CTest Catch2 tests
enable_testing()
# Directory with source code
add_subdirectory(src)

21
Dockerfile Normal file
View File

@ -0,0 +1,21 @@
FROM ubuntu:18.04
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
build-essential \
clang \
cmake \
git \
lcov \
libboost-dev \
libgmp-dev \
libmpfr-dev \
ninja-build \
qtdeclarative5-dev \
sdcc=3.5.0+dfsg-2build1 \
vim-tiny \
wget
# install more up-to-date catch2
RUN wget http://mirrors.kernel.org/ubuntu/pool/universe/c/catch2/catch2_2.13.7-1_amd64.deb
RUN echo "3ca43a3b3347ec2e220e0cc6e9c38859 catch2_2.13.7-1_amd64.deb" | md5sum --check --
RUN dpkg -i catch2_2.13.7-1_amd64.deb

View File

@ -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

348
README.md
View File

@ -1,10 +1,30 @@
# 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. This project uses SDCC 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.
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.
Note that once you change the firmware on the calculator, there's no way to go back to the original firmware (the original firmware isn't posted for download anywhere). STC's bootloader on the microcontroller deliberately prevents readback of the microcontroller's content, and STC considers this to be a "feature".
Note that once you change the firmware on the calculator,
there's no way to go back to the original firmware
(the original firmware isn't posted for download anywhere).
STC's bootloader on the microcontroller deliberately prevents readback of the microcontroller's content,
and STC considers this to be a "feature".
Here's a picture of the assembled calculator kit running the new firmware:
@ -28,7 +48,41 @@ The calculator uses RPN. Calculate (2+3)/(9^2) by pressing the following keys:
- `*`
- `÷`
The = key is used for Enter. There is automatic stack lift so that `9`, `Enter`, `*` is equivalent to 9^2. The stack is a classic 4-level RPN stack, where the T register automatically duplicates.
The = key is used for Enter.
There is automatic stack lift so that
`9`, `Enter`, `*`
is equivalent to 9^2.
The stack is a classic 4-level RPN stack,
where the T register automatically duplicates.
The decimal key also doubles as the enter exponent key.
For example the following calculates 3E8 / 1550E-9:
- 3
- .
- (1st press enters decimal point)
- .
- (2nd press begins exponent entry)
- 8
- Enter (=)
- 1
- 5
- 5
- 0
- .
- (1st press enters decimal point)
- .
- (2nd press begins exponent entry)
- .
- (3rd press begins negative exponent entry)
- 9
- `÷`
There is currently no way to force the calculator to display in scientific mode.
For extremely large numbers that are hard to read,
taking the log base 10 of a number will give its exponent.
Numbers larger than 18 digits will automatically be displayed in scientific notation,
as will numbers smaller than 1E-3.
## Keys
Some of the keys have slightly different functions, see the picture of the emulator Qt GUI.
@ -50,44 +104,90 @@ 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 calculated in degrees
- `- `: acts as to degrees when shifted
- acts as to radians when shifted down
- `+ `: acts as LastX when shifted
- `0 `: acts as off button when shifted
## Floating Point
The calculator internally calculates with an 18 digit significand for better precision, even though at most 16 digits can be displayed. The exponent display is fixed at 2 digits (when it is displayed), but the calculator doesn't prevent you from doing certain operations (e.g. basic arithmetic) which result in numbers with larger exponents.
The calculator internally calculates with an 18 digit significand for better precision,
even though at most 16 digits can be displayed.
The exponent display is fixed at 2 digits (when it is displayed),
but the calculator doesn't prevent you from doing certain operations
(e.g. basic arithmetic) which result in numbers with larger exponents.
Internally, the calculator dedicates 15 bits for representing the signed exponent, so exponents up to +/- 16,383 can be represented (see the internals section below for more information). This is to ensure that intermediate parts of certain calculations (mainly taking the reciprocal of a number) do not prematurely cause overflow or underflow, even when the result is fully representable with just 2 digits. You can do calculations with greater than 2 digits in the exponent, but only 2 digits will be displayed. For larger exponents, a 10 in the ten's place of the exponent will be displayed as a '`:`'. (This just so happens to be the next character after '`9`' in the 1602 LCD's character map).
Internally, the calculator dedicates 15 bits for representing the signed exponent,
so exponents up to +/- 16,383 can be represented
(see the internals section below for more information).
This is to ensure that intermediate parts of certain calculations
(mainly taking the reciprocal of a number)
do not prematurely cause overflow or underflow,
even when the result is fully representable with just 2 digits.
You can do calculations with greater than 2 digits in the exponent,
but only 2 digits will be displayed.
For larger exponents,
a 10 in the ten's place of the exponent will be displayed as a '`:`'.
(This just so happens to be the next character after '`9`' in the 1602 LCD's character map).
## Turning off
Press `Shift` (the `mode` key on the physical calculator) and then `0` to turn off. On older stc_rpncalc firmwares, or if the calculator is unresponsive, hold `Shift` (the `mode` key on the physical calculator) and `0` *at the same time* to turn off. NOTE: There is no auto power off.
Press `Shift` (the `mode` key on the physical calculator) and then `0` to turn off.
On older stc_rpncalc firmwares,
or if the calculator is unresponsive,
hold `Shift` (the `mode` key on the physical calculator) and `0` *at the same time* to turn off.
NOTE: There is no auto power off.
# Building
Github releases has prebuilt binaries for the calculator.
Building is fairly straigtforward though.
- Use the Makefile for building a new firmware for the calculator.
- type `make` to build
- (you must already have SDCC installed and set up so it can be found in your PATH)
- this will create a `main.hex` output file
- there is also a prebuilt binary checked in at `binaries/main.hex`
- I currently use SDCC version 3.5. Newer versions will probably produce a binary that is too big to fit in the available flash.
- See https://sourceforge.net/p/sdcc/discussion/1865/thread/9589cc8d57/
- Luckily SDCC has few dependencies, and older versions can be installed fairly easily.
- CMakeLists.txt is for building the Qt desktop application, and also the decimal-number-library test application.
- build similarly to other cmake projects:
- build similarly to other cmake projects, see [Dockerfile](Dockerfile) for build dependencies:
- `mkdir build_qt && cd build_qt`
- `cmake -DCMAKE_BUILD_TYPE=Debug -G "Eclipse CDT4 - Ninja" ..`
- (you can choose a different generator, I prefer using Ninja to build, because it's fast)
- `ninja`
# Installing
Note that once you change the firmware on the calculator, it isn't possible to go back to the original firmware. The STC microcontroller used has a bootloader permanently stored in ROM that allows downloading new firmware over a serial port (but not reading the existing firmware). You can re-program it using a USB-to-logic-level-serial (5V) dongle, and the stcgal program. WARNING: a lot of USB-to-logic-level-serial dongles are for 3.3V logic levels instead of the 5V needed. Also note that this is a "logic-level" serial port, and not RS232 levels, which are generally a high negative voltage. The diyleyuan calculator runs at 5V to make it easier to power/drive the LCD display. You have a couple of options:
Note that once you change the firmware on the calculator,
it isn't possible to go back to the original firmware.
The STC microcontroller used has a bootloader permanently stored in ROM that allows downloading new firmware over a serial port
(but not reading the existing firmware).
You can re-program it using a USB-to-logic-level-serial (5V) dongle,
and the stcgal program.
WARNING: a lot of USB-to-logic-level-serial dongles are for 3.3V logic levels instead of the 5V needed.
Also note that this is a "logic-level" serial port,
and not RS232 levels,
which are generally a high negative voltage.
The diyleyuan calculator runs at 5V to make it easier to power/drive the LCD display.
You have a couple of options:
1. Buy a USB to logic-level serial dongle that supports 5V operation (these dongles may have a jumper you need to set to switch between 3.3V and 5V). This is the best option.
- Here is one that works: https://www.amazon.com/gp/product/B00N4MCS1A/
@ -106,31 +206,131 @@ Note that once you change the firmware on the calculator, it isn't possible to g
Connect to Tx of the USB dongle.
- Pin 16 is Tx from the microcontroller (purple wire in the picture). Connect to Rx of the USB dongle.
I recommend soldering Tx and Rx wires into the plated through holes on the PCB while soldering up the kit, so that the connections are more permanent. (In the picture, I just soldered the wire directly to the microcontroller pins, but this isn't as reliable as if they were soldered into a through hole.) Note that you must "cross over" Tx/Rx going between the microcontroller and the USB dongle (i.e. Rx on the microcontroller goes to Tx on the USB dongle, and Tx on the microcontroller goes to Rx on the USB dongle).
I recommend soldering Tx and Rx wires into the plated through holes on the PCB while soldering up the kit,
so that the connections are more permanent.
I soldered the wires from the back side (leave space for the screw hole).
Note that you must "cross over" Tx/Rx going between the microcontroller and the USB dongle
(i.e. Rx on the microcontroller goes to Tx on the USB dongle,
and Tx on the microcontroller goes to Rx on the USB dongle).
You must also connect ground to the dongle. A good point to use is header P1. (You may optionally power the calculator with +5V from the USB dongle instead of using button-cell batteries. Header P1 is again a good location to use.) To program the calculator see the "Programming with stcgal" section below.
![connections_back](./connections_back.jpg)
You must also connect ground to the dongle.
A good point to use is header P1.
(You may optionally power the calculator with +5V from the USB dongle instead of using button-cell batteries.
Header P1 is again a good location to use.)
To program the calculator see the "Programming with stcgal" section below.
Pin on calculator | Color used | Pin on usb-to-serial dongle
------------------|------------|-----------------------------
U2 pin 15 (Rx) | Green | (Tx)
U2 pin 16 (Tx) | Purple | (Rx)
P1 pin 1 (+5V) | Red | (+5V if powering from dongle instead of battery)
P1 pin 2 (Gnd) | Black | (Gnd)
### Other miscellaneous hardware details
Be careful when working on the calculator. The 7550 voltage regulator used has no short circuit protection. It does have a low quiescent current and extremely low dropout voltage though, and you must match the low dropout voltage if replacing the regulator. If you do end up damaging the regulator (like I did), a good replacement is the Microchip MCP1700-5002E/TO. (In the picture though, I just removed the 7550 voltage regulator, shorted pins 2 and 3, and added a capacitor between pin 1 and pins 2/3. I am powering the calculator externally, instead of with batteries.)
#### Voltage regulator
Be careful when working on the calculator.
The 7550 voltage regulator used has no short circuit protection.
It does have a low quiescent current and extremely low dropout voltage though,
and you must match the low dropout voltage if replacing the regulator.
If you do end up damaging the regulator (like I did),
a good replacement is the Microchip MCP1700-5002E/TO.
(In the picture though, I just removed the 7550 voltage regulator, shorted pins 2 and 3,
and added a capacitor between pin 1 and pins 2/3.
I am powering the calculator externally, instead of with batteries.)
#### Parasitic powering through the usb-to-serial adapter
If you have the usb-to-serial adapter connected,
the calculator may draw power parasitically through pin 15 (Rx) of the microcontroller.
This may prevent the calculator and microcontroller from fully turning off.
This may prevent the bootloader from running
(which would prevent you from reprogramming the calculator),
since the bootloader only runs on power on.
To prevent this, use a diode on pin 15
(see image above, or consult the microcontroller datasheet).
You may also optionally add a 330 ohm resistor on pin 16 (Tx)
of the microcontroller to provide isolation on that pin.
(I don't think it's necessary to prevent parasitic power though.)
#### Adding programming connections after the fact
If you have already soldered the kit together without adding the Tx/Rx serial wires,
you can still solder wires directly to the microcontroller fairly easily.
![connections](./connections.jpg)
Here is the schematic from the diyleyuan website. Note that the schematic symbol for the microcontroller mistakenly labels P5.4 as P0.0, and mistakenly labels P5.5 as P0.1. The net name labels are correct.
#### Schematic
Here is the schematic from the diyleyuan website.
Note that the schematic symbol for the microcontroller mistakenly labels P5.4 as P0.0,
and mistakenly labels P5.5 as P0.1.
The net name labels are correct.
![schematic](./schematic.gif)
The soft-latching power switch works as follows: Initially the calculator is off. Both Q1 and Q2 are off. Pressing the On key (S4) turns on Q1 through R1 and D2. Q1 then supplies 5V to the system. Once the microcontroller has power and starts running, it turns on Q2, which keeps Q1 on through R1. To turn off, the microcontroller turns off Q2, which in turn will turn off Q1.
#### Soft-latching power switch
The soft-latching power switch works as follows:
Initially the calculator is off. Both Q1 and Q2 are off.
Pressing the On key (S4) turns on Q1 through R1 and D2.
Q1 then supplies 5V to the system.
Once the microcontroller has power and starts running,
it turns on Q2, which keeps Q1 on through R1.
To turn off, the microcontroller turns off Q2, which in turn will turn off Q1.
#### Component layout
Here is the component layout from the diyleyuan website.
![component layout](./component.gif)
The switches used are a knockoff of the Omron B3F series. A good replacement is the B3F-5050 (the switches included with the kit take more force to depress). The LCD used is a fairly standard LCD based on a HD44780-compatible controller. The hole spacing for the screw holes on the LCD is 31mm x 75mm. There are many replacements available, including ones that don't need the backlight on to be readable. I recommend a positive FSTN type, although the one included is very usable with the backlight on.
#### Keyswitches, LCD, and other recommended replacement components
The switches used are a knockoff of the Omron B3F series.
A good replacement is the B3F-5050 which requires only 130 grams force to depress.
The switches included with the kit take more force to depress.
(This is somewhat a matter of personal preference though.)
I recommend using four 11mm M2 standoffs along with eight M2 screws instead of using the four long screws and nuts provided to hold the top and bottom together.
The problem with using the long screws is that in order to tighten them tight enough that they don't become loose easily,
the top plate deforms slightly and might interfere with the keys.
User toml_12953 has made a new keyboard template here (todo: can check into git directly?):
https://www.dropbox.com/s/7xkg98ywonp4p1l/New%20DIY%20Template.jpg?dl=1
The LCD used is a fairly standard LCD based on a HD44780-compatible controller.
The hole spacing for the screw holes on the LCD is 31mm x 75mm.
There are many replacements available,
including ones that don't need the backlight on to be readable.
I recommend a positive transflective or reflective FSTN type,
although the one included (transmissive) is very usable with the backlight on.
Here is a picture of a positive FSTN display with the backlight off
(shown under strong office lighting):
![backlight off](./no_backlight.jpg)
The included LCD is a transmissive type,
and requires the backlight on to be readable.
I sometimes use an STC15F2K60S2 for development work.
This microcontroller is available in a pin-compatible DIP-28 package for less than $2 a piece in single-digit quantities,
and has almost 5 times the flash
(a downside is that when programming, the flash takes longer to erase).
To program a blank microcontroller,
you will have to add the following additional options to `stcgal`: `-l 1200 -t 12000`
## Programming with stcgal
Run `stcgal` as shown below, replacing `stc_rpncalc/main.hex` with the actual path to the main.hex you built. There are also prebuilt binaries in the `binaries` directory. In this example, I'm programming at a relatively high line rate of 230,400 bits/s. This works very reliably, but you may want to try at a slower speed to start (omit the `-b 230400` option).
Run `stcgal` as shown below,
replacing `stc_rpncalc/main.hex` with the actual path to the main.hex you built.
There are also prebuilt binaries in the Releases section of github.
In this example, I'm programming at a relatively high line rate of 230,400 bits/s.
This works very reliably, but you may want to try at a slower speed to start
(omit the `-b 230400` option),
especially when using an inline resistor and diode.
~~~~
$ ./stcgal.py -P stc15 -b 230400 stc_rpncalc/main.hex
@ -174,16 +374,43 @@ Disconnected!
(The name for `stcgal` is probably a play on words from the `avrdude` programming software used to program AVR microcontrollers.)
# 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. 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, keys which do not have a shifted 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
## Number Format
The original firmware that came with this calculator used a fixed-point format, which significantly limited the range of numbers usable. Additionally, the implementation doesn't actually have enough digits for all displayable results, which can cause errors (e.g. `3,162.277*3,162.28` gives `10,000,010` instead of the correct `10,000,005.31156`).
The original firmware that came with this calculator used a fixed-point format,
which significantly limited the range of numbers usable.
Additionally, the implementation doesn't actually have enough digits for all displayable results,
which can cause errors
(e.g. `3,162.277*3,162.28` gives `10,000,010` instead of the correct `10,000,005.31156`).
This replacement calculator firmware uses decimal floating point, using base-100 to store numbers and do calculations. Base-100 allows for efficient storage into 8-bit bytes, and is easier to work with than packed-BCD. Unlike straight binary representations, base-100 is still fairly easy to display as decimal. Also unlike binary representations, there is no conversion error from binary/decimal (e.g. numbers like `0.1` can be represented exactly).
This replacement calculator firmware uses decimal floating point,
using base-100 to store numbers and do calculations.
Base-100 allows for efficient storage into 8-bit bytes,
and is easier to work with than packed-BCD.
Unlike straight binary representations,
base-100 is still fairly easy to display as decimal.
Also unlike binary representations, there is no conversion error from binary/decimal
(e.g. numbers like `0.1` can be represented exactly).
Each `uint8_t` stores a base-100 "`digit100`", referred to as an "`lsu`", for least significant unit. (The LSU terminology is borrowed from the decNumber library: I originally considered using the decNumber library similar to the WP-34S calculator, but just the library itself takes several times more flash than is available on this calculator. I also considered using the BigNu mber arduino library, but that library uses C++ and lots of pointers passed to functions, which are extremely expensive on the 8051 architecture.) The number format is as follows:
Each `uint8_t` stores a base-100 "`digit100`",
referred to as an "`lsu`", for least significant unit.
(The LSU terminology is borrowed from the decNumber library:
I originally considered using the decNumber library similar to the WP-34S calculator,
but just the library itself takes several times more flash than is available on this calculator.
I also considered using the BigNumber arduino library,
but that library uses C++ and lots of pointers passed to functions,
which are extremely expensive on the 8051 architecture.)
The number format is as follows:
- `lsu[0]`: contains the most significant `digit100` (the most significant 2 decimal digits)
- implicit decimal point between `lsu[0]/10` and `lsu[0]%10`
@ -197,16 +424,23 @@ Each `uint8_t` stores a base-100 "`digit100`", referred to as an "`lsu`", for le
- range of exponents only needs to be `+/-99`
- using 15 bits for the exponent (instead of e.g. 7 bits) prevents certain intermediate results from prematurely causing overflow
For example, the number `13.5` is stored normalized (with no leading zeros in the representation) as follows:
For example, the number `13.5` is stored normalized
(with no leading zeros in the representation) as follows:
- `lsu[0]`: 13
- `lsu[1]`: 50
- `lsu[2]` to `lsu[n-1]`: all 0
- exponent: 1
There is an implicit decimal point between the 1 and 3 in `lsu[0]`, so the number is 1.350 * 10^1, which is equivalent to `13.5`. Similarly, the number `1.35` would be stored the exact same way, except now the exponent is 0.
There is an implicit decimal point between the 1 and 3 in `lsu[0]`,
so the number is 1.350 * 10^1, which is equivalent to `13.5`.
Similarly, the number `1.35` would be stored the exact same way, except now the exponent is 0.
The number `0.135` would be stored the same way, except now the exponent is `0x7FFF` (note that the sign bit is 0, and the bottom 15 bits are the 2's complement representation of -1). The number `-13.5` would be stored the same way, except now the exponent is `0x8001` (the sign bit is now 1 which means the number as a whole is negative, but the exponent itself is positive).
The number `0.135` would be stored the same way,
except now the exponent is `0x7FFF`
(note that the sign bit is 0, and the bottom 15 bits are the 2's complement representation of -1).
The number `-13.5` would be stored the same way, except now the exponent is `0x8001`
(the sign bit is now 1 which means the number as a whole is negative, but the exponent itself is positive).
## Arithmetic
- Addition is done the same way as it's done by hand, although in base-100 instead of decimal.
@ -219,38 +453,58 @@ The number `0.135` would be stored the same way, except now the exponent is `0x7
## Transcendental Functions
- Logarithms are calculated similar to how it's described by the HP Journal article "Personal Calculator Algorithms IV: Logarithmic Functions" by William Egbert.
- see `src/decn/proto/ln_mfp.cpp` for initial prototyping development work
- Exponentials are calculated similar to the HP 35 algorithm, as described [here](http://www.jacques-laporte.org/expx.htm) using the same constants as the logarithm algorithm.
- Exponentials are calculated similar to the HP 35 algorithm, as described [here](https://archived.hpcalc.org/laporte/expx.htm) using the same constants as the logarithm algorithm.
- 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))
- Square roots are calculated using a fixed number of Newton-Raphson iterations to calculatie 1/sqrt(x) and then multiplying by x.
- the iteration for 1/sqrt(x) is new_estimate = 0.5*estimate * (3 - x * estimate * estimate)
- see `src/decn/proto/recip_sqrt.cpp for initial prototyping development work
- 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
- Would be nice to have the resistor color band decoder if there is more flash space
- Rounding: currently, to save code space, there is no rounding being done (even for intermediate steps), and numbers are instead truncated. Still, with 18 digits of precision (two guard digits, even if all 16 digits are actually displayed), the results are fairly accurate.
- Square roots could be more-accurately implemented using digit-by-digit methods similar to those described in the HP Journal article "Personal Calculator Algorithms I: Square Roots" by William Egbert.
- calculating using Newton-Raphson iterations for the reciprocal square root 1/sqrt(x), and then multiplying by the original value would probably also be more accurate, and definitely much faster
- the iteration for the reciprocal square root is new_estimate = 0.5 * estimate * (3 - x * estimate * estimate)
- Reciprocal/division could also be more-accurately implemented using digit-by-digit methods (the Newton-Raphson iterations currently used are quite fast though).
# Key Debouncing
The keyboard matrix is scanned once every 5ms. The keyboard debouncing is based on the quick draw/integrator hybrid algorithm described [here](https://summivox.wordpress.com/2016/06/03/keyboard-matrix-scanning-and-debouncing/). This algorithm combines the advantages of both methods:
The keyboard matrix is scanned once every 5ms.
The keyboard debouncing is based on the quick draw/integrator hybrid algorithm described
[here](https://summivox.wordpress.com/2016/06/03/keyboard-matrix-scanning-and-debouncing/).
This algorithm combines the advantages of both methods:
1. It signals a key press immediately, the very first instant a keyboard matrix scan detects a key is pressed (similar to the "quick-draw" method).
1. It has an "integrator" to determine both when a key is fully pressed and when a key is fully released. This prevents the mechanically bouncy keys from registering multiple times when pressed.
1. It has an "integrator" (a saturating up/down counter) to determine both when a key is fully pressed and when a key is fully released. This prevents the mechanically bouncy keys from registering multiple times when pressed.
In practice, the keyboard debouncing works much better than the original firmware (which would occasionally miss keystrokes).
In practice, the keyboard debouncing works much better than the original firmware
(which would occasionally miss keystrokes).
# 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.
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:
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:
- only 128 bytes can be addressed directly (or indirectly)
- the start of this address space is also shared with general purpose registers, so you don't actually have the full 128 bytes of directly addressable memory
@ -260,8 +514,22 @@ Another weird thing about the 8051 is that not all of the memory is addressed th
- on the original 8051, this memory would have actually been external, but on this microcontroller the "external" ram is built in
- addressing this memory is more difficult than addressing indirect memory, which is itself more difficult than addressing memory directly
Thus, there are special compiler directives to tell it what address space to place variables in memory. (Even for a simple calculator, there isn't enough directly addressable memory (128 bytes) to store everything.) General-purpose pointers and operations using general-purpose pointers are relatively expensive since the pointers must encode the memory type.
Thus, there are special compiler directives to tell it what address space to place variables in memory.
(Even for a simple calculator, there isn't enough directly addressable memory (128 bytes) to store everything.)
General-purpose pointers and operations using general-purpose pointers are relatively expensive since the pointers must encode the memory type.
# Licensing
This code is licensed under GPLv3.
This code is licensed under GPLv3+:
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.
(Full license text is in LICENSE.md)

7
action.yml Normal file
View File

@ -0,0 +1,7 @@
# GitHub Actions
name: 'STC RPN Calc CI build'
description: 'run CI build in docker container'
runs:
using: 'docker'
image: 'Dockerfile'

View File

@ -1,400 +0,0 @@
:0400000002001332B5
:03000B0002008A66
:09006C00751700751801751900E3
:0300870002000E66
:03000E0002017874
:20008A00C021C0E0C0F0C082C083C0D075D008120B1D120BE774FFB52802801CE516700725
:2000AA00E515B514028021E5142410F8A628E514045403F5147516007880860FBF08097820
:2000CA0084860FBF0802C2B2D0D0D083D082D0F0D0E0D02132438E80758AC5758C1DC28D66
:2000EA00D28CD2A9D2AF22AFB174FB5FF5B143B204D2B222C374019518E433FFB40100E473
:20010A0033F58222751700751900900012E4F0900013F022C374019518B3E433FF703D90D9
:20012A000013E075F00AA4FF900012E0FE2FFF7405B51804C3E49FFFE5172401F582E434C3
:20014A0000F583E4F0EFF5693395E0F56A90000112265712010EE56660020566E51A902EDF
:20016A00CB93F58212288E751801756600227516011200F11208EF120ACD1200DFC2B490D5
:20018A000012E4F0900013F075272090009E1209EA78808607BF080978848607BF0802C284
:2001AA00B2E51670ECE5152410F9871AE515045403F515E514B51503751601E51A902ECB2B
:2001CA0093FFBF2A03020594BF2B03020594BF2D03020594BF2E030203ABBF2F03020594BE
:2001EA00BF30028051BF31030202BDBF32030202BDBF33030202BDBF34030202BDBF350366
:20020A000202BDBF36030202BDBF37030202BDBF38030202BDBF39030202BDBF3C03020524
:20022A0094BF3D03020480BF630302053CBF72030205940205EFE5676005C2B20205F474DA
:20024A00FC2518502EE519700B900012E4F07519010205F4900012E0FE900013F0900012AF
:20026A00E4F00519E51924FD40030205F47519010205F4C374019518B3E433FE60137518EE
:20028A0002E5172401F582E43400F583EFF00205F4E51770030205F474F0251750030205EC
:2002AA00F4E5172401F582E43400F583EFF005170205F4E567605CC374019518B3E433FE6D
:2002CA00703D900013E075F00AA4FE900012E0FD2EFE7405B51804C3E49EFEE5172401F585
:2002EA0082E43400F583E4F0EEF5693395E0F56A90000112265712010EE56660020566E57D
:20030A001A902ECB93F58212288E7518017566000205F474FC2518503DE5197013E51A90B0
:20032A002ECB9324D0FE900012F07519010205F4900012E0900013F0E51A902ECB9324D0F5
:20034A00FE900012F00519E51924FD40030205F47519010205F4C374019518B3E433FE60F1
:20036A001D751802E5172401FDE43400FEE51A902ECB93FC8D828E83F005170205F474F0F1
:20038A00251750030205F4E5172401FDE43400FEE51A902ECB93FC8D828E83F0051702054B
:2003AA00F4E567605CC374019518B3E433FE703D900013E075F00AA4FE900012E0FD2EFE9E
:2003CA007405B51804C3E49EFEE5172401F582E43400F583E4F0EEF5693395E0F56A9000A7
:2003EA000112265712010EE56660020566E51A902ECB93F58212288E751801756600020560
:20040A00F4C374019518B3E433FE6028AE170517EE2401F582E43400F5837430F0AE170550
:20042A0017EE2401F582E43400F583742EF07518030205F47402B5182CE5177011AE1705AE
:20044A0017EE2401F582E43400F5837430F0AE170517EE2401F582E43400F583742EF075CB
:20046A0018030205F4E51824FB400505180205F47518040205F4E567605CC374019518B3B7
:20048A00E433FE703D900013E075F00AA4FE900012E0FD2EFE7405B51804C3E49EFEE517C8
:2004AA002401F582E43400F583E4F0EEF5693395E0F56A90000112265712010EE5666002F1
:2004CA000566E51A902ECB93F58212288E7518017566000205F4C374019518B3E433FE70CC
:2004EA003D900013E075F00AA4FE900012E0FD2EFE7405B51804C3E49EFEE5172401F58251
:20050A00E43400F583E4F0EEF5693395E0F56A90000112265712010EE56660020566E51AC2
:20052A00902ECB93F58212288E7518017566010205F4E567700BC374019518B3E433FE601D
:20054A000F75670075660112010E7518000205F474FC251850111518751900900012E4F0E2
:20056A00900013F00205F4E51770030205F41517E5172401F582E43400F583E0FEBE2E0259
:20058A0080030205F47518028060C374019518B3E433FE703D900013E075F00AA4FE9000E6
:2005AA0012E0FD2EFE7405B51804C3E49EFEE5172401F582E43400F583E4F0EEF569339579
:2005CA00E0F56A90000112265712010EE56660020566E51A902ECB93F58212288E75180191
:2005EA0075660080058F8212288E75260075820012099BC374019518B3E433FF7004E56603
:20060A0060161226BFAE82AF837D008E828F838DF012232FAF8280141226ACAD82AE837CFC
:20062A00008D828E838CF012232FAF82EF700B75271090001F1209EA806575270D90001F78
:20064A00C0071209EAD007EF30E710758201C007120A1FD007C3E49FFF800A758200C00779
:20066A00120A1FD007C2D575F00AEF30E704B2D5F4048430D502F4042430F582C007120A9E
:20068A001FD00775F00AEFC2D530E704D2D5F40484E5F030D502F4042430F582120A1F75D8
:2006AA008200120A817401B5180280030207441226ACAD82AE837C008D828E838CF012236C
:2006CA002FE582FF700C75271090001F1209EA0207EE75270D90001FC0071209EAD007EFBE
:2006EA0030E710758201C007120A1FD007C3E49FFF800A758200C007120A1FD007C2D5754D
:20070A00F00AEF30E704B2D5F4048430D502F4042430F582C007120A1FD00775F00AEFC205
:20072A00D530E704D2D5F40484E5F030D502F4042430F582120A1F0207EEE517700975825F
:20074A0030120A1F0207EE74FC2518402A7F00C3EF951740030207EEBF100040030207EEF6
:20076A00EF2401F582E43400F583E0F582C007120A1FD0070F80D87F00C3EF9517501CBFB5
:20078A000D005017EF2401F582E43400F583E0F582C007120A1FD0070F80DEBF0D005012F5
:2007AA00BF0D005016758220C007120A1FD0070F80EE75260D75820112099B7405B51808EC
:2007CA00758201120A1F8006758200120A1F900013E02430F582120A1F900012E02430F5D0
:1907EA0082120A1F758201120A81E567600675825E120A1FC2B402019B4E
:202EB50000000A0000000000000000000017023A325C63282D44633C726D2F3938372A3661
:202ED50035342D3332312B3D2E305354432052504E20202020202020202043616C63756C7D
:0B2EF50061746F722076312E303700C0
:202F97005354432052504E20202020202020202043616C63756C61746F722076312E30379A
:012FB7000019
:20080300AF82AEB0741F5EF5B0758232C007120E64D00753070FEFC454F0F5A07582641203
:200823000E64D2B5758264120E64C2B522E582FFC4540FF582C007120803D0078F8212085A
:2008430003020886AFA0C2B7D2B6D2B5439580AE96747F5EF596758264C007120E64A2A7C4
:200863009200AE95747F5EF595439680C2B5D2B5758264120E64D007C2B58FA0A200E43359
:20088300F582227E007F00C007C006120847E582D006D007700122758201C007C006120E85
:2008A30055D006D0070EBE00010FC3EE9464EF940040D422AF82C007120886D007C2B5D23D
:2008C300B7C2B674F05FF5A0D2B5758264C007120E64D007C2B553070FEFC454F0F5A0D24C
:2008E300B5758264120E64C2B50208867595007596FFAFB1740F5FF5B143B2F075821E1252
:200903000E55758203120803758205120E55758203120803758264120E64758203120803DC
:20092300758264120E64758202120803758264120E6475820612083075820C120830758255
:20094300141208307582281208307582401208307F00758200C0071208B7D007EF600274A2
:200963000FFE8E82C0071208B77582001208B775821C1208B77582101208B7758218120812
:20098300B77582101208B775821C1208B7D0070FBF020040BD020ABDAF82BF02005023749B
:2009A300F02526401DEF030354C024802526F582C007120830D0078F2275230085262475B8
:2009C300250022E582FF700D758280120830E4F522F523800C7582C0120830752201752359
:2009E30000E4F524F52522AE82AD837F00AC278E828D838FF0122E27FB6020EC601D8B8212
:200A0300C007C006C005C004120A1FD004D005D006D0070EBE00010D1C80D422AF82BF0DC3
:200A2300028003BF0A05120ABD804FE4BF090104FE7003EE6016E522452370087582011241
:200A430009C680367582001209C6802E8F821208B70524E4B524020525C3E5249410E5251A
:200A6300648094804014E522452370087582011209C680067582001209C690000122AF8225
:200A8300E524452560188F057E00EDB52210EEB5230C758220C007120A1FD00780E222E557
:200AA30082540FFF24F6400A8F0674302EF582020A1F74572FF582020A1F75820112083004
:0A0AC300E4F522F523F524F52522C1
:200ACD007591007592007590FFAFB174FC5FF5B1AFB274FC5FF5B243B003AFC974CF5FF547
:200AED00C9AFCA74CF5FF5CA43C8307F00EF2F25E0248AFE7D00ED2EF9902F00E493FCF703
:200B0D000DBD040040F00FBF050040E17528FF227880760043B203AFB074FC5FF5B043B0EC
:200B2D0003AFB274FC5FF5B27880860F30B0047E0080027E01EE2F7880F67880860F30B165
:200B4D00047E0080027E02EE2F7880F67880860F30CC047E0080027E04EE2F7880F67880E7
:200B6D00860F30CD047E0080027E08EE2F7880F67F00902F04E493FEC3EF9E505CEF042477
:200B8D0080F87600EF04FE8FF005F07401800225E0D5F0FBFD7C0074042CF5F005F07401CD
:200BAD00800225E0D5F0FBF4F590ED5590FB70047A0180027A008A0BEE2480F9870A8CF083
:200BCD0005F0EB800225E0D5F0FBFB2AF77590FF0CBC040040C10F8099227528FF752A006F
:200BED00E52A252A25E0FEFD248AF532E52A2485F531E52A2480F530752B00C00DE52B255D
:200C0D0032FAA80A862EA831860FE52B252BFA8AF005F07403800225E0D5F0FBFA5FF52FC3
:200C2D00E52B75F002A4F5F005F0E52F8002C313D5F0FBF52F852E2C852F2DA830860F85B0
:200C4D002BF005F07A017B008006EA2AFAEB33FBD5F0F78F0D7F00ED520AEF520BD00DEAA1
:200C6D004B601B902F01E493FFC3E52E64808FF063F08095F0501FE52E04F52C8018902FDC
:200C8D0000E493FFC36480852EF063F08095F05005E52E14F52CE52F24FC5003020DECE52B
:200CAD002F75F003A4900CB673020CC2020D02020D5B020D98C00D902F00E493FF3395E08B
:200CCD00FB902F03E493FA3395E0FDEA2FFFED3BFBE52CFA3395E0FDC3EA9FED64808BF0B1
:200CED0063F08095F0D00D5003020DF8752C00752D01020DF8902F02E493FFC3E52C64801E
:200D0D008FF063F08095F0400D752D02902F01E493F52C020DF8C00DEF3395E0FBC3E49FFA
:200D2D00FFE49BFBE52CFA3395E0FDC3EF9AEB64808DF063F08095F0D00D5003020DF875E1
:200D4D002D00902F00E493F52C0529020DF8C00D902F01E493FF3395E0FB902F03E493FAF4
:200D6D003395E0FDEFC39AFFEB9DFBE52CFA3395E0FDC3EF9AEB64808DF063F08095F0D083
:200D8D000D4068752C00752D038060C00D902F02E493FFFA3395E0FBC3E49AFAE49BFBE530
:200DAD002CFC3395E0FDC3EA9CEB64808DF063F08095F0D00D400C752D00902F00E493F576
:200DCD002C8028C3E52C64808FF063F08095F0401A752D02902F01E493F52C0529800C751E
:200DED002D00902F00E493F52C05297401B52F02800A7401B52D05E52B2EF528ED248A25D8
:200E0D002BF8A62CE52A2485F9870FE52B252BFC8CF005F07403800225E0D5F0FBF4FB52BD
:200E2D000F8CF005F0E52D800225E0D5F0FBFC4FF7052B74FC252B4003020C08052A74FBA3
:080E4D00252A4003020BED22EF
:052F0000E21E020204C4
:1C0E550075F00A74E6D5E0FDD5F0F8D582F222000000000000D582F722D2B422C6
:09007500753E00753F0075400066
:200E7100AD82AE83AFF0AA76AB77AC788A828B838CF0122E27F8A3122E27F98D828E838F55
:200E9100F0E8122DA2A3E9122DA274022DFDE43EFE74022AF57AE43BF57B8C7C757900E5E2
:200EB100792DF8E43EF98F04C005C006C007E579257AFAE4357BFBAF7C8A828B838FF01227
:200ED1002E27FA888289838CF0122DA20579C3E5799409D007D006D00540C422AD82122EEC
:200EF10027FEA3122E27FF30E60C8E048F058C8274804DF583228E82747F5FF58322AD8257
:200F1100AE83AFF0E578600BAB76AC7774804CF577800353777F8D828E838FF0E576122D33
:200F3100A2A3E577022DA2AD82AE83AFF074022DFDE43EFEAC76BC09005015EC2DF9E43EEF
:200F5100FA8F0389828A838BF0E4122DA20C80E622AD82AE83AFF0754000E4F53FF53EF514
:200F71004174022DFDE43EFEE5412DFAE43EFB8F048A828B838CF0122E2775F00A84F53E3F
:200F9100E5412DFAE43EFB8F048A828B838CF0122E2775F00A8485F03FE5412DFAE43EFB35
:200FB1008F04E54075F00AA4253EF98A828B838CF0122DA2853F40054174F7254150A9224C
:200FD100AD82AE83AFF0754000753F00753E0075410874022DFDE43EFEE5412DFAE43EFBFD
:200FF1008F048A828B838CF0122E2775F00A84F53EE5412DFAE43EFB8F048A828B838CF097
:20101100122E2775F00A8485F03FE5412DFAE43EFB8F04E53F75F00AA42540F98A828B8305
:201031008CF0122DA2853E4015417401254150A92285824285834385F044122E27A3122E5C
:201051002733E433F5468542828543838544F0120EED85824785834874022542F8E43543AA
:20107100F9AC447B00EB28FAE439FE8C078A828E838FF0122E2770060BBB090040E7EB7571
:20109100F002A4D39547F4B3F547E54895F0F548EB605ABB0900505574022542FDE43543E4
:2010B100FEAF447549008B4574F725454030E5492DF54AE43EF54B8F4CE5452DFAE43EFB16
:2010D1008F048A828B838CF0122E27FA854A82854B83854CF0122DA20549054580CA85497F
:2010F100768542828543838544F0120F3874022542FDE43543FEAF448D828E838FF0122EBD
:2011110027FDBD0A0050158542828543838544F0120FD1154774FFB547021548854776853E
:2011310048778546788542828543838544F0020F0F85824F8583507D008D527B007A0085AB
:201151004F82855083E0FC700990003375F040021348BC2D027A02755100E551254FF582ED
:20117100E43550F583E0FFBF2E13EA20E00643020102134390003375F0400213F1C3EF648C
:201191008094B150030211FBC374B98FF063F08095F04056BD1200501EED30E015EDC313A9
:2011B1002435F9E55275F00AA4FEEF24D0FC2EF78005EF24D0F5520DEB700ABA010280031F
:2011D100BA03027BFFEA70047A048013BA01047A05800CBA02047A068005BA03027A07EA9D
:2011F10030E0030213430B021343E551254FF582E43550F583E0FFBF3053C374838AF06356
:20121100F08095F0E433FE601DBD12005017ED30E010EDC313FC2435F9E55275F00AA4F7A1
:2012310080037552000DEA30E019EE6003021343EB70057BFE021343EB20E7030213431BF1
:20125100021343EE70030213430B021343EF6003021343C374838AF063F08095F0400990FA
:20127100003375F040021348ED30E00FEDC313FF2435F9E55275F00AA4F70DEDC313F5768C
:2012910090003375F040C003C002120F38D002D003C374808BF063F08095F05035AF4DEB5C
:2012B100142FFFFC3395E0FEC3EC954DEE6480854EF063F08095F0401074FF954D74BF855E
:2012D1004EF063F08095F0503F90003375F0400213F1EB30E730AE4DEB2EFFFC3395E0FE83
:2012F100C3E54D9CE54E64808EF063F08095F0400CE54D9401E54E64809440500B90003343
:2013110075F0400213F1AF4DEFF5763395E0F577C3EA64809486E433FFB40100E433F578AD
:2013310090003375F040120F0F90003375F040021042055102116BAD82AE83AFF0E4122D52
:20135100A2A3122DA274022DFDE43EFE7C00EC2DF9E43EFA8F0389828A838BF0E4122DA202
:201371000CBC090040E822AD82AE83AFF074022DFDE43EFE7C00EC2DF9E43EFA8F0389823B
:201391008A838BF0122E276004758200220CBC090040E375820122AD82AE83AFF0122E275C
:2013B100FBA3122E27FCBB0005BCC00280047582002274022DFDE43EFE7C00EC2DF9E43ED0
:2013D100FA8F0389828A838BF0122E27F9B9FF028004758200220CBC090040DF758201221C
:2013F100AD82AE83AFF0E4122DA2A374C0122DA274022DFDE43EFE7C00EC2DF9E43EFA8F67
:201411000389828A838BF074FF122DA20CBC090040E722AD82AE83AFF0C007C006C0051254
:2014310013A8E582D005D006D0076001228D828E838FF0122E27FBA3122E27FC902F1CE4AE
:2014510093F9740193FAE96203EA62048D828E838FF0EB122DA2A3EC022DA27F00757633E7
:201471008F7775784090003B75F000C007120E7175769E75770075784090004675F00012B1
:201491000E7190003B75F00012104290004675F000121042D007E4FEFDF553F554F555F503
:2014B10056BE09005068BD09005063EF7023EE243DF582E43400F583ED2448FBE43400FC8D
:2014D100E0FA8B828C83E0FBC3EA9B50047FFF8023EF7020EE243DF582E43400F583ED2487
:2014F10048FBE43400FCE0FA8B828C83E0FBC39A50027F010E0D74022553F553E43554F5D1
:201511005474022555F555E43556F556809390003B75F000C007120EEDAD82AE83ED255396
:20153100FDEE3554FE90004675F000C006C005120EEDAB82AC83D005D006D007EB2555FB17
:20155100EC3556FCC3EB9DEC64808EF063F08095F0500475820122C3ED9BEE64808CF063AC
:20157100F08095F050047582FF228F822290003375F040121378E582601190009E75F04016
:20159100121378E58260047582002290003375F040121378E5826012789E860608E6FF301C
:2015B100E704758201227582FF2290009E75F040121378E582600DE53430E7047582FF226D
:2015D10075820122AE33E534FF33E433FD6019789E8603088604C3E49B74808CF063F08071
:2015F10095F050047582FF22C3E49E74808FF063F08095F0500E789E860608E6FF30E704D1
:2016110075820122ED60047FFF80027F01C00712146CAE82D0078EF0EFA4F58222AD82AEE7
:2016310083AFF0C007C006C005120EEDAB82AC83D005D006D007EBB55705ECB55801228D95
:20165100828E838FF0C007C006C005C004C003120F62D003D004D005D006D0070BBB00D646
:201671000C80D3755A0090003375F04012104290009E75F04012104290003375F040120EA0
:20169100EDAD82AE8390009E75F040C006C005120EEDAB82AC83D005D006EDB50306EEB52C
:2016B1000402801890003375F040120EED85825785835890009E75F04012162E755908E564
:2016D100592435F9E55924A0F886058D037C00AA5A7F00EA2BFBEF3CFC87078F027E00C3A3
:2016F100EA9BEE64808CF063F08095F0400CE55A2DD39FF4FE755A00800E74642FFFE55AF0
:201711002DD39FF4FE755A01E5592435F8A6061559E55930E7A9227F0090009E75F040C07C
:2017310007121378E582D00760012290003375F040C007121378E582D007601275769E75B9
:20175100770075784090003375F040020E7175769E7577007578409000A975F040C0071292
:201771000E71D007E53433E433FE605E789E860408E6FD20E75412146CAD82BD0105121651
:20179100748035BDFF2975763375770075784090009E75F040120E717576A9757700757822
:2017B1004090003375F040120E71121674800990003375F0401213487576A9757700757878
:2017D1004090009E75F040020E71EE705E789E860408E6FE30E75412146CAD82BD0105121B
:2017F10016748035BDFF2975763375770075784090009E75F040120E717576A97577007524
:20181100784090003375F040120E71121674800990003375F0401213487576A97577007517
:20183100784090009E75F040020E7190003375F040C00712104290009E75F0401210429031
:20185100003375F040120EEDAC82AE8390009E75F040C006C004120EEDAA82AB83D004D0CB
:2018710006D007C3EA9CEB64808EF063F08095F0501F90003375F040C007120EED85825783
:2018910085835890009E75F04012162ED00702195190003375F040C007120EEDAC82AE83D0
:2018B10090009E75F040C006C004120EEDAA82AB83D004D006D007C3EC9AEE64808BF063D9
:2018D100F08095F0507A755B0090009E75F040C007120EEDAB82AC8390003375F040C00439
:2018F100C003120EEDAA82AE83D003D004D007EBC39AFBEC9EFCE55BFA3395E0FEC3EA9B3B
:20191100EE64808CF063F08095F0501190003375F040C007120F62D007055B80AC90009E6C
:2019310075F040C007120EED858276858377E53433E433F57890003375F040120F0FD007E2
:201951007E08EE2435F9EE24A0F886048703EC2B2FFC75F06484E5F0F775F064EC84FF1E41
:20197100EE30E7DEEF70030219FB90003375F040C007120EEDAC82AE83D007E53433E43326
:20199100FDBF0A00502A90003375F040C007C006C005C004120F62D004D005D006D007EFB0
:2019B10075F00AA4FB2535F5350CBC002E0E802B90003375F040C007C006C005C004120F36
:2019D1006290003375F040120F62D004D005D006D0078F3574022CFCE43EFE8C768E778D3D
:2019F1007890003375F040120F0F7576A975770075784090009E75F040020E717F009000B6
:201A1100A975F040C00712134890003375F04012104290009E75F040121042D007AE347DFA
:201A310000530680789E86030886047B00530480EB6205EC6206ED4E60027401F55D90009F
:201A51003375F040C007120EEDAC82AD8390009E75F040C005C004120EEDAA82AB83D004D4
:201A7100D005D007EA2CF55EEB3DF55F755C08E55C24A0F97A08EA24ABF886068E057E001D
:201A9100EA2435FCC000A8048604D00087038BF0ECA42DFDEE35F0FE8F037C00EB2DFDEC51
:201AB1003EFE7576648C778D828E83C006C005C002C001C000122DDAAB82D000D001D002E0
:201AD100D005D006A6037576647577008D828E83C002C001122D79AD82AE83D001D0028D7B
:201AF100071AEA30E790E55C601A9000A975F040C007120F629000A975F040120F62D00708
:201B110078ABA607155CE55C20E703021A80BF0A0040249000A975F040C007120F629000A7
:201B3100A975F040120F62D007055EE4B55E02055F78ABA607801FEF601C9000A975F04074
:201B5100C007120F62D007EF75F00AA4FF78AB8606EF2E78ABF6C3E55E94FFE55F64809418
:201B7100BF5025C37401955E7440855FF063F08095F05014855E76855F77855D789000A96A
:201B910075F040120F0F800990003375F0400213F17576A975770075784090003375F04053
:201BB100120E7190003375F04002104290003375F040121378E582600990003375F0400288
:201BD10013F190003375F0401210427576337577007578409000BF75F040120E71900033A5
:201BF10075F040120EEDAE82AF83C3E49EFEE49FFFEE24FFF576EF34FFF577E53433E4338E
:201C1100F5789000B475F040120F0FAF35BF1400500678B67632801ABF2100500678B676D6
:201C31001E800FBF3200500678B67614800478B6760A7F01EF24B6F876000FBF090040F4F3
:201C51007576B475770075784090003375F040120E717F007576BF75770075784090009E52
:201C710075F040C007120E71121A0D90003375F04012142475760575772F75788090009EC5
:201C910075F040120E711217287576B475770075784090009E75F040120E71121A0D12172E
:201CB100287576337577007578409000B475F040120E71D0070FBF06004099227576337501
:201CD100770075784090001475F000120E7175769E75770075784090003375F040120E711A
:201CF100121BBD75761475770075780090009E75F040120E71021A0DE53420E70D90003394
:201D110075F040121378E582600990003375F0400213F190003375F040121042900033752E
:201D3100F040120EEDE5828583F02401FEE435F0FF78BFA60608A607E4F533F5347576050E
:201D510075772F75788090009E75F040120E71789E760108760090003375F04012142412B7
:201D710017287576337577007578409000B475F040120E717F00EF600D9000B475F040C0DE
:201D910007120FD1D0077576B475770075784090003375F040C007120E71D0077E00E5347C
:201DB10030E703021E487576337577007578409000B475F040C007C006120E7175763375BF
:201DD100770075784090009E75F040120E71D006D0077D00C3ED9F501890009E75F040C076
:201DF10007C006C005120F62D005D006D0070D80E3C007C006121728D006D00774F625357C
:201E1100502B90003375F040C007C006120EEDAC82AD83D006D007C3E49C74808DF063F022
:201E31008095F05008E53524F6F53580067533FF7534FF0E021DAFEF24C1F9EE14F70FBF91
:201E510009005003021D877576B475770075784090003375F040120E717F08EF75F00BA434
:201E7100241EFD742F35F0FEEF24C1F97C008703C3EC9B50318D768E7775788090009E7596
:201E9100F040C007C006C005C004C001120E71789E7600087600121728D001D004D005D0F4
:201EB10006D0070C80C890003375F040C007120F62D0071FBF090040A290003375F0401214
:201ED10014247576337577007578409000B475F040120E7190003375F04012134878BF8676
:201EF1000608E6FF30E713756001C3E49EFEE49FFF78BFA60608A607800375600078BF866C
:201F110006088607C3EE9410EF648094A740607576107577278E828F83122E79AB828B353C
:201F310078BF8682088683757610757727122E43AB82AC8378BFA60308A604757664757786
:201F5100008B828C83C004C003122E79AA82D003D0048A367576647577008B828C83122EEA
:201F710043AC82AD838C377533047534008042C3EE9464EF64809480402F7576647577009B
:201F91008E828F83122E79AC828C3578BF8682088683757664757700122E43AC82AD838C6E
:201FB1003675330275340080088E35753301753400E560600990003375F04012142475769F
:201FD1001075772F75788090009E75F040120E71121A0D7576B475770075784090009E7560
:201FF100F040120E71021728121D0975761075772F75788090009E75F040120E7175763391
:2020110075770075784090001475F000120E7175769E75770075784090003375F040120ED2
:2020310071121BBD75761475770075780090009E75F040120E71021A0D75620090003375C0
:20205100F0401213A8E582600990003375F0400213F1E53430E70C90003375F04012142446
:202071007562017576337577007578409000B475F040120E7190009E75F04012134878A06E
:20209100761778A1760278A2763A78A27633789E760208760090009E75F040121424121732
:2020B10028E53420E70990003375F0400213F17576B475770075784090003375F040120E10
:2020D1007175761075772F75788090009E75F040120E717576337577007578409000B475A7
:2020F100F040120E7175760575772F75788090003375F040120E71753301753400121A0D12
:2021110075763375770075784090009E75F040120E7190009E75F0401214247576B475776B
:202131000075784090003375F040120E717E00E53420E71C7576337577007578409000B433
:2021510075F040C006120E71121728D0060E80DF7D00EE24FFF576ED34FFF57790000A12AD
:202171002DBDE5828583F078BFF608A6F07576B475770075784090003375F040120E717514
:20219100761075772F75788090009E75F040120E7190009E75F0401214247EFF7D00E5348C
:2021B10020E7207576337577007578409000B475F040C006C005120E71121728D005D006AF
:2021D1000D80DBBEFF1F78BF86030886048D027F001ABAFF011FEA2BFBEF3CFC78BFA60340
:2021F10008A6048007EE24C1F9ED14F77576B475770075784090003375F040C006120E715A
:20221100D0060EBE0900502DEE75F00BA4241EFD742F35F0FF8D768F7775788090009E7564
:20223100F040C006120E7190009E75F040121424D0060221AD75760575772F75788090003B
:202251009E75F040120E7112172875760575772F75788090009E75F040120E71789E7601DF
:202271000876007FFFEF24C1F9E4BFFF0104FE756100EE600978BF86030886048006870251
:202291008A037C00AA617D00C3EA9BED64808CF063F08095F05013C007C006C001121A0DC5
:2022B100D001D006D007056180C80FBF0900502CEF700D78A07614789E760008760080A551
:2022D100BF010678A0760B809C90009E75F040C007120F62D00778A0760A8089E562600333
:2022F100021BBD2275761075772F75788090009E75F040120E71121A0D02204A90001F7422
:2023110045F09000207472F0900021F0900022746FF09000237472F0900024E4F022AD8269
:20233100AE83AFF07C008C647A008D828E838FF0C007C006C005C004C0021213A8E582D05B
:2023510002D004D005D006D007600712230D758200228D768E778F7890005175F000C00439
:20237100C002120E7190005175F000121042D002D004900053E0700F90001F7430F0900094
:2023910020E4F075820022900051E0FEA3E0FF30E70890001F742DF07C0190005175F000BC
:2023B100C004C002120EEDAE82AF83D002D004902F1BE493FDF8790018B8FF0119C3E89E80
:2023D100E964808FF063F08095F0400AEE94FDEF6480947F50027A01EA7057EF30E753ECD6
:2023F100241FF582E43400F5837430F00C8C010CE9241FF582E43400F583742EF0C3E49E3F
:20241100F8E49FF918B8FF01198C65C3E498748089F063F08095F05017E565241FF582E40A
:202431003400F5837430F0056518B8FF011980DBAC65EC241FF8E43400F9900053E075F02C
:202451000A842430FB88828983F00CEA60128C030CEB241FF582E43400F583742EF0801925
:20247100EE4F70108C030CEB241FF582E43400F583742EF01EBEFF011FEC241FF9E43400F1
:20249100FB900053E075F00A84A8F0743028F889828B83F0900053E0FB75F00A84E5F0701F
:2024B1000AEA7004EF30E7037564010CEA7019EE4F70108C030CEB241FF582E43400F583B4
:2024D100742EF01EBEFF011FEDC313FD756301C3E5639D40030225C4EC241FF8E43400F9B7
:2024F100E5632453F582E43400F583E075F00A842430FB88828983F00CEA7019EE4F7010A1
:202511008C030CEB241FF582E43400F583742EF01EBEFF011FEC241FF9E43400FBE56324A6
:2025310053F582E43400F583E075F00A84A8F0743028F889828B83F00CEA7019EE4F7010BC
:202551008C030CEB241FF582E43400F583742EF01EBEFF011FE5632453F9E43400FB898237
:202571008B83E07025EA7004EF30E71EEA700BC3EE94FEEF6480947F500605640564802EE1
:20259100BEFE2BBFFF2805648024E5632453F582E43400F583E0FB75F00A84E5F0700CEA81
:2025B1007004EF30E705756401800375640005630224E0EA700EC3E49E74808FF063F080F4
:2025D10095F04005ECC39564FCEC241FF582E43400F583E4F090005175F000C002120EED5D
:2025F100AE82AF83D002EA6023C374809E74808FF063F08095F0400AEE9480EF6480947FD7
:10261100500712230D758200228E822275820022BC
:202F050000000A0000000000000000000017023A325C63282D44100080FF7F451F2F12050D
:202F25003B5E3509FE7F5F1F014F502B185600FD7F633221083510505254FC7F635F0021AE
:202F45001E53351F43FB7F636332002121082121FA7F63635F000021211D5FF97F63636369
:1C2F6500320000210523F87F6363635F0000024C28F77F6363636332000F62417A
:09007E00756600756700756800E5
:202621007403556875F00BA42472FE740035F0FF7D0074032568540375F00BA42472FB7434
:202641000035F0FC8B768C778D788E828F838DF0120E71056822AE82AF83E56670021568F4
:2026610085694D856A4E8E828F831211427403556875F00BA42472FE740035F0FF7D0075F4
:2026810076338D777578408E828F838DF0020E717403556875F00BA42472FE740035F0FFCB
:2026A1007D008E828F838DF00213487403556875F00BA42472F582740035F0F58322E568CB
:2026C10004540375F00BA42472F582740035F0F58322AE82AF83E56804540375F00BA42408
:2026E10072FC740035F0FD7B008C828D838BF0C007C0061213A8E582D006D0077027740345
:20270100556875F00BA42472FC740035F0FD7B008C828D838BF0C007C0061213A8E582D01A
:2027210006D007601FE56804540375F00BA42472FC740035F0FD7B008C828D838BF012131F
:20274100F10227E17403556875F00BA42472FC740035F0FD8C768D7775780090006775F0BE
:2027610000C007C006120E71E56804540375F00BA42472FC740035F0FD8C768D7775780063
:2027810090003375F040120E717403556875F00BA42472FC740035F0FD8C768D77757800DC
:2027A10090009E75F040120E71D006D007C007C0068E828F83120011D006D007E5680454E3
:2027C1000375F00BA42472FE740035F0FF7D007576338D777578408E828F838DF0120E71B9
:2027E100022621AE82AF837403556875F00BA42472FC740035F0FD7B008C828D838BF0C0E9
:2028010007C0061213A8E582D006D0076001227403556875F00BA42472FC740035F0FD8C8A
:20282100768D7775780090006775F000C007C006120E717403556875F00BA42472FC740068
:2028410035F0FD8C768D7775780090003375F040120E71D006D007C007C0068E828F8312FB
:202861000011D006D0077403556875F00BA42472FE740035F0FF7D007576338D7775784059
:202881008E828F838DF0020E7163670122AF82C007120E6ED007BF2A03022931BF2B028019
:2028A10055BF2D03022937BF2E030229EFBF2F03022961BF3403022C1BBF3503022C26BFA1
:2028C1003603022C35BF3703022C62BF3803022C53BF3903022C44BF3C03022A33BF3D038E
:2028E100022967BF6303022A18BF6D03022C18BF7203022B7522E56760307401B566028081
:202901000215687403556875F00BA42472FE740035F0FF7D007576677577008D788E828F64
:20292100838DF0120E71756700229017280226D3901A0D0226D37403556875F00BA42472AD
:20294100FE740035F0FF7D008E828F838DF01214249017281226D390006775F0000214240A
:20296100901CCD0226D3E56760307401B56602800215687403556875F00BA42472FE740025
:2029810035F0FF7D0075765C7577008D788E828F838DF0120E71756700227403556875F096
:2029A1000BA42472FE740035F0FF7D008E828F838DF01213A8E582600122156874035568B7
:2029C10075F00BA42472FE740035F0FF7D00E56804540375F00BA42472FB740035F0FC8BCC
:2029E100768C778D788E828F838DF0020E71E5677001227403556875F00BA42472FE740009
:202A010035F0FF8E768F7775780090005C75F000120E71756700227403556875F00BA4244E
:202A210072FE740035F0FF7D008E828F838DF0021348E5677003022B3A75670074035568DE
:202A410075F00BA42472FE740035F0FF7D008E828F838DF0121378E582600122740355685E
:202A610075F00BA42472FE740035F0FF7D008E828F838DF01213A8E582600122740355680E
:202A810075F00BA42472FE740035F0FF8E768F7775780090006775F000120E7174035568DD
:202AA10075F00BA42472FE740035F0FF8E768F7775780090003375F040120E71E53430E7B5
:202AC1001B7403556875F00BA42472FE740035F0FF7D008E828F838DF00213F190009E75A1
:202AE100F04012134878A0760575769E75770075784090001475F000120E71121D09757646
:202B01001475770075780090009E75F040120E71121A0D12204A7403556875F00BA42472D0
:202B2100FE740035F0FF7D007576338D777578408E828F838DF0020E717403556875F00B6E
:202B4100A42472FE740035F0FF7D008E828F838DF01213A8E5826001227403556875F00B2D
:202B6100A42472FE740035F0FF7D008E828F838DF0021424E5676009756700901BBD02270C
:202B8100E47403556875F00BA42472FE740035F0FF7D008E828F838DF01213A8E58260012B
:202BA100227403556875F00BA42472FE740035F0FF8E768F7775780090006B75F040120EC7
:202BC100717403556875F00BA42472FE740035F0FF7D00E56804540375F00BA42472FB74D1
:202BE1000035F0FC8B768C778D788E828F838DF0120E71E56804540375F00BA42472FE74B6
:202C01000035F0FF7D0075766B8D777578408E828F838DF0020E7102288AE56770012205D3
:202C21006875670022E56770012290204A1227E475670022E5677001229022F51227E47521
:202C4100670022E567700122901FF91227E475670022E567700122901D091227E4756700BA
:202C610022E56804540375F00BA42472FE740035F0FF7D008E828F838DF01213A8E5827089
:202C81001F7403556875F00BA42472FE740035F0FF7D008E828F838DF01213A8E582601FD1
:202CA100E56804540375F00BA42472FE740035F0FF7D008E828F838DF01213F1022D7274E4
:202CC10003556875F00BA42472FE740035F0FF8E768F7775780090006775F000120E71E52A
:202CE1006804540375F00BA42472FE740035F0FF8E768F7775780090003375F040120E71E0
:202D01007403556875F00BA42472FE740035F0FF8E768F7775780090009E75F040120E71E3
:202D210075769E75770075784090001475F000120E71121D097576147577007578009000B6
:202D41009E75F040120E71121A0D12204AE56804540375F00BA42472FE740035F0FF7D0084
:182D61007576338D777578408E828F838DF0120E711226217567002284
:162F810000000A0000000000000000000017023A325C63282D4453
:202D79007A10E4FBFCE58225E0F582E58333F583EB33FBEC33FCEB9576F5F0EC9577400692
:092D9900FCABF0438201DADD22FB
:06004200E478FFF6D8FD92
:200020007921E94400601B7A01902F97789E75A000E493F2A308B8000205A0D9F4DAF27501
:02004000A0FF1F
:1B2DA20020F71130F6138883A88220F509F6A8837583002280FEF280F5F02230
:1D2DBD00E5828576F0A4C582C0F08577F0A4D0F025F0C5838576F0A42583F583228E
:202DDA00E576457760467A01E57625E0F576E577334012F577E5829576E583957740030A56
:202DFA0080E6C3E57713F577E57613F576C3E5829576F5F0E58395774005F58385F082C3D7
:0D2E1A00E57713F577E57613F576DAE1221A
:200048007800E84400600A790175A000E4F309D8FC789DE84400600C7901900001E4F0A318
:04006800D8FCD9FAED
:0F001100E4737581C9122EB1E582600302000EFF
:1C2E270020F71430F6148883A88220F507E6A88375830022E280F7E49322E022BA
:202E4300C2D5E58330E70DD2D5E4C39582F582E49583F583E57730E70BE4C39576F576E47D
:162E63009577F577122DDA30D50BE4C39582F582E49583F58322ED
:202E7900C2D5E58330E70DD2D5E4C39582F582E49583F583E57730E70DB2D5E4C39576F518
:182E990076E49577F577122D7930D50BE4C39582F582E49583F58322BC
:042EB1007582002204
:00000001FF

BIN
calc.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

BIN
connections_back.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 KiB

BIN
no_backlight.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -1,3 +1,18 @@
// 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/>.
//header file for main.c for gui
#ifndef QT_GUI_CALC_MAIN_H

View File

@ -1,3 +1,16 @@
// 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 <QDebug>
#include "calc_main.h"
#include "../src/lcd.h"

View File

@ -1,3 +1,16 @@
// 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 QTGUI_CALCULATOR_H
#define QTGUI_CALCULATOR_H
@ -38,7 +51,7 @@ class Calculator : public QObject
Q_OBJECT
Q_PROPERTY(QString lcdText READ lcdText WRITE setLcdText NOTIFY lcdTextChanged)
public:
explicit Calculator(QObject *parent = 0);
~Calculator();
@ -48,7 +61,7 @@ public:
signals:
void lcdTextChanged();
public slots:
void buttonClicked(const QString& in);
void updateLcd();

View File

@ -1,3 +1,16 @@
// 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 <QApplication>
#include <QFontDatabase>
#include <QQmlApplicationEngine>
@ -11,7 +24,7 @@ int main(int argc, char** argv)
//QML engine
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
//calculator
Calculator calculator;
engine.rootContext()->setContextProperty("_calculator", &calculator);

View File

@ -1,3 +1,16 @@
// 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/>.
import QtQuick 2.0
import QtQuick.Controls 1.0
@ -7,7 +20,7 @@ ApplicationWindow
width: 4 * (100 + 5)
height: 5 * (100 + 5) + 200 + 30
title: qsTr("RPN Calculator")
//calculator
Column {
id: base;
@ -25,8 +38,54 @@ ApplicationWindow
onClicked: Qt.quit()
anchors.fill: parent
}
//handle key presses from physical keyboard
focus: true //handle keypresses here
//numbers
Keys.onDigit0Pressed: _calculator.buttonClicked("4,0")
Keys.onDigit1Pressed: _calculator.buttonClicked("3,0")
Keys.onDigit2Pressed: _calculator.buttonClicked("3,1")
Keys.onDigit3Pressed: _calculator.buttonClicked("3,2")
Keys.onDigit4Pressed: _calculator.buttonClicked("2,0")
Keys.onDigit5Pressed: _calculator.buttonClicked("2,1")
Keys.onDigit6Pressed: _calculator.buttonClicked("2,2")
Keys.onDigit7Pressed: _calculator.buttonClicked("1,0")
Keys.onDigit8Pressed: _calculator.buttonClicked("1,1")
Keys.onDigit9Pressed: _calculator.buttonClicked("1,2")
//swap
Keys.onRightPressed: _calculator.buttonClicked("0,1")
//enter
Keys.onEnterPressed: _calculator.buttonClicked("4,2")
Keys.onReturnPressed: _calculator.buttonClicked("4,2")
Keys.onSpacePressed: _calculator.buttonClicked("4,2")
Keys.onPressed: {
if ((event.key == Qt.Key_Q) && (event.modifiers == Qt.ControlModifier))
Qt.quit()
else if ((event.key == Qt.Key_S))
_calculator.buttonClicked("0,0")
else if ((event.key == Qt.Key_Backspace))
_calculator.buttonClicked("0,3")
else if ((event.key == Qt.Key_Slash))
_calculator.buttonClicked("1,3")
else if ((event.key == Qt.Key_division))
_calculator.buttonClicked("1,3")
else if ((event.key == Qt.Key_Asterisk))
_calculator.buttonClicked("2,3")
else if ((event.key == Qt.Key_Minus))
_calculator.buttonClicked("3,3")
else if ((event.key == Qt.Key_Plus))
_calculator.buttonClicked("4,3")
else if ((event.key == Qt.Key_N)) //negate
_calculator.buttonClicked("0,2")
else if ((event.key == Qt.Key_Equal)) //enter
_calculator.buttonClicked("4,2")
else if ((event.key == Qt.Key_Period)) // "."
_calculator.buttonClicked("4,1")
else if ((event.key == Qt.Key_E)) // also "."
_calculator.buttonClicked("4,1")
}
}
//LCD
Rectangle {
objectName: "lcd";
@ -40,7 +99,7 @@ ApplicationWindow
anchors.centerIn: parent
}
}
//Keyboard
Repeater {
model: 5;
@ -62,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
}
@ -73,7 +132,16 @@ 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)
anchors.fill: parent
}
@ -94,19 +162,31 @@ 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)", "►deg"],
["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)", "►rad"],
["", "", "", ""]
]
return "<small>" + shifted_keys[row][col] + "</small>"
}
function getBackgroundColor(row, col) {
var background_color = [
["white", "white", "white", "#ff9494"],
@ -115,7 +195,7 @@ ApplicationWindow
["#eeeeee", "#eeeeee", "#eeeeee", "white"],
["#eeeeee", "#eeeeee", "white", "white"]
]
return background_color[row][col]
}
}

View File

@ -1,13 +1,15 @@
# Qt
include_directories(${Qt5Widgets_INCLUDE_DIRS} ${QtQml_INCLUDE_DIRS})
add_definitions(${Qt5Widgets_DEFINITIONS} ${QtQml_DEFINITIONS} ${${Qt5Quick_DEFINITIONS}})
add_library(decn decn/decn.c)
# decn library
add_subdirectory(decn)
# calculator
add_library(calc qt_main.cpp calc.c utils.c lcd_emulator.c key.c)
target_link_libraries(calc Qt5::Widgets)
# tests
add_executable(keytest key.c)
target_compile_definitions(keytest PRIVATE KEY_TEST_APP=1)
add_executable(decn_test decn/decn_test.c utils.c)
target_link_libraries(decn_test decn)

View File

@ -1,3 +1,16 @@
// 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/>.
/*
* calc.c
*
@ -8,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;
@ -20,7 +38,8 @@ __xdata dec80 LastX;
#define STACK_T 3
uint8_t NoLift = 0;
uint8_t 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
@ -33,7 +52,7 @@ static void pop(){
StackPtr++; //adjust pointer
}
void push_decn(__xdata const char* signif_str, exp_t exponent){
void push_decn(__xdata const char* signif_str, __xdata exp_t exponent){
if (!NoLift){
StackPtr--;
}
@ -74,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();
@ -85,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);
}
@ -101,22 +115,35 @@ 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_degree_decn);
} else if (IsShiftedDown) {
do_unary_op(to_radian_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){
if (NoLift != 1){
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--;
@ -126,9 +153,8 @@ void process_cmd(char cmd){
} break;
//////////
case '.':{
if (IsShifted){ //STO
if (IsShiftedUp){ //STO
copy_decn(&StoredDecn, &stack(STACK_X));
IsShifted = 0;
}
} break;
//////////
@ -137,23 +163,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));
@ -162,73 +173,97 @@ 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;
//////////
case '7':{ //y^x
if (decn_is_nan(&stack(STACK_Y)) || decn_is_nan(&stack(STACK_X))){
set_dec80_NaN(&stack(STACK_Y));
} else {
copy_decn(&LastX, &stack(STACK_X)); //save LastX
copy_decn(&AccDecn, &stack(STACK_Y));
copy_decn(&BDecn, &stack(STACK_X));
pow_decn();
copy_decn(&stack(STACK_Y), &AccDecn);
}
pop();
IsShifted = 0;
do_binary_op(pow_decn);
} 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
}

View File

@ -1,3 +1,16 @@
// 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/>.
/*
* calc.h
*
@ -17,9 +30,10 @@ extern "C" {
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, exp_t exponent);
void push_decn(__xdata const char* signif_str, __xdata exp_t exponent);
extern uint8_t NoLift;
extern uint8_t IsShifted;
extern __bit IsShiftedUp;
extern __bit IsShiftedDown;
void clear_x(void);
__xdata dec80* get_x(void);

46
src/decn/CMakeLists.txt Normal file
View File

@ -0,0 +1,46 @@
#code coverage
add_library(coverage_config INTERFACE)
target_compile_options(coverage_config INTERFACE -O0 -g --coverage)
target_link_libraries(coverage_config INTERFACE --coverage)
# decn library
add_library(decn decn.c ../utils.c)
# decn library with coverage
add_library(decn_cover decn.c)
target_link_libraries(decn_cover PUBLIC coverage_config)
# old tests (compare output with reference "golden" output file)
add_executable(decn_test
decn_test.c
../utils.c
)
target_link_libraries(decn_test
decn_cover
coverage_config
)
# catch2 unit tests
find_package(Catch2 REQUIRED)
enable_testing()
set (BUILD_TESTING ON)
add_executable(decn_tests
catch_main.cpp
decn_tests.cpp
decn_tests_div_sqrt.cpp
decn_tests_transcendental.cpp
decn_tests_trig.cpp
../utils.c
)
target_link_libraries(decn_tests
mpfr
decn_cover
coverage_config
Catch2::Catch2
)
include(CTest)
include(Catch)
catch_discover_tests(decn_tests)
# decn prototyping
add_subdirectory(proto)

24
src/decn/catch_main.cpp Normal file
View File

@ -0,0 +1,24 @@
// 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/>.
/*
* catch_main.cpp
*
* Unit tests using https://github.com/catchorg/Catch2
*
* Created on: Nov 14, 2019
*/
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include <catch2/catch.hpp>

View File

@ -1,3 +1,16 @@
// 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/>.
/*
* decn.c
*
@ -6,6 +19,7 @@
*/
#include "../utils.h"
#include "../stack_debug.h"
#include "decn.h"
@ -17,10 +31,11 @@
// #define DEBUG_MULT
// #define DEBUG_MULT_ALL //even more verbose
// #define DEBUG_DIV
#define DEBUG_LOG
// #define DEBUG_LOG
// #define DEBUG_LOG_ALL //even more verbose
#define DEBUG_EXP
// #define DEBUG_EXP
// #define DEBUG_EXP_ALL //even more verbose
// #define DEBUG_SQRT
#ifndef DESKTOP
//#undef EXTRA_CHECKS
@ -33,6 +48,7 @@
#undef DEBUG_LOG_ALL
#undef DEBUG_EXP
#undef DEBUG_EXP_ALL
#undef DEBUG_SQRT
#endif
#ifdef DESKTOP
@ -53,16 +69,61 @@ 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(), ln_decn(), exp_decn(), sqrt_decn(), and sincos_decn()
__idata dec80 Tmp3Decn; //used by ln_decn(), exp_decn(), sqrt_decn(), and sincos_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];
//ln(10) constant
const dec80 DECN_LN_10 = {
0, {23, 2, 58, 50, 92, 99, 40, 45, 68}
};
void copy_decn(dec80* dest, const dec80* src){
// pi
const dec80 DECN_PI = {
0, {31, 41, 59, 26, 53, 58, 97, 93, 24}
};
// 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
@ -71,7 +132,7 @@ void copy_decn(dec80* dest, const dec80* src){
}
}
exp_t get_exponent(const dec80* x){
exp_t get_exponent(const dec80* const x){
exp_t exponent = x->exponent;
#ifdef EXP16
if (exponent & 0x4000){ //negative
@ -88,7 +149,7 @@ exp_t get_exponent(const dec80* x){
#endif
}
static void set_exponent(dec80* acc, exp_t exponent, uint8_t num_is_neg){
void set_exponent(dec80* acc, exp_t exponent, uint8_t num_is_neg){
#ifdef EXP16
if (num_is_neg){
exponent |= 0x8000;
@ -135,11 +196,12 @@ static void shift_left(dec80* x){
}
}
static void remove_leading_zeros(dec80* x){
void remove_leading_zeros(dec80* x){
uint8_t digit100;
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){
@ -168,7 +230,7 @@ static void remove_leading_zeros(dec80* x){
set_exponent(x, exponent, is_negative);
}
void build_dec80(__xdata const char* signif_str, exp_t exponent){
void build_dec80(__xdata const char* signif_str, __xdata exp_t exponent){
enum {
SIGN_ZERO,
SIGN_ZERO_SEEN_POINT,
@ -198,6 +260,7 @@ void build_dec80(__xdata const char* signif_str, exp_t exponent){
return;
} else if (signif_str[0] == '-'){
curr_sign = SIGN_NEG_ZERO;
i++;
}
//go through digits
@ -280,7 +343,7 @@ void build_dec80(__xdata const char* signif_str, exp_t exponent){
return;
} else {
//not zero
int8_t new_exponent;
exp_t new_exponent = exponent;
//write out saved nibble, if it exists
// (saved while nibble_i even, nibble_i then incremented to odd)
if (nibble_i & 1){ //odd
@ -292,39 +355,29 @@ void build_dec80(__xdata const char* signif_str, exp_t exponent){
// adjust exponent for left-aligned significand input
// or for number of digits past decimal point
if (num_lr_points > 0){ //left count exists
assert(DEC80_NUM_LSU*2 > num_lr_points);
new_exponent = exponent + (num_lr_points - 1); //1 digit left of implicit point
//check for overflow
#ifdef EXTRA_CHECKS
if (new_exponent < exponent || exponent > DEC80_MAX_EXP){
#ifdef DEBUG
printf(" overflow (new_exp, exp)=(%d,%d)\n",
new_exponent, exponent);
#endif
set_dec80_NaN(&AccDecn);
return;
}
#endif
//overflow is checked later, should be impossible to overflow int16_t:
assert(new_exponent >= exponent);
} else if (num_lr_points < 0) { //right count exists
// (num_lr_points represents exponent shift)
// (this ends up being a subtraction)
new_exponent = exponent + num_lr_points;
//check for underflow
#ifdef EXTRA_CHECKS
if (new_exponent > exponent || exponent < DEC80_MIN_EXP){
#ifdef DEBUG
printf(" underflow (new_exp, exp)=(%d,%d)\n",
new_exponent, exponent);
#endif
set_dec80_NaN(&AccDecn);
return;
}
#endif
} else {
//no change
new_exponent = exponent;
//underflow is checked later, should be impossible to overflow int16_t:
assert(new_exponent <= exponent);
}
//check for over/underflow of exponent
exponent = new_exponent;
#ifdef EXTRA_CHECKS
if (exponent > DEC80_MAX_EXP || exponent < DEC80_MIN_EXP){
#ifdef DEBUG
printf(" over/underflow (new_exp, exp)=(%d,%d)\n",
new_exponent, exponent);
#endif
set_dec80_NaN(&AccDecn);
return;
}
#endif
//set negative bit
set_exponent(&AccDecn, exponent, IS_NEG(curr_sign));
//normalize
@ -341,6 +394,12 @@ void build_dec80(__xdata const char* signif_str, exp_t exponent){
return;
}
} else { //invalid character
#ifdef DEBUG
printf(" invalid character %c at i=%d\n", signif_str[i], i);
#endif
set_dec80_NaN(&AccDecn);
return;
}
i++;
assert(i < DECN_BUF_SIZE);
@ -363,6 +422,11 @@ void set_dec80_zero(dec80* dest){
}
}
void set_decn_one(dec80* dest){
set_dec80_zero(dest);
dest->lsu[0] = 10;
}
uint8_t decn_is_zero(const dec80* x){
uint8_t i;
for (i = 0; i < DEC80_NUM_LSU; i++){
@ -426,7 +490,7 @@ static int8_t compare_magn(void){ //returns a<b: -1, a==b: 0, a>b: 1
//compare signifcands while tracking magnitude
for (
a_i = 0, b_i = 0;
a_i < DEC80_NUM_LSU && b_i < DEC80_NUM_LSU;
a_i < DEC80_NUM_LSU;
a_i++, b_i++, a_exp+=2, b_exp+=2
)
{
@ -467,6 +531,7 @@ static int8_t compare_magn(void){ //returns a<b: -1, a==b: 0, a>b: 1
return a_signif_b;
}
#ifdef USE_COMPARE_DECN
static int8_t compare_decn(void){ //returns a<b: -1, a==b: 0, a>b: 1
int8_t is_neg;
@ -497,6 +562,7 @@ static int8_t compare_decn(void){ //returns a<b: -1, a==b: 0, a>b: 1
return is_neg * compare_magn();
}
#endif //USE_COMPARE_DECN
//WARNING: for add_decn() and sub_mag() functions only
//acc and the number from which exponent was taken MUST be stripped of leading 0s first
@ -564,6 +630,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){
@ -647,26 +714,17 @@ void add_decn(void){
uint8_t digit100 = AccDecn.lsu[i] + BDecn.lsu[i] + carry;
AccDecn.lsu[i] = digit100 % 100;
carry = digit100 / 100;
assert(carry < 100);
assert(carry <= 1);
}
//may need to rescale number
if (carry > 0){
exp_t curr_exp = get_exponent(&AccDecn);
assert(carry == 1);
rel = (AccDecn.exponent < 0); //is_neg?
#ifdef DEBUG_ADD
printf(" carry out: %d", carry);
#endif
//shift right
if (carry < 10){
shift_right(&AccDecn);
AccDecn.lsu[0] += carry*10; //carry gets shifted into most significant digit
curr_exp++;
} else {
shift_right(&AccDecn);
shift_right(&AccDecn);
AccDecn.lsu[0] = carry;
curr_exp+=2;
}
shift_right(&AccDecn);
AccDecn.lsu[0] += 10; //carry gets shifted into most significant digit
curr_exp++;
//track sign
set_exponent(&AccDecn, curr_exp, rel); //rel==is_neg?
}
@ -682,6 +740,12 @@ void mult_decn(void){
uint8_t carry = 0;
uint8_t is_neg;
exp_t new_exponent;
#ifdef EXTRA_CHECKS
if (decn_is_nan(&AccDecn) || decn_is_nan(&BDecn)) {
set_dec80_NaN(&AccDecn);
return;
}
#endif
//initialize values
set_dec80_zero(&TmpDecn);
//normalize
@ -700,7 +764,8 @@ void mult_decn(void){
//calculate new exponent
new_exponent = get_exponent(&AccDecn) + get_exponent(&BDecn);
#ifdef DEBUG_MULT
printf("\n new exponent: %d, is_neg: %u", new_exponent, is_neg);
printf("\n a_exp: %d, b_exp: %d", get_exponent(&AccDecn), get_exponent(&BDecn));
printf("\n new exponent: %d, is_neg: %u", new_exponent, is_neg);
#endif
//do multiply
for (i = DEC80_NUM_LSU - 1; i >= 0; i--){
@ -772,7 +837,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
@ -788,7 +852,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
@ -810,18 +874,16 @@ void recip_decn(void){
} else {
CURR_RECIP.lsu[0] = 10; //0.1 with implicit point and exponent
}
for (i = 1; i < DEC80_NUM_LSU; i++){
CURR_RECIP.lsu[i] = 0;
}
zero_remaining_dec80(&CURR_RECIP, 1);
copy_decn(&AccDecn, &CURR_RECIP);
//do newton raphson iterations
//do newton-raphson iterations
for (i = 0; i < 6; i++){ //just fix number of iterations for now
#ifdef DEBUG_DIV
decn_to_str_complete(&curr_recip);
decn_to_str_complete(&CURR_RECIP);
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);
@ -830,7 +892,7 @@ void recip_decn(void){
//Accum *= -1
negate_decn(&AccDecn);
//Accum += 1
copy_decn(&BDecn, &DECN_1);
set_decn_one(&BDecn);
add_decn();
#ifdef DEBUG_DIV
decn_to_str_complete(&AccDecn);
@ -848,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
}
@ -904,7 +962,7 @@ void ln_decn(void){
printf("ln() accum scaled between 1,10: %s\n", Buf);
#endif
//get initial estimate (accum = 10 - A)
copy_decn(&BDecn, &DECN_1);
set_decn_one(&BDecn);
BDecn.exponent = 1; //BDecn = 10
negate_decn(&AccDecn);
add_decn();
@ -1005,13 +1063,13 @@ void ln_decn(void){
NUM_TIMES.exponent = NUM_TIMES.exponent % 10000;
AccDecn.lsu[1] = NUM_TIMES.exponent / 100;
AccDecn.lsu[2] = NUM_TIMES.exponent % 100;
AccDecn.exponent = 4;
AccDecn.exponent = 5;
} else
#endif
if (NUM_TIMES.exponent >= 100){
AccDecn.lsu[0] = NUM_TIMES.exponent / 100;
AccDecn.lsu[1] = NUM_TIMES.exponent % 100;
AccDecn.exponent = 2;
AccDecn.exponent = 3;
} else {
AccDecn.lsu[0] = NUM_TIMES.exponent;
AccDecn.exponent = 1;
@ -1036,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();
@ -1047,8 +1105,8 @@ inline void log10_decn(void){
void exp_decn(void){
uint8_t j, k;
uint8_t need_recip = 0;
#define SAVED Tmp2Decn
#define NUM_TIMES Tmp3Decn
#define SAVED Tmp2Decn
#define NUM_TIMES Tmp3Decn
//check not error
if (decn_is_nan(&AccDecn)){
@ -1064,13 +1122,11 @@ void exp_decn(void){
//check if in range
copy_decn(&SAVED, &AccDecn); //save = accum
set_dec80_zero(&BDecn);
BDecn.lsu[0] = 23;
BDecn.lsu[1] = 02;
BDecn.lsu[2] = 58;
BDecn.lsu[2] = 51;
BDecn.exponent = 2; //b = 230.25851
BDecn.lsu[0] = 29;
BDecn.lsu[1] = 47;
BDecn.exponent = 2; //b = 294.7
negate_decn(&BDecn);
add_decn(); //accum = x - 230.25851 (should be negative if in range)
add_decn(); //accum = x - 294.7 (should be negative if in range)
if (!(AccDecn.exponent < 0)){ //if not negative
set_dec80_NaN(&AccDecn);
return;
@ -1083,7 +1139,7 @@ void exp_decn(void){
//initial b = -10*ln(10)
copy_decn(&BDecn, &DECN_LN_10); //b=ln(10)
copy_decn(&SAVED, &AccDecn); //save = accum
copy_decn(&AccDecn, &DECN_1);
set_decn_one(&AccDecn);
AccDecn.exponent = 1; //accum = 10
mult_decn(); //accum = 10*ln(10)
copy_decn(&BDecn, &AccDecn); //b = 10*ln(10)
@ -1152,10 +1208,10 @@ void exp_decn(void){
//build final value
// (currently accum = save = remainder)
// calculate 1+remainder
copy_decn(&BDecn, &DECN_1);
set_decn_one(&BDecn);
add_decn();
//get initial multiplier (10) for ln(10)
copy_decn(&BDecn, &DECN_1);
set_decn_one(&BDecn);
BDecn.exponent = 1; //BDecn = 10
//do multiplies
j = UINT8_MAX; //becomes 0 after incrementing to start (1 + 10^-j) series
@ -1208,22 +1264,333 @@ 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)
//{
// copy_decn(&Tmp4Decn, &BDecn); //save b
// ln_decn();
// copy_decn(&BDecn, &Tmp4Decn); //restore b
// mult_decn(); //accum = b*ln(accum)
// exp_decn();
//}
void pow_decn(void) {
if (decn_is_zero(&BDecn)) {
set_decn_one(&AccDecn);
return;
}
if (decn_is_zero(&AccDecn)) {
set_dec80_zero(&AccDecn);
return;
}
//calculate AccDecn = AccDecn ^ BDecn
st_push_decn(&BDecn);
ln_decn();
st_pop_decn(&BDecn);
mult_decn(); //accum = b*ln(accum)
exp_decn();
}
#ifdef USE_POW_SQRT_IMPL
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);
}
#else
void sqrt_decn(void){
#define CURR_EST Tmp2Decn //holds current 1/sqrt(x) estimate
#define X_2 Tmp3Decn //holds copy of original x / 2
uint8_t i;
exp_t initial_exp;
if (decn_is_nan(&AccDecn)) {
return;
}
if (AccDecn.exponent < 0){ //negative
set_dec80_NaN(&AccDecn);
return;
}
//normalize
remove_leading_zeros(&AccDecn);
#ifdef DEBUG_SQRT
decn_to_str_complete(&AccDecn);
printf("sqrt in: %s\n", Buf);
#endif
//store copy of x
st_push_decn(&AccDecn);
//calculate x_orig / 2
set_dec80_zero(&BDecn);
BDecn.lsu[0] = 5;
mult_decn();
copy_decn(&X_2, &AccDecn);
//restore x
st_load_decn(&AccDecn);
//get initial estimate for 1/sqrt(x) == 10^(-0.5 * log(x)):
// approximate significand == 10^(-0.5 * log(x_signif))
// with linear approximation: -0.18 * x_signif + 2.5
// new exponent part is (10^(-0.5 * log(10^x_exp)))
// == 10^(-0.5 * x^exp)
initial_exp = get_exponent(&AccDecn);
set_exponent(&AccDecn, 0, 0); //clear exponent (Acc is not negative)
#ifdef DEBUG_SQRT
printf("sqrt exponent %d ", initial_exp);
#endif
if (initial_exp & 0x1){ //odd
#ifdef DEBUG_SQRT
printf("(odd) ");
#endif
//increment x_exp and
initial_exp++;
//approximate estimated significand as (-0.056*x_signif + 0.79) * 10^0.5
// == -0.18 * x_signif + 2.5
//b = -0.18
BDecn.lsu[0] = 18;
BDecn.exponent = -1; //negative, and exponent = -1
//a = -0.18 * x_signif
mult_decn();
//b = 2.5
BDecn.lsu[0] = 25;
BDecn.exponent = 0;
//a = -0.18 * x_signif + 2.5
add_decn();
} else { //even
//keep x_exp as is and approximate estimated significand as
// -0.056*x_signif + 0.79
//b = -0.056
BDecn.lsu[0] = 56;
set_exponent(&BDecn, -2, 1);
//a = -0.056 * x_signif
mult_decn();
//b = 0.79
BDecn.lsu[0] = 7;
BDecn.lsu[1] = 90;
BDecn.exponent = 0;
//a = -0.056*x_signif + 0.79
add_decn();
}
//est_exp = -x_exp / 2;
initial_exp = -initial_exp / 2;
//est_exp-- if AccDecn exponent is negative
// (AccDecn exponent is either 0 or -1, and AccDecn is positive)
if (AccDecn.exponent != 0){
initial_exp--;
}
set_exponent(&AccDecn, initial_exp, 0); //(initial estimate is never negative)
copy_decn(&CURR_EST, &AccDecn);
#ifdef DEBUG_SQRT
printf(" -> %d\n", initial_exp);
#endif
//do newton-raphson iterations
for (i = 0; i < 6; i++){ //just fix number of iterations for now
#ifdef DEBUG_SQRT
decn_to_str_complete(&CURR_EST);
printf("sqrt %2d: %s\n", i, Buf);
#endif
//accum = est * est;
copy_decn(&BDecn, &AccDecn);
mult_decn();
//accum *= x_orig_2; //accum = x/2 * est * est
copy_decn(&BDecn, &X_2);
mult_decn();
//accum = - x/2 * est * est
negate_decn(&AccDecn);
//b = 3/2
set_dec80_zero(&BDecn);
BDecn.lsu[0] = 15;
//accum = 3/2 - x/2 * est * est
add_decn();
//accum *= est; //accum = 0.5 * est * (3 - x * est * est)
copy_decn(&BDecn, &CURR_EST);
mult_decn();
//est = accum;
copy_decn(&CURR_EST, &AccDecn);
}
//calc sqrt from recip_sqrt
st_pop_decn(&BDecn);
mult_decn();
#undef CURR_EST
#undef X_COPY
}
#endif //USE_POW_SQRT_IMPL
// normal angle to between 0 and 360 degrees
void normalize_0_360(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);
//B = 360
set_dec80_zero(&BDecn);
BDecn.lsu[0] = 36;
BDecn.exponent = 2;
if (compare_magn() > 0) {
do {
do {
//B = 3.6e...
BDecn.exponent = exponent;
if (compare_magn() >= 0) {
negate_decn(&BDecn);
add_decn();
} else {
break;
}
} while (1);
exponent--;
} while (exponent >= 2);
}
if (is_negative) {
negate_decn(&AccDecn);
//B = 360
BDecn.exponent = 2;
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) { //calculate arctan
set_dec80_zero(&THETA);
if (is_negative) negate_decn(&AccDecn);
copy_decn(&COS, &AccDecn);
set_decn_one(&SIN);
} else { //calculate sin/cos
normalize_0_360();
to_radian_decn();
copy_decn(&THETA, &AccDecn);
set_decn_one(&COS);
set_dec80_zero(&SIN);
// 0.0 00 5
SIN.lsu[2] = 50;
negate_decn(&SIN);
}
do {
if (sincos_arctan) { //calculate arctan
// THETA is in AccDecn from previous iteration
if (COS.exponent < 0) {
if (is_negative) negate_decn(&AccDecn);
break;
}
} else { //calculate sin/cos
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);
to_degree_decn();
}
// see W.E. Egbert, "Personal Calculator Algorithms III: Inverse Trigonometric Functions"
void arcsin_decn_rad(void) {
st_push_decn(&AccDecn);
copy_decn(&BDecn, &AccDecn);
mult_decn();
negate_decn(&AccDecn);
set_decn_one(&BDecn);
add_decn();
sqrt_decn();
recip_decn();
st_pop_decn(&BDecn);
mult_decn();
sincos_decn(1);
}
void arcsin_decn(void) {
arcsin_decn_rad();
to_degree_decn();
}
void arccos_decn(void) {
arcsin_decn_rad();
negate_decn(&AccDecn);
copy_decn(&BDecn, &DECN_PI2);
add_decn();
to_degree_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) {
copy_decn(&AccDecn, &DECN_PI);
}
static void set_str_error(void){
Buf[0] = 'E';
@ -1234,7 +1601,12 @@ static void set_str_error(void){
Buf[5] = '\0';
}
int8_t decn_to_str(const dec80* x){
#ifdef DESKTOP
int
#else
int8_t
#endif
decn_to_str(const dec80* x){
#define INSERT_DOT() Buf[i++]='.'
uint8_t i = 0;
uint8_t digit100;
@ -1362,7 +1734,11 @@ int8_t decn_to_str(const dec80* x){
//print exponent
if (use_sci){
//check for overflow
#ifdef DESKTOP
if (exponent > DEC80_MAX_EXP || exponent < DEC80_MIN_EXP){
#else
if (exponent > DECN_MAX_PRINT_EXP || exponent < DECN_MIN_PRINT_EXP){
#endif
set_str_error();
return 0;
}
@ -1383,7 +1759,7 @@ int8_t decn_to_str(const dec80* x){
#ifdef DESKTOP
//complete string including exponent
void decn_to_str_complete(const dec80* x){
int8_t exponent = decn_to_str(x);
int exponent = decn_to_str(x);
int i;
//find end of string
for (i = 0; Buf[i] != '\0'; i++);
@ -1409,4 +1785,3 @@ void build_decn_at(dec80* dest, const char* signif_str, exp_t exponent){
#endif //DESKTOP

View File

@ -1,3 +1,16 @@
// 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/>.
/*
* decn.h
*
@ -44,28 +57,24 @@ typedef struct {
//implicit decimal point between (lsu[0]/10) and (lsu[0]%10)
} dec80;
//1 constant
static const dec80 DECN_1 = {
0, {10, 0}
};
//ln(10) constant
static const dec80 DECN_LN_10 = {
0, {23, 2, 58, 50, 92, 99, 40, 45, 68}
};
//remove sign bit, and return 15 bit exponent sign-extended to 16 bits
exp_t get_exponent(const dec80* x);
exp_t get_exponent(const dec80* const x);
void copy_decn(dec80* dest, const dec80* src);
void set_exponent(dec80* acc, exp_t exponent, uint8_t num_is_neg);
void remove_leading_zeros(dec80* x);
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, exp_t exponent);
void build_dec80(__xdata const char* signif_str, __xdata exp_t exponent);
void set_dec80_zero(dec80* dest);
void set_decn_one(dec80* dest);
void set_dec80_NaN(dec80* dest);
uint8_t decn_is_zero(const dec80* x);
uint8_t decn_is_nan(const dec80* x);
@ -81,20 +90,29 @@ void log10_decn(void);
void exp_decn(void);
void exp10_decn(void);
void pow_decn(void);
void sqrt_decn(void);
//calculate AccDecn = AccDecn ^ BDecn
#define pow_decn() do {\
copy_decn(&Tmp4Decn, &BDecn); \
ln_decn(); \
copy_decn(&BDecn, &Tmp4Decn); \
mult_decn(); \
exp_decn(); \
} while (0);
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
extern __xdata char Buf[DECN_BUF_SIZE];
int8_t decn_to_str(const dec80* x);
#ifdef DESKTOP
int
#else
int8_t
#endif
decn_to_str(const dec80* x);
#ifdef DESKTOP
//complete string including exponent
@ -102,6 +120,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

View File

@ -1,3 +1,16 @@
// 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/>.
/*
* decn_test.c
*
@ -7,9 +20,6 @@
#include <stdio.h>
#include "decn.h"
char Buf[DECN_BUF_SIZE];
static dec80 diff;
//diff = (acc - diff) / diff
@ -274,6 +284,12 @@ int main(void){
"0.176091259055681242", 0
);
//new acc for log test
log_test(
"9", 99,
"230.153148783746742", 0
);
//new acc for exp test
exp_test(
"4.4", 0,

View File

@ -226,6 +226,33 @@ ln() exponent from initial: 1.
log(a): 0.176091259055681285
: 2.44191564252505631E-16
a : 9.E99
ln() accum scaled between 1,10: 9.
ln() initial accum: 1. (100)
0: num_times: 0, 1.
1: num_times: 1, 1.
2: num_times: 1, 0.1
3: num_times: 0, 1.
4: num_times: 1, 0.001
5: num_times: 0, 0.01
6: num_times: 0, 0.1
7: num_times: 0, 1.
8: num_times: 1, 1.E-7
ln() remainder: 1.E-7
8: ln() remainder: 1.0000000050001598
7: ln() remainder: 0.1000000005000159
6: ln() remainder: 0.0100000000500015
5: ln() remainder: 0.0010000000050001
4: ln() remainder: 1.0000500033335833
3: ln() remainder: 0.1000050003333583
2: ln() remainder: 1.0050335853501441
1: ln() remainder: 1.053605156578263
0: ln() remainder: 0.1053605156578263
ln() accum after summing: 0.1053605156578263
ln() exponent from initial: 100.
ln(a): 230.153148783746742
: 0
a : 4.4
exp() num_times for 10*ln(10): 4.4 (0)
exp() num_times for 255: 2.09741490700595432 (1)

267
src/decn/decn_tests.cpp Normal file
View File

@ -0,0 +1,267 @@
// 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/>.
/*
* decn_tests.cpp
*
* Unit tests using https://github.com/catchorg/Catch2
*
* Created on: Nov 14, 2019
*/
#include <string>
#include <random>
#include <boost/multiprecision/mpfr.hpp>
#include <catch2/catch.hpp>
#include "decn.h"
#include "../utils.h"
#include "decn_tests.h"
namespace bmp = boost::multiprecision;
using Catch::Matchers::Equals;
TEST_CASE("build decn"){
build_dec80("0.0009234567890123456", 7);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("9234.567890123456"));
build_dec80("9.234567890123456", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("9234.567890123456"));
negate_decn(&AccDecn);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-9234.567890123456"));
//small positive
build_dec80(".1", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("100."));
build_dec80("0.1", -1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0.01"));
build_dec80("0.01", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("10."));
//zero
build_dec80(".", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
build_dec80(".0", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
build_dec80("0.", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
build_dec80("-0.0", -1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
build_dec80("0", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
//small negative
build_dec80("-.1", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-100."));
build_dec80("-0.1", -1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-0.01"));
build_dec80("-0.001", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-1."));
//empty string -> 0
build_dec80("", 90);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
//too many .
build_dec80("..", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
//very long (truncated)
build_dec80("12345678901234567890", -2);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("123456789012345678."));
build_dec80("12345678901234567890", +2);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("1.23456789012345678E21"));
//overflow
build_dec80("100", DEC80_MAX_EXP-1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
build_dec80("1", DEC80_MAX_EXP+1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
build_dec80("0.1", DEC80_MAX_EXP+2);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
//underflow
build_dec80("10", DEC80_MIN_EXP-2);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
build_dec80("1", DEC80_MIN_EXP-1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
build_dec80("0.3", DEC80_MIN_EXP);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
CHECK(decn_is_nan(&AccDecn) == 1);
//left/right count
build_dec80("-100.001", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-100001."));
//invalid
build_dec80(":", 0);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
//special number that is not NaN
AccDecn.lsu[1] = 3;
CHECK(decn_is_nan(&AccDecn) == 0);
}
TEST_CASE("build_large"){
int large_exp = DEC80_MAX_EXP/2 - 50;
build_dec80("9.99", large_exp);
decn_to_str_complete(&AccDecn);
CHECK(AccDecn.exponent == large_exp);
std::string expected = "9.99E";
expected += std::to_string(large_exp);
CHECK_THAT(Buf, Equals(expected));
}
TEST_CASE("small fractions >= 1/10"){
build_dec80("0.333", 0);
build_decn_at(&BDecn, "3.33", -1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0.333"));
decn_to_str_complete(&BDecn);
CHECK_THAT(Buf, Equals("0.333"));
negate_decn(&BDecn);
add_decn();
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
}
TEST_CASE("add"){
dec80 tmp_copy;
build_dec80("-9.234567890123456", 3);
copy_decn(&tmp_copy, &AccDecn); //save
build_dec80("-92.3456789012345678", 1);
copy_decn(&BDecn, &AccDecn);
copy_decn(&AccDecn, &tmp_copy); //restore
copy_decn(&tmp_copy, &BDecn); //save
decn_to_str_complete(&BDecn);
CHECK_THAT(Buf, Equals("-923.456789012345678")); //b
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-9234.567890123456")); //-acc
//compare result of b - acc
add_decn();
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-10158.0246791358016")); //b-acc
//new acc for acc - b test
decn_to_str_complete(&AccDecn);
copy_decn(&BDecn, &tmp_copy); //restore
negate_decn(&BDecn);
decn_to_str_complete(&BDecn);
CHECK_THAT(Buf, Equals("923.456789012345678")); //-b
add_decn();
decn_to_str_complete(&AccDecn);
//compare result of new acc - b
CHECK_THAT(Buf, Equals("-9234.567890123456")); //acc-b
//add 0
build_decn_at(&BDecn, "0", 0);
add_decn();
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-9234.567890123456")); //same
//carry into MSB
build_dec80( "-82345678901234567.8", -1);
build_decn_at(&BDecn, "-87654321098765432.2", -1);
add_decn();
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf,Equals("-17000000000000000.")); //acc+b
//don't negate NaN
set_dec80_NaN(&AccDecn);
negate_decn(&AccDecn);
CHECK(decn_is_nan(&AccDecn));
}
TEST_CASE("multiply"){
build_dec80("92.34567890123456", 2);
build_decn_at(&BDecn, "-92.3456789012345678", 1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("9234.567890123456")); //acc
decn_to_str_complete(&BDecn);
CHECK_THAT(Buf, Equals("-923.456789012345678")); //b
mult_decn();
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-8527724.41172991849")); //acc*b
//overflow
build_dec80("9.99", DEC80_MAX_EXP/2);
build_decn_at(&BDecn, "9.99", DEC80_MAX_EXP/2);
mult_decn();
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error")); //acc*b
//NaN
build_dec80("9.99", DEC80_MAX_EXP/2);
set_dec80_NaN(&BDecn);
mult_decn();
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error")); //acc*b
//NaN
set_dec80_NaN(&AccDecn);
build_decn_at(&BDecn, "9.99", DEC80_MAX_EXP/2);
mult_decn();
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error")); //acc*b
}
TEST_CASE("u32str corner"){
u32str(0, &Buf[0], 10);
CHECK_THAT(Buf, Equals("0"));
}

28
src/decn/decn_tests.h Normal file
View File

@ -0,0 +1,28 @@
// 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/>.
/*
* decn_tests.h
*
* Created on: Oct 26, 2020
*/
#ifndef DECN_TESTS_H_
#define DECN_TESTS_H_
static const int NUM_RAND_TESTS = 123456;
#endif

View File

@ -0,0 +1,200 @@
// 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/>.
/*
* decn_tests_div_sqrt.cpp
*
* Unit tests using https://github.com/catchorg/Catch2
*
* separate out reciprocal/division and sqrt tests
*
* Created on: Oct 26, 2020
*/
#include <string>
#include <random>
#include <boost/multiprecision/mpfr.hpp>
#include <catch2/catch.hpp>
#include "decn.h"
#include "../utils.h"
#include "decn_tests.h"
namespace bmp = boost::multiprecision;
using Catch::Matchers::Equals;
static void div_test(){ //acc / b
bmp::mpf_float::default_precision(50);
decn_to_str_complete(&AccDecn);
CAPTURE(Buf);
bmp::mpfr_float a_actual(Buf);
decn_to_str_complete(&BDecn);
CAPTURE(Buf);
bmp::mpfr_float b_actual(Buf);
//calc result
div_decn();
decn_to_str_complete(&AccDecn);
CAPTURE(Buf); // acc / b
//calculate actual result
a_actual /= b_actual;
if (decn_is_nan(&AccDecn)){
//check that NaN result of division by 0
CAPTURE(a_actual);
CHECK(b_actual == 0);
} else {
bmp::mpfr_float calculated(Buf);
bmp::mpfr_float rel_diff = abs((a_actual - calculated) / a_actual);
CHECK(rel_diff < 2e-17);
}
}
static void div_test(
//input
const char* a_str, int a_exp,
const char* b_str, int b_exp
)
{
CAPTURE(a_str); CAPTURE(a_exp);
CAPTURE(b_str); CAPTURE(b_exp);
//do division
build_dec80(a_str, a_exp);
build_decn_at(&BDecn, b_str, b_exp);
div_test();
}
TEST_CASE("division"){
div_test(
"1", 0,
"0", 0
);
div_test(
"3.14", 60,
"-1.5", -2
);
div_test(
"4", 0,
"4", 0
);
div_test(
"1", 0,
"3", 0
);
div_test(
"500", 0,
"99", 0
);
div_test(
"500", 0,
"2", 0
);
div_test(
"3", 0,
"25", -15
);
div_test(
"0.02", 0,
"0.03", 0
);
}
TEST_CASE("division random"){
std::default_random_engine gen;
std::uniform_int_distribution<int> distrib(0, 99);
std::uniform_int_distribution<int> sign_distrib(0,1);
for (int j = 0; j < NUM_RAND_TESTS; j++){
AccDecn.lsu[0] = distrib(gen);
BDecn.lsu[0] = distrib(gen);
for (int i = 1; i < DEC80_NUM_LSU; i++){
AccDecn.lsu[i] = distrib(gen);
BDecn.lsu[i] = distrib(gen);
}
set_exponent(&AccDecn, distrib(gen), sign_distrib(gen));
set_exponent(&BDecn, distrib(gen), sign_distrib(gen));
div_test();
}
}
static void sqrt_test(){
decn_to_str_complete(&AccDecn);
CAPTURE(Buf);
//calculate result
sqrt_decn();
//build mpfr float
bmp::mpfr_float::default_precision(50);
bmp::mpfr_float x_actual(Buf);
//print calc result
decn_to_str_complete(&AccDecn);
CAPTURE(Buf);
//calculate actual result
CAPTURE(x_actual);
if (decn_is_nan(&AccDecn)){
//check that NaN is from result of sqrt(-)
CHECK(x_actual <= 0);
} else if (decn_is_zero(&AccDecn)){
//check actual is also 0
CHECK(x_actual == 0);
} else {
x_actual = sqrt(x_actual);
CAPTURE(x_actual);
bmp::mpfr_float calculated(Buf);
bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual);
CHECK(rel_diff < 2e-17);
}
}
static void sqrt_test(const char* x_str, int x_exp)
{
CAPTURE(x_str); CAPTURE(x_exp);
build_dec80(x_str, x_exp);
sqrt_test();
}
TEST_CASE("sqrt"){
sqrt_test("0", 0);
sqrt_test("2", 0);
sqrt_test("-1", 0);
sqrt_test("0.155", 0);
sqrt_test("10", 0);
sqrt_test("1.1", 10);
sqrt_test("2.02", -10);
sqrt_test("2.02", 0);
sqrt_test("1.5", 0);
sqrt_test("9", 99);
sqrt_test("123", 12345);
}
TEST_CASE("sqrt random"){
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(0,99);
std::uniform_int_distribution<int> exp_distrib(-99,99);
std::uniform_int_distribution<int> sign_distrib(0,1);
for (int j = 0; j < NUM_RAND_TESTS; j++){
for (int i = 0; i < DEC80_NUM_LSU; i++){
AccDecn.lsu[i] = distribution(generator);
}
int sign = sign_distrib(generator);
set_exponent(&AccDecn, exp_distrib(generator), sign);
sqrt_test();
}
}

View File

@ -0,0 +1,490 @@
// 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/>.
/*
* decn_tests_transcendental.cpp
*
* Unit tests using https://github.com/catchorg/Catch2
*
* separate out transcendental function tests
*
* Created on: Oct 26, 2020
*/
#include <string>
#include <random>
#include <boost/multiprecision/mpfr.hpp>
#include <catch2/catch.hpp>
#include "decn.h"
#include "../utils.h"
#include "decn_tests.h"
namespace bmp = boost::multiprecision;
using Catch::Matchers::Equals;
static void log_test_(bool base10, double epsilon){
bmp::mpfr_float::default_precision(50);
CAPTURE(base10);
decn_to_str_complete(&AccDecn);
CAPTURE(Buf);
//build mpfr float
bmp::mpfr_float x_actual(Buf);
//calculate result
if (base10){
log10_decn();
} else {
ln_decn();
}
decn_to_str_complete(&AccDecn);
CAPTURE(Buf); // log(x)
//calculate actual result
CAPTURE(x_actual);
if (decn_is_nan(&AccDecn)){
//check that NaN is from result of log(-)
CHECK(x_actual <= 0);
} else {
if (base10){
x_actual = log10(x_actual);
} else {
x_actual = log(x_actual);
}
bmp::mpfr_float calculated(Buf);
CAPTURE(calculated);
bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual);
CHECK(rel_diff < epsilon);
}
}
static void log_test(bool base10=false){
//check if near 1.0
remove_leading_zeros(&AccDecn);
double lsu0 = AccDecn.lsu[0];
int exp = get_exponent(&AccDecn);
if (exp == -1){
lsu0 /= (double) 10;
lsu0 += (double) AccDecn.lsu[1] / (10*100);
lsu0 += (double) AccDecn.lsu[2] / (10*100*100);
lsu0 += (double) AccDecn.lsu[3] / (10*100*100*100);
} else if (exp == 0){
lsu0 += (double) AccDecn.lsu[1] / 100;
lsu0 += (double) AccDecn.lsu[2] / (100*100);
lsu0 += (double) AccDecn.lsu[3] / (100*100*100);
}
CAPTURE((int) AccDecn.lsu[0]); CAPTURE((int) AccDecn.lsu[1]);
CAPTURE(exp);
CAPTURE(lsu0);
if (exp == 0 || exp == -1){
//check if near 1.0
if (lsu0 >= 7 && lsu0 < 8){
log_test_(base10, 7.5e-16);
} else if (lsu0 >= 8 && lsu0 < 9){
log_test_(base10, 1.5e-15);
} else if (lsu0 >= 9 && lsu0 < 9.6){
log_test_(base10, 1.0e-14);
} else if (lsu0 >= 9.6 && lsu0 < 9.9){
log_test_(base10, 4.1e-13);
} else if (lsu0 >= 9.9 && lsu0 < 9.999){
log_test_(base10, 1.5e-11);
} else if (lsu0 >= 9.999 && lsu0 < 9.99999){
log_test_(base10, 6.0e-10);
} else if (lsu0 >= 9.99999 && lsu0 < 9.9999999){
log_test_(base10, 3.0e-9);
} else if (lsu0 >= 9.9999999 && lsu0 < 10.0){
log_test_(base10, 1.3e-7);
} else if (lsu0 >= 10.0 && lsu0 < 10.00001){
log_test_(base10, 6.0e-10);
} else if (lsu0 >= 10.00001 && lsu0 < 10.001){
log_test_(base10, 6.0e-11);
} else if (lsu0 >= 10.001 && lsu0 < 10.1){
log_test_(base10, 1.5e-12);
} else if (lsu0 >= 10.1 && lsu0 < 11){
log_test_(base10, 1.6e-14);
} else if (lsu0 >= 11 && lsu0 < 13){
log_test_(base10, 2.0e-15);
} else {
log_test_(base10, 6.5e-16);
}
} else {
log_test_(base10, 2e-16);
}
}
static void log_test(
//input
const char* x_str, int x_exp,
bool base10=false
)
{
CAPTURE(x_str); CAPTURE(x_exp);
CAPTURE(base10);
build_dec80(x_str, x_exp);
log_test(base10);
}
TEST_CASE("log"){
log_test("0", 0);
log_test("-1", 0);
log_test("0.155", 0);
log_test("10", 0);
log_test("1.1", 10);
log_test("2.02", -10);
log_test("2.02", 0);
log_test("1.5", 0, true);
log_test("9", 99);
log_test("123", 12345);
}
TEST_CASE("log random"){
std::default_random_engine gen;
std::uniform_int_distribution<int> distrib(0,99);
std::uniform_int_distribution<int> exp_distrib(-99,99);
std::uniform_int_distribution<int> sign_distrib(0,1);
for (int j = 0; j < NUM_RAND_TESTS; j++){
for (int i = 0; i < DEC80_NUM_LSU; i++){
AccDecn.lsu[i] = distrib(gen);
}
int exp = exp_distrib(gen);
set_exponent(&AccDecn, exp, 0);
int base10 = sign_distrib(gen);
log_test(base10);
}
}
static void log_test_near1(int lsu0_low, int lsu0_high, int exp){
std::default_random_engine gen;
std::uniform_int_distribution<int> lsu0_distrib(lsu0_low, lsu0_high);
std::uniform_int_distribution<int> distrib(0,99);
std::uniform_int_distribution<int> exp_distrib(-99,99);
std::uniform_int_distribution<int> sign_distrib(0,1);
for (int j = 0; j < NUM_RAND_TESTS; j++){
AccDecn.lsu[0] = lsu0_distrib(gen);
for (int i = 1; i < DEC80_NUM_LSU; i++){
AccDecn.lsu[i] = distrib(gen);
}
set_exponent(&AccDecn, exp, 0);
int base10 = sign_distrib(gen);
log_test(base10);
}
}
TEST_CASE("log random 0 to 0.99..."){
log_test_near1(0, 99, -1);
}
TEST_CASE("log random 0.8 to 0.99..."){
log_test_near1(80, 99, -1);
}
TEST_CASE("log random 1.0 to 9.9"){
log_test_near1(10, 99, 0);
}
TEST_CASE("log random 1.0 to 2.0"){
log_test_near1(10, 20, 0);
}
static void exp_test_(bool base10, double epsilon){
bmp::mpfr_float::default_precision(50);
CAPTURE(base10);
decn_to_str_complete(&AccDecn);
CAPTURE(Buf); //x
CAPTURE(AccDecn.exponent);
//build mpfr float
bmp::mpfr_float x_actual(Buf);
//calculate result
if (base10){
exp10_decn();
} else {
exp_decn();
}
decn_to_str_complete(&AccDecn);
CAPTURE(Buf); // exp(x)
//calculate actual result
bmp::mpfr_float calculated(Buf);
if (base10){
x_actual *= log(10);
}
x_actual = exp(x_actual);
CAPTURE(x_actual);
bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual);
CHECK(rel_diff < epsilon);
}
static void exp_test(bool base10=false){
double x;
int exp = get_exponent(&AccDecn);
if (exp == 1){
x = AccDecn.lsu[0];
x += (double) AccDecn.lsu[1] / 100;
} else if (exp == 2){
x = (double) AccDecn.lsu[0] * 10;
x += (double) AccDecn.lsu[1] / 10;
}
CAPTURE((int) AccDecn.lsu[0]); CAPTURE((int) AccDecn.lsu[1]);
CAPTURE(exp);
CAPTURE(x);
double epsilon;
if (exp == 1 || exp == 2){
if (x > 230){
epsilon = 8e-15;
} else if (x > 210){
epsilon = 6e-15;
} else if (x > 180){
epsilon = 5e-15;
} else if (x > 150){
epsilon = 4e-15;
} else if (x > 125){
epsilon = 3e-15;
} else if (x > 100){
epsilon = 2e-15;
} else if (x > 65){
epsilon = 1e-15;
}
} else {
epsilon = 6e-16;
}
CAPTURE(base10);
if (base10){
epsilon *= 20;
}
exp_test_(base10, epsilon);
}
static void exp_test(
//input
const char* x_str, int x_exp,
bool base10=false
)
{
CAPTURE(x_str); CAPTURE(x_exp);
CAPTURE(base10);
build_dec80(x_str, x_exp);
exp_test(base10);
}
static void exp10_test(const char* x_str, int x_exp){
exp_test(x_str, x_exp, true);
}
TEST_CASE("exp"){
exp_test("4.4", 0);
exp_test("0.155", 0);
exp_test("9.999", 0);
exp_test("10", 0);
exp_test("10.001", 0);
exp_test("2.3", 2);//, 6e-15);
exp_test("2.02", -10);
exp_test("2.02", 0);
exp_test("1.5", 0);
exp_test("99.999999", 0);
exp_test("230.2", 0);//, 6e-15);
exp_test("-230", 0);//, 6e-15);
exp_test("294.69999999", 0);//, 8e-15);
//do not operate on NaN
set_dec80_NaN(&AccDecn);
exp_decn();
CHECK(decn_is_nan(&AccDecn)); //still NaN
}
TEST_CASE("exp10"){
exp10_test("4.4", 0);
exp10_test("0.155", 0);
exp10_test("9.999", 0);
exp10_test("10", 0);
exp10_test("10.001", 0);
exp10_test("2.02", -10);
exp10_test("2.02", 0);
exp10_test("1.5", 0);
exp10_test("127", 0);//, 3e-14);
exp10_test("99.999999", 0);//, 2e-14);
}
static void test_exp_random(int exp_distrib_low){
std::default_random_engine gen;
std::uniform_int_distribution<int> distrib(0, 99);
std::uniform_int_distribution<int> lsu0_high_distrib(0, 23);
std::uniform_int_distribution<int> exp_distrib(exp_distrib_low, 2);
std::uniform_int_distribution<int> sign_distrib(0, 1);
for (int j = 0; j < NUM_RAND_TESTS; j++){
int exp = exp_distrib(gen);
int sign = sign_distrib(gen);
if (exp == 2) {
//limit x to approximately +/- 230
AccDecn.lsu[0] = lsu0_high_distrib(gen);
} else {
AccDecn.lsu[0] = distrib(gen);
}
for (int i = 1; i < DEC80_NUM_LSU; i++){
AccDecn.lsu[i] = distrib(gen);
}
set_exponent(&AccDecn, exp, sign);
exp_test();
}
}
TEST_CASE("exp random"){
test_exp_random(-99);
}
TEST_CASE("exp large random"){
test_exp_random(1);
}
static void pow_test(){ // a^b
bmp::mpf_float::default_precision(50);
decn_to_str_complete(&AccDecn);
CAPTURE(Buf); // a
bmp::mpfr_float a_actual(Buf);
decn_to_str_complete(&BDecn);
CAPTURE(Buf); // b
bmp::mpfr_float b_actual(Buf);
//calculate result
pow_decn();
//calculate actual result
bmp::mpfr_float res_actual(pow(a_actual, b_actual));
//check overflow or underflow
if (decn_is_nan(&AccDecn)){
//check overflow or underflow
if (b_actual > 0) {
CHECK(log(res_actual) > 100);
} else {
CHECK(log(res_actual) < -100);
}
return;
}
//not over/underflow, get string and log calculated result
decn_to_str_complete(&AccDecn);
CAPTURE(Buf); // a^b
bmp::mpfr_float calculated(Buf);
//check relative error
double rel_tol = 4.5e-14;
if (a_actual > 1.0 && a_actual < 1.0001){
rel_tol = 1e-7;
} else if (a_actual > 0.9 && a_actual < 2.0){
rel_tol = 1.5e-10;
} else if (log(res_actual) > 100){
rel_tol = 1e-12;
}
CAPTURE(a_actual);
CAPTURE(rel_tol);
if (decn_is_zero(&AccDecn)) {
bmp::mpfr_float diff = abs(res_actual - calculated);
CHECK(diff < rel_tol);
} else {
bmp::mpfr_float rel_diff = abs((res_actual - calculated)/res_actual);
CHECK(rel_diff < rel_tol);
}
}
static void pow_test(
//input
const char* a_str, int a_exp,
const char* b_str, int b_exp
)
{
CAPTURE(a_str); CAPTURE(a_exp);
CAPTURE(b_str); CAPTURE(b_exp);
//compute power
build_decn_at(&BDecn, b_str, b_exp);
build_dec80(a_str, a_exp);
pow_test();
}
TEST_CASE("power"){
pow_test(
"3.14", 60,
"-1.5", -2
);
pow_test(
"3", 0,
"201", 0
);
pow_test(
"5", 0,
"0", 0
);
pow_test(
"5", 0,
"0", 2
);
pow_test(
"0", 0,
"5", 0
);
pow_test(
"0", 0,
"0", 0
);
}
static void power_test(int lsu0_low, int lsu0_high, int exp_low=-99, int exp_high=99){
std::default_random_engine gen;
std::uniform_int_distribution<int> lsu0_distrib(lsu0_low, lsu0_high);
std::uniform_int_distribution<int> distrib(0, 99);
std::uniform_int_distribution<int> exp_distrib(exp_low, exp_high);
std::uniform_int_distribution<int> sign_distrib(0,1);
for (int j = 0; j < NUM_RAND_TESTS; j++){
AccDecn.lsu[0] = lsu0_distrib(gen);
for (int i = 1; i < DEC80_NUM_LSU; i++){
AccDecn.lsu[i] = distrib(gen);
BDecn.lsu[i] = distrib(gen);
}
set_exponent(&AccDecn, exp_distrib(gen), 0);
//generate exponent for b to minimize chance of a^b overflowing:
// a^b <= 1e100
// b*log(a) <= log(1e100) = 100
// b <= 100/log(a)
// b_exponent <= log(100/log(a)) = log(100) - log(log(a))
// b_exponent <= 2 - log(log(a))
decn_to_str_complete(&AccDecn);
bmp::mpfr_float acc(Buf);
acc = 2.0 - log(log(acc));
double b_exponent_high_flt = acc.convert_to<double>();
int b_exponent_high = b_exponent_high_flt;
int b_exponent_low = -99;
//ensure b_exponent high in range
if (b_exponent_high > 99){
b_exponent_high = 99;
} else if (b_exponent_high < b_exponent_low){
b_exponent_high = b_exponent_low;
}
CAPTURE(b_exponent_low);
CAPTURE(b_exponent_high);
std::uniform_int_distribution<int> b_exp_distrib(b_exponent_low, b_exponent_high);
int b_exponent = b_exp_distrib(gen);
CAPTURE(b_exponent);
int b_neg = sign_distrib(gen);
set_exponent(&BDecn, b_exponent, b_neg);
pow_test();
}
}
TEST_CASE("power random"){
power_test(0, 99);
}
TEST_CASE("power random 0.9 to 0.99..."){
power_test(90, 99, -1, -1);
}
TEST_CASE("power random 1.0 to 2.0..."){
power_test(10, 20, 0, 0);
}

View File

@ -0,0 +1,562 @@
// 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 <string>
#include <boost/multiprecision/mpfr.hpp>
#include <catch2/catch.hpp>
#include "decn.h"
#include "decn_tests.h"
namespace bmp = boost::multiprecision;
using Catch::Matchers::Equals;
static const bmp::mpfr_float mPI = boost::math::constants::pi<bmp::mpfr_float>();
static void trig_test(void (*operation)(void), bmp::mpfr_float (*mpfr_operation)(bmp::mpfr_float x),
double rtol, double atol)
{
//build mpfr float
bmp::mpfr_float::default_precision(50);
decn_to_str_complete(&AccDecn);
CAPTURE(Buf);
bmp::mpfr_float a_actual(Buf);
//calculate
operation();
decn_to_str_complete(&AccDecn);
CAPTURE(Buf);
//calculate actual
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(double rtol=6e-3, double atol=1e-3)
{
CAPTURE("sin test");
trig_test(sin_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return sin(x * mPI / 180);}, rtol, atol);
}
static void sin_test(const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3)
{
CAPTURE(a_str); CAPTURE(a_exp);
build_dec80(a_str, a_exp);
sin_test(rtol, atol);
}
static void cos_test(double rtol=6e-3, double atol=1e-3)
{
CAPTURE("cos test");
trig_test(cos_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return cos(x * mPI / 180);}, rtol, atol);
}
static void cos_test(const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3)
{
CAPTURE(a_str); CAPTURE(a_exp);
build_dec80(a_str, a_exp);
cos_test(rtol, atol);
}
static void tan_test(double rtol=5e-3, double atol=1e-3)
{
trig_test(tan_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return tan(x * mPI / 180);}, rtol, atol);
}
static void tan_test(const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3)
{
CAPTURE(a_str); CAPTURE(a_exp);
build_dec80(a_str, a_exp);
tan_test(rtol, atol);
}
static void atan_test(double rtol=5e-3, double atol=1e-3)
{
trig_test(arctan_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return atan(x)*180/mPI;}, rtol, atol);
}
static void atan_test(const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3)
{
CAPTURE(a_str); CAPTURE(a_exp);
build_dec80(a_str, a_exp);
atan_test(rtol, atol);
}
static void asin_test(double rtol=5e-3, double atol=1e-3)
{
trig_test(arcsin_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return asin(x)*180/mPI;}, rtol, atol);
}
static void asin_test(const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3)
{
CAPTURE(a_str); CAPTURE(a_exp);
build_dec80(a_str, a_exp);
asin_test(rtol, atol);
}
static void acos_test(double rtol=5e-3, double atol=1e-3)
{
trig_test(arccos_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return acos(x)*180/mPI;}, rtol, atol);
}
static void acos_test(const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3)
{
CAPTURE(a_str); CAPTURE(a_exp);
build_dec80(a_str, a_exp);
acos_test(rtol, atol);
}
// const char * const pi = "3.141592653589793239";
// const char * const pi_threequarters = "2.356194490192344929";
// const char * const pi_halved = "1.570796326794896619";
// const char * const pi_quarter = ".7853981633974483096";
TEST_CASE("sin") {
sin_test("0.1", 0, 0.2);
sin_test("0.0", 0, -1);
sin_test("1.5", 0, 0.02);
sin_test("2.0", 0, 0.02);
sin_test("2.5", 0, 0.02);
sin_test("3.0", 0, 0.02);
sin_test("10", 0);
sin_test("20", 0);
sin_test("30", 0);
sin_test("40", 0);
sin_test("80", 0);
sin_test("120", 0);
sin_test("160", 0);
sin_test("200", 0);
sin_test("240", 0);
sin_test("280", 0);
sin_test("320", 0);
sin_test("359", 0, 0.02);
sin_test("360", 0, -1, 0.001);
sin_test("361", 0, 0.02);
sin_test("400", 0);
// sin_test(pi, 0, -1);
// sin_test(pi_quarter, 0);
// sin_test(pi_halved, 0);
// sin_test(pi_threequarters, 0);
sin_test("180.0", 0, -1);
sin_test("45.0", 0);
sin_test("90.0", 0);
sin_test("135.0", 0);
sin_test("1000.0", 0);
sin_test("-0.5", 0, 0.2);
sin_test("-1.5", 0, 0.02);
sin_test("-2.0", 0, 0.02);
sin_test("-2.5", 0, 0.02);
sin_test("-3.0", 0, 0.02);
sin_test("-9.0", 0);
sin_test("-18.0", 0);
sin_test("-27.0", 0);
sin_test("-1000.0", 0);
sin_test("-30", 0);
sin_test("-40", 0);
sin_test("-80", 0);
sin_test("-120", 0);
sin_test("-160", 0);
sin_test("-200", 0);
sin_test("-240", 0);
sin_test("-280", 0);
sin_test("-320", 0);
sin_test("-360", 0, -1, 0.001);
sin_test("-400", 0);
}
TEST_CASE("cos") {
cos_test("0.1", 0, 0.2);
cos_test("0.0", 0, -1);
cos_test("1.5", 0, 0.02);
cos_test("2.0", 0, 0.02);
cos_test("2.5", 0, 0.02);
cos_test("3.0", 0, 0.02);
cos_test("10", 0);
cos_test("20", 0);
cos_test("30", 0);
cos_test("40", 0);
cos_test("80", 0);
cos_test("120", 0);
cos_test("160", 0);
cos_test("200", 0);
cos_test("240", 0);
cos_test("280", 0, 0.006);
cos_test("320", 0);
cos_test("359", 0, 0.02);
cos_test("360", 0, -1, 0.001);
cos_test("361", 0, 0.02);
cos_test("400", 0);
// cos_test(pi, 0, -1);
// cos_test(pi_quarter, 0);
// cos_test(pi_halved, 0);
// cos_test(pi_threequarters, 0);
cos_test("180.0", 0, -1);
cos_test("45.0", 0);
cos_test("90.0", 0, -1, 0.001);
cos_test("135.0", 0);
cos_test("1000.0", 0, 0.006);
cos_test("-0.5", 0, 0.2);
cos_test("-1.5", 0, 0.02);
cos_test("-2.0", 0, 0.02);
cos_test("-2.5", 0, 0.02);
cos_test("-3.0", 0, 0.02);
cos_test("-9.0", 0);
cos_test("-18.0", 0);
cos_test("-27.0", 0);
cos_test("-1000.0", 0);
cos_test("-30", 0);
cos_test("-40", 0);
cos_test("-80", 0, 0.006);
cos_test("-120", 0);
cos_test("-160", 0);
cos_test("-200", 0);
cos_test("-240", 0);
cos_test("-280", 0);
cos_test("-320", 0);
cos_test("-360", 0, -1, 0.001);
cos_test("-400", 0);
}
TEST_CASE("tan") {
tan_test("0.1", 0, 0.2);
tan_test("0.0", 0, -1);
tan_test("1.5", 0, 0.02);
tan_test("2.0", 0, 0.02);
tan_test("2.5", 0, 0.02);
tan_test("3.0", 0, 0.02);
tan_test("10", 0);
tan_test("20", 0);
tan_test("30", 0);
tan_test("40", 0);
tan_test("80", 0);
tan_test("120", 0);
tan_test("160", 0);
tan_test("200", 0);
tan_test("240", 0);
tan_test("280", 0, 0.006);
tan_test("320", 0);
tan_test("359", 0, 0.02);
tan_test("360", 0, -1, 0.001);
tan_test("361", 0, 0.02);
tan_test("400", 0);
// tan_test(pi, 0, -1);
// tan_test(pi_quarter, 0);
// tan_test(pi_halved, 0);
// tan_test(pi_threequarters, 0);
tan_test("180.0", 0, -1);
tan_test("45.0", 0);
tan_test("90.0", 0, 2);
tan_test("135.0", 0);
tan_test("1000.0", 0, 0.006);
tan_test("-0.5", 0, 0.2);
tan_test("-1.5", 0, 0.02);
tan_test("-2.0", 0, 0.02);
tan_test("-2.5", 0, 0.02);
tan_test("-3.0", 0, 0.02);
tan_test("-9.0", 0);
tan_test("-18.0", 0);
tan_test("-27.0", 0);
tan_test("-1000.0", 0);
tan_test("-30", 0);
tan_test("-40", 0);
tan_test("-80", 0, 0.006);
tan_test("-120", 0);
tan_test("-160", 0);
tan_test("-200", 0);
tan_test("-240", 0);
tan_test("-280", 0);
tan_test("-320", 0);
tan_test("-360", 0, -1, 0.001);
tan_test("-400", 0);
}
TEST_CASE("arctan") {
atan_test("0.001", 0, -1, 0.06);
atan_test("-0.001", 0, -1, 0.06);
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, 0.06);
}
TEST_CASE("arcsin") {
asin_test("0.001", 0, -1, 0.06);
asin_test("-0.001", 0, -1, 0.06);
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);
}
static const int NUM_RAND_TRIG_TESTS = 4321; //trig tests are slow
TEST_CASE("sin random"){
std::default_random_engine gen;
std::uniform_int_distribution<int> distrib(0,99);
std::uniform_int_distribution<int> exp_distrib(0, 2); //restrict range for now
std::uniform_int_distribution<int> sign_distrib(0,1);
for (int j = 0; j < NUM_RAND_TRIG_TESTS; j++){
for (int i = 0; i < DEC80_NUM_LSU; i++){
AccDecn.lsu[i] = distrib(gen);
}
int exp = exp_distrib(gen);
int sign = sign_distrib(gen);
set_exponent(&AccDecn, exp, sign);
remove_leading_zeros(&AccDecn);
int lsu0 = AccDecn.lsu[0];
exp = get_exponent(&AccDecn);
CAPTURE(lsu0);
CAPTURE(exp);
CAPTURE(sign);
if (exp <= -1){
//very small
sin_test(4000);
} else if (exp == 0 && lsu0 < 50){
//small
sin_test(0.4);
} else if (exp == 2 && lsu0 >= 17 && lsu0 <= 19){
//near 180
sin_test(3);
} else if (exp == 2 && lsu0 >= 35 && lsu0 <= 36){
//near 360
sin_test(12);
} else if (exp == 2 && lsu0 > 50){
//large
sin_test(35);
} else {
sin_test(0.02);
}
}
}
TEST_CASE("cos random"){
std::default_random_engine gen;
std::uniform_int_distribution<int> distrib(0,99);
std::uniform_int_distribution<int> exp_distrib(0, 2); //restrict range for now
std::uniform_int_distribution<int> sign_distrib(0,1);
for (int j = 0; j < NUM_RAND_TRIG_TESTS; j++){
for (int i = 0; i < DEC80_NUM_LSU; i++){
AccDecn.lsu[i] = distrib(gen);
}
int exp = exp_distrib(gen);
int sign = sign_distrib(gen);
set_exponent(&AccDecn, exp, sign);
remove_leading_zeros(&AccDecn);
int lsu0 = AccDecn.lsu[0];
exp = get_exponent(&AccDecn);
CAPTURE(lsu0);
CAPTURE(exp);
CAPTURE(sign);
if (exp == 1 && lsu0 >= 89 && lsu0 <= 90){
//very near 90
cos_test(500);
} else if (exp == 1 && lsu0 >= 87 && lsu0 <= 92){
//near 90
cos_test(2);
} else if (exp == 2 && lsu0 >= 26 && lsu0 <= 27){
//near 270
cos_test(500);
} else if (exp == 2 && lsu0 >= 44){
//large
cos_test(20);
} else {
cos_test(0.02);
}
}
}
TEST_CASE("tan random"){
std::default_random_engine gen;
std::uniform_int_distribution<int> distrib(0,99);
std::uniform_int_distribution<int> exp_distrib(0, 2); //restrict range for now
std::uniform_int_distribution<int> sign_distrib(0,1);
for (int j = 0; j < NUM_RAND_TRIG_TESTS; j++){
for (int i = 0; i < DEC80_NUM_LSU; i++){
AccDecn.lsu[i] = distrib(gen);
}
int exp = exp_distrib(gen);
int sign = sign_distrib(gen);
set_exponent(&AccDecn, exp, sign);
remove_leading_zeros(&AccDecn);
int lsu0 = AccDecn.lsu[0];
exp = get_exponent(&AccDecn);
CAPTURE(lsu0);
CAPTURE(exp);
CAPTURE(sign);
if (exp <= -3){
//extremely small
tan_test(5000);
} if (exp <= -1){
//very small
tan_test(400);
} else if (exp == 0 && lsu0 < 50){
//small
tan_test(1);
} else if (exp == 1 && lsu0 >= 89 && lsu0 <= 90){
//very near 90
tan_test(5);
} else if (exp == 1 && lsu0 >= 87 && lsu0 <= 92){
//near 90
tan_test(1);
} else if (exp == 2 && lsu0 >= 17 && lsu0 <= 19){
//near 180
tan_test(3);
} else if (exp == 2 && lsu0 >= 26 && lsu0 <= 27){
//near 270
tan_test(5);
} else if (exp == 2 && lsu0 >= 35 && lsu0 <= 37){
//near 360
tan_test(20);
} else if (exp == 2 && lsu0 >= 44){
//large
tan_test(50);
} else {
tan_test(0.02);
}
}
}
TEST_CASE("atan random"){
std::default_random_engine gen;
std::uniform_int_distribution<int> distrib(0, 99);
std::uniform_int_distribution<int> exp_distrib(-1, 0); //restrict range for now
std::uniform_int_distribution<int> sign_distrib(0, 1);
for (int j = 0; j < NUM_RAND_TRIG_TESTS; j++){
for (int i = 0; i < DEC80_NUM_LSU; i++){
AccDecn.lsu[i] = distrib(gen);
}
int exp = exp_distrib(gen);
int sign = sign_distrib(gen);
set_exponent(&AccDecn, exp, sign);
remove_leading_zeros(&AccDecn);
int lsu0 = AccDecn.lsu[0];
exp = get_exponent(&AccDecn);
CAPTURE(lsu0);
CAPTURE(exp);
CAPTURE(sign);
if (exp <= -6){
//extremely small
atan_test(10000);
} else if (exp < -1 || (exp == -1 && lsu0 == 0)){
//very small
atan_test(100);
} else if ((exp == -1 && lsu0 < 10) || (exp == 0 && lsu0 == 0)){
//small
atan_test(3);
} else {
atan_test(0.02);
}
}
}
TEST_CASE("asin random"){
std::default_random_engine gen;
std::uniform_int_distribution<int> distrib(0, 99);
std::uniform_int_distribution<int> exp_distrib(-2, -1); //restrict range for now
std::uniform_int_distribution<int> sign_distrib(0, 1);
for (int j = 0; j < NUM_RAND_TRIG_TESTS; j++){
for (int i = 0; i < DEC80_NUM_LSU; i++){
AccDecn.lsu[i] = distrib(gen);
}
int exp = exp_distrib(gen);
int sign = sign_distrib(gen);
set_exponent(&AccDecn, exp, sign);
remove_leading_zeros(&AccDecn);
int lsu0 = AccDecn.lsu[0];
exp = get_exponent(&AccDecn);
CAPTURE(lsu0);
CAPTURE(exp);
CAPTURE(sign);
if (exp <= -7) {
//extremely small
asin_test(50000);
} else if (exp < -5) {
//very very small
asin_test(1000);
} else if (exp < -1 || (exp == -1 && lsu0 == 0)){
//very small
asin_test(100);
} else if ((exp == -1 && lsu0 < 10) || (exp == 0 && lsu0 == 0)){
//small
asin_test(0.5);
} else {
asin_test(0.02);
}
}
}
TEST_CASE("acos random"){
std::default_random_engine gen;
std::uniform_int_distribution<int> distrib(0, 99);
std::uniform_int_distribution<int> exp_distrib(-2, -1); //restrict range for now
std::uniform_int_distribution<int> sign_distrib(0, 1);
for (int j = 0; j < NUM_RAND_TRIG_TESTS; j++){
for (int i = 0; i < DEC80_NUM_LSU; i++){
AccDecn.lsu[i] = distrib(gen);
}
int exp = exp_distrib(gen);
int sign = sign_distrib(gen);
set_exponent(&AccDecn, exp, sign);
remove_leading_zeros(&AccDecn);
int lsu0 = AccDecn.lsu[0];
exp = get_exponent(&AccDecn);
CAPTURE(lsu0);
CAPTURE(exp);
CAPTURE(sign);
if ((exp == -1 && lsu0 == 99)){
//near 1
acos_test(10);
} else {
acos_test(0.02);
}
}
}

View File

@ -0,0 +1,12 @@
add_executable(div div_mfp.cpp)
target_link_libraries(div gmp)
add_executable(ln ln_mfp.cpp)
target_link_libraries(ln mpfr)
add_executable(exp exp.cpp)
target_link_libraries(exp mpfr)
add_executable(sqrt recip_sqrt.cpp)
target_link_libraries(sqrt mpfr)

View File

@ -1,10 +0,0 @@
all: div ln exp
div: div_mfp.cpp
g++ -Wall div_mfp.cpp -o div -lgmp
ln: ln_mfp.cpp
g++ -Wall ln_mfp.cpp -o ln -lmpfr
exp: exp.cpp
g++ -Wall exp.cpp -o exp -lmpfr

View File

@ -1,3 +1,18 @@
// 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/>.
//prototype for reciprocal function for division based on newton-raphson iteration
#include <stdio.h>

View File

@ -1,3 +1,17 @@
// 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/>.
//prototype for exponential function
// complement of ln() function based on HP Journal article
// "Personal Calculator Algorithms IV: Logarithmic Functions"

View File

@ -1,3 +1,18 @@
// 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/>.
//prototype for natural log function based on HP Journal article
// "Personal Calculator Algorithms IV: Logarithmic Functions"
@ -10,7 +25,7 @@ using namespace boost::multiprecision;
using std::cout;
using std::endl;
static const int NUM_A_ARR = 9;
static const int NUM_A_ARR = 10;
mpfr_float a_arr[NUM_A_ARR];
mpfr_float ln_a_arr[NUM_A_ARR];
@ -82,7 +97,7 @@ int main(void){
calc = accum;
diff = abs((actual - calc)/actual);
if (diff > 5e-17){
if (diff > 1e-17){
cout << x << ": ";
cout << std::setprecision(18) << accum << ", ";
cout << diff << endl;

View File

@ -0,0 +1,138 @@
// 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/>.
//prototype for calculating reciprocal square root 1/sqrt(x)
//based on 0x5F3759DF fast inverse square root:
// calculate using newton-raphson iterations,
// get initial estimate using linear approximation
#include <stdio.h>
#include <iostream>
#include <math.h>
#include <boost/multiprecision/mpfr.hpp>
using namespace boost::multiprecision;
#define DEBUG
using std::cout;
using std::endl;
static const unsigned int CALC_PRECISION = 18;
int get_exp(mpfr_float& x){
mpfr_float x_exp(log10(x), CALC_PRECISION);
return x_exp.convert_to<int>();
}
double get_significand(mpfr_float& x){
int exp = get_exp(x);
double signif = x.convert_to<double>();
signif = signif / pow(10.0, exp);
return signif;
}
int main(void){
cout << std::scientific << std::setprecision(CALC_PRECISION);
//loop through values to test
#ifdef DEBUG
mpfr_float x(2.0, CALC_PRECISION);
{
#else
for (mpfr_float x(1e-99, CALC_PRECISION); x < 1e99; x *= 1.03){
#endif
//build mpf values
mpfr_float x_orig_2(x/2, CALC_PRECISION); //copy of original x / 2
mpfr_float est(0, CALC_PRECISION); //current estimate for 1/sqrt(x)
mpfr_float accum(0, CALC_PRECISION); //working register
mpfr_float_1000 actual;
actual = 1 / sqrt(x);
//get decimal floating point representation
int x_exp = get_exp(x);
double x_signif = get_significand(x);
//normalize: make sure significand is between 1 and 10
while (x_signif < 1.0){
x_signif *= 10;
x_exp--;
}
#ifdef DEBUG
printf("x = %f*10^%d\n", x_signif, x_exp);
#endif
//get initial estimate for 1/sqrt(x) == 10^(-0.5 * log(x)):
// approximate significand == 10^(-0.5 * log(x_signif))
// with linear approximation: -0.18 * x_signif + 2.5
// new exponent part is (10^(-0.5 * log(10^x_exp)))
// == 10^(-0.5 * x^exp)
double est_signif;
if (x_exp & 0x1){ //odd
//increment x_exp and
//approximate estimate significand as (-0.056*x_signif + 0.79) * 10^0.5
// == -0.18 * x_signif + 2.5
x_exp++;
est_signif = -0.18 * x_signif + 2.5;
} else { //even
//keep x_exp as is and approximate estimated significand as
// -0.056*x_signif + 0.79
est_signif = -0.056 * x_signif + 0.79;
}
int est_exp = -x_exp / 2;
est = est_signif * pow(10.0, est_exp);
#ifdef DEBUG
printf("x: %f (%d)\n", x_signif, x_exp);
printf("est: %f (%d)\n", est_signif, est_exp);
cout << "initial estimate for " << x << ": " << est << endl;
#endif
//newton-raphson iterations
for (int i = 0; i < 6; i++){
accum = est * est;
accum *= x_orig_2; //accum = x/2 * est * est
accum = -accum;
accum += 1.5; //accum = 3/2 - x/2 * est * est
accum *= est; //accum = 0.5 * est * (3 - x * est * est)
est = accum;
#ifdef DEBUG
printf("%2d: ", i);
cout << est << endl;
#endif
}
//calculate relative error
mpfr_float_1000 calc, diff;
calc = accum;
diff = abs((actual - calc)/actual);
#ifdef DEBUG_ALL
if (1){
#else
if (diff > 5e-17){
#endif
cout << x << ": ";
cout << std::setprecision(18) << accum << ", ";
#if 1
cout << actual << ", ";
#endif
cout << diff << endl;
}
}
return 0;
}

View File

@ -0,0 +1,26 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Dec 11 00:56:01 2019
Calculate constants used for "0x5F3759DF" reciprocal sqrt
@author: jeffrey
"""
import numpy as np
import matplotlib.pyplot as plt
xvals = np.arange(1, 10, 0.01)
yvals = 10 ** (-0.5 * np.log10(xvals))
#linear fit for significand of 1/sqrt(x)
coef = np.polyfit(xvals, yvals, 1)
poly1d_fn = np.poly1d(coef)
plt.plot(xvals, yvals, 'b')
plt.plot(xvals, poly1d_fn(xvals), '--k')
plt.xlabel('significand')
plt.ylabel('1/sqrt(significand)')
plt.show()
print("linear approx coeff:", coef)
print(" * 3.16:", coef * 3.16227766)

View File

@ -1,3 +1,16 @@
// 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/>.
/*
* key.c
*
@ -138,7 +151,9 @@ void raw_scan(void) __using(1){
//based on quick draw/integrator hybrid debounce algorithm from
//https://summivox.wordpress.com/2016/06/03/keyboard-matrix-scanning-and-debouncing/
#ifndef DESKTOP
#pragma nooverlay
#endif
void debounce(void) __using(1){
uint8_t i, j;
NewKeyPressed = -1; //initially

View File

@ -1,3 +1,16 @@
// 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/>.
/*
* key.h
*

View File

@ -1,3 +1,18 @@
// 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/>.
//#define DEBUG
#include "stc15.h"
@ -28,7 +43,7 @@
static int row, col;
static uint8_t row, col;
#define CLEAR_BIT(port, bit) (port &= ~(_BV(bit)))
#define CLEAR_BITS(port, bits) (port &= ~(bits))
@ -102,7 +117,7 @@ static char readBusy() {
}
static void wait_busy() {
unsigned int i;
uint8_t i;
for (i = 0; i < 100; i++){
if (!readBusy()){
return;
@ -114,7 +129,6 @@ static void wait_busy() {
}
static void LCD_OutChar(unsigned char c) {
unsigned char lower = (c & 0x0f);
DISABLE_INTERRUPTS();
wait_busy();
//output upper 4 bits:
@ -140,9 +154,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)
@ -180,6 +200,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();
@ -212,6 +239,13 @@ void LCD_OutString(__xdata const char *string, uint8_t max_chars) {
}
}
void LCD_OutString_Initial(__code const char *s) {
while (*s) {
TERMIO_PutChar(*s);
s++;
}
}
short TERMIO_PutChar(unsigned char letter) {
if (letter == CR || letter == '\n') {
LCD_Clear();
@ -257,3 +291,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;
}
}

View File

@ -1,3 +1,16 @@
// 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 LCD_H
#define LCD_H
@ -16,18 +29,16 @@ void LCD_Clear(void);
void LCD_GoTo(uint8_t row, uint8_t col);
void LCD_OutString(__xdata const char* string, uint8_t max_chars);
#ifdef DESKTOP
void LCD_OutString_Initial(__xdata const char* string, uint8_t max_chars);
#else
#define LCD_OutString_Initial(a, b) LCD_OutString(a, b)
#endif
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

View File

@ -1,3 +1,16 @@
// 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/>.
/*
* lcd_emulator.c
*
@ -7,6 +20,8 @@
#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include "lcd.h"
#define CR 13 // \r
@ -73,9 +88,10 @@ void LCD_OutString(const char *string, uint8_t max_chars) {
}
}
void LCD_OutString_Initial(const char *string, uint8_t max_chars) {
void LCD_OutString_Initial(const char *string) {
enable_checks = 0;
LCD_OutString(string, max_chars);
assert(strlen(string) <= 32);
LCD_OutString(string, 32);
enable_checks = 1;
}
@ -88,6 +104,8 @@ static int is_valid_character(char letter){
return 1;
} else if(letter == '^'){
return 1;
} else if(letter == 'E' || letter == 'r' || letter == 'o'){
return 1;
}
return 0;
@ -113,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;
}

View File

@ -1,3 +1,17 @@
// 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/>.
//
// STC15 RPN calculator
//
@ -14,6 +28,7 @@
#else
#include "stc15.h"
#endif
#include "stack_debug.h"
#define FOSC 11583000
@ -35,7 +50,7 @@ QSemaphore LcdAvailable(1);
int8_t NewKeyBuf[4];
volatile uint8_t new_key_write_i;
volatile uint8_t new_key_read_i;
volatile uint8_t NewKeyEmpty;
volatile __bit NewKeyEmpty;
#define INCR_NEW_KEY_I(i) i = (i + 1) & 3
@ -125,7 +140,7 @@ static void latch_on(void)
__xdata char EntryBuf[MAX_CHARS_PER_LINE + 1];
__xdata uint8_t ExpBuf[2];
__xdata const char VER_STR[32+1] = "STC RPN Calculator v1.07";
__code const char VER_STR[32+1] = "STC RPN Calculator v1.14";
enum {
@ -211,12 +226,14 @@ 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;
LCD_OutString_Initial(VER_STR, 32);
LCD_OutString_Initial(VER_STR);
#ifdef DESKTOP
LcdAvailable.release();
#endif
@ -290,7 +307,7 @@ int main()
switch(KEY_MAP[I_Key]){
//////////
case '0': {
if (IsShifted){
if (IsShiftedUp || IsShiftedDown){
//off
TURN_OFF();
} else {
@ -326,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){
@ -351,7 +368,7 @@ int main()
} break;
//////////
case '.': {
if (IsShifted){
if (IsShiftedUp || IsShiftedDown){
//STO
finish_process_entry();
} else {
@ -374,7 +391,7 @@ int main()
} break;
//////////
case '=': {
if (IsShifted){ //RCL
if (IsShiftedUp || IsShiftedDown){ //RCL
finish_process_entry();
} else { //Enter
//track stack lift
@ -384,13 +401,15 @@ 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;
//do not increment entry_i from 0, until first non-0 entry
process_cmd(KEY_MAP[I_Key]);
} else if ( EnteringExp >= ENTERING_EXP){
//go back to digit entry
EnteringExp--;
@ -503,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
@ -514,7 +540,7 @@ int main()
LcdAvailable.release();
#endif
//turn backlight back on
BACKLIGHT_ON();
backlight_on();
} //while (1)
}
/* ------------------------------------------------------------------------- */

View File

@ -1,3 +1,18 @@
// 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/>.
// dummy .cpp file, so that this is c++
#include "main.c"

98
src/stack_debug.c Normal file
View 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
View 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

View File

@ -1,3 +1,16 @@
// 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 _STC15_H_
#define _STC15_H_
@ -31,8 +44,8 @@ __sbit __at (0xcf) P5_7 ;
__sfr __at 0x94 P0M0;
__sfr __at 0x93 P0M1;
__sfr __at 0x92 P1M0;
__sfr __at 0x91 P1M1;
__sfr __at 0x92 P1M0;
__sfr __at 0x91 P1M1;
__sfr __at 0x96 P2M0;
__sfr __at 0x95 P2M1;
__sfr __at 0xB2 P3M0;
@ -46,7 +59,7 @@ __sfr __at 0xCB P6M1;
__sfr __at 0xE2 P7M0;
__sfr __at 0xE1 P7M1;
__sfr __at 0x8E AUXR;
__sfr __at 0x8E AUXR;
__sfr __at 0xA2 AUXR1;
__sfr __at 0xA2 P_SW1;
__sfr __at 0x97 CLK_DIV;
@ -89,7 +102,7 @@ __sfr __at 0xA9 SADDR;
__sfr __at 0xB9 SADEN;
//ADC
__sfr __at 0xBC ADC_CONTR;
__sfr __at 0xBC ADC_CONTR;
__sfr __at 0xBD ADC_RES;
__sfr __at 0xBE ADC_RESL;
@ -99,42 +112,42 @@ __sfr __at 0xCE SPCTL;
__sfr __at 0xCF SPDAT;
//IAP/ISP
__sfr __at 0xC2 IAP_DATA;
__sfr __at 0xC2 IAP_DATA;
__sfr __at 0xC3 IAP_ADDRH;
__sfr __at 0xC4 IAP_ADDRL;
__sfr __at 0xC5 IAP_CMD;
__sfr __at 0xC6 IAP_TRIG;
__sfr __at 0xC7 IAP_CONTR;
__sfr __at 0xC7 IAP_CONTR;
//PCA/PWM
__sfr __at 0xD8 CCON;
//PCA/PWM
__sfr __at 0xD8 CCON;
__sbit __at 0xDF CF;
__sbit __at 0xDE CR;
__sbit __at 0xDA CCF2;
__sbit __at 0xD9 CCF1;
__sbit __at 0xD8 CCF0;
__sfr __at 0xD9 CMOD;
__sfr __at 0xE9 CL;
__sfr __at 0xF9 CH;
__sfr __at 0xDA CCAPM0;
__sfr __at 0xD9 CMOD;
__sfr __at 0xE9 CL;
__sfr __at 0xF9 CH;
__sfr __at 0xDA CCAPM0;
__sfr __at 0xDB CCAPM1;
__sfr __at 0xDC CCAPM2;
__sfr __at 0xEA CCAP0L;
__sfr __at 0xEB CCAP1L;
__sfr __at 0xEC CCAP2L;
__sfr __at 0xF2 PCA_PWM0;
__sfr __at 0xF3 PCA_PWM1;
__sfr __at 0xF4 PCA_PWM2;
__sfr __at 0xFA CCAP0H;
__sfr __at 0xDC CCAPM2;
__sfr __at 0xEA CCAP0L;
__sfr __at 0xEB CCAP1L;
__sfr __at 0xEC CCAP2L;
__sfr __at 0xF2 PCA_PWM0;
__sfr __at 0xF3 PCA_PWM1;
__sfr __at 0xF4 PCA_PWM2;
__sfr __at 0xFA CCAP0H;
__sfr __at 0xFB CCAP1H;
__sfr __at 0xFC CCAP2H;
__sfr __at 0xFC CCAP2H;
__sfr __at 0xE6 CMPCR1;
__sfr __at 0xE7 CMPCR2;
//PWM
__sfr __at 0xf1 PWMCFG;
__sfr __at 0xf1 PWMCFG;
__sfr __at 0xf5 PWMCR;
__sfr __at 0xf6 PWMIF;
__sfr __at 0xf7 PWMFDCR;
@ -178,8 +191,8 @@ __sfr __at 0xf7 PWMFDCR;
#define PWM6T2H (*(unsigned char volatile xdata *)0xff42)
#define PWM6T2L (*(unsigned char volatile xdata *)0xff43)
#define PWM6CR (*(unsigned char volatile xdata *)0xff44)
#define PWM7T1 (*(unsigned int volatile xdata *)0xff50)
#define PWM7T1H (*(unsigned char volatile xdata *)0xff50)
#define PWM7T1 (*(unsigned int volatile xdata *)0xff50)
#define PWM7T1H (*(unsigned char volatile xdata *)0xff50)
#define PWM7T1L (*(unsigned char volatile xdata *)0xff51)
#define PWM7T2 (*(unsigned int volatile xdata *)0xff52)
#define PWM7T2H (*(unsigned char volatile xdata *)0xff52)

View File

@ -1,3 +1,16 @@
// 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 "utils.h"
#ifndef DESKTOP
@ -7,14 +20,15 @@
#ifdef DESKTOP
void _delay_ms(uint8_t ms){
//TODO:
(void) ms;
}
#ifdef ACCURATE_DELAY_US
void _delay_us(uint8_t us)
{
//TODO:
(void) us;
}
#endif
void backlight_off(void){ }
#else //!DESKTOP
void _delay_ms(uint8_t ms)
{
@ -54,10 +68,6 @@ void _delay_us(uint8_t us)
}
#endif
void backlight_off(void){
P3_4 = 1;
}
#endif //ifdef desktop
#ifdef DESKTOP

View File

@ -1,3 +1,16 @@
// 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/>.
/*
* utils.h
*
@ -22,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
@ -35,18 +45,19 @@ void backlight_off(void);
#endif
#if defined(DESKTOP) || defined(IS_ECLIPSE)
#include <stdbool.h>
char* u32str(uint32_t x, char* buf, uint8_t base);
#define __bit bool
#define __code
#define __xdata
#define __idata
#define __sfr
#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

9
steps/build_calc.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
set -e
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
# build
cd $SCRIPT_DIR/..
make

9
steps/compose_build.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
set -e
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
# build
cd $SCRIPT_DIR
docker-compose build

21
steps/compose_run.sh Executable file
View File

@ -0,0 +1,21 @@
#!/bin/bash
set -e
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
# set GITHUBWORKSPACE if building locally
if [[ -z "${GITHUB_WORKSPACE}" ]]; then
export GITHUB_WORKSPACE="$SCRIPT_DIR/.."
fi
echo "GITHUB_WORKSPACE: ${GITHUB_WORKSPACE}"
# build
cd $SCRIPT_DIR
if [ "$#" -eq 0 ]; then
# start shell
docker-compose run build_tools bash
else
docker-compose run build_tools /code/steps/$1
fi

29
steps/desktop_build_check.sh Executable file
View File

@ -0,0 +1,29 @@
#!/bin/bash
set -e
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
# build
cd $SCRIPT_DIR/..
if [ "$1" == "--rebuild" ]; then
rm -rf build_qt
mkdir build_qt
else
mkdir -p build_qt
fi
cd build_qt
cmake .. -GNinja
ninja
# run tests
ctest -j $(nproc)
# get coverage
echo "Running lcov"
lcov --capture --directory src/decn --output-file coverage.info
lcov --remove coverage.info "/usr/*" --output-file coverage.info
genhtml coverage.info --output-directory lcov
echo "Running gcov"
gcov -b src/decn/CMakeFiles/decn_cover.dir/decn.c.gcno

10
steps/docker-compose.yml Normal file
View File

@ -0,0 +1,10 @@
version: '3.2'
services:
build_tools:
build:
context: ..
dockerfile: Dockerfile
volumes:
- type: bind
source: $GITHUB_WORKSPACE
target: /code