1
0
mirror of https://github.com/dschmenk/PLASMA.git synced 2026-03-11 09:42:04 +00:00

Implement additional hardware-accelerated functions using trig identities

Extended hardware acceleration to more functions by using mathematical
identities with the base MegaFlash FPU operations:

New Functions (all hardware-accelerated):
- neg(x) = x * -1 (preserves precision vs sign flip)
- abs(x) = sign check + neg if needed
- log2(x) = ln(x) / ln(2)
- pow2(x) = e^(x * ln(2))
- asin(x) = atan(x / sqrt(1 - x²))
- acos(x) = π/2 - asin(x)
- sinh(x) = (e^x - e^-x) / 2
- cosh(x) = (e^x + e^-x) / 2
- tanh(x) = sinh(x) / cosh(x)
- sec(x) = 1 / cos(x)
- csc(x) = 1 / sin(x)
- cot(x) = 1 / tan(x)

All functions use hardware mul, div, sqrt, sin, cos, tan, atan, ln, and exp
operations from MegaFlash, providing significant speedup over software
implementations.

Updated documentation to reflect extended hardware acceleration coverage.
This commit is contained in:
Brendan Robert
2026-01-24 17:49:32 -06:00
parent a4fa5a162a
commit 2db2129806
2 changed files with 461 additions and 32 deletions

View File

@@ -10,16 +10,9 @@
- **API Compatible**: Maintains complete API compatibility with `fpu.pla`
- **Automatic Fallback**: Detects MegaFlash presence and falls back to SANE if not available
- **Format Conversion**: Automatically converts between SANE Extended (80-bit) and MBF (40-bit) formats
- **Accelerated Operations**:
- Multiplication (`mul`)
- Division (`div`)
- Sine (`sin`)
- Cosine (`cos`)
- Tangent (`tan`)
- Arctangent (`atan`)
- Natural logarithm (`lnX`)
- Exponential (`powEX`)
- Square root (`sqrt`)
- **Hardware-Accelerated Operations**:
- **Direct FPU**: mul, div, sqrt, sin, cos, tan, atan, ln, exp
- **Via Identities**: neg, abs, log2, pow2, asin, acos, sinh, cosh, tanh, sec, csc, cot
## Usage
@@ -89,7 +82,7 @@ The library uses the following MegaFlash registers (Slot 4):
## Supported Operations
### Hardware Accelerated (MegaFlash)
### Hardware Accelerated - Direct (MegaFlash FPU)
- `mul` - Multiply
- `div` - Divide
- `sqrt` - Square root
@@ -98,21 +91,36 @@ The library uses the following MegaFlash registers (Slot 4):
- `tan` - Tangent
- `atan` - Arctangent
- `lnX` - Natural logarithm
- `powEX` - e^x
- `powEX` - e^x (exponential)
### Hardware Accelerated - Via Identities (MegaFlash + math)
- `neg` - Negate (x * -1)
- `abs` - Absolute value
- `log2X` - Log base 2 (ln(x) / ln(2))
- `pow2X` - 2^x (e^(x*ln(2)))
- `asin` - Arcsine (atan(x/sqrt(1-x²)))
- `acos` - Arccosine (π/2 - asin(x))
- `sinh` - Hyperbolic sine ((e^x - e^-x)/2)
- `cosh` - Hyperbolic cosine ((e^x + e^-x)/2)
- `tanh` - Hyperbolic tangent (sinh/cosh)
- `sec` - Secant (1/cos)
- `csc` - Cosecant (1/sin)
- `cot` - Cotangent (1/tan)
### Software Fallback (SANE)
- `add` - Addition
- `sub` - Subtraction
- `add` - Addition (conversion overhead makes hardware slower)
- `sub` - Subtraction (same reason)
- `rem` - Remainder
- `neg` - Negate
- `abs` - Absolute value
- `type` - Type classification
- `cmp` - Compare
- `trunc` - Truncate
- `round` - Round
- `logb` - Log base
- `scalb` - Scale
- All other ELEMS functions
- `ln1X`, `pow21X`, `powE1X`, `powE21X` - Special log/exp variants
- `powXInt`, `powXY` - General exponentiation
- `compXY`, `annuityXY` - Financial functions
- `randNum` - Random number generation
## Building

