diff --git a/CHANGELOG.md b/CHANGELOG.md index 223a1eab..6d9ac4bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,58 @@ # Changelog - Unreleased +- 1.0.0 (2024-07-19) + - Documentation + - add "Troubleshooting" section to `USER_GUIDE.md` + - User Interface + - `CLEAR` button clears only to the end of the line if the cursor is in + the interior of the input buffer + - if the cursor is at the beginning or the end of the input buffer, + the entire buffer is cleared + - identical to the CLEAR button on the TI-89/92+/Voyage 200 + - similar to 2ND CLEAR on HP-50g in Algebraic mode (but not in RPN + mode) + - Variables + - print 'Err: Archived' error message if `STO` or `RCL` acts on + a variable (A-Z,Theta) that is archived + - TVM + - Change the criteria for using the small-i approximation to the + following: N*i <~ 6e-5. + - Improve performance of `PRIM` by 2.4X + - convert `modOP1ByBC()` to use the `IX` register instead of the stack + `(SP)`: 43-67% faster + - use DEIX instead of HLIX: ~12% faster + - process using in 8-bit chunks instead of 1-bit: ~18% faster + - use `A` register instead of `D` register for chunks: ~5% faster + - use nonrestoring division algorithm: ~11% faster + - unroll the 8-bit division loop 8 times: ~11% faster + - thanks go to the responders of [this Cemetech + thread](https://www.cemetech.net/forum/viewtopic.php?p=307636) for + improving the `modHLIXByBC()` algorithm + - MODE + - set Trig, Floating Display, and Display Digits settings to a known + state if the restoration of RPN83SAV fails, instead of inheriting the + modes from the TI-OS + - trig mode: RAD + - floating display: FIX + - display digits: floating + - CLR folder + - add `CLD` (clear display) menu item + - clear the display and re-render everything + - should almost never be needed, except during debugging + - analogous to the `CLD` command on the HP-42S + - UNIT + - car fuel consumption + - add `>Lkm`: convert mpg (miles per US gallon) to L/100km (liters + per 100 km) + - add `>mpg`: convert L/100km to mpg + - car tire pressure + - add `>kPa`: convert psi (pounds per square inch) to kPa + (kiloPascal) + - add `>psi`: convert kPa to psi + - land area + - add `>ha`: convert acre to hectare + - add `>acr`: convert hectare to acre - 0.12.0 (2024-06-24) - **Bug Fix**: update logic that determines when the comma `,` character can be inserted into the input buffer diff --git a/README.md b/README.md index 36c288da..b12d9e8d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ bytes), `RPN83SAV` (140 byte), `RPN83STA` (272 bytes), and `RPN83STK` (120 to Summary of features: - traditional RPN stack (`X`, `Y`, `Z`, `T`), with `LASTX` register - - configurable stack levels between 4 and 8: `SSIZ`, `SIZ?` + - configurable stack levels between 4 and 8: `SSIZ`, `SSZ?` - input edit line with scrollable cursor using arrow keys - `LEFT`, `RIGHT`, `2ND LEFT`, `2ND RIGHT` - 8-line display showing 4 stack registers @@ -56,7 +56,8 @@ Summary of features: - angle conversions: `>DEG`, `>RAD`, `>HR`, `>HMS`, `>REC`, `>POL` - unit conversions: `>C`, `>F`, `>hPa`, `>inHg`, `>km`, `>mi`, `>m`, `>ft`, `>cm`, `>in`, `>um`, `>mil`, `>kg`, `>lbs`, `>g`, `>oz`, `>L`, `>gal`, - `>mL`, `>floz`, `>kJ`, `>cal`, `>kW`, `>hp` + `>mL`, `>floz`, `>kJ`, `>cal`, `>kW`, `>hp`, `>Lkm`, `>mpg`, `>kPa`, + `>psi`, `>ha`, `>acr` - statistics and curve fitting, inspired by HP-42S - statistics: `Σ+`, `Σ-`, `SUM`, `MEAN`, `WMN` (weighted mean), `SDEV` (sample standard deviation), `SCOV` (sample covariance), @@ -76,7 +77,8 @@ Summary of features: - carry flag and bit masks: `CCF`, `SCF`, `CF?`, `CB`, `SB`, `B?` - word sizes: `WSIZ`, `WSZ?`: 8, 16, 24, 32 bits - time value of money (TVM), inspired by HP-12C, HP-17B, and HP-30b - - `N`, `I%YR`, `PV`, `PMT`, `FV`, `P/YR`, `BEG`, `END`, `CLTV` (clear TVM) + - `N`, `I%YR`, `PV`, `PMT`, `FV` + - `P/YR`, `C/YR`, `BEG`, `END`, `CLTV` (clear TVM) - complex numbers, inspired by HP-42S and HP-35s - stored in RPN stack registers (`X`, `Y`, `Z`, `T`, `LASTX`) and storage registers `R00-R99` @@ -113,7 +115,7 @@ Missing features (partial list): - vectors and matrices - keystroke programming -**Version**: 0.12.0 (2024-06-24) +**Version**: 1.0.0 (2024-07-19) **Changelog**: [CHANGELOG.md](CHANGELOG.md) @@ -331,8 +333,8 @@ the 4 ways that complex numbers can be entered into RPN83P: - `100 - i/(2*pi*60*(1e-5))` using `2ND LINK` - `100 + 250i` using `2ND i` -- `200 e^(i 10deg)` using `2ND ANGLE` -- `300 e^(i 0.1)` using `2ND ANGLE 2ND ANGLE` +- `200 ∠ 10°` using `2ND ANGLE` +- `300 ∠ 0.1` using `2ND ANGLE 2ND ANGLE` The keystrokes are: @@ -340,17 +342,17 @@ The keystrokes are: - Press `MODE` button, `downarrow`, `RECT`: ![MODE MenuRow 2](docs/images/menu-root-mode-2.png) - Press `100` `ENTER` -- Press `2` `PI` `*` `60` `*` `1 EE 5` `(-)` `*` `1/X` `(-)` (-265.26) -- Press `2ND LINK` (100-265.26i) -- Press `100` `2ND i` `250` `+` (200-15.26i) -- Press `200` `2ND ANGLE` `10` `+` (396.96+19.47i) -- Press `300` `2ND ANGLE` `2ND ANGLE` `0.1` `+` (695.46+49.42i) -- Press `4` `/` (173.89+12.35i) -- Press `PRAD` (174.30 e^(i 0.07) -- Press `PDEG` (174.30 e^(i 4.04deg)) +- Press `2` `PI` `*` `60` `*` `1 EE 5` `(-)` `*` `1/X` `(-)` (X: -265.26) +- Press `2ND LINK` (X: 100 i -265.26) +- Press `100` `2ND i` `250` `+` (X: 200 i -15.26) +- Press `200` `2ND ANGLE` `10` `+` (X: 396.96 i 19.47) +- Press `300` `2ND ANGLE` `2ND ANGLE` `0.1` `+` (X: 695.46 i 49.42) +- Press `4` `/` (X: 173.89 i 12.35) +- Press `PRAD` (X: 174.30 ∠ 0.07) +- Press `PDEG` (X: 174.30 ∠° 4.04) - Press `MATH` button `CPLX`: ![CPLX MenuRow 1](docs/images/menu-root-cplx-1.png) -- Press `CABS` (174.30) +- Press `CABS` (X: 174.30) ![RPN83P Example 4 GIF](docs/images/rpn83p-example4.gif) diff --git a/docs/DEVELOPER.md b/docs/DEVELOPER.md index e663d494..1677d56d 100644 --- a/docs/DEVELOPER.md +++ b/docs/DEVELOPER.md @@ -3,7 +3,7 @@ Notes for the developers of the RPN83P app, likely myself in 6 months when I cannot remember how the code works. -**Version**: 0.12.0 (2024-06-24) +**Version**: 1.0.0 (2024-07-19) **Project Home**: https://github.com/bxparks/rpn83p @@ -134,7 +134,7 @@ Here are some notes about how the `PRIM` algorithm works: implement a custom `mod(u32, u16)` function which is about 25% faster than the full `div(u32, u16)` function. - In v0.10, the inner loop of the `mod(u32,u16)` function was made 40-50% faster - using the following observations: + through the `modHLSPByBC()` function using the following observations: - The Z80 has only 16-bit registers, so the `u32` type must typically be stored in 4 bytes of RAM, and the `u32` operations must work against the 4 bytes of RAM. @@ -149,10 +149,25 @@ Here are some notes about how the `PRIM` algorithm works: `ex (sp), hl` instruction of the Z80 to swap the 2 halves back and forth. This made the `mod(u32,u16)` function about 40-50% faster compared to v0.9.0. - -I can think of one additional optimization that *may* give us a 10-20% speed -increase, but it would come at the cost of code that would be significantly -harder to maintain, so I don't think it's worth it. +- In v0.13.0-dev, the inner loop `mod(u32,u16)` became another ~42% faster: + - discovered that the Z80 supports `add ix, ix` instruction + - replacing `ex (sp), hl; add hl, hl` combo with `add ix, ix`: ~29% faster + - replacing `rl e; rl d` combo with `adc hl, hl`: ~4% faster + - deleting an unnecessary `or a` instruction: ~5% faster + - rearranging some code to eliminate a branch in the common case, and + selecting `jr` or `jp` judiciously: 1-2% faster +- In v1.0.0, the `mod(u32,u16)` became another 140-160% (i.e. 2.4X to 2.6X) + faster, based on the ideas from [this Cemetech + thread](https://www.cemetech.net/forum/viewtopic.php?t=19790): + - initial benchmark: 20.5 s + - chunking using 8-bit registers, instead of shifting the entire 32-bit + dividend: 18% faster + - using DEIX instead of HLIX, eliminating a bunch of 'ex de, hl': 12% faster + - using a nonrestoring division: 9-13% faster + - using register A instead of register D for each 8-bit chunk: 5% faster + - unrolling the 8-bit division loop eight times: 11-15% faster + - end result: 11.8 s or 74% faster, i.e. 1.74X faster + - total improvement from v0.12: 2.4X (83+/84+) to 2.6X (Nspire) faster ### Prime Factor Improvements diff --git a/docs/FUTURE.md b/docs/FUTURE.md index d57cec80..1854eae6 100644 --- a/docs/FUTURE.md +++ b/docs/FUTURE.md @@ -14,7 +14,7 @@ because it is faster and easier to use compared to a web app, especially for small features that can be described in a few sentences. Usually only the larger and more complicated features will get their own GitHub tickets. -**Version**: 0.12.0 (2024-06-24) +**Version**: 1.0.0 (2024-07-19) **Parent Document**: [USER_GUIDE.md](USER_GUIDE.md) @@ -219,7 +219,7 @@ These are features which are unlikely to be implemented for various reasons: - The TI-OS supports only single-letter variables for real or complex types and access to these from RPN83P are provided through the `STO` and `RCL` commands. The TI-OS supports multi-letter user-defined names only for real - or complex Lists, which are not currently (v0.12.0) supported in RPN83P. + or complex Lists, which are not currently supported in RPN83P. - On the other hand, the HP-42S allows multi-letter user-defined variables for all types, and they which are accessible through the menu system. - To support multi-letter variables in RPN83P, we would have to write our diff --git a/docs/TVM.md b/docs/TVM.md index 954e110a..980d83b7 100644 --- a/docs/TVM.md +++ b/docs/TVM.md @@ -3,7 +3,7 @@ Equations, algorithms, and other tricks used to calculate the Time Value of Money (TVM) variables in the RPN83P calculator app. -**Version**: 0.12.0 (2024-06-24) +**Version**: 1.0.0 (2024-07-19) **Project Home**: https://github.com/bxparks/rpn83p @@ -12,17 +12,20 @@ Money (TVM) variables in the RPN83P calculator app. - [TVM Basics](#tvm-algorithms) - [Interest Rate Conversions](#interest-rate-conversions) - [TVM Formulas](#tvm-formulas) -- [Small Interest Limits](#small-interest-limits) +- [Small Interest Accuracy](#small-interest-accuracy) + - [expm1() and log1p() Functions](#expm1-and-log1p-functions) - [CF1 and CF2](#cf1-and-cf2) - [N and N0](#n-and-n0) - [TVM Solver](#tvm-solver) - [Number of Solutions](#number-of-solutions) - [Taming Overflows](#taming-overflows) + - [Small i Approximation](#small-i-approximation) - [Secant Method](#secant-method) - [Initial Guesses](#initial-guesses) - [Terminating Conditions](#terminating-conditions) - [Maximum Iterations](#maximum-iterations) - [Convergence](#convergence) +- [TVM Puzzles](#tvm-puzzles) - [References](#references) Notes about the equations and algorithms used by the TVM functions. @@ -170,13 +173,17 @@ later. Also, the `(1+ip)` term is always associated with `CF2(i)`, so the `compoundingFactors()` routine in the code calculates both `CF1(i)` and `CF3(i)=(1+ip)CF2(i)` at once. -## Small Interest Limits +## Small Interest Accuracy When the interest rate `i` is small (say, smaller than 1e-6), special precautions are needed to ensure that numerical cancellation errors are reduced -so that the correct answers are returned. To accomplish this, we need to take a -side trip to define 2 new functions. These functions are designed to return -accurate values when `x` is very small: +so that the correct answers are returned. + +### expm1() and log1p() Functions + +To obtain the most accuracy results when when `i` is small, we need to take a +side trip to define 2 new functions which are designed to return accurate values +when `x` is very small: ``` expm1(x) = e^x-1 @@ -381,7 +388,9 @@ the `1e-99` limit of the TI-OS. The solution provided by Albert Chan is brilliant. It uses the fact that when we are solving for the zeros of an equation, we can divide the equation by an arbitrary function whose values are `> 0` everywhere, and the roots of the -equation are *unchanged*. Let's define a new term `CFN(i,N)`: +equation are *unchanged*. + +Let's define a new term `CFN(i,N)`: ``` CFN(i,N) = CF2(i)/N = [(1+i)^N-1]/Ni @@ -395,7 +404,7 @@ CFN(i,N) = CF2(i)/N = [(1+i)^N-1]/Ni (Note that this is slightly different than the `C(i,N)` function defined by Albert Chan in [TVM formula error in programming -manual?](https://www.hpmuseum.org/forum/thread-20739.html) (2023)) The +manual?](https://www.hpmuseum.org/forum/thread-20739.html) (2023)) Our `CFN(i,N)` function normalized by `N` compared to `CF2(i)` so that it evaluates to `1` at `i=0` for all `N`. @@ -417,6 +426,30 @@ same as the `FV` term but with the `N` replaced with `-N`. In other words: CFN(i,-N) = CFN(i,N)/(1+i)^N ``` +For implementation purposes, it is convenient to define the `ICFN(i,N)` +function which is the reciprocal of `CFN(i,N)` which allows us to avoid at +least 2 floating point division operations (which are expensive relative to +multiplication): + +``` +ICFN(i,N) = 1/CFN(i,N) = Ni/((1+i)^N-1) +``` + +This function is computed in the `inverseCompoundingFactor()` routine in the +code. + +(The `ICFN(i,N)` function is similar to the `C(n)` function given by Albert Chan +in [TVM formula error in programming +manual?](https://www.hpmuseum.org/forum/thread-20739-post-179371.html#pid179371) +(2023), within a factor of `(1+i)^N`, or equivalently, a substitution of `-N` +for `N`.) + +Combining all these, the equation solved by the TVM Solver is: + +``` +NPMT(i,n) = PV * ICFN(i,-N) + (1+ip)N*PMT + FV * ICFN(i,N) = 0 +``` + Solving the roots of `NPMT(i)=0` will yield that exactly the same roots as solving for `NFV(i)=0`, because the `CFN(i,n)` function is positive for all `i` over the domain of interest `(1+i)>0`. @@ -463,21 +496,65 @@ beginning or end of the cash flow. The `NPMT(i)` function essentially averages all 3 terms over the entire duration of the `N` payment periods, instead of pulling everything to the present or pushing everything to the future. -**Implementation Note**: +### Small i Approximation -The `inverseCompoundingFactor()` routine calculates the reciprocal of -`CFN(i,N)`. In other words, it calculates +When `i` is large enough, we can compute `ICFN(i,N)` using the usual conversion +to `expm1()` and `log1p()` functions: ``` -ICFN(i,N) = 1/CFN(i,N) = Ni/((1+i)^N-1) +ICFN(i,N) = 1/CFN(i,N) + = Ni/((1+i)^N-1) + = Ni / expm1(N*log1p(i)) ``` -for a slight gain in efficiency by avoiding a division or two. This makes -`ICFN(i,N)` similar to the `C(n)` function given by Albert Chan in [TVM formula -error in programming -manual?](https://www.hpmuseum.org/forum/thread-20739-post-179371.html#pid179371) -(2023), within a factor of `(1+i)^N`, or equivalently, a substitution of `-N` -for `N`. +But when `i` becomes very close to 0, even the `expm1()` and `log1p()` functions +cannot prevent significant rounding errors. In this region of very small `i`, we +can use the Taylor series expansion of `ICFN(i,N)`: + +``` +ICFN(i,N) = 1 - ((N-1)/2) i + ((N^2-1)/12) i^2 - ((N^2-1)/24) i^3 + O(i^4) +``` + +When `i` becomes very small, we want to use the quadratic truncation of the +Taylor series. Ideally, the transition to the quadratic approximation should +happen when the error relative to the full equation is less than the limit of +the floating point format of the TI-83+/84+ calculator (14 digits). My guess is +that error in the quadratic equation is roughly equal to the 3rd order term of +the Taylor series, in other words: + +``` +err =~ ((N^2-1)/24) i^3 +``` + +So that means we want to use the small-i quadratic approximation when: + +``` +err <~ 1e-14 + +=> (N^2-1)/24) i^3 <~ 1e-14 + +=> N*i <~ N^(1/3) * 6.2e-5 +``` + +Let's assume that `N>1` for all real-life problems. Then it is reasonable to +replace the above with a more conservative (and easier to calculate) constraint: + +``` +N*i <~ 6e-5 +``` + +which satisfies the original constraint for `N>1`. + +Finally, we get the formula that the `inverseCompoundingFactor()` routine uses +to calculate the `ICFN(i,n)` function: + +``` + Ni / expm1(N*log1p(i)) (when N*i >= 6e-5) + / +ICFN(i,N) = + \ + 1 - ((N-1)/2) i + ((N^2-1)/12) i^2 (when N*i < 6e-5) +``` ### Secant Method @@ -504,8 +581,8 @@ tedious to translate to Z80 assembly. convergence characteristics of the Newton's method (quadratic convergence) or Halley's method (cubic convergence). -The convergence of the Secant method seems to be fast enough for get a tolerance -of about 1e-8 within 7-8 iterations. +The convergence of the Secant method seems to be fast enough to get answers to +within a tolerance of about 1e-10 within 7-8 iterations. ### Initial Guesses @@ -602,6 +679,16 @@ of his posts, for example [TVM solve for interest rate, revisited](https://www.hpmuseum.org/forum/thread-18359.html) (2022), but I have not yet digested these posts. +## TVM Puzzles + +Duncan Murray has evaluated the performance and accuracy of TVM solvers on +various calculators (including RPN83P) using a number of TVM puzzles. The +puzzles and the results are available here: + +- [Looking for financial/TVM + solutions](https://forum.swissmicros.com/viewtopic.php?f=2&t=3987) +- [TVM puzzles 1:12 in 5 minutes](https://www.youtube.com/watch?v=7ragp9pMR3w) + ## References Here is an incomplete list of references that I consulted: diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index 42ebf415..06232e1b 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -2,7 +2,7 @@ RPN calculator app for the TI-83 Plus and TI-84 Plus inspired by the HP-42S. -**Version**: 0.12.0 (2024-06-24) +**Version**: 1.0.0 (2024-07-19) **Project Home**: https://github.com/bxparks/rpn83p @@ -72,6 +72,10 @@ RPN calculator app for the TI-83 Plus and TI-84 Plus inspired by the HP-42S. - [Complex Numbers](#complex-numbers) - [DATE Functions](#date-functions) - [TI-OS Interaction](#ti-os-interaction) +- [Troubleshooting](#troubleshooting) + - [Clear Display](#clear-display) + - [Reset MODE to Factory Defaults](#reset-mode-to-factory-defaults) + - [Wipe to Factory State](#wipe-to-factory-state) - [Future Enhancements](#future-enhancements) ## Introduction @@ -130,7 +134,8 @@ Summary of features: - angle conversions: `>DEG`, `>RAD`, `>HR`, `>HMS`, `>REC`, `>POL` - unit conversions: `>C`, `>F`, `>hPa`, `>inHg`, `>km`, `>mi`, `>m`, `>ft`, `>cm`, `>in`, `>um`, `>mil`, `>kg`, `>lbs`, `>g`, `>oz`, `>L`, `>gal`, - `>mL`, `>floz`, `>kJ`, `>cal`, `>kW`, `>hp` + `>mL`, `>floz`, `>kJ`, `>cal`, `>kW`, `>hp`, `>Lkm`, `>mpg`, `>kPa`, + `>psi`, `>ha`, `>acr` - statistics and curve fitting, inspired by HP-42S - statistics: `Σ+`, `Σ-`, `SUM`, `MEAN`, `WMN` (weighted mean), `SDEV` (sample standard deviation), `SCOV` (sample covariance), @@ -672,23 +677,49 @@ If the `X` line is *not* in edit mode (i.e. the cursor is not shown), then the #### CLEAR Key -The `CLEAR` key either clears the `X` register or clears the current input line. -If `CLEAR` is pressed when the input buffer is already empty , then it performs -the `CLST` (Clear Stack) operation. This condition will always happen if CLEAR -is pressed 3 times consecutively. Here is an example where we fill up the RPN -stack first, then hit `CLEAR` a few times: - -| **Keys** | **Display**| -| --------------------- | ---------- | -| `1` `ENTER` `2` `ENTER` `3` `ENTER` `4` | ![Input Clear](images/input-clear-1.png) | -| `CLEAR` | ![Input Clear](images/input-clear-2.png) | -| `CLEAR` | ![Input Clear](images/input-clear-3.png) | -| `CLEAR` | ![Input Clear](images/input-clear-4.png) | - -Hitting `CLEAR` 3 times is a convenient alternative to navigating the menu -system to the `CLST` menu function nested under the `ROOT > CLR` menu folder. -(Another alternative could have been `2ND CLEAR` but the TI-OS does not support -that keystroke because it returns the same key code as `CLEAR`.) +The `CLEAR` key performs slightly different functions depending on the context: + +- If the input has been terminated (i.e. not in edit mode), `CLEAR` clears the + `X` register, similar to the `CLX` (Clear X Register) menu function. +- If the input is in edit mode, then: + - If the cursor is at the end of the input line, `CLEAR` erases the entire + line. + - If the cursor is at the beginning of the input line, `CLEAR` also erases + the entire line. + - If the cursor is in the middle of the input line, then `CLEAR` erases + *only* to the end of the line. + - If the input line is already empty when `CLEAR` is pressed, then it + interprets that as a `CLST` (Clear Stack) operation, and warns the user + with a message. + - If the `CLEAR` is pressed again after the warning, then the `CLST` + operation is performed, clearing the RPN stack. + +The `CLEAR` button erases only to the end of line if the cursor is in the middle +of the input buffer, which is convenient when the input line becomes lengthy. I +borrowed this behavior from the `CLEAR` button on the TI-89, TI-89 Titanium, +TI-92+, and TI Voyage 200 calculators. (The `2ND CLEAR` on the HP-50g works in a +similar way, but only in Algebraic mode, not in RPN mode.) + +I hope the following example illustrates the different behaviors of `CLEAR` +clearly: + +| **Keys** | **Display** | +| --------------------- | ---------- | +| `1` `ENTER` `2` `ENTER` `3` `ENTER` | ![Input Clear](images/input-clear-1.png) | +| `CLEAR` (invokes `CLX`) | ![Input Clear](images/input-clear-2.png) | +| `4.5678` | ![Input Clear](images/input-clear-3.png) | +| `CLEAR` (clears entire line) | ![Input Clear](images/input-clear-4.png) | +| `4.5678` `LEFT` `LEFT` `LEFT` | ![Input Clear](images/input-clear-5.png) | +| `CLEAR` (clears to end of line) | ![Input Clear](images/input-clear-6.png) | +| `CLEAR` (clears entire line) | ![Input Clear](images/input-clear-7.png) | +| `CLEAR` (requests `CLST`) | ![Input Clear](images/input-clear-8.png) | +| `CLEAR` (invokes `CLST`) | ![Input Clear](images/input-clear-9.png) | + +In most cases, pressing `CLEAR` 3 times will invoke the `CLST` function. +This is often far more convenient than navigating to the `CLST` menu function +nested under the `ROOT > CLR` menu folder. (Another alternative could have been +`2ND CLEAR` but the TI-OS does not support that keystroke because it returns the +same key code as `CLEAR`.) An empty string will be interpreted as a `0` if the `ENTER` key or a function key is pressed. @@ -830,7 +861,7 @@ others were not. the number. - Since the `ON/EXIT` button is used to navigate the menu hierarchy, it cannot cause input termination, unlike the HP-42S. - - The only exceptions are menus which changes the rendering of the values on + - The only exceptions are menus which change the rendering of the values on the RPN stack, for example: - `BASE` menu folder, which interprets the values on the RPN stack as integers not floating point numbers @@ -1125,7 +1156,7 @@ The Help pages are intended to capture some of the more obscure tidbits about the RPN83P app which may be hard to remember. Hopefully it reduces the number of times that this User Guide needs to be consulted. -The message at the bottom of each page is not completely honest. A number of +The message at the bottom of each page is not completely honest. A few navigational keys are recognized by the Help system: - `UP`, `LEFT`: previous page with wraparound @@ -1140,6 +1171,7 @@ listed in the TI-83 SDK. The SDK unfortunately does not describe how these errors are actually triggered. By trial-and-error, I could reverse engineer only a few of them as described below: +- `Err: Archived`: storage variable (A-Z,Theta) is archived - `Err: Argument`: incorrect number of arguments - `Err: Bad Guess` - `Err: Break` @@ -1431,11 +1463,13 @@ buttons just under the LCD screen. Use the `UP`, `DOWN`, `ON` (EXIT/ESC), and - `RSTV`: reset TVM Solver parameters to factory defaults - ![ROOT > CLR](images/menu-root-clr.png) (`ROOT > CLR`) - ![ROOT > CLR > Row1](images/menu-root-clr-1.png) + - ![ROOT > CLR > Row2](images/menu-root-clr-2.png) - `CLX`: clear `X` stack register (stack lift disabled) - `CLST`: clear all RPN stack registers - `CLRG`: clear all storage registers `R00` to `R99` - `CLΣ`: clear STAT storage registers [`R11`, `R16`] or [`R11`, `R23`] - `CLTV`: clear TVM variables and parameters + - `CLD`: clear display and rerender everything - ![ROOT > MODE](images/menu-root-mode.png) (`ROOT > MODE`) - ![ROOT > MODE > Row1](images/menu-root-mode-1.png) - ![ROOT > MODE > Row2](images/menu-root-mode-2.png) @@ -1481,6 +1515,8 @@ buttons just under the LCD screen. Use the `UP`, `DOWN`, `ON` (EXIT/ESC), and - ![ROOT > UNIT > Row4](images/menu-root-unit-4.png) - ![ROOT > UNIT > Row5](images/menu-root-unit-5.png) - ![ROOT > UNIT > Row6](images/menu-root-unit-6.png) + - ![ROOT > UNIT > Row7](images/menu-root-unit-7.png) + - ![ROOT > UNIT > Row8](images/menu-root-unit-8.png) - `>C`: Fahrenheit to Celsius - `>F`: Celsius to Fahrenheit - `>hPa`: hectopascals (i.e. millibars) to inches of mercury (Hg) @@ -1505,6 +1541,12 @@ buttons just under the LCD screen. Use the `UP`, `DOWN`, `ON` (EXIT/ESC), and - `>cal`: kilo Joules to kilo calories - `>kW`: horsepowers (mechanical) to kilo Watts - `>hp`: kilo Watts to horsepowers (mechanical) + - `>Lkm`: miles per US gallon to liters per 100 km + - `>mpg`: liters per 100 km to miles per US gallon + - `>kPa`: pounds per square inch to kilo Pascals + - `>psi`: kilo Pascals to pounds per square inch + - `>ha`: acres to hectares + - `>acr`: hectares to acres - ![ROOT > DATE](images/menu-root-date.png) (`ROOT > DATE`) - ![ROOT > DATE > Row1](images/date/menu-root-date-1.png) - ![ROOT > DATE > Row2](images/date/menu-root-date-2.png) @@ -1997,7 +2039,7 @@ For example, let's find the prime factors of `2_122_438_477 = 53 * 4001 * For computational efficiency, `PRIM` supports only integers between `2` and `2^32-1` (4 294 967 295). This allows `PRIM` to use integer arithmetic, making -it about 10X faster than the equivalent algorithm using floating point routines. +it about 25X faster than the equivalent algorithm using floating point routines. Any number outside of this range produces an `Err: Domain` message. (The number `1` is not considered a prime number.) @@ -2008,10 +2050,10 @@ times of the `PRIM` function for this number for various TI models that I own: | **Model** | **PRIM Running Time** | | --- | --- | -| TI-83+ (6 MHz) | 20 s | -| TI-83+SE (15 MHz) | 7.7 s | -| TI-84+SE (15 MHz) | 9.5 s | -| TI-Nspire w/ TI-84+ keypad | 8.2 s | +| TI-83+ (6 MHz) | 8.3 s | +| TI-83+SE (15 MHz) | 3.2 s | +| TI-84+SE (15 MHz) | 3.9 s | +| TI-Nspire w/ TI-84+ keypad | 3.0 s | During the calculation, the "run indicator" on the upper-right corner will be active. You can press the `ON` key to break from the `PRIM` loop with an `Err: @@ -2177,6 +2219,59 @@ The RPN83P app interacts with the underlying TI-OS in the following ways. Finance app is that these are the only RPN83P variables which are *not* saved in the `RPN83SAV` appVar. +## Troubleshooting + +### Clear Display + +It is possible for the display to contain leftover pixels or line segments that +did not get properly cleared or overwritten due to some bug. When this happens, +clearing the display using the `CLD` (Clear Display) function under the `CLR` +menu folder will probably fix the problem. This function is modeled after the +`CLD` function on the HP-42S. + +I have rarely seen display rendering bugs. In all cases that I can remember, I +was doing some internal debugging which would not be performed by normal users. + +### Reset MODE to Factory Defaults + +The RPN83P currently has only a handful of settings, and they can be reset +relatively easily through the `MODE` menu (or through the `MODE` button). There +is no explicit `CLxx` menu function under the `CLR` menu folder to reset the +MODE settings to their factory defaults. + +If for some reason the factory defaults must be explicitly set, the current +workaround is to use the TI-OS: + +- `2ND MEM` +- `2` (Mem Mgmt/Del) +- `B` (AppVars) +- scroll down to the `RPN83SAV` variable +- delete it using the `DEL` button + +Upon restarting RPN83P, the various MODE parameters will be set to their factory +defaults. + +### Wipe to Factory State + +I have resisted the temptation to add a `CLAL` (Clear All) menu function because +it seems too dangerous, and because I'm not sure that everyone has the same +idea about what "all" means. + +If RPN83P gets into a state where everything must be reset, a complete wipe +can be performed through the TI-OS: + +- `2ND MEM` +- `2` (Mem Mgmt/Del) +- `B` (AppVars) +- delete all variables with the pattern `RPN83***` using the `DEL` button: + - `RPN83REG` (storage registers) + - `RPN83SAV` (MODE settings) + - `RPN83STA` (STAT registers) + - `RPN83STK` (RPN stack) + +When RPN83P restarts, those appVars will be recreated with a completely clean +slate. + ## Future Enhancements Moved to [FUTURE.md](FUTURE.md). diff --git a/docs/USER_GUIDE_BASE.md b/docs/USER_GUIDE_BASE.md index c70dc43c..1b2f2a4a 100644 --- a/docs/USER_GUIDE_BASE.md +++ b/docs/USER_GUIDE_BASE.md @@ -5,7 +5,7 @@ allow numbers to be converted between 4 different bases (DEC, HEX, OCT, and BIN) and support various arithmetic and bitwise operations similar to the HP-16C. It has been extracted from [USER_GUIDE.md](USER_GUIDE.md) due to its length. -**Version**: 0.12.0 (2024-06-24) +**Version**: 1.0.0 (2024-07-19) **Parent Document**: [USER_GUIDE.md](USER_GUIDE.md) diff --git a/docs/USER_GUIDE_COMPLEX.md b/docs/USER_GUIDE_COMPLEX.md index b15bd966..dd7c324d 100644 --- a/docs/USER_GUIDE_COMPLEX.md +++ b/docs/USER_GUIDE_COMPLEX.md @@ -10,7 +10,7 @@ independent of each other. For example, a complex number can be entered in rectangular form, even if the current display mode is polar form. Internally, complex numbers are *always* stored in rectangular format. -**Version**: 0.12.0 (2024-06-24) +**Version**: 1.0.0 (2024-07-19) **Parent Document**: [USER_GUIDE.md](USER_GUIDE.md) @@ -89,8 +89,8 @@ For example, the number `1-2i` would be entered like this: Notice that the RPN83P follows the convention used by the HP-35s in rendering the complex number with an imaginary `i` delimiter between the two components. -The negative sign on the `-2` appears *after* the `i`, because it is a delimiter -not a multiplier of the number `-2`. +The negative sign on the `-2` appears *after* the `i`, because `i` is a +delimiter not a multiplier of the number `-2`. Pressing `2ND LINK` on a complex number performs the *reverse* operation. The complex number is broken up into its real components, with the real part going diff --git a/docs/USER_GUIDE_DATE.md b/docs/USER_GUIDE_DATE.md index 63d5bc00..d47c671c 100644 --- a/docs/USER_GUIDE_DATE.md +++ b/docs/USER_GUIDE_DATE.md @@ -27,7 +27,7 @@ These features were inspired by various datetime libraries: - C# [Noda Time](https://nodatime.org) library - Python [datetime](https://docs.python.org/3/library/datetime.html) library -**Version**: 0.12.0 (2024-06-24) +**Version**: 1.0.0 (2024-07-19) **Parent Document**: [USER_GUIDE.md](USER_GUIDE.md) diff --git a/docs/USER_GUIDE_STAT.md b/docs/USER_GUIDE_STAT.md index eaf5a3a1..3a0ffc72 100644 --- a/docs/USER_GUIDE_STAT.md +++ b/docs/USER_GUIDE_STAT.md @@ -3,7 +3,7 @@ This document describes the `STAT` functions of the RPN83P application which supports all statistical and curve fitting functionality of the HP-42S. -**Version**: 0.12.0 (2024-06-24) +**Version**: 1.0.0 (2024-07-19) **Parent Document**: [USER_GUIDE.md](USER_GUIDE.md) diff --git a/docs/USER_GUIDE_TVM.md b/docs/USER_GUIDE_TVM.md index 7e813621..857bfa5e 100644 --- a/docs/USER_GUIDE_TVM.md +++ b/docs/USER_GUIDE_TVM.md @@ -4,7 +4,7 @@ This document describes the `TVM` functions of the RPN83P application which solves the Time Value of Money equation. It has been extracted from [USER_GUIDE.md](USER_GUIDE.md) due to its length. -**Version**: 0.12.0 (2024-06-24) +**Version**: 1.0.0 (2024-07-19) **Parent Document**: [USER_GUIDE.md](USER_GUIDE.md) @@ -301,8 +301,8 @@ original menu with the addition of a question mark (e.g. `WSIZ` and `WSZ?`). This helps with discovery because each function is directly shown through the menu system, with no hidden features. But there are so many TVM variables and parameters, that adding the `?` variant of all those menu buttons would have -made the menu rows too cluttered and hard to navigate. Currently (v0.12.0), the -TVM submenu is the only place where the `2ND` button is used for hidden menu +made the menu rows too cluttered and hard to navigate. Currently, the TVM +submenu is the only place where the `2ND` button is used for hidden menu functionality.) ## TVM Examples diff --git a/docs/images/help-page-1.png b/docs/images/help-page-1.png index fd1a500c..38be94ed 100644 Binary files a/docs/images/help-page-1.png and b/docs/images/help-page-1.png differ diff --git a/docs/images/input-clear-1.png b/docs/images/input-clear-1.png index 44419de5..91fd6849 100644 Binary files a/docs/images/input-clear-1.png and b/docs/images/input-clear-1.png differ diff --git a/docs/images/input-clear-2.png b/docs/images/input-clear-2.png index 2b68cfba..df55c81b 100644 Binary files a/docs/images/input-clear-2.png and b/docs/images/input-clear-2.png differ diff --git a/docs/images/input-clear-3.png b/docs/images/input-clear-3.png index 757e7821..6eb6e297 100644 Binary files a/docs/images/input-clear-3.png and b/docs/images/input-clear-3.png differ diff --git a/docs/images/input-clear-4.png b/docs/images/input-clear-4.png index b0a257a0..df55c81b 100644 Binary files a/docs/images/input-clear-4.png and b/docs/images/input-clear-4.png differ diff --git a/docs/images/input-clear-5.png b/docs/images/input-clear-5.png new file mode 100644 index 00000000..099f4d90 Binary files /dev/null and b/docs/images/input-clear-5.png differ diff --git a/docs/images/input-clear-6.png b/docs/images/input-clear-6.png new file mode 100644 index 00000000..f949d626 Binary files /dev/null and b/docs/images/input-clear-6.png differ diff --git a/docs/images/input-clear-7.png b/docs/images/input-clear-7.png new file mode 100644 index 00000000..df55c81b Binary files /dev/null and b/docs/images/input-clear-7.png differ diff --git a/docs/images/input-clear-8.png b/docs/images/input-clear-8.png new file mode 100644 index 00000000..0ce6c777 Binary files /dev/null and b/docs/images/input-clear-8.png differ diff --git a/docs/images/input-clear-9.png b/docs/images/input-clear-9.png new file mode 100644 index 00000000..a2582485 Binary files /dev/null and b/docs/images/input-clear-9.png differ diff --git a/docs/images/menu-root-clr-2.png b/docs/images/menu-root-clr-2.png new file mode 100644 index 00000000..cccd5197 Binary files /dev/null and b/docs/images/menu-root-clr-2.png differ diff --git a/docs/images/menu-root-unit-7.png b/docs/images/menu-root-unit-7.png new file mode 100644 index 00000000..2726c254 Binary files /dev/null and b/docs/images/menu-root-unit-7.png differ diff --git a/docs/images/menu-root-unit-8.png b/docs/images/menu-root-unit-8.png new file mode 100644 index 00000000..bad19897 Binary files /dev/null and b/docs/images/menu-root-unit-8.png differ diff --git a/misc/cursorinfo/Makefile b/misc/cursorinfo/Makefile index b64f9149..196012a3 100644 --- a/misc/cursorinfo/Makefile +++ b/misc/cursorinfo/Makefile @@ -1,6 +1,6 @@ TARGETS := cursorinfo.8xp cursorinfo.8xk -SPASM_DIR := /home/brian/Downloads/TICalc/Z80/dev/spasm +SPASM_DIR := ../../../spasm SPASM_INC := $(SPASM_DIR)/inc SPASM := $(SPASM_DIR)/spasm diff --git a/misc/prime/Makefile b/misc/prime/Makefile new file mode 100644 index 00000000..8b6bb147 --- /dev/null +++ b/misc/prime/Makefile @@ -0,0 +1,18 @@ +TARGETS := prime.8xp validate.8xp + +SPASM_DIR := ../../../spasm +SPASM_INC := $(SPASM_DIR)/inc +SPASM := $(SPASM_DIR)/spasm +SPASM_FLAGS := -A -N -I $(SPASM_INC) \ + -DMOD_DEIX_BY_BC_NONRESTORING_CHUNK8_REGA_UNROLLED + +all: $(TARGETS) + +prime.8xp: prime.asm modu32u16.asm Makefile + $(SPASM) $(SPASM_FLAGS) $< $@ + +validate.8xp: validate.asm modu32u16.asm print.asm Makefile + $(SPASM) $(SPASM_FLAGS) $< $@ + +clean: + rm -f $(TARGETS) diff --git a/misc/prime/modu32u16.asm b/misc/prime/modu32u16.asm new file mode 100644 index 00000000..e0a8378a --- /dev/null +++ b/misc/prime/modu32u16.asm @@ -0,0 +1,1010 @@ +;----------------------------------------------------------------------------- +; Various implementations of the mod(u32,u16) function. +; +; The RESTORING division algorithm is fairly straightforward. See for example, +; https://tutorials.eeems.ca/Z80ASM/part4.htm. + +; The NONRESTORING algorithm is more obscure. The Wikipedia entry +; (https://en.wikipedia.org/wiki/Division_algorithm#Non-restoring_division) is +; impenetrable. A better explanation is the flowchart in this StackOverflow +; (https://stackoverflow.com/questions/12133810) post. However, there is a +; complexity when trying to implement that flowchart into a Z80 processor: the +; remainder (HL) requires an extra bit (17 bits total, instead of 16 bits) in +; addition to the Carry Flag (CF). So the extra bit of information gets encoded +; into 2 separate code paths, one for positive remainder and one for negative +; remainder. The resulting implementation looks twice as complex as the +; flowchart, but it's actually implementing the same thing. +; +; Benchmarks: +; - MOD_HLIX_BY_BC_RESTORING_MONOLITH (original): 20.5 s +; - MOD_HLIX_BY_BC_RESTORING_MONOLITH (remove 'jp'): 20.4 s +; - MOD_HLIX_BY_BC_RESTORING_UNROLLED: 19.5 s +; - MOD_HLIX_BY_BC_RESTORING_CHUNK8: 17.4 s +; - MOD_DEIX_BY_BC_RESTORING_CHUNK8: 15.6 s +; - MOD_DEIX_BY_BC_RESTORING_CHUNK8_REGA: 14.9 s +; - MOD_DEIX_BY_BC_RESTORING_CHUNK8_REGA_TAILLOOP: 15.8 s +; - MOD_DEIX_BY_BC_RESTORING_CHUNK8_REGA_UNROLLED: 13.4 s +; +; - MOD_HLIX_BY_BC_NONRESTORING_MONOLITH: 18.9 s +; - MOD_DEIX_BY_BC_NONRESTORING_CHUNK8: 14.4 s +; - MOD_DEIX_BY_BC_NONRESTORING_CHUNK8_REGA: 13.7 s +; - MOD_DEIX_BY_BC_NONRESTORING_CHUNK8_REGA_UNROLLED: 11.8 s +; - MOD_DEIX_BY_BC_NONRESTORING_CALC84MANIAC: 11.9 s +;----------------------------------------------------------------------------- + +#ifdef MOD_HLIX_BY_BC_RESTORING_MONOLITH +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: A, DE, IX +modDEIXByBC: + ex de, hl + call modHLIXByBC + ex de, hl + ret + +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - HL:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - DE:u16=remainder +; Destroys: A, HL, IX +modHLIXByBC: + ld de, 0 ; DE=remainder + ld a, 32 +modHLIXByBCLoop: + add ix, ix + adc hl, hl + ex de, hl ; HL=remainder; DE=dividend + adc hl, hl + jr c, modHLIXByBCOverflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modHLIXByBCNextBit + add hl, bc ; revert the subtraction +modHLIXByBCNextBit: + ex de, hl ; DE=remainder; HL=dividend + dec a + jp nz, modHLIXByBCLoop + ret +modHLIXByBCOverflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + jp modHLIXByBCNextBit +#endif + +;----------------------------------------------------------------------------- + +#ifdef MOD_HLIX_BY_BC_RESTORING_UNROLLED +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: A, DE, IX +modDEIXByBC: + ex de, hl + call modHLIXByBC + ex de, hl + ret + +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - HL:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - DE:u16=remainder +; Destroys: A, HL, IX +modHLIXByBC: + ld de, 0 ; DE=remainder + call modHLIXByBCSansSpill + ; if divisor < $8000: spill into carry cannot occur + bit 7, b + jr nz, modHLIXByBCWithSpill + ; [[fallthrough]] +modHLIXByBCSansSpill: + ld a, 16 +modHLIXByBCSansSpillLoop: + add ix, ix + adc hl, hl + ex de, hl ; HL=remainder; DE=dividend + adc hl, hl + sbc hl, bc ; remainder -= divisor + jp nc, modHLIXByBCSansSpillNextBit + add hl, bc ; revert the subtraction +modHLIXByBCSansSpillNextBit: + ex de, hl ; DE=remainder; HL=dividend + dec a + jp nz, modHLIXByBCSansSpillLoop + ret +; +modHLIXByBCWithSpill: + ld a, 16 +modHLIXByBCWithSpillLoop: + add ix, ix + adc hl, hl + ex de, hl ; HL=remainder; DE=dividend + adc hl, hl + jr c, modHLIXByBCWithSpillOverflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modHLIXByBCWithSpillNextBit + add hl, bc ; revert the subtraction +modHLIXByBCWithSpillNextBit: + ex de, hl ; DE=remainder; HL=dividend + dec a + jp nz, modHLIXByBCWithSpillLoop + ret +modHLIXByBCWithSpillOverflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + ; duplicate modHLIXByBCWithSpillNextBit to save a 'jp' + ex de, hl ; DE=remainder; HL=dividend + dec a + jp nz, modHLIXByBCWithSpillLoop + ret +#endif + +;----------------------------------------------------------------------------- + +#ifdef MOD_HLIX_BY_BC_RESTORING_CHUNK8 +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: A, DE, IX +modDEIXByBC: + ex de, hl + call modHLIXByBC + ex de, hl + ret + +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - HL:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - DE:u16=remainder +; Destroys: A, HL, IX +modHLIXByBC: + ld de, 0 ; DE=remainder + call modHLIXByBC8 + ld h, l + call modHLIXByBC8 + push ix + pop hl + call modHLIXByBC8 + ld h, l + ; [[fallthrough]] +; Input: H:u8=high 8 bits of the shifted dividend +; Output: DE:u16=remainder +modHLIXByBC8: + ld a, 8 +modHLIXByBCLoop: + sla h + ex de, hl ; HL=remainder; DE=dividend + adc hl, hl + jr c, modHLIXByBCOverflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modHLIXByBCNextBit + add hl, bc ; revert the subtraction +modHLIXByBCNextBit: + ex de, hl ; DE=remainder; HL=dividend + dec a + jp nz, modHLIXByBCLoop + ret +modHLIXByBCOverflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + jp modHLIXByBCNextBit +#endif + +;----------------------------------------------------------------------------- + +#ifdef MOD_DEIX_BY_BC_RESTORING_CHUNK8 +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: A, DE, IX +modDEIXByBC: + ld hl, 0 ; HL=remainder + call modDEIXByBC8 + ld d, e + call modDEIXByBC8 + push ix + pop de + call modDEIXByBC8 + ld d, e + ; [[fallthrough]] +; Input: D:u8=high 8 bits of the shifted dividend +; Output: HL:u16=remainder +modDEIXByBC8: + ld a, 8 +modDEIXByBCLoop: + sla d + adc hl, hl + jr c, modDEIXByBCOverflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modDEIXByBCNextBit + add hl, bc ; revert the subtraction +modDEIXByBCNextBit: + dec a + jp nz, modDEIXByBCLoop + ret +modDEIXByBCOverflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + dec a + jp nz, modDEIXByBCLoop + ret +#endif + +;----------------------------------------------------------------------------- + +#ifdef MOD_DEIX_BY_BC_RESTORING_CHUNK8_REGA +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: A, DE, IX +modDEIXByBC: + ld hl, 0 ; HL=remainder + ld a, d + call modDEIXByBC8 + ld a, e + call modDEIXByBC8 + push ix + pop de + ld a, d + call modDEIXByBC8 + ld a, e + ; [[fallthrough]] +; Input: A:u8=high 8 bits of the shifted dividend +; Output: HL:u16=remainder +modDEIXByBC8: + ld d, 8 +modDEIXByBCLoop: + rla + adc hl, hl + jr c, modDEIXByBCOverflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modDEIXByBCNextBit + add hl, bc ; revert the subtraction +modDEIXByBCNextBit: + dec d + jp nz, modDEIXByBCLoop + ret +modDEIXByBCOverflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + dec d + jp nz, modDEIXByBCLoop + ret +#endif + +;----------------------------------------------------------------------------- + +#ifdef MOD_DEIX_BY_BC_RESTORING_CHUNK8_REGA_TAILLOOP +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: A, DE, IX +modDEIXByBC: + ld hl, 0 ; HL=remainder + ld a, d + call modDEIXByBC8 + ld a, e + call modDEIXByBC8 + push ix + pop de + ld a, d + call modDEIXByBC8 + ld a, e + ; [[fallthrough]] +; Input: A:u8=high 8 bits of the shifted dividend +; Output: HL:u16=remainder +modDEIXByBC8: + call modDEIXByBCTail4 +modDEIXByBCTail4: + call modDEIXByBCTail2 +modDEIXByBCTail2: + call modDEIXByBCTail1 +modDEIXByBCTail1: + rla + adc hl, hl + jr c, modDEIXByBCOverflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + ret nc + add hl, bc ; revert the subtraction + ret +modDEIXByBCOverflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + ret +#endif + +;----------------------------------------------------------------------------- + +#ifdef MOD_DEIX_BY_BC_RESTORING_CHUNK8_REGA_UNROLLED +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: A, DE, IX +modDEIXByBC: + ld hl, 0 ; HL=remainder + ld a, d + call modDEIXByBC8 + ld a, e + call modDEIXByBC8 + push ix + pop de + ld a, d + call modDEIXByBC8 + ld a, e + ; [[fallthrough]] +; Input: A:u8=high 8 bits of the shifted dividend +; Output: HL:u16=remainder +modDEIXByBC8: + rla + adc hl, hl + jr c, modDEIXByBCBit0Overflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modDEIXByBCBit1 + add hl, bc ; revert the subtraction + jp modDEIXByBCBit1 +modDEIXByBCBit0Overflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + ; +modDEIXByBCBit1: + rla + adc hl, hl + jr c, modDEIXByBCBit1Overflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modDEIXByBCBit2 + add hl, bc ; revert the subtraction + jp modDEIXByBCBit2 +modDEIXByBCBit1Overflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + ; +modDEIXByBCBit2: + rla + adc hl, hl + jr c, modDEIXByBCBit2Overflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modDEIXByBCBit3 + add hl, bc ; revert the subtraction + jp modDEIXByBCBit3 +modDEIXByBCBit2Overflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + ; +modDEIXByBCBit3: + rla + adc hl, hl + jr c, modDEIXByBCBit3Overflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modDEIXByBCBit4 + add hl, bc ; revert the subtraction + jp modDEIXByBCBit4 +modDEIXByBCBit3Overflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + ; +modDEIXByBCBit4: + rla + adc hl, hl + jr c, modDEIXByBCBit4Overflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modDEIXByBCBit5 + add hl, bc ; revert the subtraction + jp modDEIXByBCBit5 +modDEIXByBCBit4Overflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + ; +modDEIXByBCBit5: + rla + adc hl, hl + jr c, modDEIXByBCBit5Overflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modDEIXByBCBit6 + add hl, bc ; revert the subtraction + jp modDEIXByBCBit6 +modDEIXByBCBit5Overflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + ; +modDEIXByBCBit6: + rla + adc hl, hl + jr c, modDEIXByBCBit6Overflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modDEIXByBCBit7 + add hl, bc ; revert the subtraction + jp modDEIXByBCBit7 +modDEIXByBCBit6Overflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + ; +modDEIXByBCBit7: + rla + adc hl, hl + jr c, modDEIXByBCBit7Overflow ; remainder overflowed, so subtract + sbc hl, bc ; remainder -= divisor + jp nc, modDEIXByBCBit8 + add hl, bc ; revert the subtraction + jp modDEIXByBCBit8 +modDEIXByBCBit7Overflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + ; +modDEIXByBCBit8: + ret +#endif + +;============================================================================ + +#ifdef MOD_HLIX_BY_BC_NONRESTORING_MONOLITH +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: A, DE, IX +modDEIXByBC: + ex de, hl + call modHLIXByBC + ex de, hl + ret + +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - HL:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - DE:u16=remainder +; Destroys: A, HL, IX +modHLIXByBC: + ld de, 0 ; DE=remainder + ld a, 32 + or a ; CF=0 +modHLIXByBCLoop: + jp c, modHLIXByBCNeg +modHLIXByBCPos: + add ix, ix + adc hl, hl + ex de, hl ; HL=remainder; DE=dividend + adc hl, hl + jr c, modHLIXByBCPosOverflow + sbc hl, bc ; remainder -= divisor + ex de, hl ; HL=dividend; DE=remainder + dec a + jp nz, modHLIXByBCLoop + jp modHLIXByBCEnd +modHLIXByBCPosOverflow: + or a ; CF=0 + sbc hl, bc ; remainder -= divisor + ex de, hl ; HL=dividend; DE=remainder + dec a + jp nz, modHLIXByBCPos + or a ; always clear CF + jp modHLIXByBCEnd +modHLIXByBCNeg: + add ix, ix + adc hl, hl + ex de, hl ; HL=remainder; DE=dividend + adc hl, hl + jp c, modHLIXByBCNegOverflow +modHLIXByBCNegPos: + ; Left-shift of A generated CF=0. Add M will always generate a negative + ; remainder. + add hl, bc ; remainder += divisor + ex de, hl ; HL=dividend; DE=remainder + dec a + jp nz, modHLIXByBCNeg + scf ; set the CF=1 before returning to indicate negative remainder + jp modHLIXByBCEnd +modHLIXByBCNegOverflow: + ; Left-shift of A generated CF=1. Add M, then look at the CF again. If it + ; transitions to a 1 again, then the resulting remainder becomes positive, + ; so set CF=0 for the next iteration. + add hl, bc ; remainder += divisor + ex de, hl ; HL=dividend; DE=remainder + ccf ; CF=0 if positive, 1 if negative + dec a + jp nz, modHLIXByBCLoop + ; [[fallthrough]] +modHLIXByBCEnd: + ; if the remainder is negative, restore it + ret nc + ex de, hl + add hl, bc + ex de, hl + ret +#endif + +;----------------------------------------------------------------------------- + +#ifdef MOD_DEIX_BY_BC_NONRESTORING_CHUNK8 +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: A, DE, IX +modDEIXByBC: + ld hl, 0 ; HL=remainder + or a ; CF=0 + call modDEIXByBC8 + ld d, e + call modDEIXByBC8 + push ix + pop de + call modDEIXByBC8 + ld d, e + call modDEIXByBC8 + ; if the remainder is negative, restore it + ret nc + add hl, bc + ret +; Description: Process an 8-bit chunk of the dividend. +; Input: +; - D:u8=8-bit chunks of the dividend +; Output: +; - HL:u16=remainder +; - CF=1 if remainder is negative +modDEIXByBC8: + ld a, 8 +modDEIXByBC8Loop: + jp c, modDEIXByBC8Neg +modDEIXByBC8Pos: + sla d + adc hl, hl + jp c, modDEIXByBC8PosOverflow + sbc hl, bc ; remainder -= divisor + dec a + jp nz, modDEIXByBC8Loop + ret +modDEIXByBC8PosOverflow: + ; Left-shift of A generated CF=1. Subtract M will always produce a positive + ; remainder. + or a + sbc hl, bc ; remainder -= divisor + dec a + jp nz, modDEIXByBC8Pos + or a ; set CF=0 before returning to indicate positive remainder + ret +modDEIXByBC8Neg: + sla d + adc hl, hl + jp c, modDEIXByBC8NegOverflow + ; Left-shift of A generated CF=0. Add M will always generate a negative + ; remainder. + add hl, bc ; remainder += divisor + dec a + jp nz, modDEIXByBC8Neg + scf ; set CF=1 returning to indicate negative remainder + ret +modDEIXByBC8NegOverflow: + ; Left-shift of A generated CF=1. Add M generates a positive remainder + ; if CF=1 (which cascades to change the implicit sign bit to 0), or a + ; negative remainder if CF=0 (retains the implicit sign bit of 1). We can + ; capture both conditions by inverting the CF using CCF. + add hl, bc ; remainder += divisor + ccf + dec a + jp nz, modDEIXByBC8Loop + ret +#endif + +;----------------------------------------------------------------------------- + +#ifdef MOD_DEIX_BY_BC_NONRESTORING_CHUNK8_REGA +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: A, D +; Preserves: BC, E, IX +modDEIXByBC: + ld hl, 0 ; HL=remainder + or a ; CF=0 + ld a, d + call modDEIXByBC8 + ld a, e + call modDEIXByBC8 + push ix + pop de + ld a, d + call modDEIXByBC8 + ld a, e + call modDEIXByBC8 + ; if the remainder is negative, restore it + ret nc + add hl, bc + ret +; Description: Process an 8-bit chunk of the dividend. +; Input: +; - A:u8=8-bit chunks of the dividend +; Output: +; - HL:u16=remainder +; - CF=1 if remainder is negative +; Destroys: D +modDEIXByBC8: + ld d, 8 +modDEIXByBC8Loop: + jp c, modDEIXByBC8Neg +modDEIXByBC8Pos: + ; We are here if the remainder A is positive. + rla + adc hl, hl + jp c, modDEIXByBC8PosOverflow + sbc hl, bc ; remainder -= divisor + dec d + jp nz, modDEIXByBC8Loop + ret +modDEIXByBC8PosOverflow: + ; Left-shift of A generated CF=1. Subtract M will always produce a positive + ; remainder. + or a + sbc hl, bc ; remainder -= divisor + dec d + jp nz, modDEIXByBC8Pos + or a ; set CF=0 before returning to indicate positive remainder + ret +modDEIXByBC8Neg: + ; We are here if the remainder A is negative. + rla + adc hl, hl + jp c, modDEIXByBC8NegOverflow + ; Left-shift of A generated CF=0. Add M will always generate a negative + ; remainder. + add hl, bc ; remainder += divisor + dec d + jp nz, modDEIXByBC8Neg + scf ; set CF=1 before returning to indicate negative remainder + ret +modDEIXByBC8NegOverflow: + ; Left-shift of A generated CF=1. Add M generates a positive remainder + ; if CF=1 (which cascades to change the implicit sign bit to 0), or a + ; negative remainder if CF=0 (retains the implicit sign bit of 1). We can + ; capture both conditions by inverting the CF using CCF. + add hl, bc ; remainder += divisor + ccf + dec d + jp nz, modDEIXByBC8Loop + ret +#endif + +;----------------------------------------------------------------------------- + +#ifdef MOD_DEIX_BY_BC_NONRESTORING_CHUNK8_REGA_UNROLLED +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: AF +; Preserves: BC, DE, IX +modDEIXByBC: + ld hl, 0 ; HL=remainder + or a ; CF=0 + ld a, d + call modDEIXByBC8 + ld a, e + call modDEIXByBC8 + push ix + pop de + ld a, d + call modDEIXByBC8 + ld a, e + call modDEIXByBC8 + ; if the remainder is negative, restore it + ret nc + add hl, bc + ret +; Description: Process an 8-bit chunk of the dividend with the 8 loop +; iterations unrolled for performance. +; Input: +; - A:u8=8-bit chunks of the dividend +; Output: +; - HL:u16=remainder +modDEIXByBC8: +modDEIXByBC8Bit0: + jp c, modDEIXByBC8Bit0Neg +modDEIXByBC8Bit0Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit0PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit1 +modDEIXByBC8Bit0PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit1Pos +modDEIXByBC8Bit0Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit0NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit1Neg +modDEIXByBC8Bit0NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit1: + jp c, modDEIXByBC8Bit1Neg +modDEIXByBC8Bit1Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit1PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit2 +modDEIXByBC8Bit1PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit2Pos +modDEIXByBC8Bit1Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit1NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit2Neg +modDEIXByBC8Bit1NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit2: + jp c, modDEIXByBC8Bit2Neg +modDEIXByBC8Bit2Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit2PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit3 +modDEIXByBC8Bit2PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit3Pos +modDEIXByBC8Bit2Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit2NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit3Neg +modDEIXByBC8Bit2NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit3: + jp c, modDEIXByBC8Bit3Neg +modDEIXByBC8Bit3Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit3PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit4 +modDEIXByBC8Bit3PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit4Pos +modDEIXByBC8Bit3Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit3NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit4Neg +modDEIXByBC8Bit3NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit4: + jp c, modDEIXByBC8Bit4Neg +modDEIXByBC8Bit4Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit4PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit5 +modDEIXByBC8Bit4PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit5Pos +modDEIXByBC8Bit4Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit4NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit5Neg +modDEIXByBC8Bit4NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit5: + jp c, modDEIXByBC8Bit5Neg +modDEIXByBC8Bit5Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit5PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit6 +modDEIXByBC8Bit5PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit6Pos +modDEIXByBC8Bit5Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit5NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit6Neg +modDEIXByBC8Bit5NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit6: + jp c, modDEIXByBC8Bit6Neg +modDEIXByBC8Bit6Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit6PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit7 +modDEIXByBC8Bit6PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit7Pos +modDEIXByBC8Bit6Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit6NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit7Neg +modDEIXByBC8Bit6NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit7: + jp c, modDEIXByBC8Bit7Neg +modDEIXByBC8Bit7Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit7PosOverflow + sbc hl, bc ; remainder -= divisor + ret +modDEIXByBC8Bit7PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + or a ; always clear CF + ret +modDEIXByBC8Bit7Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit7NegOverflow + add hl, bc ; remainder += divisor + scf + ret +modDEIXByBC8Bit7NegOverflow: + add hl, bc ; remainder += divisor + ccf +; + ret +#endif + +#ifdef MOD_DEIX_BY_BC_NONRESTORING_CALC84MANIAC +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend is +; divided by a u16 divisor. +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: AF, DE +modDEIXByBC: + ld hl, 0 ; HL=remainder + scf ; Initialize input CF + call modBCIter16 + push ix + pop de +; Process the dividend bits in DE +; Input: HL=remainder, DE=dividend bits, CF is set +; Output: HL=remainder, A=0, CF is set +modBCIter16: + ld a, d + call modBCIter8 + ld a, e +; Process the dividend bits in A +; Input: HL=remainder, A=dividend bits, CF is set +; Output: HL=remainder, A=0, CF is set +modBCIter8: + ; CF is set only on the first iteration +modBCPositiveLoop: + adc a, a ; Always sets CF when the result is zero + ret z +modBCPositiveContinue: + adc hl, hl + jr c, modBCOverflow ; remainder overflowed, so subtract +modBCNoOverflow: + sbc hl, bc ; HL(remainder) -= divisor + jp nc, modBCPositiveLoop +modBCNegativeLoop: + add a, a + jr z, modBCNegativeEnd +modBCNegativeContinue: + adc hl, hl + jr nc, modBCUnderflow ; remainder underflowed, so add + add hl, bc ; HL(remainder) += divisor + jp nc, modBCNegativeLoop + add a, a ; Always sets CF when the result is zero + ret z + adc hl, hl + jp nc, modBCNoOverflow +modBCOverflow: + or a ;reset CF + sbc hl, bc ; HL(remainder) -= divisor + add a, a ; Always sets CF when the result is zero + jp nz, modBCPositiveContinue + ret +; +modBCUnderflow: + add hl, bc ; HL(remainder) += divisor + add a, a + jp nz, modBCNegativeContinue +modBCNegativeEnd: + add hl, bc ; Restore remainder, always sets CF + ret +#endif diff --git a/misc/prime/prime.asm b/misc/prime/prime.asm new file mode 100644 index 00000000..e79ee3b5 --- /dev/null +++ b/misc/prime/prime.asm @@ -0,0 +1,41 @@ +;----------------------------------------------------------------------------- +; Test the performance of the modDEIXByBC() routine selected by the `-D` flag +; in the Makefile by searching for the prime factor of a large number +; (input=65521*65521). +; +; On a TI-83+/84+, this program runs at 6 MHz by default. The main loop +; iterates through every odd number between 3 and 65521 in steps of 2 (i.e. +; 32759 iterations). According to the benchmarks listed in modu32u16.asm, the +; reference implementation (MOD_HLIX_BY_BC_RESTORING_MONOLITH) takes 20.5 +; seconds. The fastest (MOD_DEIX_BY_BC_NONRESTORING_CHUNK8_REGA_UNROLLED) takes +; 11.8 seconds. +;----------------------------------------------------------------------------- + +.nolist +#include "ti83plus.inc" +.list +.org userMem - 2 +.db t2ByteTok, tasmCmp + +input equ (65521*65521) +inputHigh16 equ ((input & $ffff0000) >> 16) +inputLow16 equ (input & $ffff) + +main: + bcall(_homeup) + bcall(_ClrLCDFull) + ld bc, 3 ; check all odd numbers from 3 to 65521 +primeLoop: + ld de, inputHigh16 + ld ix, inputLow16 + call modDEIXByBC ; HL=remainder + inc bc + inc bc + ld a, h + or l + jp nz, primeLoop + ret + +#include "modu32u16.asm" + +.end diff --git a/misc/prime/print.asm b/misc/prime/print.asm new file mode 100644 index 00000000..89ce1ca4 --- /dev/null +++ b/misc/prime/print.asm @@ -0,0 +1,72 @@ +;----------------------------------------------------------------------------- +; Print routines. +;----------------------------------------------------------------------------- + +; Description: Print the 4 bytes pointed by HL in little-endian format at the +; current curCol/CurRow. +; Input: HL: pointer to 4 bytes +; Destroys: None +PrintU32AsHex: + push af + push bc + push de + push hl + ; Prep loop + ld b, 4 +printU32AsHexLoop: + ld a, (hl) + call printUnsignedAAsHex + inc hl + ld a, ' ' + bcall(_PutC) ; preserves B + djnz printU32AsHexLoop + pop hl + pop de + pop bc + pop af + ret + +; Description: Print HL as a 4-digit hex, little-endian format. +; Destroys: A +; Preserves: BC, DE, HL +PrintUnsignedHLAsHex: + ld a, l + call printUnsignedAAsHex + ld a, ' ' + bcall(_PutC) ; preserves B + ld a, h + call printUnsignedAAsHex + ret + +;----------------------------------------------------------------------------- + +; Description: Print A has a 2-digit hex. +; Destroys: A +; Preserves: BC, DE, HL +printUnsignedAAsHex: + push af + srl a + srl a + srl a + srl a + call convertAToChar + bcall(_PutC) + ; + pop af + and $0F + call convertAToChar + bcall(_PutC) + ret + +; Description: Convert A into an Ascii Char ('0'-'9','A'-'F'). +; Destroys: A +; Preserves: BC, DE, HL +convertAToChar: + cp 10 + jr c, convertAToCharDecimal + sub 10 + add a, 'A' + ret +convertAToCharDecimal: + add a, '0' + ret diff --git a/misc/prime/validate.asm b/misc/prime/validate.asm new file mode 100644 index 00000000..037c55e7 --- /dev/null +++ b/misc/prime/validate.asm @@ -0,0 +1,205 @@ +;----------------------------------------------------------------------------- +; Validate a particular mod(u32,u16) routine against the reference +; implementation (modHLIXByBCRef()). Examine a bunch of prime numbers < 2^32 +; and verify that the modDEIXByBC() routine gives the same answer as the +; reference. +; +; The Makefile rule for 'validate.8xp' should contain the `-D` define that +; identifies the specific mod(u32,u16) routine that is being tested. +;----------------------------------------------------------------------------- + +.nolist +#include "ti83plus.inc" +.list +.org userMem - 2 +.db t2ByteTok, tasmCmp + +;input equ (4001*4001) +;input equ (10007*10007) +;input equ (19997*19997) +;input equ (40013*40013) +input equ (65521*65521) +inputHigh16 equ ((input & $ffff0000) >> 16) +inputLow16 equ (input & $ffff) + +main: + ; Initialize OS. + call setFastSpeed + bcall(_homeup) + bcall(_ClrLCDFull) + + ; Initialize app. + ld hl, inputLow16 + ld (OP1), hl + ld hl, inputHigh16 + ld (OP1+2), hl + ; [[fallthrough]] + +; Description: Validate modDEIXByC() using the u32 at OP1, starting with 3 +; until a prime factor is found. +; Input: +; - OP1:u32=dividend +validate: + ld bc, 3 ; check all odd numbers from 3 to a prime factor of OP1 +validateLoop: + ; Check for ON/Break + bit onInterrupt, (iy + onFlags) + jr nz, validateBreak + ; + ld hl, (OP1+2) ; inputHigh16 + ld ix, (OP1) ; inputLow16 + call modHLIXByBCRef ; DE=remainder + push de ; stack=[expected] + ; + ld de, inputHigh16 + ld ix, inputLow16 + call modDEIXByBC ; HL=remainder + pop de ; stack=[]; DE=expected + ; compare the result + or a + push hl + sbc hl, de + pop hl + jr nz, validateFailed + ; + inc bc + inc bc + ld a, h + or l + jp nz, validateLoop + ; [[fallthrough]] + +validateOk: + ld hl, msgOk + bcall(_PutS) + bcall(_NewLine) + ret + +validateBreak: + ld hl, msgBreak + bcall(_PutS) + bcall(_NewLine) + ret + +; Description: Print failure message. +; Input: +; - OP1:u32=dividend +; - BC:u16=divisor +; - DE:u16=expected +; - HL:u16=observed +validateFailed: + push hl ; stack=[observed] + ; + ld hl, msgFailed + bcall(_PutS) + bcall(_NewLine) + ; + ld hl, msgDividend + bcall(_PutS) + ld hl, OP1 + call PrintU32AsHex + bcall(_NewLine) + ; + ld hl, msgDivisor + bcall(_PutS) + ld h, b + ld l, c + call PrintUnsignedHLAsHex + bcall(_NewLine) + ; + ld hl, msgExpected + bcall(_PutS) + ld h, d + ld l, e + call PrintUnsignedHLAsHex + bcall(_NewLine) + ; + ld hl, msgObserved + bcall(_PutS) + pop hl ; stack=[]; HL=observed + call PrintUnsignedHLAsHex + bcall(_NewLine) + ret + +msgOk: + .db "Ok", 0 + +msgFailed: + .db "Failed!", 0 + +msgBreak: + .db "Break", 0 + +msgDividend: + .db "D:", 0 + +msgDivisor: + .db "V:", 0 + +msgExpected: + .db "Exp:", 0 + +msgObserved: + .db "Obs:", 0 + +;----------------------------------------------------------------------------- + +; Decription: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend is +; divided by a u16 divisor. This is the reference implementation, which uses +; the same algorithm as MOD_HLIX_BY_BC_RESTORING_MONOLITH. +; Input: +; - HL:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - DE:u16=remainder +; Destroys: A, HL, IX +modHLIXByBCRef: + ld de, 0 ; DE=remainder + ld a, 32 +modHLIXByBCRefLoop: + add ix, ix + adc hl, hl + ex de, hl ; HL=remainder; DE=dividend + adc hl, hl + jr c, modHLIXByBCRefOverflow ; remainder overflowed, so substract + sbc hl, bc ; remainder -= divisor + jp nc, modHLIXByBCRefNextBit + add hl, bc ; revert the subtraction +modHLIXByBCRefNextBit: + ex de, hl ; DE=remainder; HL=dividend + dec a + jp nz, modHLIXByBCRefLoop + ret +modHLIXByBCRefOverflow: + or a ; reset CF + sbc hl, bc ; remainder -= divisor + jp modHLIXByBCRefNextBit + +;----------------------------------------------------------------------------- + +; Description: Set CPU speed to 15 MHz on supported hardware (83+SE, 84+, +; 84+SE) on OS 1.13 or higher. See TI-83 Plus SDK reference for SetExSpeed(). +setFastSpeed: + call checkOS113 ; CF=0 if OS>=1.13 + ret c + ld a, $ff + bcall(_SetExSpeed) + ret + +; Description: Check if OS is >= 1.13. +; Output: CF=0 if OS >= 1.13; 1 otherwise +checkOS113: + bcall(_GetBaseVer) ; OS version in A (major), B (minor) + cp 1 ; CF=1 if major < 1; CF=0 and ZF=0 if major > 1 + ret nz ; returns if major >= 2 or < 1 + ld a, b + cp 13 ; CF=0 if minor version > 13, otherwise CF=1 + ret + +;----------------------------------------------------------------------------- + +#include "modu32u16.asm" +#include "print.asm" + +.end diff --git a/src/Makefile b/src/Makefile index 27fb776e..98fc3862 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,19 +3,21 @@ TARGETS := rpn83p.8xk rpn83p.8xp rpn83p.lst SPASM_DIR := ../../spasm SPASM_INC := $(SPASM_DIR)/inc SPASM := $(SPASM_DIR)/spasm +SPASM_FLAGS := -A -I $(SPASM_INC) -N +#SPASM_FLAGS := -A -I $(SPASM_INC) -N -DDEBUG + SRCS := $(wildcard *.asm) # TI Flash app. Use -DDEBUG to activate functions in debug1.asm. rpn83p.8xk: $(SRCS) Makefile - $(SPASM) -I $(SPASM_INC) -N rpn83p.asm $@ - @#$(SPASM) -DDEBUG -I $(SPASM_INC) -N rpn83p.asm $@ + $(SPASM) $(SPASM_FLAGS) rpn83p.asm $@ # TI assembly program. No longer works because the program is > 8 kiB. #rpn83p.8xp: $(SRCS) Makefile -# $(SPASM) -I $(SPASM_INC) -N rpn83p.asm $@ +# $(SPASM) $(SPASM_FLAGS) rpn83p.asm $@ rpn83p.lst: $(SRCS) Makefile rpn83p.8xk - $(SPASM) -I $(SPASM_INC) -N -T rpn83p.asm rpn83p.8xk + $(SPASM) $(SPASM_FLAGS) -T rpn83p.asm rpn83p.8xk menudef.asm: menudef.txt ../tools/compilemenu.py ../tools/compilemenu.py -o $@ $< diff --git a/src/common.asm b/src/common.asm index 8e40e5c4..7666c961 100644 --- a/src/common.asm +++ b/src/common.asm @@ -17,7 +17,7 @@ ; - HL:(const void* const*)=pointer to a list of pointers to subroutines ; Output: depends on the routine called ; Destroys: DE, HL, and others depending on the routine called -jumpAofHL: +jumpAOfHL: call getString ; [[fallthrough]] diff --git a/src/const.asm b/src/const.asm index e91959c6..4bcdeb58 100644 --- a/src/const.asm +++ b/src/const.asm @@ -116,17 +116,11 @@ op1SetMaxFloat: ;----------------------------------------------------------------------------- -; Description: Set OP1 to StandardGravity. -; Destroys: all, HL -; op1SetStandardGravity: -; ld hl, constStandardGravity -; jp move9ToOp1 - ; Description: Set OP2 to StandardGravity. ; Destroys: all, HL -; op2SetStandardGravity: -; ld hl, constStandardGravity -; jp move9ToOp2 +op2SetStandardGravity: + ld hl, constStandardGravity + jp move9ToOp2 ;----------------------------------------------------------------------------- @@ -202,7 +196,7 @@ op2SetKwPerHp: ;----------------------------------------------------------------------------- -; Description: Set OP2 to KwPerHp +; Description: Set OP2 to HpaPerInHg ; Destroys: all, HL op2SetHpaPerInhg: ld hl, constHpaPerInhg @@ -210,6 +204,14 @@ op2SetHpaPerInhg: ;----------------------------------------------------------------------------- +; Description: Set OP2 to SqFtPerAcre +; Destroys: all, HL +op2SetSqFtPerAcre: + ld hl, constSqFtPerAcre + jp move9ToOp2 + +;----------------------------------------------------------------------------- + constM1: ; -1 .db $80, $80, $10, $00, $00, $00, $00, $00, $00 @@ -241,8 +243,8 @@ constEuler: ; 2.7182818284594(0452) constMaxFloat: ; 9.9999999999999E99 .db $00, $E3, $99, $99, $99, $99, $99, $99, $99 -; constStandardGravity: ; g_0 = 9.806 65 m/s^2, exact -; .db $00, $80, $98, $06, $65, $00, $00, $00, $00 +constStandardGravity: ; g_0 = 9.806 65 m/s^2, exact + .db $00, $80, $98, $06, $65, $00, $00, $00, $00 constKmPerMi: ; 1.609344 km/mi, exact .db $00, $80, $16, $09, $34, $40, $00, $00, $00 @@ -283,3 +285,8 @@ constKwPerHp: ; 0.745 699 871 582 270 22 kW/hp, approx ; = 33.863 886 403 41 hPa (exact) constHpaPerInhg: .db $00, $81, $33, $86, $38, $86, $40, $34, $10 + +; Number of square feet in one acre = 66*660 = 43560, see +; https://en.wikipedia.org/wiki/Acre. +constSqFtPerAcre: + .db $00, $84, $43, $56, $00, $00, $00, $00, $00 diff --git a/src/const2.asm b/src/const2.asm index 1b3d4806..6dfe40f1 100644 --- a/src/const2.asm +++ b/src/const2.asm @@ -29,6 +29,14 @@ op2Set1EM10PageTwo: ;----------------------------------------------------------------------------- +; Description: Set OP2 to 6e-5 +; Destroys: all, HL +op2Set6EM5PageTwo: + ld hl, const6EM5PageTwo + jp move9ToOp2PageTwo + +;----------------------------------------------------------------------------- + ; Description: Set OP2 to 1. ; Destroys: all, HL op2Set1PageTwo: @@ -115,9 +123,12 @@ constM50PageTwo: ; -50 const0PageTwo: ; 0.0 .db $00, $80, $00, $00, $00, $00, $00, $00, $00 -const1EM10PageTwo: ; 10^-10 +const1EM10PageTwo: ; 1E-10 .db $00, $76, $10, $00, $00, $00, $00, $00, $00 +const6EM5PageTwo: ; 6E-5 + .db $00, $7B, $60, $00, $00, $00, $00, $00, $00 + const1PageTwo: ; 1 .db $00, $80, $10, $00, $00, $00, $00, $00, $00 diff --git a/src/debug1.asm b/src/debug1.asm index 2ad0f2e6..7e2e1380 100644 --- a/src/debug1.asm +++ b/src/debug1.asm @@ -21,11 +21,11 @@ DebugInputBuf: push bc push de push hl - ld hl, (CurRow) + ld hl, (curRow) push hl ld hl, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl ld hl, inputBuf call putPSPageOne ld a, cursorCharAlt @@ -33,7 +33,7 @@ DebugInputBuf: bcall(_EraseEOL) pop hl - ld (CurRow), hl + ld (curRow), hl pop hl pop de pop bc @@ -51,11 +51,11 @@ DebugParseBuf: push bc push de push hl - ld hl, (CurRow) + ld hl, (curRow) push hl ld hl, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl ld hl, parseBuf call putPSPageOne ld a, cursorCharAlt @@ -63,7 +63,7 @@ DebugParseBuf: bcall(_EraseEOL) pop hl - ld (CurRow), hl + ld (curRow), hl pop hl pop de pop bc @@ -81,16 +81,16 @@ DebugString: push bc push de push hl - ld de, (CurRow) + ld de, (curRow) push de ld de, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), de + ld (curRow), de call putSPageOne bcall(_EraseEOL) pop de - ld (CurRow), de + ld (curRow), de pop hl pop de pop bc @@ -106,16 +106,16 @@ DebugPString: push bc push de push hl - ld de, (CurRow) + ld de, (curRow) push de ld de, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), de + ld (curRow), de call putPSPageOne bcall(_EraseEOL) pop de - ld (CurRow), de + ld (curRow), de pop hl pop de pop bc @@ -128,15 +128,15 @@ DebugPString: ; Destroys: none DebugClear: push hl - ld hl, (CurRow) + ld hl, (curRow) push hl ld hl, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl bcall(_EraseEOL) pop hl - ld (CurRow), hl + ld (curRow), hl pop hl ret @@ -151,11 +151,11 @@ DebugOP1: push bc push de push hl - ld hl, (CurRow) + ld hl, (curRow) push hl ld hl, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl ld a, 15 ; width of output bcall(_FormReal) ld hl, OP3 @@ -163,7 +163,7 @@ DebugOP1: bcall(_EraseEOL) pop hl - ld (CurRow), hl + ld (curRow), hl pop hl pop de pop bc @@ -184,11 +184,11 @@ DebugUnsignedA: push de push hl push ix - ; Save CurRow/CurCol - ld hl, (CurRow) + ; Save curRow/CurCol + ld hl, (curRow) push hl ld hl, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl ; Create 4-byte buffer on the stack push hl push hl @@ -206,9 +206,9 @@ DebugUnsignedA: ; Clean up stack buffer pop hl pop hl - ; Restore CurRow/CurCol + ; Restore curRow/CurCol pop hl - ld (CurRow), hl + ld (curRow), hl ; Restore all registers. pop ix pop hl @@ -231,11 +231,11 @@ DebugSignedA: push de push hl push ix - ; Save CurRow/CurCol - ld hl, (CurRow) + ; Save curRow/CurCol + ld hl, (curRow) push hl ld hl, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl ; Create 4-byte buffer on the stack push hl push hl @@ -263,9 +263,9 @@ debugSignedAPrint: ; Clean up stack buffer pop hl pop hl - ; Restore CurRow/CurCol + ; Restore curRow/CurCol pop hl - ld (CurRow), hl + ld (curRow), hl ; Restore all registers. pop ix pop hl @@ -289,11 +289,11 @@ DebugFlags: push bc push de push hl - ld hl, (CurRow) + ld hl, (curRow) push hl ld hl, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl ; Print Input dirty flag bit dirtyFlagsInput, (iy + dirtyFlags) @@ -326,7 +326,7 @@ DebugFlags: bcall(_EraseEOL) pop hl - ld (CurRow), hl + ld (curRow), hl pop hl pop de pop bc @@ -364,10 +364,10 @@ DebugU32AsHex: push de push hl ; Set cursor position, saving the previous on the stack. - ld de, (CurRow) + ld de, (curRow) push de ld de, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), de + ld (curRow), de ; Prep loop ld b, 4 debugU32AsHexLoop: @@ -379,7 +379,7 @@ debugU32AsHexLoop: djnz debugU32AsHexLoop ; pop de - ld (CurRow), de + ld (curRow), de pop hl pop de pop bc @@ -423,10 +423,10 @@ DebugU40AsHex: push de push hl ; Set cursor position, saving the previous on the stack. - ld de, (CurRow) + ld de, (curRow) push de ld de, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), de + ld (curRow), de ; Prep loop ld b, 5 debugU40AsHexLoop: @@ -438,7 +438,7 @@ debugU40AsHexLoop: djnz debugU40AsHexLoop ; pop de - ld (CurRow), de + ld (curRow), de pop hl pop de pop bc @@ -460,11 +460,11 @@ DebugHL: bcall(_PushRealO4) ; up to 16 bytes starting at OP3, which spills into OP4 pop hl push hl - ld de, (CurRow) + ld de, (curRow) push de ; ld de, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), de + ld (curRow), de ; Converting to float is not efficient, but ok for debugging. bcall(_SetXXXXOP2) ; OP2=float(HL) bcall(_OP2ToOP1) ; OP1=float(HL) @@ -475,7 +475,7 @@ DebugHL: bcall(_EraseEOL) ; pop de - ld (CurRow), de + ld (curRow), de bcall(_PopRealO4) bcall(_PopRealO3) bcall(_PopRealO2) @@ -494,11 +494,11 @@ DebugHLAsHex: push bc push de push hl - ld de, (CurRow) + ld de, (curRow) push de ; ld de, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), de + ld (curRow), de ; ld a, h call debugUnsignedAAsHex @@ -507,7 +507,7 @@ DebugHLAsHex: bcall(_EraseEOL) ; pop de - ld (CurRow), de + ld (curRow), de pop hl pop de pop bc @@ -522,11 +522,11 @@ DebugDEHLAsHex: push bc push de push hl - ld bc, (CurRow) + ld bc, (curRow) push bc ; ld bc, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), bc + ld (curRow), bc ; ld a, d call debugUnsignedAAsHex @@ -541,7 +541,7 @@ DebugDEHLAsHex: bcall(_EraseEOL) ; pop bc - ld (CurRow), bc + ld (curRow), bc pop hl pop de pop bc diff --git a/src/display.asm b/src/display.asm index 67e4109a..2b0c41b2 100644 --- a/src/display.asm +++ b/src/display.asm @@ -207,7 +207,7 @@ displayStatusArrow: bit dirtyFlagsMenu, (iy + dirtyFlags) ret z ld hl, statusPenRow*$100 + statusMenuPenCol; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ; check arrow status bcall(_GetCurrentMenuArrowStatus) ; B=menuArrowStatus call displayStatusArrowLeft @@ -268,7 +268,7 @@ displayStatusFloatMode: bit dirtyFlagsStatus, (iy + dirtyFlags) ret z ld hl, statusPenRow*$100 + statusFloatModePenCol; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ; check float mode bit fmtExponent, (iy + fmtFlags) jr nz, displayStatusFloatModeSciOrEng @@ -305,7 +305,7 @@ displayStatusTrig: bit dirtyFlagsStatus, (iy + dirtyFlags) ret z ld hl, statusPenRow*$100 + statusTrigPenCol; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl bit trigDeg, (iy + trigFlags) jr z, displayStatusTrigRad displayStatusTrigDeg: @@ -324,7 +324,7 @@ displayStatusBase: bit dirtyFlagsStatus, (iy + dirtyFlags) ret z ld hl, statusPenRow*$100 + statusBasePenCol; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ; Determine state of Carry Flag. ld a, (baseCarryFlag) or a @@ -345,7 +345,7 @@ displayStatusComplexMode: bit dirtyFlagsStatus, (iy + dirtyFlags) ret z ld hl, statusPenRow*$100 + statusComplexModePenCol; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ; Determine state of complexMode ld a, (complexMode) ; Check complexModeRad @@ -381,7 +381,7 @@ displayStatusStackMode: bit dirtyFlagsStatus, (iy + dirtyFlags) ret z ld hl, statusPenRow*$100 + statusStackModePenCol; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ; print the stackSize variable first ld a, (stackSize) add a, '0' @@ -407,7 +407,7 @@ displayErrorCode: ; Display nothing if errorCode == OK (0) res fracDrawLFont, (iy + fontFlags) ; use small font ld hl, errorPenRow*$100 ; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ld a, (errorCode) or a jr z, displayErrorCodeEnd @@ -468,39 +468,39 @@ displayStack: displayStackYZT: ; print T label ld hl, stTPenRow*$100 ; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ld hl, msgTLabel call vPutSmallS ; print T value ld hl, stTCurCol*$100 + stTCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl call rclT ld b, displayStackFontFlagsT call printOP1 ; print Z label ld hl, stZPenRow*$100 ; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ld hl, msgZLabel call vPutSmallS ; print Z value ld hl, stZCurCol*$100 + stZCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl call rclZ ld b, displayStackFontFlagsZ call printOP1 ; print Y label ld hl, stYPenRow*$100 ; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ld hl, msgYLabel call vPutSmallS ; print Y value ld hl, stYCurCol*$100 + stYCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl call rclY ld b, displayStackFontFlagsY call printOP1 @@ -542,7 +542,7 @@ displayStackXNormal: call displayStackXLabel ; print the X register ld hl, stXCurCol*$100 + stXCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl call rclX ld b, displayStackFontFlagsX jp printOP1 @@ -551,7 +551,7 @@ displayStackXInput: call displayStackXLabel ; print the inputBuf ld hl, inputCurCol*$100 + inputCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl ld b, displayStackFontFlagsX call displayStackSetLargeFont bcall(_PrintInputBuf) @@ -560,7 +560,7 @@ displayStackXInput: ; Display the inputBuf in the debug line. Used for DRAW mode 3. displayStackXInputAtDebug: ld hl, debugCurCol*$100+debugCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl bcall(_PrintInputBuf) ret @@ -570,14 +570,14 @@ displayStackXLabel: bit dirtyFlagsXLabel, (iy + dirtyFlags) jr nz, displayStackXLabelContinue ld hl, 0*$100 + stXCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl ld a, Lspace bcall(_PutC) res dirtyFlagsXLabel, (iy + dirtyFlags) displayStackXLabelContinue: ; print X label ld hl, inputPenRow*$100 ; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ld hl, msgXLabel call vPutSmallS ret @@ -586,23 +586,23 @@ displayStackXLabelContinue: ; Display the argBuf in the X register line. ; Input: (argBuf) -; Output: (CurCol) updated +; Output: (curCol) updated displayStackXArg: ; Set commandArg cursor position. ld hl, argCurCol*$100 + argCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl ld b, displayStackFontFlagsX ; [[fallthrough]] -; Description: Print the arg buffer at the (CurRow) and (CurCol). +; Description: Print the arg buffer at the (curRow) and (curCol). ; Input: ; - B=displayFontMask ; - argBuf (same as inputBuf) ; - argPrompt ; - argModifier -; - (CurCol) cursor position +; - (curCol) cursor position ; Output: -; - (CurCol) is updated +; - (curCol) is updated ; - (displayStackFontFlagsX) cleared to indicate large font ; Destroys: A, HL; BC destroyed by PutPS() printArgBuf: @@ -673,65 +673,65 @@ msgArgModifierIndirect: displayTvm: ; print TVM n label ld hl, tvmNPenRow*$100 ; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ld hl, msgTvmNLabel call vPutSmallS ; print TVM n value ld hl, tvmNCurCol*$100 + tvmNCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl bcall(_RclTvmSolverCount) ld b, displayStackFontFlagsA call printOP1 ; print TVM i0 label ld hl, tvmI0PenRow*$100 ; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ld hl, msgTvmI0Label call vPutSmallS ; print TVM i0 value ld hl, tvmI0CurCol*$100 + tvmI0CurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl bcall(_RclTvmI0) ld b, displayStackFontFlagsZ call printOP1 ; print TVM i1 label ld hl, tvmI1PenRow*$100 ; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ld hl, msgTvmI1Label call vPutSmallS ; print TVM i1 value ld hl, tvmI1CurCol*$100 + tvmI1CurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl bcall(_RclTvmI1) ld b, displayStackFontFlagsY call printOP1 ; print TVM f0 label ld hl, tvmF0PenRow*$100 ; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ld hl, msgTvmF0Label call vPutSmallS ; print TVM f0 value ld hl, tvmF0CurCol*$100 + tvmF0CurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl bcall(_RclTvmNPMT0) ld b, displayStackFontFlagsZ call printOP1 ; print TVM f1 label ld hl, tvmF1PenRow*$100 ; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ld hl, msgTvmF1Label call vPutSmallS ; print TVM f1 value ld hl, tvmF1CurCol*$100 + tvmF1CurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl bcall(_RclTvmNPMT1) ld b, displayStackFontFlagsX call printOP1 @@ -784,12 +784,12 @@ msgShowLabel: displayShow: ; Print 'SHOW' label on Error Code line ld hl, errorPenRow*$100 ; $(penRow)(penCol) - ld (PenCol), hl + ld (penCol), hl ld hl, msgShowLabel call printSmallHLString ; Call special FormShowable() function to show all digits of OP1. ld hl, showCurCol*$100 + showCurRow ; $(curCol)(curRow) - ld (CurRow), hl + ld (curRow), hl call rclX ; fmtString is a buffer of 65 bytes used by FormDCplx(). There should be no ; problems using it as our string buffer. @@ -799,21 +799,6 @@ displayShow: call putS ret -; Clear the display area used by the SHOW feature (errorCode, T, Z, Y, X). -; Input: none -; Destroys: A, B, HL -clearShowArea: - ld hl, errorCurCol*$100 + errorCurRow ; $(curCol)(curRow) - ld (CurRow), hl - ld b, 5 -clearShowAreaLoop: - bcall(_EraseEOL) ; saves all registers - ld hl, (CurRow) - inc l - ld (CurRow), hl - djnz clearShowAreaLoop - ret - ;----------------------------------------------------------------------------- ; Low-level helper routines. ;----------------------------------------------------------------------------- @@ -1005,7 +990,7 @@ printOP1AsFloat: ; Destroys: A, HL printHLString: call putS - ld a, (CurCol) + ld a, (curCol) or a ret z ; if spilled to next line, don't call EraseEOL bcall(_EraseEOL) diff --git a/src/display2.asm b/src/display2.asm index 01bcd27c..f5881eb5 100644 --- a/src/display2.asm +++ b/src/display2.asm @@ -50,11 +50,11 @@ PrintMenuNameAtC: push af ; stack=[numRows] push bc ; stack=[numRows, loopCounter/penCol] push de ; stack=[numRows, loopCounter/penCol,menuIndex] - ; Set (PenCol,PenRow), preserving HL + ; Set (penCol,penRow), preserving HL ld a, c ; A=penCol - ld (PenCol), a + ld (penCol), a ld a, menuPenRow - ld (PenRow), a + ld (penRow), a ; Predict the width of menu name. ld de, menuName ld c, menuNameBufMax @@ -186,10 +186,10 @@ displayMenuFolderEnd: ; Description: Print the input buffer. ; Input: ; - inputBuf -; - (CurRow) cursor row -; - (CurCol) cursor column +; - (curRow) cursor row +; - (curCol) cursor column ; Output: -; - (CurCol) updated +; - (curCol) updated ; - (renderWindowStart) updated ; - (renderWindowEnd) updated ; - (cursorRenderPos) updated @@ -437,7 +437,7 @@ printRenderWindowPutC: ; Skip EraseEOL() if the PutC() above wrapped to next line. ; Destroys: A clearEndOfRenderLine: - ld a, (CurCol) + ld a, (curCol) or a ret z bcall(_EraseEOL) @@ -449,8 +449,8 @@ clearEndOfRenderLine: ; - cursorRenderPos ; Output: ; - cursorScreenPos updated -; - CurCol updated -; - CurRow updated +; - curCol updated +; - curRow updated setInputCursor: ; update cursor screen logical position ld a, (renderWindowStart) @@ -463,9 +463,9 @@ setInputCursor: ; beginning of th line. Also update physical row, because the TI-OS cursor ; could have wrapped to the next row add a, inputCurCol - ld (CurCol), a + ld (curCol), a ld a, inputCurRow - ld (CurRow), a + ld (curRow), a ; update cursor-under character ld hl, renderBuf inc hl diff --git a/src/errorcode1.asm b/src/errorcode1.asm index b70d44d9..b08e4c42 100644 --- a/src/errorcode1.asm +++ b/src/errorcode1.asm @@ -191,7 +191,8 @@ errorCodeLinkXmit equ 31 .dw errorStrUnknown ; 44 .dw errorStrUnknown ; 45 .dw errorStrUnknown ; 46 - .dw errorStrUnknown ; 47 +errorCodeArchived equ 47 + .dw errorStrArchived .dw errorStrUnknown ; 48 .dw errorStrUnknown ; 49 .dw errorStrUnknown ; 50 @@ -312,6 +313,11 @@ errorStrTolTooSmall: errorStrUndefined: .db "Err: Undefined", 0 ; indicates the system error "Undefined" +; Additional errors that RPN83P has encountered, verified by +; https://learn.cemetech.net/index.php?title=Z80:Error_Codes +errorStrArchived: + .db "Err: Archived", 0 + ; Start of RPN83P custom messages, which map to a specific custom handler code. ; This part of the application feels clunky, but I have not figure out an ; elegant architecture to handle the different types of handler diff --git a/src/handlers.asm b/src/handlers.asm index b854b7a8..c2fbfefc 100644 --- a/src/handlers.asm +++ b/src/handlers.asm @@ -581,11 +581,27 @@ handleKeyClearNormal: ; mode with an empty inputBuf. bit rpnFlagsEditing, (iy + rpnFlags) jr z, handleKeyClearToEmptyInput - ; We are here if clearing the inputBuf. +handleKeyClearInEditMode: + ; We are here if CLEAR was pressed in edit mode. ld a, (inputBuf) or a - ; If the inputBuf has stuff, then clear the inputBuf. - jr nz, handleKeyClearToEmptyInput + jr z, handleKeyClearWhileClear +handleKeyClearInputBufNotEmpty: + ; We are here if the inputBuf is not empty. There are 3 cases: + ; 1) If cursor is at the beginning, clear the entire inputBuf. + ; 2) If cursor is at the end, clear the entire inputBuf. + ; 3) If cursor is in the middle, clear only to the end of line. + ld b, a ; B=inputBufLen + ld a, (cursorInputPos) ; A=cursorInputPos + or a + jr z, handleKeyClearToEmptyInput + cp b + jr z, handleKeyClearToEmptyInput +handleKeyClearToEndOfLine: + ; We are here if the cursor is in middle of inputBuf. Clear to end of line. + ld (inputBuf), a + set dirtyFlagsInput, (iy + dirtyFlags) + ret handleKeyClearWhileClear: ; We are here if CLEAR was pressed while the inputBuffer was already empty. ; Go into ClearAgain mode, where the next CLEAR invokes CLST. diff --git a/src/handlertab.asm b/src/handlertab.asm index 1b9efcd6..8a187785 100644 --- a/src/handlertab.asm +++ b/src/handlertab.asm @@ -234,7 +234,7 @@ keyCodeHandlerTable: ; `Rollup` soft menu key in the `STK` menufolder. ; 2) The `u` can be a mnemonic for the "Rollup" name of the command. .db kunA - .dw handleKeyRollup + .dw handleKeyRollUp ; bind ANS to lastX. .db kAns ; ANS diff --git a/src/help1.asm b/src/help1.asm index c3d5b9c1..60a04642 100644 --- a/src/help1.asm +++ b/src/help1.asm @@ -34,8 +34,8 @@ helpPageCount equ (helpPagesEnd-helpPages)/2 msgHelpPage1: .db escapeLargeFont, "RPN83P", Lenter - ; .db escapeSmallFont, "v0.12.0", Shyphen, "rc3 (2024", Shyphen, "06", Shyphen, "21)", Senter - .db escapeSmallFont, "v0.12.0 (2024", Shyphen, "06", Shyphen, "24)", Senter + ; .db escapeSmallFont, "v0.13.0", Shyphen, "dev (2024", Shyphen, "07", Shyphen, "19)", Senter + .db escapeSmallFont, "v1.0.0 (2024", Shyphen, "07", Shyphen, "19)", Senter .db "(c) 2023", Shyphen, "2024 Brian T. Park", Senter .db Senter .db "An RPN calculator for the", Senter diff --git a/src/helpscanner1.asm b/src/helpscanner1.asm index a70df678..2fec1605 100644 --- a/src/helpscanner1.asm +++ b/src/helpscanner1.asm @@ -96,7 +96,7 @@ displayHelpPage: bcall(_ClrLCDFull) ld hl, 0 - ld (PenCol), hl + ld (penCol), hl ; Get the string for page A, and display it. ld hl, helpPages ; HL = (char**) diff --git a/src/main.asm b/src/main.asm index 21d85705..2cca7e26 100644 --- a/src/main.asm +++ b/src/main.asm @@ -71,7 +71,7 @@ mainExit: set appAutoScroll, (iy + appFlags) ld (iy + textFlags), 0 ; reset text flags bcall(_ClrLCDFull) - bcall(_HomeUp) + bcall(_homeup) ; Restore various OS states. bcall(_RestoreOSState) diff --git a/src/menudef.asm b/src/menudef.asm index 44e239ce..402c0c47 100644 --- a/src/menudef.asm +++ b/src/menudef.asm @@ -29,7 +29,7 @@ ; DO NOT EDIT: This file was autogenerated. ;----------------------------------------------------------------------------- -mMenuTableSize equ 272 +mMenuTableSize equ 287 mMenuTable: mNull: mNullId equ 0 @@ -148,7 +148,7 @@ mClearId equ 12 .dw mClearId ; id .dw mRootId ; parentId .dw mClearNameId ; nameId - .db 1 ; numRows + .db 2 ; numRows .dw mClearXId ; rowBeginId or altNameId .dw mGroupHandler ; handler (predefined) .dw 0 ; nameSelector @@ -175,7 +175,7 @@ mUnitId equ 15 .dw mUnitId ; id .dw mRootId ; parentId .dw mUnitNameId ; nameId - .db 6 ; numRows + .db 8 ; numRows .dw mFToCId ; rowBeginId or altNameId .dw mGroupHandler ; handler (predefined) .dw 0 ; nameSelector @@ -1672,10 +1672,56 @@ mClearTvmId equ 176 .dw 0 ; rowBeginId or altNameId .dw mClearTvmHandler ; handler (to be implemented) .dw 0 ; nameSelector +; MenuGroup CLR: children: row 1 +mClearDisplay: +mClearDisplayId equ 177 + .dw mClearDisplayId ; id + .dw mClearId ; parentId + .dw mClearDisplayNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mClearDisplayHandler ; handler (to be implemented) + .dw 0 ; nameSelector +mBlank178: +mBlank178Id equ 178 + .dw mBlank178Id ; id + .dw mClearId ; parentId + .dw mNullNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mNullHandler ; handler (predefined) + .dw 0 ; nameSelector +mBlank179: +mBlank179Id equ 179 + .dw mBlank179Id ; id + .dw mClearId ; parentId + .dw mNullNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mNullHandler ; handler (predefined) + .dw 0 ; nameSelector +mBlank180: +mBlank180Id equ 180 + .dw mBlank180Id ; id + .dw mClearId ; parentId + .dw mNullNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mNullHandler ; handler (predefined) + .dw 0 ; nameSelector +mBlank181: +mBlank181Id equ 181 + .dw mBlank181Id ; id + .dw mClearId ; parentId + .dw mNullNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mNullHandler ; handler (predefined) + .dw 0 ; nameSelector ; MenuGroup MODE: children ; MenuGroup MODE: children: row 0 mFix: -mFixId equ 177 +mFixId equ 182 .dw mFixId ; id .dw mModeId ; parentId .dw mFixNameId ; nameId @@ -1684,7 +1730,7 @@ mFixId equ 177 .dw mFixHandler ; handler (to be implemented) .dw mFixNameSelector ; nameSelector mSci: -mSciId equ 178 +mSciId equ 183 .dw mSciId ; id .dw mModeId ; parentId .dw mSciNameId ; nameId @@ -1693,7 +1739,7 @@ mSciId equ 178 .dw mSciHandler ; handler (to be implemented) .dw mSciNameSelector ; nameSelector mEng: -mEngId equ 179 +mEngId equ 184 .dw mEngId ; id .dw mModeId ; parentId .dw mEngNameId ; nameId @@ -1702,7 +1748,7 @@ mEngId equ 179 .dw mEngHandler ; handler (to be implemented) .dw mEngNameSelector ; nameSelector mRad: -mRadId equ 180 +mRadId equ 185 .dw mRadId ; id .dw mModeId ; parentId .dw mRadNameId ; nameId @@ -1711,7 +1757,7 @@ mRadId equ 180 .dw mRadHandler ; handler (to be implemented) .dw mRadNameSelector ; nameSelector mDeg: -mDegId equ 181 +mDegId equ 186 .dw mDegId ; id .dw mModeId ; parentId .dw mDegNameId ; nameId @@ -1721,7 +1767,7 @@ mDegId equ 181 .dw mDegNameSelector ; nameSelector ; MenuGroup MODE: children: row 1 mNumResultModeReal: -mNumResultModeRealId equ 182 +mNumResultModeRealId equ 187 .dw mNumResultModeRealId ; id .dw mModeId ; parentId .dw mNumResultModeRealNameId ; nameId @@ -1730,7 +1776,7 @@ mNumResultModeRealId equ 182 .dw mNumResultModeRealHandler ; handler (to be implemented) .dw mNumResultModeRealNameSelector ; nameSelector mNumResultModeComplex: -mNumResultModeComplexId equ 183 +mNumResultModeComplexId equ 188 .dw mNumResultModeComplexId ; id .dw mModeId ; parentId .dw mNumResultModeComplexNameId ; nameId @@ -1739,7 +1785,7 @@ mNumResultModeComplexId equ 183 .dw mNumResultModeComplexHandler ; handler (to be implemented) .dw mNumResultModeComplexNameSelector ; nameSelector mComplexModeRect: -mComplexModeRectId equ 184 +mComplexModeRectId equ 189 .dw mComplexModeRectId ; id .dw mModeId ; parentId .dw mComplexModeRectNameId ; nameId @@ -1748,7 +1794,7 @@ mComplexModeRectId equ 184 .dw mComplexModeRectHandler ; handler (to be implemented) .dw mComplexModeRectNameSelector ; nameSelector mComplexModeRad: -mComplexModeRadId equ 185 +mComplexModeRadId equ 190 .dw mComplexModeRadId ; id .dw mModeId ; parentId .dw mComplexModeRadNameId ; nameId @@ -1757,7 +1803,7 @@ mComplexModeRadId equ 185 .dw mComplexModeRadHandler ; handler (to be implemented) .dw mComplexModeRadNameSelector ; nameSelector mComplexModeDeg: -mComplexModeDegId equ 186 +mComplexModeDegId equ 191 .dw mComplexModeDegId ; id .dw mModeId ; parentId .dw mComplexModeDegNameId ; nameId @@ -1767,7 +1813,7 @@ mComplexModeDegId equ 186 .dw mComplexModeDegNameSelector ; nameSelector ; MenuGroup MODE: children: row 2 mSetRegSize: -mSetRegSizeId equ 187 +mSetRegSizeId equ 192 .dw mSetRegSizeId ; id .dw mModeId ; parentId .dw mSetRegSizeNameId ; nameId @@ -1776,7 +1822,7 @@ mSetRegSizeId equ 187 .dw mSetRegSizeHandler ; handler (to be implemented) .dw 0 ; nameSelector mGetRegSize: -mGetRegSizeId equ 188 +mGetRegSizeId equ 193 .dw mGetRegSizeId ; id .dw mModeId ; parentId .dw mGetRegSizeNameId ; nameId @@ -1784,9 +1830,9 @@ mGetRegSizeId equ 188 .dw 0 ; rowBeginId or altNameId .dw mGetRegSizeHandler ; handler (to be implemented) .dw 0 ; nameSelector -mBlank189: -mBlank189Id equ 189 - .dw mBlank189Id ; id +mBlank194: +mBlank194Id equ 194 + .dw mBlank194Id ; id .dw mModeId ; parentId .dw mNullNameId ; nameId .db 0 ; numRows @@ -1794,7 +1840,7 @@ mBlank189Id equ 189 .dw mNullHandler ; handler (predefined) .dw 0 ; nameSelector mSetStackSize: -mSetStackSizeId equ 190 +mSetStackSizeId equ 195 .dw mSetStackSizeId ; id .dw mModeId ; parentId .dw mSetStackSizeNameId ; nameId @@ -1803,7 +1849,7 @@ mSetStackSizeId equ 190 .dw mSetStackSizeHandler ; handler (to be implemented) .dw 0 ; nameSelector mGetStackSize: -mGetStackSizeId equ 191 +mGetStackSizeId equ 196 .dw mGetStackSizeId ; id .dw mModeId ; parentId .dw mGetStackSizeNameId ; nameId @@ -1813,7 +1859,7 @@ mGetStackSizeId equ 191 .dw 0 ; nameSelector ; MenuGroup MODE: children: row 3 mCommaEENormal: -mCommaEENormalId equ 192 +mCommaEENormalId equ 197 .dw mCommaEENormalId ; id .dw mModeId ; parentId .dw mCommaEENormalNameId ; nameId @@ -1822,7 +1868,7 @@ mCommaEENormalId equ 192 .dw mCommaEENormalHandler ; handler (to be implemented) .dw mCommaEENormalNameSelector ; nameSelector mCommaEESwapped: -mCommaEESwappedId equ 193 +mCommaEESwappedId equ 198 .dw mCommaEESwappedId ; id .dw mModeId ; parentId .dw mCommaEESwappedNameId ; nameId @@ -1830,9 +1876,9 @@ mCommaEESwappedId equ 193 .dw mCommaEESwappedAltNameId ; rowBeginId or altNameId .dw mCommaEESwappedHandler ; handler (to be implemented) .dw mCommaEESwappedNameSelector ; nameSelector -mBlank194: -mBlank194Id equ 194 - .dw mBlank194Id ; id +mBlank199: +mBlank199Id equ 199 + .dw mBlank199Id ; id .dw mModeId ; parentId .dw mNullNameId ; nameId .db 0 ; numRows @@ -1840,7 +1886,7 @@ mBlank194Id equ 194 .dw mNullHandler ; handler (predefined) .dw 0 ; nameSelector mFormatRecordRaw: -mFormatRecordRawId equ 195 +mFormatRecordRawId equ 200 .dw mFormatRecordRawId ; id .dw mModeId ; parentId .dw mFormatRecordRawNameId ; nameId @@ -1849,7 +1895,7 @@ mFormatRecordRawId equ 195 .dw mFormatRecordRawHandler ; handler (to be implemented) .dw mFormatRecordRawNameSelector ; nameSelector mFormatRecordString: -mFormatRecordStringId equ 196 +mFormatRecordStringId equ 201 .dw mFormatRecordStringId ; id .dw mModeId ; parentId .dw mFormatRecordStringNameId ; nameId @@ -1860,7 +1906,7 @@ mFormatRecordStringId equ 196 ; MenuGroup STK: children ; MenuGroup STK: children: row 0 mStackDup: -mStackDupId equ 197 +mStackDupId equ 202 .dw mStackDupId ; id .dw mStackId ; parentId .dw mStackDupNameId ; nameId @@ -1869,7 +1915,7 @@ mStackDupId equ 197 .dw mStackDupHandler ; handler (to be implemented) .dw 0 ; nameSelector mStackRollUp: -mStackRollUpId equ 198 +mStackRollUpId equ 203 .dw mStackRollUpId ; id .dw mStackId ; parentId .dw mStackRollUpNameId ; nameId @@ -1878,7 +1924,7 @@ mStackRollUpId equ 198 .dw mStackRollUpHandler ; handler (to be implemented) .dw 0 ; nameSelector mStackRollDown: -mStackRollDownId equ 199 +mStackRollDownId equ 204 .dw mStackRollDownId ; id .dw mStackId ; parentId .dw mStackRollDownNameId ; nameId @@ -1887,7 +1933,7 @@ mStackRollDownId equ 199 .dw mStackRollDownHandler ; handler (to be implemented) .dw 0 ; nameSelector mStackDrop: -mStackDropId equ 200 +mStackDropId equ 205 .dw mStackDropId ; id .dw mStackId ; parentId .dw mStackDropNameId ; nameId @@ -1896,7 +1942,7 @@ mStackDropId equ 200 .dw mStackDropHandler ; handler (to be implemented) .dw 0 ; nameSelector mStackExchangeXY: -mStackExchangeXYId equ 201 +mStackExchangeXYId equ 206 .dw mStackExchangeXYId ; id .dw mStackId ; parentId .dw mStackExchangeXYNameId ; nameId @@ -1907,7 +1953,7 @@ mStackExchangeXYId equ 201 ; MenuGroup UNIT: children ; MenuGroup UNIT: children: row 0 mFToC: -mFToCId equ 202 +mFToCId equ 207 .dw mFToCId ; id .dw mUnitId ; parentId .dw mFToCNameId ; nameId @@ -1916,7 +1962,7 @@ mFToCId equ 202 .dw mFToCHandler ; handler (to be implemented) .dw 0 ; nameSelector mCToF: -mCToFId equ 203 +mCToFId equ 208 .dw mCToFId ; id .dw mUnitId ; parentId .dw mCToFNameId ; nameId @@ -1924,9 +1970,9 @@ mCToFId equ 203 .dw 0 ; rowBeginId or altNameId .dw mCToFHandler ; handler (to be implemented) .dw 0 ; nameSelector -mBlank204: -mBlank204Id equ 204 - .dw mBlank204Id ; id +mBlank209: +mBlank209Id equ 209 + .dw mBlank209Id ; id .dw mUnitId ; parentId .dw mNullNameId ; nameId .db 0 ; numRows @@ -1934,7 +1980,7 @@ mBlank204Id equ 204 .dw mNullHandler ; handler (predefined) .dw 0 ; nameSelector mInhgToHpa: -mInhgToHpaId equ 205 +mInhgToHpaId equ 210 .dw mInhgToHpaId ; id .dw mUnitId ; parentId .dw mInhgToHpaNameId ; nameId @@ -1943,7 +1989,7 @@ mInhgToHpaId equ 205 .dw mInhgToHpaHandler ; handler (to be implemented) .dw 0 ; nameSelector mHpaToInhg: -mHpaToInhgId equ 206 +mHpaToInhgId equ 211 .dw mHpaToInhgId ; id .dw mUnitId ; parentId .dw mHpaToInhgNameId ; nameId @@ -1953,7 +1999,7 @@ mHpaToInhgId equ 206 .dw 0 ; nameSelector ; MenuGroup UNIT: children: row 1 mMiToKm: -mMiToKmId equ 207 +mMiToKmId equ 212 .dw mMiToKmId ; id .dw mUnitId ; parentId .dw mMiToKmNameId ; nameId @@ -1962,7 +2008,7 @@ mMiToKmId equ 207 .dw mMiToKmHandler ; handler (to be implemented) .dw 0 ; nameSelector mKmToMi: -mKmToMiId equ 208 +mKmToMiId equ 213 .dw mKmToMiId ; id .dw mUnitId ; parentId .dw mKmToMiNameId ; nameId @@ -1970,9 +2016,9 @@ mKmToMiId equ 208 .dw 0 ; rowBeginId or altNameId .dw mKmToMiHandler ; handler (to be implemented) .dw 0 ; nameSelector -mBlank209: -mBlank209Id equ 209 - .dw mBlank209Id ; id +mBlank214: +mBlank214Id equ 214 + .dw mBlank214Id ; id .dw mUnitId ; parentId .dw mNullNameId ; nameId .db 0 ; numRows @@ -1980,7 +2026,7 @@ mBlank209Id equ 209 .dw mNullHandler ; handler (predefined) .dw 0 ; nameSelector mFtToM: -mFtToMId equ 210 +mFtToMId equ 215 .dw mFtToMId ; id .dw mUnitId ; parentId .dw mFtToMNameId ; nameId @@ -1989,7 +2035,7 @@ mFtToMId equ 210 .dw mFtToMHandler ; handler (to be implemented) .dw 0 ; nameSelector mMToFt: -mMToFtId equ 211 +mMToFtId equ 216 .dw mMToFtId ; id .dw mUnitId ; parentId .dw mMToFtNameId ; nameId @@ -1999,7 +2045,7 @@ mMToFtId equ 211 .dw 0 ; nameSelector ; MenuGroup UNIT: children: row 2 mInToCm: -mInToCmId equ 212 +mInToCmId equ 217 .dw mInToCmId ; id .dw mUnitId ; parentId .dw mInToCmNameId ; nameId @@ -2008,7 +2054,7 @@ mInToCmId equ 212 .dw mInToCmHandler ; handler (to be implemented) .dw 0 ; nameSelector mCmToIn: -mCmToInId equ 213 +mCmToInId equ 218 .dw mCmToInId ; id .dw mUnitId ; parentId .dw mCmToInNameId ; nameId @@ -2016,9 +2062,9 @@ mCmToInId equ 213 .dw 0 ; rowBeginId or altNameId .dw mCmToInHandler ; handler (to be implemented) .dw 0 ; nameSelector -mBlank214: -mBlank214Id equ 214 - .dw mBlank214Id ; id +mBlank219: +mBlank219Id equ 219 + .dw mBlank219Id ; id .dw mUnitId ; parentId .dw mNullNameId ; nameId .db 0 ; numRows @@ -2026,7 +2072,7 @@ mBlank214Id equ 214 .dw mNullHandler ; handler (predefined) .dw 0 ; nameSelector mMilToMicron: -mMilToMicronId equ 215 +mMilToMicronId equ 220 .dw mMilToMicronId ; id .dw mUnitId ; parentId .dw mMilToMicronNameId ; nameId @@ -2035,7 +2081,7 @@ mMilToMicronId equ 215 .dw mMilToMicronHandler ; handler (to be implemented) .dw 0 ; nameSelector mMicronToMil: -mMicronToMilId equ 216 +mMicronToMilId equ 221 .dw mMicronToMilId ; id .dw mUnitId ; parentId .dw mMicronToMilNameId ; nameId @@ -2045,7 +2091,7 @@ mMicronToMilId equ 216 .dw 0 ; nameSelector ; MenuGroup UNIT: children: row 3 mLbsToKg: -mLbsToKgId equ 217 +mLbsToKgId equ 222 .dw mLbsToKgId ; id .dw mUnitId ; parentId .dw mLbsToKgNameId ; nameId @@ -2054,7 +2100,7 @@ mLbsToKgId equ 217 .dw mLbsToKgHandler ; handler (to be implemented) .dw 0 ; nameSelector mKgToLbs: -mKgToLbsId equ 218 +mKgToLbsId equ 223 .dw mKgToLbsId ; id .dw mUnitId ; parentId .dw mKgToLbsNameId ; nameId @@ -2062,9 +2108,9 @@ mKgToLbsId equ 218 .dw 0 ; rowBeginId or altNameId .dw mKgToLbsHandler ; handler (to be implemented) .dw 0 ; nameSelector -mBlank219: -mBlank219Id equ 219 - .dw mBlank219Id ; id +mBlank224: +mBlank224Id equ 224 + .dw mBlank224Id ; id .dw mUnitId ; parentId .dw mNullNameId ; nameId .db 0 ; numRows @@ -2072,7 +2118,7 @@ mBlank219Id equ 219 .dw mNullHandler ; handler (predefined) .dw 0 ; nameSelector mOzToG: -mOzToGId equ 220 +mOzToGId equ 225 .dw mOzToGId ; id .dw mUnitId ; parentId .dw mOzToGNameId ; nameId @@ -2081,7 +2127,7 @@ mOzToGId equ 220 .dw mOzToGHandler ; handler (to be implemented) .dw 0 ; nameSelector mGToOz: -mGToOzId equ 221 +mGToOzId equ 226 .dw mGToOzId ; id .dw mUnitId ; parentId .dw mGToOzNameId ; nameId @@ -2091,7 +2137,7 @@ mGToOzId equ 221 .dw 0 ; nameSelector ; MenuGroup UNIT: children: row 4 mGalToL: -mGalToLId equ 222 +mGalToLId equ 227 .dw mGalToLId ; id .dw mUnitId ; parentId .dw mGalToLNameId ; nameId @@ -2100,7 +2146,7 @@ mGalToLId equ 222 .dw mGalToLHandler ; handler (to be implemented) .dw 0 ; nameSelector mLToGal: -mLToGalId equ 223 +mLToGalId equ 228 .dw mLToGalId ; id .dw mUnitId ; parentId .dw mLToGalNameId ; nameId @@ -2108,9 +2154,9 @@ mLToGalId equ 223 .dw 0 ; rowBeginId or altNameId .dw mLToGalHandler ; handler (to be implemented) .dw 0 ; nameSelector -mBlank224: -mBlank224Id equ 224 - .dw mBlank224Id ; id +mBlank229: +mBlank229Id equ 229 + .dw mBlank229Id ; id .dw mUnitId ; parentId .dw mNullNameId ; nameId .db 0 ; numRows @@ -2118,7 +2164,7 @@ mBlank224Id equ 224 .dw mNullHandler ; handler (predefined) .dw 0 ; nameSelector mFlozToMl: -mFlozToMlId equ 225 +mFlozToMlId equ 230 .dw mFlozToMlId ; id .dw mUnitId ; parentId .dw mFlozToMlNameId ; nameId @@ -2127,7 +2173,7 @@ mFlozToMlId equ 225 .dw mFlozToMlHandler ; handler (to be implemented) .dw 0 ; nameSelector mMlToFloz: -mMlToFlozId equ 226 +mMlToFlozId equ 231 .dw mMlToFlozId ; id .dw mUnitId ; parentId .dw mMlToFlozNameId ; nameId @@ -2137,7 +2183,7 @@ mMlToFlozId equ 226 .dw 0 ; nameSelector ; MenuGroup UNIT: children: row 5 mCalToKj: -mCalToKjId equ 227 +mCalToKjId equ 232 .dw mCalToKjId ; id .dw mUnitId ; parentId .dw mCalToKjNameId ; nameId @@ -2146,7 +2192,7 @@ mCalToKjId equ 227 .dw mCalToKjHandler ; handler (to be implemented) .dw 0 ; nameSelector mKjToCal: -mKjToCalId equ 228 +mKjToCalId equ 233 .dw mKjToCalId ; id .dw mUnitId ; parentId .dw mKjToCalNameId ; nameId @@ -2154,9 +2200,9 @@ mKjToCalId equ 228 .dw 0 ; rowBeginId or altNameId .dw mKjToCalHandler ; handler (to be implemented) .dw 0 ; nameSelector -mBlank229: -mBlank229Id equ 229 - .dw mBlank229Id ; id +mBlank234: +mBlank234Id equ 234 + .dw mBlank234Id ; id .dw mUnitId ; parentId .dw mNullNameId ; nameId .db 0 ; numRows @@ -2164,7 +2210,7 @@ mBlank229Id equ 229 .dw mNullHandler ; handler (predefined) .dw 0 ; nameSelector mHpToKw: -mHpToKwId equ 230 +mHpToKwId equ 235 .dw mHpToKwId ; id .dw mUnitId ; parentId .dw mHpToKwNameId ; nameId @@ -2173,7 +2219,7 @@ mHpToKwId equ 230 .dw mHpToKwHandler ; handler (to be implemented) .dw 0 ; nameSelector mKwToHp: -mKwToHpId equ 231 +mKwToHpId equ 236 .dw mKwToHpId ; id .dw mUnitId ; parentId .dw mKwToHpNameId ; nameId @@ -2181,10 +2227,102 @@ mKwToHpId equ 231 .dw 0 ; rowBeginId or altNameId .dw mKwToHpHandler ; handler (to be implemented) .dw 0 ; nameSelector +; MenuGroup UNIT: children: row 6 +mMpgToLkm: +mMpgToLkmId equ 237 + .dw mMpgToLkmId ; id + .dw mUnitId ; parentId + .dw mMpgToLkmNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mMpgToLkmHandler ; handler (to be implemented) + .dw 0 ; nameSelector +mLkmToMpg: +mLkmToMpgId equ 238 + .dw mLkmToMpgId ; id + .dw mUnitId ; parentId + .dw mLkmToMpgNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mLkmToMpgHandler ; handler (to be implemented) + .dw 0 ; nameSelector +mBlank239: +mBlank239Id equ 239 + .dw mBlank239Id ; id + .dw mUnitId ; parentId + .dw mNullNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mNullHandler ; handler (predefined) + .dw 0 ; nameSelector +mPsiToKpa: +mPsiToKpaId equ 240 + .dw mPsiToKpaId ; id + .dw mUnitId ; parentId + .dw mPsiToKpaNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mPsiToKpaHandler ; handler (to be implemented) + .dw 0 ; nameSelector +mKpaToPsi: +mKpaToPsiId equ 241 + .dw mKpaToPsiId ; id + .dw mUnitId ; parentId + .dw mKpaToPsiNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mKpaToPsiHandler ; handler (to be implemented) + .dw 0 ; nameSelector +; MenuGroup UNIT: children: row 7 +mAcreToHectare: +mAcreToHectareId equ 242 + .dw mAcreToHectareId ; id + .dw mUnitId ; parentId + .dw mAcreToHectareNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mAcreToHectareHandler ; handler (to be implemented) + .dw 0 ; nameSelector +mHectareToAcre: +mHectareToAcreId equ 243 + .dw mHectareToAcreId ; id + .dw mUnitId ; parentId + .dw mHectareToAcreNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mHectareToAcreHandler ; handler (to be implemented) + .dw 0 ; nameSelector +mBlank244: +mBlank244Id equ 244 + .dw mBlank244Id ; id + .dw mUnitId ; parentId + .dw mNullNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mNullHandler ; handler (predefined) + .dw 0 ; nameSelector +mBlank245: +mBlank245Id equ 245 + .dw mBlank245Id ; id + .dw mUnitId ; parentId + .dw mNullNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mNullHandler ; handler (predefined) + .dw 0 ; nameSelector +mBlank246: +mBlank246Id equ 246 + .dw mBlank246Id ; id + .dw mUnitId ; parentId + .dw mNullNameId ; nameId + .db 0 ; numRows + .dw 0 ; rowBeginId or altNameId + .dw mNullHandler ; handler (predefined) + .dw 0 ; nameSelector ; MenuGroup DATE: children ; MenuGroup DATE: children: row 0 mLeapYear: -mLeapYearId equ 232 +mLeapYearId equ 247 .dw mLeapYearId ; id .dw mDateId ; parentId .dw mLeapYearNameId ; nameId @@ -2193,7 +2331,7 @@ mLeapYearId equ 232 .dw mLeapYearHandler ; handler (to be implemented) .dw 0 ; nameSelector mDayOfWeek: -mDayOfWeekId equ 233 +mDayOfWeekId equ 248 .dw mDayOfWeekId ; id .dw mDateId ; parentId .dw mDayOfWeekNameId ; nameId @@ -2201,9 +2339,9 @@ mDayOfWeekId equ 233 .dw 0 ; rowBeginId or altNameId .dw mDayOfWeekHandler ; handler (to be implemented) .dw 0 ; nameSelector -mBlank234: -mBlank234Id equ 234 - .dw mBlank234Id ; id +mBlank249: +mBlank249Id equ 249 + .dw mBlank249Id ; id .dw mDateId ; parentId .dw mNullNameId ; nameId .db 0 ; numRows @@ -2211,7 +2349,7 @@ mBlank234Id equ 234 .dw mNullHandler ; handler (predefined) .dw 0 ; nameSelector mDateToEpochDays: -mDateToEpochDaysId equ 235 +mDateToEpochDaysId equ 250 .dw mDateToEpochDaysId ; id .dw mDateId ; parentId .dw mDateToEpochDaysNameId ; nameId @@ -2220,7 +2358,7 @@ mDateToEpochDaysId equ 235 .dw mDateToEpochDaysHandler ; handler (to be implemented) .dw 0 ; nameSelector mEpochDaysToDate: -mEpochDaysToDateId equ 236 +mEpochDaysToDateId equ 251 .dw mEpochDaysToDateId ; id .dw mDateId ; parentId .dw mEpochDaysToDateNameId ; nameId @@ -2230,7 +2368,7 @@ mEpochDaysToDateId equ 236 .dw 0 ; nameSelector ; MenuGroup DATE: children: row 1 mDateRelatedToSeconds: -mDateRelatedToSecondsId equ 237 +mDateRelatedToSecondsId equ 252 .dw mDateRelatedToSecondsId ; id .dw mDateId ; parentId .dw mDateRelatedToSecondsNameId ; nameId @@ -2239,7 +2377,7 @@ mDateRelatedToSecondsId equ 237 .dw mDateRelatedToSecondsHandler ; handler (to be implemented) .dw 0 ; nameSelector mSecondsToDuration: -mSecondsToDurationId equ 238 +mSecondsToDurationId equ 253 .dw mSecondsToDurationId ; id .dw mDateId ; parentId .dw mSecondsToDurationNameId ; nameId @@ -2248,7 +2386,7 @@ mSecondsToDurationId equ 238 .dw mSecondsToDurationHandler ; handler (to be implemented) .dw 0 ; nameSelector mSecondsToTime: -mSecondsToTimeId equ 239 +mSecondsToTimeId equ 254 .dw mSecondsToTimeId ; id .dw mDateId ; parentId .dw mSecondsToTimeNameId ; nameId @@ -2257,7 +2395,7 @@ mSecondsToTimeId equ 239 .dw mSecondsToTimeHandler ; handler (to be implemented) .dw 0 ; nameSelector mEpochSecondsToAppDateTime: -mEpochSecondsToAppDateTimeId equ 240 +mEpochSecondsToAppDateTimeId equ 255 .dw mEpochSecondsToAppDateTimeId ; id .dw mDateId ; parentId .dw mEpochSecondsToAppDateTimeNameId ; nameId @@ -2266,7 +2404,7 @@ mEpochSecondsToAppDateTimeId equ 240 .dw mEpochSecondsToAppDateTimeHandler ; handler (to be implemented) .dw 0 ; nameSelector mEpochSecondsToUTCDateTime: -mEpochSecondsToUTCDateTimeId equ 241 +mEpochSecondsToUTCDateTimeId equ 256 .dw mEpochSecondsToUTCDateTimeId ; id .dw mDateId ; parentId .dw mEpochSecondsToUTCDateTimeNameId ; nameId @@ -2276,7 +2414,7 @@ mEpochSecondsToUTCDateTimeId equ 241 .dw 0 ; nameSelector ; MenuGroup DATE: children: row 2 mTimeZoneToHours: -mTimeZoneToHoursId equ 242 +mTimeZoneToHoursId equ 257 .dw mTimeZoneToHoursId ; id .dw mDateId ; parentId .dw mTimeZoneToHoursNameId ; nameId @@ -2285,7 +2423,7 @@ mTimeZoneToHoursId equ 242 .dw mTimeZoneToHoursHandler ; handler (to be implemented) .dw 0 ; nameSelector mHoursToTimeZone: -mHoursToTimeZoneId equ 243 +mHoursToTimeZoneId equ 258 .dw mHoursToTimeZoneId ; id .dw mDateId ; parentId .dw mHoursToTimeZoneNameId ; nameId @@ -2294,7 +2432,7 @@ mHoursToTimeZoneId equ 243 .dw mHoursToTimeZoneHandler ; handler (to be implemented) .dw 0 ; nameSelector mDops: -mDopsId equ 244 +mDopsId equ 259 .dw mDopsId ; id .dw mDateId ; parentId .dw mDopsNameId ; nameId @@ -2303,7 +2441,7 @@ mDopsId equ 244 .dw mGroupHandler ; handler (predefined) .dw 0 ; nameSelector mEpoch: -mEpochId equ 245 +mEpochId equ 260 .dw mEpochId ; id .dw mDateId ; parentId .dw mEpochNameId ; nameId @@ -2312,7 +2450,7 @@ mEpochId equ 245 .dw mGroupHandler ; handler (predefined) .dw 0 ; nameSelector mClk: -mClkId equ 246 +mClkId equ 261 .dw mClkId ; id .dw mDateId ; parentId .dw mClkNameId ; nameId @@ -2323,7 +2461,7 @@ mClkId equ 246 ; MenuGroup DOPS: children ; MenuGroup DOPS: children: row 0 mDateShrink: -mDateShrinkId equ 247 +mDateShrinkId equ 262 .dw mDateShrinkId ; id .dw mDopsId ; parentId .dw mDateShrinkNameId ; nameId @@ -2332,7 +2470,7 @@ mDateShrinkId equ 247 .dw mDateShrinkHandler ; handler (to be implemented) .dw 0 ; nameSelector mDateExtend: -mDateExtendId equ 248 +mDateExtendId equ 263 .dw mDateExtendId ; id .dw mDopsId ; parentId .dw mDateExtendNameId ; nameId @@ -2341,7 +2479,7 @@ mDateExtendId equ 248 .dw mDateExtendHandler ; handler (to be implemented) .dw 0 ; nameSelector mDateCut: -mDateCutId equ 249 +mDateCutId equ 264 .dw mDateCutId ; id .dw mDopsId ; parentId .dw mDateCutNameId ; nameId @@ -2350,7 +2488,7 @@ mDateCutId equ 249 .dw mDateCutHandler ; handler (to be implemented) .dw 0 ; nameSelector mDateLink: -mDateLinkId equ 250 +mDateLinkId equ 265 .dw mDateLinkId ; id .dw mDopsId ; parentId .dw mDateLinkNameId ; nameId @@ -2358,9 +2496,9 @@ mDateLinkId equ 250 .dw 0 ; rowBeginId or altNameId .dw mDateLinkHandler ; handler (to be implemented) .dw 0 ; nameSelector -mBlank251: -mBlank251Id equ 251 - .dw mBlank251Id ; id +mBlank266: +mBlank266Id equ 266 + .dw mBlank266Id ; id .dw mDopsId ; parentId .dw mNullNameId ; nameId .db 0 ; numRows @@ -2370,7 +2508,7 @@ mBlank251Id equ 251 ; MenuGroup EPCH: children ; MenuGroup EPCH: children: row 0 mEpochUnix: -mEpochUnixId equ 252 +mEpochUnixId equ 267 .dw mEpochUnixId ; id .dw mEpochId ; parentId .dw mEpochUnixNameId ; nameId @@ -2379,7 +2517,7 @@ mEpochUnixId equ 252 .dw mEpochUnixHandler ; handler (to be implemented) .dw mEpochUnixNameSelector ; nameSelector mEpochNtp: -mEpochNtpId equ 253 +mEpochNtpId equ 268 .dw mEpochNtpId ; id .dw mEpochId ; parentId .dw mEpochNtpNameId ; nameId @@ -2388,7 +2526,7 @@ mEpochNtpId equ 253 .dw mEpochNtpHandler ; handler (to be implemented) .dw mEpochNtpNameSelector ; nameSelector mEpochGps: -mEpochGpsId equ 254 +mEpochGpsId equ 269 .dw mEpochGpsId ; id .dw mEpochId ; parentId .dw mEpochGpsNameId ; nameId @@ -2397,7 +2535,7 @@ mEpochGpsId equ 254 .dw mEpochGpsHandler ; handler (to be implemented) .dw mEpochGpsNameSelector ; nameSelector mEpochTios: -mEpochTiosId equ 255 +mEpochTiosId equ 270 .dw mEpochTiosId ; id .dw mEpochId ; parentId .dw mEpochTiosNameId ; nameId @@ -2406,7 +2544,7 @@ mEpochTiosId equ 255 .dw mEpochTiosHandler ; handler (to be implemented) .dw mEpochTiosNameSelector ; nameSelector mEpochY2k: -mEpochY2kId equ 256 +mEpochY2kId equ 271 .dw mEpochY2kId ; id .dw mEpochId ; parentId .dw mEpochY2kNameId ; nameId @@ -2416,7 +2554,7 @@ mEpochY2kId equ 256 .dw mEpochY2kNameSelector ; nameSelector ; MenuGroup EPCH: children: row 1 mEpochCustom: -mEpochCustomId equ 257 +mEpochCustomId equ 272 .dw mEpochCustomId ; id .dw mEpochId ; parentId .dw mEpochCustomNameId ; nameId @@ -2424,18 +2562,18 @@ mEpochCustomId equ 257 .dw mEpochCustomAltNameId ; rowBeginId or altNameId .dw mEpochCustomHandler ; handler (to be implemented) .dw mEpochCustomNameSelector ; nameSelector -mBlank258: -mBlank258Id equ 258 - .dw mBlank258Id ; id +mBlank273: +mBlank273Id equ 273 + .dw mBlank273Id ; id .dw mEpochId ; parentId .dw mNullNameId ; nameId .db 0 ; numRows .dw 0 ; rowBeginId or altNameId .dw mNullHandler ; handler (predefined) .dw 0 ; nameSelector -mBlank259: -mBlank259Id equ 259 - .dw mBlank259Id ; id +mBlank274: +mBlank274Id equ 274 + .dw mBlank274Id ; id .dw mEpochId ; parentId .dw mNullNameId ; nameId .db 0 ; numRows @@ -2443,7 +2581,7 @@ mBlank259Id equ 259 .dw mNullHandler ; handler (predefined) .dw 0 ; nameSelector mEpochSetCustom: -mEpochSetCustomId equ 260 +mEpochSetCustomId equ 275 .dw mEpochSetCustomId ; id .dw mEpochId ; parentId .dw mEpochSetCustomNameId ; nameId @@ -2452,7 +2590,7 @@ mEpochSetCustomId equ 260 .dw mEpochSetCustomHandler ; handler (to be implemented) .dw 0 ; nameSelector mEpochGetCustom: -mEpochGetCustomId equ 261 +mEpochGetCustomId equ 276 .dw mEpochGetCustomId ; id .dw mEpochId ; parentId .dw mEpochGetCustomNameId ; nameId @@ -2463,7 +2601,7 @@ mEpochGetCustomId equ 261 ; MenuGroup CLK: children ; MenuGroup CLK: children: row 0 mGetNow: -mGetNowId equ 262 +mGetNowId equ 277 .dw mGetNowId ; id .dw mClkId ; parentId .dw mGetNowNameId ; nameId @@ -2472,7 +2610,7 @@ mGetNowId equ 262 .dw mGetNowHandler ; handler (to be implemented) .dw 0 ; nameSelector mGetNowDate: -mGetNowDateId equ 263 +mGetNowDateId equ 278 .dw mGetNowDateId ; id .dw mClkId ; parentId .dw mGetNowDateNameId ; nameId @@ -2481,7 +2619,7 @@ mGetNowDateId equ 263 .dw mGetNowDateHandler ; handler (to be implemented) .dw 0 ; nameSelector mGetNowTime: -mGetNowTimeId equ 264 +mGetNowTimeId equ 279 .dw mGetNowTimeId ; id .dw mClkId ; parentId .dw mGetNowTimeNameId ; nameId @@ -2490,7 +2628,7 @@ mGetNowTimeId equ 264 .dw mGetNowTimeHandler ; handler (to be implemented) .dw 0 ; nameSelector mGetNowAppDateTime: -mGetNowAppDateTimeId equ 265 +mGetNowAppDateTimeId equ 280 .dw mGetNowAppDateTimeId ; id .dw mClkId ; parentId .dw mGetNowAppDateTimeNameId ; nameId @@ -2499,7 +2637,7 @@ mGetNowAppDateTimeId equ 265 .dw mGetNowAppDateTimeHandler ; handler (to be implemented) .dw 0 ; nameSelector mGetNowUTCDateTime: -mGetNowUTCDateTimeId equ 266 +mGetNowUTCDateTimeId equ 281 .dw mGetNowUTCDateTimeId ; id .dw mClkId ; parentId .dw mGetNowUTCDateTimeNameId ; nameId @@ -2509,7 +2647,7 @@ mGetNowUTCDateTimeId equ 266 .dw 0 ; nameSelector ; MenuGroup CLK: children: row 1 mSetTimeZone: -mSetTimeZoneId equ 267 +mSetTimeZoneId equ 282 .dw mSetTimeZoneId ; id .dw mClkId ; parentId .dw mSetTimeZoneNameId ; nameId @@ -2518,7 +2656,7 @@ mSetTimeZoneId equ 267 .dw mSetTimeZoneHandler ; handler (to be implemented) .dw 0 ; nameSelector mGetTimeZone: -mGetTimeZoneId equ 268 +mGetTimeZoneId equ 283 .dw mGetTimeZoneId ; id .dw mClkId ; parentId .dw mGetTimeZoneNameId ; nameId @@ -2527,7 +2665,7 @@ mGetTimeZoneId equ 268 .dw mGetTimeZoneHandler ; handler (to be implemented) .dw 0 ; nameSelector mSetClockTimeZone: -mSetClockTimeZoneId equ 269 +mSetClockTimeZoneId equ 284 .dw mSetClockTimeZoneId ; id .dw mClkId ; parentId .dw mSetClockTimeZoneNameId ; nameId @@ -2536,7 +2674,7 @@ mSetClockTimeZoneId equ 269 .dw mSetClockTimeZoneHandler ; handler (to be implemented) .dw 0 ; nameSelector mGetClockTimeZone: -mGetClockTimeZoneId equ 270 +mGetClockTimeZoneId equ 285 .dw mGetClockTimeZoneId ; id .dw mClkId ; parentId .dw mGetClockTimeZoneNameId ; nameId @@ -2545,7 +2683,7 @@ mGetClockTimeZoneId equ 270 .dw mGetClockTimeZoneHandler ; handler (to be implemented) .dw 0 ; nameSelector mSetClock: -mSetClockId equ 271 +mSetClockId equ 286 .dw mSetClockId ; id .dw mClkId ; parentId .dw mSetClockNameId ; nameId @@ -2555,7 +2693,7 @@ mSetClockId equ 271 .dw 0 ; nameSelector ; Table of 2-byte pointers to names in the pool of strings below. -mMenuNameTableSize equ 280 +mMenuNameTableSize equ 287 mMenuNameTable: mNullNameId equ 0 .dw mNullName @@ -2911,211 +3049,225 @@ mClearStatNameId equ 175 .dw mClearStatName mClearTvmNameId equ 176 .dw mClearTvmName -mFixNameId equ 177 +mClearDisplayNameId equ 177 + .dw mClearDisplayName +mFixNameId equ 178 .dw mFixName -mFixAltNameId equ 178 +mFixAltNameId equ 179 .dw mFixAltName -mSciNameId equ 179 +mSciNameId equ 180 .dw mSciName -mSciAltNameId equ 180 +mSciAltNameId equ 181 .dw mSciAltName -mEngNameId equ 181 +mEngNameId equ 182 .dw mEngName -mEngAltNameId equ 182 +mEngAltNameId equ 183 .dw mEngAltName -mRadNameId equ 183 +mRadNameId equ 184 .dw mRadName -mRadAltNameId equ 184 +mRadAltNameId equ 185 .dw mRadAltName -mDegNameId equ 185 +mDegNameId equ 186 .dw mDegName -mDegAltNameId equ 186 +mDegAltNameId equ 187 .dw mDegAltName -mNumResultModeRealNameId equ 187 +mNumResultModeRealNameId equ 188 .dw mNumResultModeRealName -mNumResultModeRealAltNameId equ 188 +mNumResultModeRealAltNameId equ 189 .dw mNumResultModeRealAltName -mNumResultModeComplexNameId equ 189 +mNumResultModeComplexNameId equ 190 .dw mNumResultModeComplexName -mNumResultModeComplexAltNameId equ 190 +mNumResultModeComplexAltNameId equ 191 .dw mNumResultModeComplexAltName -mComplexModeRectNameId equ 191 +mComplexModeRectNameId equ 192 .dw mComplexModeRectName -mComplexModeRectAltNameId equ 192 +mComplexModeRectAltNameId equ 193 .dw mComplexModeRectAltName -mComplexModeRadNameId equ 193 +mComplexModeRadNameId equ 194 .dw mComplexModeRadName -mComplexModeRadAltNameId equ 194 +mComplexModeRadAltNameId equ 195 .dw mComplexModeRadAltName -mComplexModeDegNameId equ 195 +mComplexModeDegNameId equ 196 .dw mComplexModeDegName -mComplexModeDegAltNameId equ 196 +mComplexModeDegAltNameId equ 197 .dw mComplexModeDegAltName -mSetRegSizeNameId equ 197 +mSetRegSizeNameId equ 198 .dw mSetRegSizeName -mGetRegSizeNameId equ 198 +mGetRegSizeNameId equ 199 .dw mGetRegSizeName -mSetStackSizeNameId equ 199 +mSetStackSizeNameId equ 200 .dw mSetStackSizeName -mGetStackSizeNameId equ 200 +mGetStackSizeNameId equ 201 .dw mGetStackSizeName -mCommaEENormalNameId equ 201 +mCommaEENormalNameId equ 202 .dw mCommaEENormalName -mCommaEENormalAltNameId equ 202 +mCommaEENormalAltNameId equ 203 .dw mCommaEENormalAltName -mCommaEESwappedNameId equ 203 +mCommaEESwappedNameId equ 204 .dw mCommaEESwappedName -mCommaEESwappedAltNameId equ 204 +mCommaEESwappedAltNameId equ 205 .dw mCommaEESwappedAltName -mFormatRecordRawNameId equ 205 +mFormatRecordRawNameId equ 206 .dw mFormatRecordRawName -mFormatRecordRawAltNameId equ 206 +mFormatRecordRawAltNameId equ 207 .dw mFormatRecordRawAltName -mFormatRecordStringNameId equ 207 +mFormatRecordStringNameId equ 208 .dw mFormatRecordStringName -mFormatRecordStringAltNameId equ 208 +mFormatRecordStringAltNameId equ 209 .dw mFormatRecordStringAltName -mStackDupNameId equ 209 +mStackDupNameId equ 210 .dw mStackDupName -mStackRollUpNameId equ 210 +mStackRollUpNameId equ 211 .dw mStackRollUpName -mStackRollDownNameId equ 211 +mStackRollDownNameId equ 212 .dw mStackRollDownName -mStackDropNameId equ 212 +mStackDropNameId equ 213 .dw mStackDropName -mStackExchangeXYNameId equ 213 +mStackExchangeXYNameId equ 214 .dw mStackExchangeXYName -mFToCNameId equ 214 +mFToCNameId equ 215 .dw mFToCName -mCToFNameId equ 215 +mCToFNameId equ 216 .dw mCToFName -mInhgToHpaNameId equ 216 +mInhgToHpaNameId equ 217 .dw mInhgToHpaName -mHpaToInhgNameId equ 217 +mHpaToInhgNameId equ 218 .dw mHpaToInhgName -mMiToKmNameId equ 218 +mMiToKmNameId equ 219 .dw mMiToKmName -mKmToMiNameId equ 219 +mKmToMiNameId equ 220 .dw mKmToMiName -mFtToMNameId equ 220 +mFtToMNameId equ 221 .dw mFtToMName -mMToFtNameId equ 221 +mMToFtNameId equ 222 .dw mMToFtName -mInToCmNameId equ 222 +mInToCmNameId equ 223 .dw mInToCmName -mCmToInNameId equ 223 +mCmToInNameId equ 224 .dw mCmToInName -mMilToMicronNameId equ 224 +mMilToMicronNameId equ 225 .dw mMilToMicronName -mMicronToMilNameId equ 225 +mMicronToMilNameId equ 226 .dw mMicronToMilName -mLbsToKgNameId equ 226 +mLbsToKgNameId equ 227 .dw mLbsToKgName -mKgToLbsNameId equ 227 +mKgToLbsNameId equ 228 .dw mKgToLbsName -mOzToGNameId equ 228 +mOzToGNameId equ 229 .dw mOzToGName -mGToOzNameId equ 229 +mGToOzNameId equ 230 .dw mGToOzName -mGalToLNameId equ 230 +mGalToLNameId equ 231 .dw mGalToLName -mLToGalNameId equ 231 +mLToGalNameId equ 232 .dw mLToGalName -mFlozToMlNameId equ 232 +mFlozToMlNameId equ 233 .dw mFlozToMlName -mMlToFlozNameId equ 233 +mMlToFlozNameId equ 234 .dw mMlToFlozName -mCalToKjNameId equ 234 +mCalToKjNameId equ 235 .dw mCalToKjName -mKjToCalNameId equ 235 +mKjToCalNameId equ 236 .dw mKjToCalName -mHpToKwNameId equ 236 +mHpToKwNameId equ 237 .dw mHpToKwName -mKwToHpNameId equ 237 +mKwToHpNameId equ 238 .dw mKwToHpName -mLeapYearNameId equ 238 +mMpgToLkmNameId equ 239 + .dw mMpgToLkmName +mLkmToMpgNameId equ 240 + .dw mLkmToMpgName +mPsiToKpaNameId equ 241 + .dw mPsiToKpaName +mKpaToPsiNameId equ 242 + .dw mKpaToPsiName +mAcreToHectareNameId equ 243 + .dw mAcreToHectareName +mHectareToAcreNameId equ 244 + .dw mHectareToAcreName +mLeapYearNameId equ 245 .dw mLeapYearName -mDayOfWeekNameId equ 239 +mDayOfWeekNameId equ 246 .dw mDayOfWeekName -mDateToEpochDaysNameId equ 240 +mDateToEpochDaysNameId equ 247 .dw mDateToEpochDaysName -mEpochDaysToDateNameId equ 241 +mEpochDaysToDateNameId equ 248 .dw mEpochDaysToDateName -mDateRelatedToSecondsNameId equ 242 +mDateRelatedToSecondsNameId equ 249 .dw mDateRelatedToSecondsName -mSecondsToDurationNameId equ 243 +mSecondsToDurationNameId equ 250 .dw mSecondsToDurationName -mSecondsToTimeNameId equ 244 +mSecondsToTimeNameId equ 251 .dw mSecondsToTimeName -mEpochSecondsToAppDateTimeNameId equ 245 +mEpochSecondsToAppDateTimeNameId equ 252 .dw mEpochSecondsToAppDateTimeName -mEpochSecondsToUTCDateTimeNameId equ 246 +mEpochSecondsToUTCDateTimeNameId equ 253 .dw mEpochSecondsToUTCDateTimeName -mTimeZoneToHoursNameId equ 247 +mTimeZoneToHoursNameId equ 254 .dw mTimeZoneToHoursName -mHoursToTimeZoneNameId equ 248 +mHoursToTimeZoneNameId equ 255 .dw mHoursToTimeZoneName -mDopsNameId equ 249 +mDopsNameId equ 256 .dw mDopsName -mEpochNameId equ 250 +mEpochNameId equ 257 .dw mEpochName -mClkNameId equ 251 +mClkNameId equ 258 .dw mClkName -mDateShrinkNameId equ 252 +mDateShrinkNameId equ 259 .dw mDateShrinkName -mDateExtendNameId equ 253 +mDateExtendNameId equ 260 .dw mDateExtendName -mDateCutNameId equ 254 +mDateCutNameId equ 261 .dw mDateCutName -mDateLinkNameId equ 255 +mDateLinkNameId equ 262 .dw mDateLinkName -mEpochUnixNameId equ 256 +mEpochUnixNameId equ 263 .dw mEpochUnixName -mEpochUnixAltNameId equ 257 +mEpochUnixAltNameId equ 264 .dw mEpochUnixAltName -mEpochNtpNameId equ 258 +mEpochNtpNameId equ 265 .dw mEpochNtpName -mEpochNtpAltNameId equ 259 +mEpochNtpAltNameId equ 266 .dw mEpochNtpAltName -mEpochGpsNameId equ 260 +mEpochGpsNameId equ 267 .dw mEpochGpsName -mEpochGpsAltNameId equ 261 +mEpochGpsAltNameId equ 268 .dw mEpochGpsAltName -mEpochTiosNameId equ 262 +mEpochTiosNameId equ 269 .dw mEpochTiosName -mEpochTiosAltNameId equ 263 +mEpochTiosAltNameId equ 270 .dw mEpochTiosAltName -mEpochY2kNameId equ 264 +mEpochY2kNameId equ 271 .dw mEpochY2kName -mEpochY2kAltNameId equ 265 +mEpochY2kAltNameId equ 272 .dw mEpochY2kAltName -mEpochCustomNameId equ 266 +mEpochCustomNameId equ 273 .dw mEpochCustomName -mEpochCustomAltNameId equ 267 +mEpochCustomAltNameId equ 274 .dw mEpochCustomAltName -mEpochSetCustomNameId equ 268 +mEpochSetCustomNameId equ 275 .dw mEpochSetCustomName -mEpochGetCustomNameId equ 269 +mEpochGetCustomNameId equ 276 .dw mEpochGetCustomName -mGetNowNameId equ 270 +mGetNowNameId equ 277 .dw mGetNowName -mGetNowDateNameId equ 271 +mGetNowDateNameId equ 278 .dw mGetNowDateName -mGetNowTimeNameId equ 272 +mGetNowTimeNameId equ 279 .dw mGetNowTimeName -mGetNowAppDateTimeNameId equ 273 +mGetNowAppDateTimeNameId equ 280 .dw mGetNowAppDateTimeName -mGetNowUTCDateTimeNameId equ 274 +mGetNowUTCDateTimeNameId equ 281 .dw mGetNowUTCDateTimeName -mSetTimeZoneNameId equ 275 +mSetTimeZoneNameId equ 282 .dw mSetTimeZoneName -mGetTimeZoneNameId equ 276 +mGetTimeZoneNameId equ 283 .dw mGetTimeZoneName -mSetClockTimeZoneNameId equ 277 +mSetClockTimeZoneNameId equ 284 .dw mSetClockTimeZoneName -mGetClockTimeZoneNameId equ 278 +mGetClockTimeZoneNameId equ 285 .dw mGetClockTimeZoneName -mSetClockNameId equ 279 +mSetClockNameId equ 286 .dw mSetClockName ; Table of names as NUL terminated C strings. @@ -3473,6 +3625,8 @@ mClearStatName: .db 'C', 'L', ScapSigma, 0 mClearTvmName: .db "CLTV", 0 +mClearDisplayName: + .db "CLD", 0 mFixName: .db "FIX", 0 mFixAltName: @@ -3530,13 +3684,13 @@ mCommaEESwappedName: mCommaEESwappedAltName: .db 'E', 'E', Scomma, Sblock, 0 mFormatRecordRawName: - .db SlBrace, SPeriod, SPeriod, SrBrace, 0 + .db SlBrace, Speriod, Speriod, SrBrace, 0 mFormatRecordRawAltName: - .db SlBrace, SPeriod, SPeriod, SrBrace, Sblock, 0 + .db SlBrace, Speriod, Speriod, SrBrace, Sblock, 0 mFormatRecordStringName: - .db Squote, SPeriod, SPeriod, Squote, 0 + .db Squote, Speriod, Speriod, Squote, 0 mFormatRecordStringAltName: - .db Squote, SPeriod, SPeriod, Squote, Sblock, 0 + .db Squote, Speriod, Speriod, Squote, Sblock, 0 mStackDupName: .db "DUP", 0 mStackRollUpName: @@ -3595,6 +3749,18 @@ mHpToKwName: .db Sconvert, 'k', 'W', 0 mKwToHpName: .db Sconvert, 'h', 'p', 0 +mMpgToLkmName: + .db Sconvert, 'L', 'k', 'm', 0 +mLkmToMpgName: + .db Sconvert, 'm', 'p', 'g', 0 +mPsiToKpaName: + .db Sconvert, 'k', 'P', 'a', 0 +mKpaToPsiName: + .db Sconvert, 'p', 's', 'i', 0 +mAcreToHectareName: + .db Sconvert, 'h', 'a', 0 +mHectareToAcreName: + .db Sconvert, 'a', 'c', 'r', 0 mLeapYearName: .db "LEAP", 0 mDayOfWeekName: diff --git a/src/menudef.txt b/src/menudef.txt index 7ec7b084..01571bb2 100644 --- a/src/menudef.txt +++ b/src/menudef.txt @@ -311,6 +311,9 @@ MenuGroup root mRoot [ MenuItem CL mClearStat MenuItem CLTV mClearTvm ] + MenuRow [ + MenuItem CLD mClearDisplay + ] ] MenuGroup MODE mMode [ MenuRow [ @@ -338,8 +341,8 @@ MenuGroup root mRoot [ MenuItemAlt EE EE mCommaEENormal MenuItemAlt EE EE mCommaEESwapped MenuItem * * - MenuItemAlt mFormatRecordRaw - MenuItemAlt mFormatRecordString + MenuItemAlt mFormatRecordRaw + MenuItemAlt mFormatRecordString ] ] MenuGroup STK mStack [ @@ -394,20 +397,20 @@ MenuGroup root mRoot [ MenuItem kW mHpToKw MenuItem hp mKwToHp ] - # MenuRow [ - # MenuItem Lkm mL100kmToMpg - # MenuItem mpg mMpgToL100km - # MenuItem * * - # MenuItem kPa mPsiToKpa - # MenuItem psi mKpaToPsi - # ] - # MenuRow [ - # MenuItem ha mAcreToHectare - # MenuItem acr mHectareToAcre - # MenuItem * * - # MenuItem * * - # MenuItem * * - # ] + MenuRow [ + MenuItem Lkm mMpgToLkm + MenuItem mpg mLkmToMpg + MenuItem * * + MenuItem kPa mPsiToKpa + MenuItem psi mKpaToPsi + ] + MenuRow [ + MenuItem ha mAcreToHectare + MenuItem acr mHectareToAcre + MenuItem * * + MenuItem * * + MenuItem * * + ] ] MenuGroup DATE mDate [ MenuRow [ diff --git a/src/menuhandlers.asm b/src/menuhandlers.asm index cdb0ce2f..4535a664 100644 --- a/src/menuhandlers.asm +++ b/src/menuhandlers.asm @@ -591,6 +591,105 @@ mKwToHpHandler: bcall(_FPDiv) jp replaceX +;----------------------------------------------------------------------------- + +; Description: Convert mpg (miles per US gallon) to lkm (Liters per 100 km): +; lkm = 100/[mpg * (km/mile) / (litre/gal)] +mMpgToLkmHandler: + call closeInputAndRecallX + call op2SetKmPerMi + bcall(_FPMult) + call op2SetLPerGal + bcall(_FPDiv) + call op1ToOp2 + call op1Set100 + bcall(_FPDiv) + jp replaceX + +; Description: Convert lkm to mpg: mpg = 100/lkm * (litre/gal) / (km/mile). +mLkmToMpgHandler: + call closeInputAndRecallX + call op1ToOp2 + call op1Set100 + bcall(_FPDiv) + call op2SetLPerGal + bcall(_FPMult) + call op2SetKmPerMi + bcall(_FPDiv) + jp replaceX + +; Description: Convert PSI (pounds per square inch) to kiloPascal. +; P(Pa) = P(psi) * 0.45359237 kg/lbf * (9.80665 m/s^2) / (0.0254 m/in)^2 +; P(Pa) = P(psi) * 0.45359237 kg/lbf * (9.80665 m/s^2) / (2.54 cm/in)^2 * 10000 +; P(kPa) = P(psi) * 0.45359237 kg/lbf * (9.80665 m/s^2) / (2.54 cm/in)^2 * 10 +; See https://en.wikipedia.org/wiki/Pound_per_square_inch. +mPsiToKpaHandler: + call closeInputAndRecallX + call op2SetKgPerLbs + bcall(_FPMult) + call op2SetStandardGravity + bcall(_FPMult) + call op2SetCmPerIn + bcall(_FPDiv) + call op2SetCmPerIn + bcall(_FPDiv) + call op2Set10 + bcall(_FPMult) + jp replaceX + +; Description: Convert PSI (pounds per square inch) to kiloPascal. +; P(psi) = P(kPa) / 10 * (2.54m/in)^2 / (0.45359237 kg/lbf) / (9.80665 m/s^2) +; See https://en.wikipedia.org/wiki/Pound_per_square_inch. +mKpaToPsiHandler: + call closeInputAndRecallX + call op2Set10 + bcall(_FPDiv) + call op2SetCmPerIn + bcall(_FPMult) + call op2SetCmPerIn + bcall(_FPMult) + call op2SetStandardGravity + bcall(_FPDiv) + call op2SetKgPerLbs + bcall(_FPDiv) + jp replaceX + +;----------------------------------------------------------------------------- + +; Description: Convert US acre (66 ft x 660 ft) to hectare (100 m)^2. See +; https://en.wikipedia.org/wiki/Acre, and +; https://en.wikipedia.org/wiki/Hectare. +; Area(ha) = Area(acre) * 43560 * (0.3048 m/ft)^2 / (100 m)^2 +mAcreToHectareHandler: + call closeInputAndRecallX + call op2SetSqFtPerAcre + bcall(_FPMult) + call op2SetMPerFt + bcall(_FPMult) + call op2SetMPerFt + bcall(_FPMult) + call op2Set100 + bcall(_FPDiv) + call op2Set100 + bcall(_FPDiv) + jp replaceX + +; Description: Convert hectare to US acre. +; Area(acre) = Area(ha) * (100 m)^2 / 43560 / (0.3048 m/ft)^2 +mHectareToAcreHandler: + call closeInputAndRecallX + call op2Set100 + bcall(_FPMult) + call op2Set100 + bcall(_FPMult) + call op2SetMPerFt + bcall(_FPDiv) + call op2SetMPerFt + bcall(_FPDiv) + call op2SetSqFtPerAcre + bcall(_FPDiv) + jp replaceX + ;----------------------------------------------------------------------------- ; Children nodes of CONV menu. ;----------------------------------------------------------------------------- @@ -630,7 +729,7 @@ mPToRHandler: ; Output: ; - Y: theta ; - X: r -mRtoPHandler: +mRToPHandler: call closeInputAndRecallXY ; OP1=Y; OP2=X call op1ExOp2 ; OP1=x; OP2=y call rectToPolar ; OP1=r; OP2=theta @@ -708,7 +807,7 @@ saveFormatDigits: ld a, (argValue) cp 10 jr c, saveFormatDigitsContinue - ld a, $FF ; variable number of digits, not fixed + ld a, fmtDigitsFloating ; "floating" number of digits, i.e. not fixed saveFormatDigitsContinue: ld (fmtDigits), a ret @@ -1030,3 +1129,13 @@ mClearStatHandler: mClearTvmHandler: jp mTvmClearHandler + +mClearDisplayHandler: + bcall(_ClrLCDFull) + bcall(_ColdInitDisplay) + bcall(_InitDisplay) + ret + +; mClearVarsHandler: +; bcall(_ClearVars) +; ret diff --git a/src/modes2.asm b/src/modes2.asm index c5be00fe..27ede242 100644 --- a/src/modes2.asm +++ b/src/modes2.asm @@ -7,9 +7,29 @@ ; Description: Initialize miscellaneous settings under the MODES menu. ColdInitModes: - ld a, commaEEModeNormal ; set ',EE' button to act as labeled on keypad + ; set ',EE' button to act as labeled on keypad + ld a, commaEEModeNormal ld (commaEEMode), a - ; + ; set to {..} mode instead of ".." mode ld a, formatRecordModeRaw ld (formatRecordMode), a + ; [[fallthrough]] + +; Description: Set the Trig, Display, and Display Digit modes which are shared +; with TI-OS to a known state. +; +; The complex result modes (RRES, CRES) are also shared with the TI-OS through +; the `numMode` variable (which is the same byte as the `fmtFlags`), but those +; are cold initialized by ColdInitComplex(), so we don't need to initialize +; them here. +; +; Destroys: A +coldInitOSModes: + ; set to RAD (instead of DEG) + res trigDeg, (iy + trigFlags) + ; set to FIX (instead of SCI or ENG) + res fmtExponent, (iy + fmtFlags) + ; set number of digits to "floating" + ld a, fmtDigitsFloating + ld (fmtDigits), a ret diff --git a/src/offsetdatetime2.asm b/src/offsetdatetime2.asm index 575a9356..5a630db5 100644 --- a/src/offsetdatetime2.asm +++ b/src/offsetdatetime2.asm @@ -238,7 +238,7 @@ SubRpnOffsetDateTimeByObject: call getOp3RpnObjectTypePageTwo ; A=type; HL=OP3 cp rpnObjectTypeReal jr z, subRpnOffsetDateTimeBySeconds - cp RpnObjectTypeOffsetDateTime ; ZF=1 if RpnOffsetDateTime + cp rpnObjectTypeOffsetDateTime ; ZF=1 if RpnOffsetDateTime jr z, subRpnOffsetDateTimeByRpnOffsetDateTime cp rpnObjectTypeDuration jr z, subRpnOffsetDateTimeByRpnDuration diff --git a/src/prime2.asm b/src/prime2.asm index 738ad298..bddfc30e 100644 --- a/src/prime2.asm +++ b/src/prime2.asm @@ -3,7 +3,61 @@ ; Copyright (c) 2023 Brian T. Park ; ; Various implementations of the primeFactor() function which calculates -; the smallest prime factor, or 1 if the number is a prime. +; the smallest prime factor, or returns 1 if the number is a prime. +; +; There are at least 4 versions. Each can be selected passing the appropriate +; `-D` flag to `spasm` in the Makefile: +; +; - USE_PRIME_FACTOR_FLOAT +; - uses TI-OS _FPDiv() and _Frac() routines to determine if `candidate` is a +; factor of `input` +; - Benchmarks (15 MHz, TilEm): +; - 19997*19997: 28.6 seconds +; - 65521*65521: 94.5 seconds +; - About 693 effective-candidates / second. +; - USE_PRIME_FACTOR_INT +; - uses divU32U32() to determine if `candidate` is a factor of `input` +; - Benchmarks (15 MHz, TilEm, divU32U32): +; - 19997*19997: 10.7 seconds +; - 65521*65521: 34.4 seconds +; - About 1905 effective-candidates / second +; - USE_PRIME_FACTOR_MOD_U32_BY_BC +; - uses modU32ByBC() to determine if `candidate` is a factor of `input` +; - Benchmarks (15 MHz, TilEm, modU32ByBC): +; - 19997*19997: 4.2 seconds +; - 65521*65521: 12.9 seconds +; - About 5079 effective-candidates / second. +; - USE_PRIME_FACTOR_MOD_HLSP_BY_BC +; - uses modHLSPByBC() to determine if `candidate` is a factor of `input` +; - Benchmarks (15 MHz, TilEm, modHLSPByBC using (SP)): +; - 19997*19997: 2.9 seconds +; - 65521*65521: 9.0 seconds +; - About 7280 effective-candidates / second. +; - USE_PRIME_FACTOR_MOD_HLIX_BY_BC +; - uses modHLIXByBC() to determine if `candidate` is a factor of `input` +; - Benchmarks (15 MHz, TilEm, modHLIXByBC): +; - 65521*65521: 7.0 seconds +; - About 9360 effective-candidates / second. +; - Benchmarks (15 MHz, TilEm, modHLIXByBC, rearrange code assuming rare +; overflow): +; - 65521*65521: 6.7 seconds +; - About 9880 effective-candidates / second. +; - Benchmarks (15 MHz, modHLIXByBC, use JR instead of JP on rare branch): +; - 65521*65521: 6.6 seconds +; - About 9927 effective-candidates / second +; - Benchmarks (15 MHz, modHLIXByBC, remove unnecessary 'or a') +; - 65521*65521: 6.3 seconds +; - About 10400 effective-candidates / second +; - USE_PRIME_FACTOR_MOD_DEIX_BY_BC (default) +; - Benchmarks (15 MHz, modDEIXByBC) +; - 65521*65521: 3.8 seconds +; - About 17242 effective-candidates / second +; - thankss goes to the responders of the +; https://www.cemetech.net/forum/viewtopic.php?p=307636 thread for various +; ideas on improving this algorithm +; +; The USE_PRIME_FACTOR_MOD_DEIX_BY_BC version is now 25X faster than the +; floating point version. ; ; All of these take advantage of the fact that every prime above 3 is of the ; form (6n-1) or (6n+1), where n=1,2,3,... It checks candidate divisors from 5 @@ -12,20 +66,61 @@ ; end of the iteration, then no prime factor was found, so X is a prime. ;----------------------------------------------------------------------------- +; Description: Choose one of the various primeFactorXXX() routines. +; Input: +; - OP1:real=input +; Output: +; - OP1=1 if prime, or its smallest prime factor (>1) otherwise +; Throws: Err:Domain if OP1 is not an integer in the interval [2,2^32-1]. +PrimeFactor: + ; TODO: Replace the following validation with convertOP1ToU32(). I think we + ; just need to check for 0 and 1. Oh, and create a sqrtU32() replacement + ; for the floating point function _SqRoot(). + ; + ; Check 0 + bcall(_CkOP1FP0) + jr z, primeFactorError + ; Check 1 + bcall(_OP2Set1) ; OP2 = 1 + bcall(_CpOP1OP2) ; if OP1==1: ZF=1 + jr z, primeFactorError + bcall(_OP1ToOP4) ; save OP4 = X + ; Check integer >= 0 + bcall(_CkPosInt) ; if OP1 >= 0: ZF=1 + jr nz, primeFactorError + ; Check unsigned 32-bit integer, i.e. < 2^32. + call op2Set2Pow32PageTwo ; if OP1 >= 2^32: CF=0 + bcall(_CpOP1OP2) + jr nc, primeFactorError + +#ifdef USE_PRIME_FACTOR_FLOAT + jp primeFactorFloat +#else + #ifdef USE_PRIME_FACTOR_INT + jp primeFactorInt + #else + jp primeFactorMod + #endif +#endif + +primeFactorError: + bcall(_ErrDomain) ; throw exception + +primeFactorBreak: + bcall(_RunIndicOff) ; disable run indicator + res onInterrupt, (iy + onFlags) + bcall(_ErrBreak) ; throw exception + +;----------------------------------------------------------------------------- + #ifdef USE_PRIME_FACTOR_FLOAT ; Description determine if OP1 is a prime using floating point routines ; provided by the TI-OS. This is the slowest. ; -; Input: OP1: an integer in the range of [2, 2^32-1]. -; Output: OP1: 1 if prime, smallest prime factor if not +; Input: OP1:real=input, an integer in the range of [2, 2^32-1]. +; Output: OP1:real=1 if prime, or the smallest prime factor if not ; Destroys: all registers, OP2, OP4, OP5, OP6 -; -; Benchmarks (6 MHz): -; - 4001*4001: 15 seconds -; - 10007*10007: 36 seconds -; - 19997*19997: 72 seconds -; - About 280 effective-candidates / second. primeFactorFloat: ; Check 2 bcall(_OP2Set2) ; OP2 = 2 @@ -104,45 +199,32 @@ primeFactorFloatCheckDiv: ;----------------------------------------------------------------------------- -primeFactorBreak: - bcall(_RunIndicOff) ; disable run indicator - res onInterrupt, (iy + onFlags) - bcall(_ErrBreak) ; throw exception - -;----------------------------------------------------------------------------- - #ifdef USE_PRIME_FACTOR_INT ; Description determine if OP1 is a prime using divU32U32() routine. This is ; almost 3X fastger than primeFactorFloat() but I think we can better. ; -; Input: OP1: an integer in the range of [2, 2^32-1]. -; Output: OP1: 1 if prime, smallest prime factor if not +; Input: OP1:real=an integer in the range of [2, 2^32-1]. +; Output: OP1:real=1 if prime, or the smallest prime factor if not ; Destroys: all registers, OP1, OP2, OP3, OP4, OP5, OP6 -; -; Benchmarks (6 MHz): -; -; Using divU32U32(): -; - 4001*4001: 5 seconds -; - 10007*10007: 14 seconds -; - 19997*19997: 27 seconds -; - About 750-800 effective-candidates / second. +; Throws: if OP1 not an integer primeFactorInt: - ld hl, OP4 - call convertOP1ToU32 ; OP4=X - ; Calc root(X) to OP5, to get it out of the way. The sqrt() function could - ; be done using integer operations, but it's done only once in the routine, - ; so we don't gain much speed improvement. - bcall(_SqRoot) ; OP1 = sqrt(OP1), uses OP1-OP3 + bcall(_OP1ToOP4) ; OP4=X + call convertOP1ToU32 ; OP1=int(X); throws if not integer + ; Calc limit=sqrt(X). The sqrt() function could be done using integer + ; operations, but it's done only once in the routine, so we don't gain much + ; speed improvement. + bcall(_OP1ExOP4) ; OP1=X; OP4=int(X) + bcall(_SqRoot) ; OP1=sqrt(X), uses OP1-OP3 bcall(_RndGuard) - bcall(_Trunc) ; OP1 = trunc(sqrt(X)), uses OP1,OP2 - ld hl, OP5 - call convertOP1ToU32 ; OP5=limit=sqrt(X) + bcall(_Trunc) ; OP1=trunc(sqrt(X)), uses OP1,OP2 + call convertOP1ToU32 ; OP1=limit=int(trunc(sqrt(X))) + bcall(_OP1ToOP5) ; OP5=limit ; Check 2 ld a, 2 ld hl, OP6 call setU32ToA ; OP6=candidate=2 - ld de, OP4 + ld de, OP4 ; OP4=int(X) call cmpU32U32 ; if X==2: ZF=1 jr z, primeFactorIntYes ; Check divisible by 2 @@ -190,19 +272,20 @@ primeFactorIntLoop: call addU32ByA jr primeFactorIntLoop primeFactorIntNo: - ld hl, OP6 - call convertU32ToOP1 + bcall(_OP6ToOP1) ; OP1=candidate + call convertU32ToOP1 ; OP1=float(candidate) ret primeFactorIntYes: bcall(_OP1Set1) ret +; Description: Check if `candidate` (OP6) is an integer factor of `input` (OP4). ; Input: -; - OP4=X -; - OP6=candidate +; - OP4:u32=input +; - OP6:u32=candidate ; Output: -; - OP1=quotient -; - OP2=remainder +; - OP1:u32=quotient +; - OP2:u32=remainder ; - ZF=1 if remainder==0 ; Destroys: A, BC, DE, HL primeFactorIntCheckDiv: @@ -223,29 +306,8 @@ primeFactorIntCheckDiv: ; Description determine if OP1 is a prime using the modU32ByBC() routine. This ; is 7X faster than primeFactorFloat(), and 2.5X faster than primeFactorInt(). ; -; Benchmarks (6 MHz, modU32ByBC): -; - 4001*4001: 2.4 seconds -; - 10007*10007: 5.5 seconds -; - 19997*19997: 10.5 seconds -; - 65521*65521: 33 seconds -; - About 2000 effective-candidates / second. -; -; Benchmarks (15 MHz, modU32ByBC): -; - 4001*4001: 1.0 seconds -; - 10007*10007: 2.3 seconds -; - 19997*19997: 4.2 seconds -; - 65521*65521: 12.9 seconds -; - About 5000 effective-candidates / second. -; -; Benchmarks (15 MHz, modOP1ByBC): -; - 4001*4001: 0.85 seconds -; - 10007*10007: 1.6 seconds -; - 19997*19997: 2.9 seconds -; - 65521*65521: 9.0 seconds -; - About 7280 effective-candidates / second. -; -; Input: OP1: an integer in the range of [2, 2^32-1]. -; Output: OP1: 1 if prime, smallest prime factor if not +; Input: OP1:real=an integer in the range of [2, 2^32-1]. +; Output: OP1:real=1 if prime, or the smallest prime factor if not ; Destroys: all registers, OP1-OP3 primeFactorMod: call pushRaw9Op1 ; FPS=[X]; HL=X @@ -285,10 +347,6 @@ primeFactorModSetup: ; OP2:u16=limit ; BC:u16=candidate primeFactorModLoop: - ld hl, (OP2) ; HL=limit - or a ; clear CF - sbc hl, bc ; CF=1 if candidate>limit - jr c, primeFactorModYes ; Check for ON/Break bit onInterrupt, (iy + onFlags) jr nz, primeFactorBreak @@ -306,51 +364,105 @@ primeFactorModLoop: inc bc inc bc inc bc - jr primeFactorModLoop + ; Terminate if all candidates have been checked. + ; NOTE 1: At first glance, the 'sbc' instruction that checks for the + ; terminating (candidate>limit) will fail if `limit=sqrt(input)=65535` + ; because no 16-bit candiate value will satisfy the terminating condition. + ; It actually looks worse than that. The candidate prime factor increments + ; in steps of 6 (first by 2, then by 4) through each iteration of the loop, + ; so at the upper end, the candidate goes from 65531, to 65533, then to + ; 65537, which wraps to 1 since BC is an unsigned 16-bit integer. So it + ; looks like this terminating condition will fail for `limit>=65531`. + ; However, through pure luck, when the candidate wraps around to 1 (i.e. + ; 65537), the mod(u32,u16) function returns 0 (since 1 divides into any + ; number), so the primeFactorModCheckDiv() function will return ZF=1, which + ; then branches to 'primeFactorModNo', but returns the prime factor as *1*, + ; which then correctly identifies the dividend as a prime number. This is a + ; dirty hack, but the end result is that we don't need any additional code + ; to correctly handle `dividend>=65531^2`, which makes this loop faster. + ; If the size of the dividend is changed from u32 to a larger integer type, + ; the terminating condition *must* be reexamined. + ; NOTE 2: We can place the check for the terminating condition at the end + ; of the 'primeFactorModLoop' (i.e. a do-while-loop) instead of at the + ; beginning (i.e. a while-loop) without affecting correctness. The loop + ; will perform a handful of unnecessary iterations for `dividend<=121=11^2` + ; because the candidate must reach 11 before the loop terminates. But on + ; the flip side, moving the terminating condition to the end allows us to + ; eliminate an extra 'jp' instruction for each loop, which makes the PRIM + ; function faster for large input values. + ld hl, (OP2) ; HL=limit + or a ; clear CF + sbc hl, bc ; CF=1 if candidate>limit + jp nc, primeFactorModLoop +primeFactorModYes: + bcall(_OP1Set1) + ret primeFactorModNo: ld (OP1), bc ; OP1=candidate ld hl, 0 ld (OP1+2), hl call convertU32ToOP1 ; OP1=real(candidate) ret -primeFactorModYes: - bcall(_OP1Set1) - ret +; Description: Check if `candidate` (BC) is an integer factor of `input` (OP1). ; Input: ; - OP1:u32=x ; - BC:u16=candidate ; Output: -; - DE:u16=remainder ; - ZF=1 if remainder==0 ; Destroys: A, DE, HL, OP3 ; Preserves: BC, OP1 primeFactorModCheckDiv: -#ifdef USE_PRIME_FACTOR_U32_BY_BC +#ifdef USE_PRIME_FACTOR_MOD_U32_BY_BC ld hl, OP1 ld de, OP3 call copyU32HLToDE ; OP3=x; preserves BC ex de, hl ; HL=OP3 call modU32ByBC ; DE:u16=remainder; destroys (*HL); preserves BC=candidate -#else - call modOP1ByBC + ld a, d + or e ; if remainder==0: ZF=1 +#endif + +#ifdef USE_PRIME_FACTOR_MOD_HLSP_BY_BC + call modHLSPByBC + ld a, d + or e ; if remainder==0: ZF=1 #endif + +#ifdef USE_PRIME_FACTOR_MOD_HLIX_BY_BC + call modHLIXByBC ld a, d or e ; if remainder==0: ZF=1 +#endif + +; #ifdef USE_PRIME_FACTOR_MOD_DEIX_BY_BC + ld ix, (OP1) ; low16 + ld de, (OP1+2) ; high16 + call modDEIXByBC + ld a, h + or l ; if remainder==0: ZF=1 +; #endif ret -; Decription: Highly specialized version of modU32ByBC() to get the highest -; performance. For example, uses 'jp' instead 'jr' because 'jp' is faster. I -; tried to use IX instead of the stack (SP), but the Z80 does not support 'add -; ix,ix' or 'adc ix,ix' which forced the use of the stack anyway. The benchmark -; says that this is about 45% faster than modU32ByBC(). +;----------------------------------------------------------------------------- + +#ifdef USE_PRIME_FACTOR_MOD_HLSP_BY_BC + +; Decription: A specialized version of modU32ByBC() which is faster by storing +; 16-bits of the u32 in HL and the other 16-bits on the stack (SP). I +; implemented this version because my Z80 cheatsheet said (incorrectly) that +; the `add ix, ix` instruction did not exist. Once I realized that `add ix, ix` +; is available, I implemented modHLIXByBC() below. +; +; The benchmark says that this is about 45% faster than modU32ByBC(). ; ; Input: ; - OP1:u32=dividend ; - BC:u16=divisor ; Output: -; - DE:u16=dividend -modOP1ByBC: +; - DE:u16=remainder +; Destroys: A, HL +modHLSPByBC: ; push the dividend (x) to a combination of HL and the stack ld hl, (OP1+2) ; HL=high16 push hl ; stack=[high16] @@ -358,7 +470,7 @@ modOP1ByBC: ; ld de, 0 ; DE=remainder ld a, 32 -primeFactorModCheckDivLoop: +modHLSPByBCLoop: ; shift x by one bit to the left add hl, hl ex (sp), hl ; stack=[low16]; HL=high16 @@ -368,60 +480,307 @@ primeFactorModCheckDivLoop: rl e rl d ; DE=remainder ex de, hl ; HL=remainder; DE=dividend - jp c, primeFactorModCheckDivOverflow ; remainder overflowed, so substract + jp c, modHLSPByBCOverflow ; remainder overflowed, so substract or a ; CF=0 sbc hl, bc ; HL(remainder) -= divisor - jp nc, primeFactorModCheckDivNextBit + jp nc, modHLSPByBCNextBit add hl, bc ; revert the subtraction - jp primeFactorModCheckDivNextBit -primeFactorModCheckDivOverflow: + jp modHLSPByBCNextBit +modHLSPByBCOverflow: or a ; reset CF sbc hl, bc ; HL(remainder) -= divisor -primeFactorModCheckDivNextBit: +modHLSPByBCNextBit: ex de, hl ; DE=remainder; HL=dividend dec a - jp nz, primeFactorModCheckDivLoop + jp nz, modHLSPByBCLoop pop hl ; stack=[]; HL=high16 ret +#endif + ;----------------------------------------------------------------------------- -; Description: Choose one of the various primeFactorXXX() routines. +#ifdef USE_PRIME_FACTOR_MOD_HLIX_BY_BC + +; Description: An improved version of modHLSPByBC() which uses IX instead of +; 2 bytes on the stack (SP). This means that the entire computation can be +; performed using just the Z80 registers, without touching RAM. This improves +; PRIM by another 25%. Another 3-4% comes from replacing the sequence of `rl e; +; rl d; ex de, hl` with `ex de, hl; adc hl, hl`. We get a few extra percent +; improvement from selecting a 'jr' or 'jp' instruction judiciously. And +; another 5 percent comes from from deleting an unnecessary 'or a' instruction +; in one of the branches. Overall, this version is a surprising 43% faster than +; modHLSPByBC(). See benchmarks above. +; ; Input: -; - OP1:real=input +; - HL:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor ; Output: -; - OP1=1 if prime, or its smallest prime factor (>1) otherwise -; Throws: Err:Domain if OP1 is not an integer in the interval [2,2^32-1]. -PrimeFactor: - ; TODO: Replace the following validation with convertOP1ToU32(). I think we - ; just need to check for 0 and 1. Oh, and create a sqrtU32() replacement - ; for the floating point function _SqRoot(). - ; - ; Check 0 - bcall(_CkOP1FP0) - jr z, primeFactorError - ; Check 1 - bcall(_OP2Set1) ; OP2 = 1 - bcall(_CpOP1OP2) ; if OP1==1: ZF=1 - jr z, primeFactorError - bcall(_OP1ToOP4) ; save OP4 = X - ; Check integer >= 0 - bcall(_CkPosInt) ; if OP1 >= 0: ZF=1 - jr nz, primeFactorError - ; Check unsigned 32-bit integer, i.e. < 2^32. - call op2Set2Pow32PageTwo ; if OP1 >= 2^32: CF=0 - bcall(_CpOP1OP2) - jr nc, primeFactorError +; - DE:u16=remainder +; Destroys: A, HL, IX +modHLIXByBC: + ld de, 0 ; DE=remainder + ld a, 32 +modHLIXByBCLoop: + ; shift dividend by one bit to the left + add ix, ix + adc hl, hl + ; shift bit into remainder + ex de, hl ; HL=remainder; DE=dividend + adc hl, hl + ; Use 'jr c' instead of 'jp c' because the common case is expected to be + ; CF=0, so the branch is not taken, which means only 7 T cycles in the + ; common case. + jr c, modHLIXByBCOverflow ; remainder overflowed, so subtract + sbc hl, bc ; HL(remainder) -= divisor + ; I *think* CF=0 and CF=1 will occur in equal probability, so it makes + ; almost no difference whether we use 'jr nc' or 'jp nc'. The 'jr nc' + ; instruction takes 7 T cycles if the branch is not taken, and 12 cycles if + ; the branch is taken, for an average of 9.5 cycles. The 'jp nc' + ; instruction takes 10 cycles whether or not the branch is taken. The + ; difference is so small, I prefer the predictability of 'jp nc' always + ; taking 10 cycles. + jp nc, modHLIXByBCNextBit + add hl, bc ; revert the subtraction +modHLIXByBCNextBit: + ex de, hl ; DE=remainder; HL=dividend + dec a + ; Use 'jp nz' instead of 'jr nz' because 'jp nz' is faster in the common + ; case of ZF=0 when the branch is taken 32 times. + jp nz, modHLIXByBCLoop + ret +modHLIXByBCOverflow: + ; This branch (CF=1) is more rare than (CF=0) because it will happen only + ; after the 16th iteration. + or a ; reset CF + sbc hl, bc ; HL(remainder) -= divisor + jp modHLIXByBCNextBit -#ifdef USE_PRIME_FACTOR_FLOAT - jp primeFactorFloat -#else - #ifdef USE_PRIME_FACTOR_INT - jp primeFactorInt - #else - jp primeFactorMod - #endif #endif -primeFactorError: - bcall(_ErrDomain) ; throw exception +;----------------------------------------------------------------------------- + +; #ifdef USE_PRIME_FACTOR_MOD_DEIX_BY_BC + +; Description: Calculate mod(u32,u16), i.e. the remainder when a u32 dividend +; is divided by a u16 divisor. +; +; This is an improved version of modHLIXByBC() which uses a nonrestoring +; division algorith, with various tricks for a 1.75X speed increase: chunking +; into 8 bit registers, using register A instead of A for chunking, using DEIX +; instead of HLIX for the dividend, unrolling the 8-bit loop. +; +; Input: +; - DE:u16=high 16 bits of u32 dividend +; - IX:u16=low 16 bits of u32 dividend +; - BC:u16=divisor +; Output: +; - HL:u16=remainder +; Destroys: AF +; Preserves: BC, DE, IX +modDEIXByBC: + ld hl, 0 ; HL=remainder + or a ; CF=0 + ld a, d + call modDEIXByBC8 + ld a, e + call modDEIXByBC8 + push ix + pop de + ld a, d + call modDEIXByBC8 + ld a, e + call modDEIXByBC8 + ; if the remainder is negative, restore it + ret nc + add hl, bc + ret +; Description: Process an 8-bit chunk of the dividend with the 8 loop +; iterations unrolled for performance. +; Input: +; - A:u8=8-bit chunks of the dividend +; Output: +; - HL:u16=remainder +modDEIXByBC8: +modDEIXByBC8Bit0: + jp c, modDEIXByBC8Bit0Neg +modDEIXByBC8Bit0Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit0PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit1 +modDEIXByBC8Bit0PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit1Pos +modDEIXByBC8Bit0Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit0NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit1Neg +modDEIXByBC8Bit0NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit1: + jp c, modDEIXByBC8Bit1Neg +modDEIXByBC8Bit1Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit1PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit2 +modDEIXByBC8Bit1PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit2Pos +modDEIXByBC8Bit1Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit1NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit2Neg +modDEIXByBC8Bit1NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit2: + jp c, modDEIXByBC8Bit2Neg +modDEIXByBC8Bit2Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit2PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit3 +modDEIXByBC8Bit2PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit3Pos +modDEIXByBC8Bit2Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit2NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit3Neg +modDEIXByBC8Bit2NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit3: + jp c, modDEIXByBC8Bit3Neg +modDEIXByBC8Bit3Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit3PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit4 +modDEIXByBC8Bit3PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit4Pos +modDEIXByBC8Bit3Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit3NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit4Neg +modDEIXByBC8Bit3NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit4: + jp c, modDEIXByBC8Bit4Neg +modDEIXByBC8Bit4Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit4PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit5 +modDEIXByBC8Bit4PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit5Pos +modDEIXByBC8Bit4Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit4NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit5Neg +modDEIXByBC8Bit4NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit5: + jp c, modDEIXByBC8Bit5Neg +modDEIXByBC8Bit5Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit5PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit6 +modDEIXByBC8Bit5PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit6Pos +modDEIXByBC8Bit5Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit5NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit6Neg +modDEIXByBC8Bit5NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit6: + jp c, modDEIXByBC8Bit6Neg +modDEIXByBC8Bit6Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit6PosOverflow + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit7 +modDEIXByBC8Bit6PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + jp modDEIXByBC8Bit7Pos +modDEIXByBC8Bit6Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit6NegOverflow + add hl, bc ; remainder += divisor + jp modDEIXByBC8Bit7Neg +modDEIXByBC8Bit6NegOverflow: + add hl, bc ; remainder += divisor + ccf +; +modDEIXByBC8Bit7: + jp c, modDEIXByBC8Bit7Neg +modDEIXByBC8Bit7Pos: + rla + adc hl, hl + jp c, modDEIXByBC8Bit7PosOverflow + sbc hl, bc ; remainder -= divisor + ret +modDEIXByBC8Bit7PosOverflow: + or a + sbc hl, bc ; remainder -= divisor + or a ; always clear CF + ret +modDEIXByBC8Bit7Neg: + rla + adc hl, hl + jp c, modDEIXByBC8Bit7NegOverflow + add hl, bc ; remainder += divisor + scf + ret +modDEIXByBC8Bit7NegOverflow: + add hl, bc ; remainder += divisor + ccf +; + ret + +; #endif diff --git a/src/print.asm b/src/print.asm index aafa2188..717dfd69 100644 --- a/src/print.asm +++ b/src/print.asm @@ -118,15 +118,15 @@ vEraseEOLLoop2: ; string is too long to fit into the current line, we print 2 dots (4 pixels ; wide) are printed at the end to indicate truncation. ; -; I would have thought that checking for PenCol>=92 would have been sufficient, +; I would have thought that checking for penCol>=92 would have been sufficient, ; since the display is 96 pixels wide, and a dot is only 2 pixels wide. But it ; turns out that VPutMap() function has some strange, undocumented behavior, so -; PenCol>=89 seems to work a lot better. +; penCol>=89 seems to work a lot better. ; ; It has been hard to characterize the exact behavior of VPutMap() at the end ; of the line. Even if there are 4 pixels available at the end of the line, it ; does not want to write a 4-pixel wide character. Furthermore, if the -; character does not fit, it is simply ignored, PenCol is not updated, and a +; character does not fit, it is simply ignored, penCol is not updated, and a ; subsequent call to VPutMap() with a narrow enough character will print that ; narrow character into that space. So sometimes, some random character at the ; end seems to have been lost. @@ -150,7 +150,7 @@ vPutSmallSLoop: ; funny business with vPutMap() that I cannot quite characterize. With 8 ; spaces, we get more flexibility. ld b, a ; B=saved A - ld a, (PenCol) + ld a, (penCol) cp 88 jr nc, vPutSmallSMaybeTruncate ld a, b ; A=restored A @@ -237,10 +237,10 @@ putSEnd: putSEnter: ; Handle newline push hl - ld hl, (CurRow) - inc l ; CurRow++ + ld hl, (curRow) + inc l ; curRow++ ld h, 0 ; CurCol=0 - ld (CurRow), hl + ld (curRow), hl pop hl jr putSCheck diff --git a/src/print1.asm b/src/print1.asm index 4f958106..06fd9d88 100644 --- a/src/print1.asm +++ b/src/print1.asm @@ -61,9 +61,9 @@ eVPutSEnter: ; move to the next line push af push hl - ld hl, PenCol + ld hl, penCol xor a - ld (hl), a ; PenCol = 0 + ld (hl), a ; penCol = 0 inc hl ; PenRow ld a, (hl) ; A = PenRow add a, c ; A += C (font height) @@ -136,10 +136,10 @@ putSPageOneEnd: putSPageOneEnter: ; Handle newline push hl - ld hl, (CurRow) - inc l ; CurRow++ + ld hl, (curRow) + inc l ; curRow++ ld h, 0 ; CurCol=0 - ld (CurRow), hl + ld (curRow), hl pop hl jr putSPageOneCheck diff --git a/src/rpn83p.asm b/src/rpn83p.asm index 47125d10..7fa59cd8 100644 --- a/src/rpn83p.asm +++ b/src/rpn83p.asm @@ -317,10 +317,17 @@ appStateInputBufFlags equ appStateRpnFlags + 1 ; u8 ; Copy of the trigFlags, fmtFlags, and fmtDigits as used by this app. When the ; app starts, these values will be used to configure the corresponding OS ; settings. When the app quits, the OS settings are copied here. +; +; The `numMode` flags (fmtReal, fmtRect, fmtPolar) are stored in the same +; location as the `fmtFlags`, so we don't have to save the `numMode flags +; separately. appStateTrigFlags equ appStateInputBufFlags + 1 ; u8 appStateFmtFlags equ appStateTrigFlags + 1 ; u8 appStateFmtDigits equ appStateFmtFlags + 1 ; u8 +; fmtDigits value that indicates "floating" number of digits +fmtDigitsFloating equ $ff + ; The result code after the execution of each handler. Success is code 0. If a ; TI-OS exception is thrown (through a `bcall(ErrXxx)`), the exception handler ; places a system error code into here. Before calling a handler, set this to 0 @@ -1849,6 +1856,10 @@ _FormatAToString equ _FormatAToStringLabel-branchTableBase .db 2 ; show2.asm +_ClearShowAreaLabel: +_ClearShowArea equ _ClearShowAreaLabel-branchTableBase + .dw ClearShowArea + .db 2 _FormShowableLabel: _FormShowable equ _FormShowableLabel-branchTableBase .dw FormShowable diff --git a/src/show2.asm b/src/show2.asm index d9d3deb1..7bfe89a0 100644 --- a/src/show2.asm +++ b/src/show2.asm @@ -13,6 +13,23 @@ ; entry. ;------------------------------------------------------------------------------ +; Clear the display area used by the SHOW feature (errorCode, T, Z, Y, X). +; Input: none +; Destroys: A, B, HL +ClearShowArea: + ld hl, errorCurCol*$100 + errorCurRow ; $(curCol)(curRow) + ld (curRow), hl + ld b, 5 +clearShowAreaLoop: + bcall(_EraseEOL) ; saves all registers + ld hl, (curRow) + inc l + ld (curRow), hl + djnz clearShowAreaLoop + ret + +;------------------------------------------------------------------------------ + ; Description: Format the number in OP1 to a NUL terminated string that shows ; all significant digits, suitable for a SHOW function. ; Input: @@ -315,7 +332,7 @@ msgShowComplexDegSpacer: ; Destroys: OP1, OP2 formRealString: push de - bcall(_CkOp1FP0) ; if OP1==0: ZF=1 + bcall(_CkOP1FP0) ; if OP1==0: ZF=1 pop de jr nz, formRealStringNonZero ; Generate just a "0" if zero. diff --git a/src/showscanner.asm b/src/showscanner.asm index a26bbcff..fab73255 100644 --- a/src/showscanner.asm +++ b/src/showscanner.asm @@ -9,7 +9,7 @@ ; Description: Read loop for the SHOW mode. processShowCommands: - call clearShowArea + bcall(_ClearShowArea) set rpnFlagsShowModeEnabled, (iy + rpnFlags) set dirtyFlagsStack, (iy + dirtyFlags) ; Show the new display. @@ -18,10 +18,10 @@ processShowCommands: bcall(_GetKey) res onInterrupt, (iy + onFlags) ; Quit the app on QUIT. - cp a, KQuit + cp a, kQuit jp z, mainExit ; Anything else exits the SHOW mode. - call clearShowArea + bcall(_ClearShowArea) res rpnFlagsShowModeEnabled, (iy + rpnFlags) set dirtyFlagsStack, (iy + dirtyFlags) set dirtyFlagsErrorCode, (iy + dirtyFlags) ; errorCode displays "SHOW" diff --git a/src/tvm2.asm b/src/tvm2.asm index 1159e623..73397f4e 100644 --- a/src/tvm2.asm +++ b/src/tvm2.asm @@ -611,12 +611,17 @@ inverseCompoundingFactorZero: ; Output: OP1=NPMT(i) ; Destroys: OP1-OP5 nominalPMT: - ; Check if N*i <~ tol; tol=1e-10 + ; Check if if the 3rd order term of the Ni/((1+i)^N-1) term is less than + ; the last digit that can be represented by the TI-OS floating point + ; (14-digits). So we need to check if (N^2-1)*i^3/24 <~ tol; tol=1e-14. + ; This is equivalent to N*i <~ N^(1/3)*6e-5. But N can be assumed to be + ; greater than 1. So we can use N*i <~ 6e-5, and still satify the original + ; constraint. call op1ToOp2PageTwo ; OP2=i call RclTvmN ; OP1=N bcall(_FPMult) ; OP1=i*N; OP2=i call pushRaw9Op2 ; FPS=[i] - call op2Set1EM10PageTwo ; OP2=1e-10 + call op2Set6EM5PageTwo ; OP2=6e-5 bcall(_AbsO1O2Cp) ; CF=1 if OP1