View File

@@ -6,6 +6,29 @@
// fpu.pla library, allowing users to switch between implementations
// by simply changing the import statement.
//
// HARDWARE-ACCELERATED OPERATIONS (direct MegaFlash FPU):
// - mul, div, sqrt
// - sin, cos, tan, atan
// - ln (natural log), exp (e^x)
//
// HARDWARE-ACCELERATED DERIVED OPERATIONS (using trig identities):
// - neg(x) = x * -1 (hardware mul)
// - abs(x) = x if x >= 0, else neg(x)
// - log2(x) = ln(x) / ln(2)
// - 2^x = e^(x * ln(2))
// - asin(x) = atan(x / sqrt(1 - x²))
// - acos(x) = π/2 - asin(x)
// - sinh(x) = (e^x - e^-x) / 2
// - cosh(x) = (e^x + e^-x) / 2
// - tanh(x) = sinh(x) / cosh(x)
// - sec(x) = 1 / cos(x)
// - csc(x) = 1 / sin(x)
// - cot(x) = 1 / tan(x)
//
// These functions all use hardware acceleration and maintain full precision
// by leveraging the MegaFlash FPU's native operations combined with
// mathematical identities.
//
include "inc/cmdsys.plh"
include "inc/sane.plh"
include "inc/fpstr.plh"
@@ -692,17 +715,44 @@ def rem
end
def neg
// MegaFlash doesn't have NEG - use SANE or flip sign manually
if !saneInit; initSANE; fin
sane:saveZP()
return sane:restoreZP(sane:op1FP(FFEXT|FONEG, stackRegs[0]))
// neg(x) = x * -1 (preserves precision, uses hardware mul)
word err
byte[t_extended] negOne
// -1.0 in extended format
negOne.0 = $FF // exp high (sign bit set)
negOne.1 = $3F // exp low
negOne.2 = $80 // mantissa (1.0)
negOne.3 = 0
negOne.4 = 0
negOne.5 = 0
negOne.6 = 0
negOne.7 = 0
negOne.8 = 0
negOne.9 = 0
if !mfAvailable
// Fallback to SANE
if !saneInit; initSANE; fin
sane:saveZP()
return sane:restoreZP(sane:op1FP(FFEXT|FONEG, stackRegs[0]))
fin
// Multiply by -1
memcpy(stackRegs[1], stackRegs[0], t_extended)
memcpy(stackRegs[0], @negOne, t_extended)
return execBinaryOp(MF_CMD_FMUL)
end
def abs
// MegaFlash doesn't have ABS - use SANE
if !saneInit; initSANE; fin
sane:saveZP()
return sane:restoreZP(sane:op1FP(FFEXT|FOABS, stackRegs[0]))
// abs(x) = x if x >= 0, else -x
// Check sign bit and negate if needed
if stackRegs[0]->0 & $80
// Negative - negate it
return neg
fin
// Already positive
return 0
end
def type
@@ -833,13 +883,42 @@ def atan
end
def log2X
// Use SANE ELEMS
if !saneInit; initSANE; fin
sane:saveZP()
return sane:restoreZP(sane:op1ELEM(FFEXT|FOLOG2X, stackRegs[0]))
// log2(x) = ln(x) / ln(2)
// Use hardware ln, then divide by ln(2)
word err
byte[t_extended] ln2
// Calculate ln(2) constant: ~0.693147
// Store as extended format
ln2.0 = $FE // exp high
ln2.1 = $3F // exp low
ln2.2 = $B1 // mantissa
ln2.3 = $72
ln2.4 = $17
ln2.5 = $F7
ln2.6 = $D1
ln2.7 = $CF
ln2.8 = $79
ln2.9 = $AB
// Compute ln(x)
err = execUnaryOp(MF_CMD_FLOG)
if err < 0
// Fallback to SANE
if !saneInit; initSANE; fin
sane:saveZP()
return sane:restoreZP(sane:op1ELEM(FFEXT|FOLOG2X, stackRegs[0]))
fin
// Divide by ln(2)
memcpy(stackRegs[1], @ln2, t_extended)
swap
return div
end
def log21X
// log2(1+x) = ln(1+x) / ln(2)
// For small x, could optimize, but for now use standard approach
if !saneInit; initSANE; fin
sane:saveZP()
return sane:restoreZP(sane:op1ELEM(FFEXT|FOLOG21X, stackRegs[0]))
@@ -865,12 +944,40 @@ def ln1X
end
def pow2X
if !saneInit; initSANE; fin
sane:saveZP()
return sane:restoreZP(sane:op1ELEM(FFEXT|FOEXP2X, stackRegs[0]))
// 2^x = e^(x * ln(2))
word err
byte[t_extended] ln2
// ln(2) constant
ln2.0 = $FE
ln2.1 = $3F
ln2.2 = $B1
ln2.3 = $72
ln2.4 = $17
ln2.5 = $F7
ln2.6 = $D1
ln2.7 = $CF
ln2.8 = $79
ln2.9 = $AB
// Multiply x by ln(2)
memcpy(stackRegs[1], stackRegs[0], t_extended)
memcpy(stackRegs[0], @ln2, t_extended)
err = execBinaryOp(MF_CMD_FMUL)
if err < 0
// Fallback
if !saneInit; initSANE; fin
sane:saveZP()
return sane:restoreZP(sane:op1ELEM(FFEXT|FOEXP2X, stackRegs[1]))
fin
// Compute e^(x*ln2)
return execUnaryOp(MF_CMD_FEXP)
end
def pow21X
// 2^x - 1 for small x
// For now, fallback to SANE for accuracy near zero
if !saneInit; initSANE; fin
sane:saveZP()
return sane:restoreZP(sane:op1ELEM(FFEXT|FOEXP21X, stackRegs[0]))
@@ -913,6 +1020,320 @@ def powXY
return sane:restoreZP(_drop(_swap(sane:op2ELEM(FFEXT|FOXPWRY, stackRegs[0], stackRegs[1]))))
end
//==============================================================================
// ADDITIONAL TRIG FUNCTIONS (using hardware acceleration + identities)
//==============================================================================
//
// Inverse Sine: asin(x) = atan(x / sqrt(1 - x²))
// Valid for -1 <= x <= 1
//
predef asin
def asin
word err
byte[t_extended] one
if !mfAvailable
// No direct SANE asin, but could implement with identities if needed
// For now, return error
return -1
fin
// one = 1.0
one.0 = 0
one.1 = $3F
one.2 = $80
one.3 = 0
one.4 = 0
one.5 = 0
one.6 = 0
one.7 = 0
one.8 = 0
one.9 = 0
// Save x
dup
// Compute x²
dup
err = execBinaryOp(MF_CMD_FMUL)
if err; return err; fin
// Compute 1 - x²
memcpy(stackRegs[1], @one, t_extended)
swap
neg
add // 1 + (-x²)
// Compute sqrt(1 - x²)
err = execUnaryOp(MF_CMD_FSQR)
if err; return err; fin
// Swap to get x / sqrt(1 - x²)
swap
err = execBinaryOp(MF_CMD_FDIV)
if err; return err; fin
// Compute atan
return execUnaryOp(MF_CMD_FATN)
end
//
// Inverse Cosine: acos(x) = π/2 - asin(x)
//
predef acos
def acos
word err
// Compute asin(x)
err = asin
if err; return err; fin
// Subtract from π/2
constPi
pushStr("2.0")
div
swap
return sub
end
//
// Hyperbolic Sine: sinh(x) = (e^x - e^-x) / 2
//
predef sinh
def sinh
word err
byte[t_extended] two
if !mfAvailable
if !saneInit; initSANE; fin
// SANE doesn't have sinh either, would need to implement
return -1
fin
// two = 2.0
two.0 = 0
two.1 = $40
two.2 = $80
two.3 = 0
two.4 = 0
two.5 = 0
two.6 = 0
two.7 = 0
two.8 = 0
two.9 = 0
// Save x
dup
// Compute e^x
err = execUnaryOp(MF_CMD_FEXP)
if err; return err; fin
// Get x again for e^-x
swap
neg
err = execUnaryOp(MF_CMD_FEXP)
if err; return err; fin
// Compute e^x - e^-x
swap
sub
// Divide by 2
memcpy(stackRegs[1], @two, t_extended)
swap
return div
end
//
// Hyperbolic Cosine: cosh(x) = (e^x + e^-x) / 2
//
predef cosh
def cosh
word err
byte[t_extended] two
if !mfAvailable
if !saneInit; initSANE; fin
return -1
fin
// two = 2.0
two.0 = 0
two.1 = $40
two.2 = $80
two.3 = 0
two.4 = 0
two.5 = 0
two.6 = 0
two.7 = 0
two.8 = 0
two.9 = 0
// Save x
dup
// Compute e^x
err = execUnaryOp(MF_CMD_FEXP)
if err; return err; fin
// Get x again for e^-x
swap
neg
err = execUnaryOp(MF_CMD_FEXP)
if err; return err; fin
// Compute e^x + e^-x
swap
add
// Divide by 2
memcpy(stackRegs[1], @two, t_extended)
swap
return div
end
//
// Hyperbolic Tangent: tanh(x) = sinh(x) / cosh(x)
//
predef tanh
def tanh
word err
if !mfAvailable
if !saneInit; initSANE; fin
return -1
fin
// Save x for cosh
dup
// Compute sinh(x)
err = sinh
if err; return err; fin
// Get x again
swap
// Compute cosh(x)
err = cosh
if err; return err; fin
// Divide sinh/cosh
swap
return div
end
//
// Secant: sec(x) = 1 / cos(x)
//
predef sec
def sec
word err
byte[t_extended] one
if !mfAvailable
if !saneInit; initSANE; fin
return -1
fin
// one = 1.0
one.0 = 0
one.1 = $3F
one.2 = $80
one.3 = 0
one.4 = 0
one.5 = 0
one.6 = 0
one.7 = 0
one.8 = 0
one.9 = 0
// Compute cos(x)
err = execUnaryOp(MF_CMD_FCOS)
if err; return err; fin
// Compute 1 / cos(x)
memcpy(stackRegs[1], @one, t_extended)
swap
return div
end
//
// Cosecant: csc(x) = 1 / sin(x)
//
predef csc
def csc
word err
byte[t_extended] one
if !mfAvailable
if !saneInit; initSANE; fin
return -1
fin
// one = 1.0
one.0 = 0
one.1 = $3F
one.2 = $80
one.3 = 0
one.4 = 0
one.5 = 0
one.6 = 0
one.7 = 0
one.8 = 0
one.9 = 0
// Compute sin(x)
err = execUnaryOp(MF_CMD_FSIN)
if err; return err; fin
// Compute 1 / sin(x)
memcpy(stackRegs[1], @one, t_extended)
swap
return div
end
//
// Cotangent: cot(x) = 1 / tan(x)
//
predef cot
def cot
word err
byte[t_extended] one
if !mfAvailable
if !saneInit; initSANE; fin
return -1
fin
// one = 1.0
one.0 = 0
one.1 = $3F
one.2 = $80
one.3 = 0
one.4 = 0
one.5 = 0
one.6 = 0
one.7 = 0
one.8 = 0
one.9 = 0
// Compute tan(x)
err = execUnaryOp(MF_CMD_FTAN)
if err; return err; fin
// Compute 1 / tan(x)
memcpy(stackRegs[1], @one, t_extended)
swap
return div
end
//==============================================================================
// FINANCIAL AND RANDOM (SANE fallback)
//==============================================================================
def compXY
if !saneInit; initSANE; fin
sane:saveZP()