diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml
index d4e42c2a0..e2fd55ddd 100644
--- a/.github/workflows/ci-workflow.yml
+++ b/.github/workflows/ci-workflow.yml
@@ -18,6 +18,64 @@ on:
required: true
default: 'yes'
jobs:
+ fxcg:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: 'recursive'
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install curl git python3 build-essential cmake pkg-config -y
+ - name: Get latest gint commit hash
+ run: |
+ LATEST_COMMIT_HASH=$(curl --silent https://gitea.planet-casio.com/api/v1/repos/Lephenixnoir/gint/branches/master | jq -r .commit.id)
+ echo "Latest commit hash is: $LATEST_COMMIT_HASH"
+ echo "LATEST_COMMIT_HASH=$LATEST_COMMIT_HASH" >> $GITHUB_OUTPUT
+ id: get-latest-commit-hash
+ - name: Cache gint/fxsdk installation
+ id: cache-gint
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.local/*/*
+ !~/.local/share/containers
+ key: ${{ runner.os }}-gint-${{ steps.get-latest-commit-hash.outputs.LATEST_COMMIT_HASH }}
+ - name: Install gint/fxsdk
+ if: steps.cache-gint.outputs.cache-hit != 'true'
+ env:
+ URL: "https://gitea.planet-casio.com/Lephenixnoir/GiteaPC/archive/master.tar.gz"
+ run: |
+ export PATH="~/.local/bin:$PATH"
+ cd "$(mktemp -d)"
+ curl "$URL" -o giteapc-master.tar.gz
+ tar -xzf giteapc-master.tar.gz
+ cd giteapc
+ python3 giteapc.py install Lephenixnoir/GiteaPC -y
+ sudo apt-get install python3-pil libusb-1.0-0-dev libudev-dev libsdl2-dev libpng-dev libudisks2-dev libglib2.0-dev libmpfr-dev libmpc-dev libppl-dev -y
+ giteapc install Lephenixnoir/fxsdk:noudisks2 Lephenixnoir/sh-elf-binutils Lephenixnoir/sh-elf-gcc -y
+ giteapc install Lephenixnoir/OpenLibm Vhex-Kernel-Core/fxlibc Lephenixnoir/sh-elf-gcc -y
+ giteapc install Lephenixnoir/gint -y
+ - name: Add fxsdk to PATH
+ run: echo "~/.local/bin" >> $GITHUB_PATH
+ - run: make -j2 PLATFORM=simulator TARGET=fxcg
+ - id: 'auth'
+ if: ${{ github.event_name == 'push' && github.ref_name == 'upsilon-dev' && github.repository == 'UpsilonNumworks/Upsilon' }}
+ uses: 'google-github-actions/auth@v0'
+ with:
+ credentials_json: '${{secrets.GOOGLE_CREDENTIALS}}'
+ - id: 'upload-directory'
+ if: ${{ github.event_name == 'push' && github.ref_name == 'upsilon-dev' && github.repository == 'UpsilonNumworks/Upsilon' }}
+ uses: 'google-github-actions/upload-cloud-storage@v0'
+ with:
+ path: 'output/release/simulator/fxcg/epsilon.g3a'
+ destination: 'upsilon-binfiles.appspot.com/dev/simulator/'
+ parent: false
+ - uses: actions/upload-artifact@master
+ with:
+ name: epsilon.g3a
+ path: output/release/simulator/fxcg/epsilon.g3a
nintendo_3ds:
if: github.event.inputs.trigger3DS == 'yes' || github.event.inputs.trigger3DS == ''
runs-on: ubuntu-latest
diff --git a/Makefile b/Makefile
index c42ed081a..b3705e7f4 100644
--- a/Makefile
+++ b/Makefile
@@ -93,6 +93,7 @@ help:
@echo " make PLATFORM=simulator TARGET=web"
@echo " make PLATFORM=simulator TARGET=windows"
@echo " make PLATFORM=simulator TARGET=3ds"
+ @echo " make PLATFORM=simulator TARGET=fxcg"
.PHONY: doc
doc:
@@ -127,6 +128,7 @@ ifndef USE_LIBA
endif
ifeq ($(USE_LIBA),0)
include liba/Makefile.bridge
+include libaxx/Makefile.bridge
else
SFLAGS += -ffreestanding -nostdinc -nostdlib
include liba/Makefile
diff --git a/README.md b/README.md
index 4135856a1..f9fbad9f8 100644
--- a/README.md
+++ b/README.md
@@ -264,7 +264,7 @@ git checkout upsilon-dev
```bash
make MODEL=n0100 clean
-make MODEL=n0100 EPSILON_I18N=en OMEGA_USERNAME="{Your name, max 15 characters}" -j4
+make MODEL=n0100 EPSILON_I18N=en OMEGA_USERNAME="{Your name, max 15 characters}" -j(nproc)
```
Now, run either:
@@ -280,7 +280,7 @@ to directly flash the calculator after pressing simultaneously `reset` and `6` b
or:
```bash
-make MODEL=n0100 OMEGA_USERNAME="" binpack -j4
+make MODEL=n0100 OMEGA_USERNAME="" binpack -j(nproc)
```
to make binpack which you can flash to the calculator from [Ti-planet's webDFU](https://ti-planet.github.io/webdfu_numworks/n0100/). Binpacks are a great way to share a custom build of Upsilonto friends.
@@ -301,7 +301,7 @@ Then, build with:
```bash
make clean
-make OMEGA_USERNAME="{Your name, max 15 characters}" -j4
+make OMEGA_USERNAME="{Your name, max 15 characters}" -j(nproc)
```
Now, run either:
@@ -317,7 +317,7 @@ to directly flash the calculator into the current slot, or thought bootloader's
or:
```bash
-make OMEGA_USERNAME="" binpack -j4
+make OMEGA_USERNAME="" binpack -j(nproc)
```
to make binpack which you can flash to the calculator from [Ti-planet's webDFU](https://ti-planet.github.io/webdfu_numworks/n0110/). You'll find them at `output/release/device/bootloader/`. Binpacks are a great way to share a custom build of Upsilon to friends.
@@ -330,7 +330,7 @@ to make binpack which you can flash to the calculator from [Ti-planet's webDFU](
```bash
make MODEL=n0110 clean
-make MODEL=n0110 OMEGA_USERNAME="{Your name, max 15 characters}" -j4
+make MODEL=n0110 OMEGA_USERNAME="{Your name, max 15 characters}" -j(nproc)
```
Now, run either:
@@ -346,7 +346,7 @@ to directly flash the calculator after pressing simultaneously `reset` and `6` b
or:
```bash
-make MODEL=n0110 OMEGA_USERNAME="" binpack -j4
+make MODEL=n0110 OMEGA_USERNAME="" binpack -j(nproc)
```
to make binpack which you can flash to the calculator from [Ti-planet's webDFU](https://ti-planet.github.io/webdfu_numworks/n0110/). You'll find them at `output/release/device/bootloader/`. Binpacks are a great way to share a custom build of Upsilon to friends.
@@ -400,7 +400,7 @@ Then, compile Upsilon :
```bash
make clean
-make PLATFORM=simulator TARGET=web OMEGA_USERNAME="{Your name, max 15 characters}" -j4
+make PLATFORM=simulator TARGET=web OMEGA_USERNAME="{Your name, max 15 characters}" -j$(nproc)
```
The simulator is now in `output/release/simulator/web/simulator.zip`
@@ -416,8 +416,8 @@ You need devkitPro and devkitARM installed and in your path (instructions [here]
```bash
git clone --recursive https://github.com/UpsilonNumworks/Upsilon.git
cd Upsilon
-git checkout --recursive upsilon-dev
-make PLATFORM=simulator TARGET=3ds -j
+git checkout upsilon-dev
+make PLATFORM=simulator TARGET=3ds -j(nproc)
```
You can then put epsilon.3dsx on a SD card to run it from the HBC or use 3dslink to launch it over the network:
@@ -430,6 +430,22 @@ You can then put epsilon.3dsx on a SD card to run it from the HBC or use 3dslink
+
+ Casio fx-CG-series Port
+
+First, install gint and fxsdk along with a cross compiler for the calculator. There are instructions for this (in French, but Google Translate works well enough) [here](https://www.planet-casio.com/Fr/forums/topic16614-last-giteapc-installer-et-mettre-a-jour-automatiquement-des-projets-gitea.html).
+
+Next:
+```bash
+git clone --recursive https://github.com/UpsilonNumworks/Upsilon.git
+cd Omega
+git checkout upsilon-dev
+make PLATFORM=simulator TARGET=fxcg -j$(nproc)
+```
+Then copy the file at `./output/release/simulator/fxcg/epsilon.g3a` to the calculator over USB.
+
+
+
Important: Don't forget the `--recursive` tag, because Upsilon relies on submodules.
Also, you can change the number of processes that run in parallel during the build by changing the value of the `-j` flag.
Don't forget to put your pseudo instead of `{your pseudo, max 15 char}`. If you don't want one, just remove the `OMEGA_USERNAME=""` argument.
@@ -476,7 +492,8 @@ You can try Epsilon straight from your browser in the [online simulator](https:/
NumWorks is a registered trademark of NumWorks SAS, 24 Rue Godot de Mauroy, 75009 Paris, France.
Nintendo and Nintendo 3DS are registered trademarks of Nintendo of America Inc, 4600 150th Ave NE, Redmond, WA 98052, USA.
-NumWorks SAS and Nintendo of America Inc aren't associated in any shape or form with this project.
+Casio is a registered trademark of Casio Computer Co., Ltd. CORPORATION JAPAN 6-2, Hon-machi 1-chome Shibuya-ku, Tokyo JAPAN 151-8543.
+NumWorks SAS, Nintendo of America Inc and Casio aren't associated in any shape or form with this project.
- NumWorks Epsilon is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode).
- Omega is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode).
diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n
index 5ead15875..b5a22857c 100644
--- a/apps/code/catalog.de.i18n
+++ b/apps/code/catalog.de.i18n
@@ -1,5 +1,11 @@
PythonPound = "Kommentar"
PythonPercent = "Modulo"
+PythonColon = "Doppelpunkt"
+PythonSemicon = "Semikolon"
+PythonExclamationMark = "Ausrufezeichen"
+PythonLessThan = "Kleiner als"
+PythonGreaterThan = "Größer als"
+PythonQuestionMark = "Fragezeichen"
Python1J = "Imaginäres i"
PythonLF = "Zeilenvorschub"
PythonTab = "Tabulator"
diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n
index 62a940057..dd3936409 100644
--- a/apps/code/catalog.en.i18n
+++ b/apps/code/catalog.en.i18n
@@ -1,5 +1,11 @@
PythonPound = "Comment"
PythonPercent = "Modulo"
+PythonColon = "Colon"
+PythonSemicon = "Semicolon"
+PythonExclamationMark = "Exclamation mark"
+PythonLessThan = "Less than"
+PythonGreaterThan = "Greater than"
+PythonQuestionMark = "Question mark"
Python1J = "Imaginary i"
PythonLF = "Line feed"
PythonTab = "Tabulation"
diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n
index 883b47cf4..62d5bf2c9 100644
--- a/apps/code/catalog.es.i18n
+++ b/apps/code/catalog.es.i18n
@@ -1,5 +1,11 @@
PythonPound = "Comment"
PythonPercent = "Modulo"
+PythonColon = "Colon"
+PythonSemicon = "Semicolon"
+PythonExclamationMark = "Exclamation mark"
+PythonLessThan = "Less than"
+PythonGreaterThan = "Greater than"
+PythonQuestionMark = "Question mark"
Python1J = "Imaginary i"
PythonLF = "Line feed"
PythonTab = "Tabulation"
diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n
index 993770157..3185c5ce5 100644
--- a/apps/code/catalog.fr.i18n
+++ b/apps/code/catalog.fr.i18n
@@ -1,5 +1,11 @@
PythonPound = "Commentaire"
PythonPercent = "Modulo"
+PythonColon = "Deux-points"
+PythonSemicon = "Point-virgule"
+PythonExclamationMark = "Point d'exclamation"
+PythonLessThan = "Inférieur à"
+PythonGreaterThan = "Supérieur à"
+PythonQuestionMark = "Point d'interrogation"
Python1J = "i complexe"
PythonLF = "Saut à la ligne"
PythonTab = "Tabulation"
diff --git a/apps/code/catalog.hu.i18n b/apps/code/catalog.hu.i18n
index 6242e4dc3..7dd162659 100644
--- a/apps/code/catalog.hu.i18n
+++ b/apps/code/catalog.hu.i18n
@@ -1,5 +1,11 @@
PythonPound = "Megjegyzés"
PythonPercent = "Modulo"
+PythonColon = "Kettőspont"
+PythonSemicon = "Pontosvessző"
+PythonExclamationMark = "Felkiáltójel"
+PythonLessThan = "Kisebb mint"
+PythonGreaterThan = "Nagyobb mint"
+PythonQuestionMark = "Kérdőjel"
Python1J = "Képzeletbeli i"
PythonLF = "Enter"
PythonTab = "Táblázat"
diff --git a/apps/code/catalog.it.i18n b/apps/code/catalog.it.i18n
index d8a24f355..cb17c6bb2 100644
--- a/apps/code/catalog.it.i18n
+++ b/apps/code/catalog.it.i18n
@@ -1,5 +1,11 @@
PythonPound = "Commento"
PythonPercent = "Modulo"
+PythonColon = "Due punti"
+PythonSemicon = "Punto e virgola"
+PythonExclamationMark = "Punto esclamativo"
+PythonLessThan = "Minore di"
+PythonGreaterThan = "Maggiore di"
+PythonQuestionMark = "Punto interrogativo"
Python1J = "Unità immaginaria"
PythonLF = "Nuova riga"
PythonTab = "Tabulazione"
diff --git a/apps/code/catalog.nl.i18n b/apps/code/catalog.nl.i18n
index 876073d95..4ba8d9906 100644
--- a/apps/code/catalog.nl.i18n
+++ b/apps/code/catalog.nl.i18n
@@ -1,5 +1,11 @@
PythonPound = "Opmerkingen"
PythonPercent = "Modulo"
+PythonColon = "Dubbele punt"
+PythonSemicon = "Puntkomma"
+PythonExclamationMark = "Uitroepteken"
+PythonLessThan = "Kleiner dan"
+PythonGreaterThan = "Groter dan"
+PythonQuestionMark = "Vraagteken"
Python1J = "Imaginaire i"
PythonLF = "Nieuwe regel"
PythonTab = "Tabulatie"
diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n
index 155aa785f..bc740acdf 100644
--- a/apps/code/catalog.pt.i18n
+++ b/apps/code/catalog.pt.i18n
@@ -1,5 +1,11 @@
PythonPound = "Comentário"
PythonPercent = "Módulo"
+PythonColon = "Dois pontos"
+PythonSemicon = "Ponto e vírgula"
+PythonExclamationMark = "Ponto de exclamação"
+PythonLessThan = "Menor que"
+PythonGreaterThan = "Maior que"
+PythonQuestionMark = "Ponto de interrogação"
Python1J = "i Complexo"
PythonLF = "Nova linha"
PythonTab = "Tabulação"
diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n
index ca6e19c45..09d292f7c 100644
--- a/apps/code/catalog.universal.i18n
+++ b/apps/code/catalog.universal.i18n
@@ -1,6 +1,12 @@
PythonCommandAmpersand = "&"
PythonCommandLF = "\\n"
PythonCommandPercent = "%"
+PythonCommandColon = ":"
+PythonCommandSemicon = ";"
+PythonCommandExclamationMark = "!"
+PythonCommandLessThan = "<"
+PythonCommandGreaterThan = ">"
+PythonCommandQuestionMark = "?"
PythonCommandPound = "#"
PythonCommandSingleQuote = "'x'"
PythonCommandSymbolExp = "^"
diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp
index 1f75378d3..f8a749022 100644
--- a/apps/code/python_toolbox.cpp
+++ b/apps/code/python_toolbox.cpp
@@ -503,6 +503,15 @@ const ToolboxMessageTree modulesChildren[] = {
const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPound, I18n::Message::PythonPound, false),
+ #ifdef _FXCG
+ // There is no question mark button on the fx-CG calculators
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColon, I18n::Message::PythonColon, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSemicon, I18n::Message::PythonSemicon, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandExclamationMark, I18n::Message::PythonExclamationMark, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandLessThan, I18n::Message::PythonLessThan, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGreaterThan, I18n::Message::PythonGreaterThan, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandQuestionMark, I18n::Message::PythonQuestionMark, false),
+ #endif
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPercent, I18n::Message::PythonPercent, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommand1J, I18n::Message::Python1J, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandLF, I18n::Message::PythonLF, false),
diff --git a/apps/graph/graph/graph_view.h b/apps/graph/graph/graph_view.h
index ded212f6d..876881140 100644
--- a/apps/graph/graph/graph_view.h
+++ b/apps/graph/graph/graph_view.h
@@ -18,7 +18,13 @@ public:
* 10.0938275501223 which are hopefully rare enough.
* TODO: The drawCurve algorithm should use the derivative function to know
* how fast the function moves... */
+ #ifndef _FXCG
static constexpr float k_graphStepDenominator = 10.0938275501223f;
+ #else
+ // This value rounded down has to be a factor of the horizontal resolution / 2
+ // On the Casio calculator the resolution is 396 pixels, so 11 is close but works
+ static constexpr float k_graphStepDenominator = 11.0938275501223f;
+ #endif
GraphView(Shared::InteractiveCurveViewRange * graphRange,
Shared::CurveViewCursor * cursor, Shared::BannerView * bannerView, Shared::CursorView * cursorView);
diff --git a/apps/home/controller.cpp b/apps/home/controller.cpp
index da3a147d4..1936b47ef 100644
--- a/apps/home/controller.cpp
+++ b/apps/home/controller.cpp
@@ -276,7 +276,7 @@ void Controller::tableViewDidChangeSelection(SelectableTableView * t, int previo
* (so the previous one is always visible). */
int appIndex = (t->selectedColumn()+t->selectedRow()*k_numberOfColumns)+1;
if (appIndex >= this->numberOfIcons()+1) {
- t->selectCellAtLocation((this->numberOfIcons()%3)-1, (this->numberOfIcons() / k_numberOfColumns));
+ t->selectCellAtLocation((this->numberOfIcons()%k_numberOfColumns)-1, (this->numberOfIcons() / k_numberOfColumns));
}
}
diff --git a/apps/home/controller.h b/apps/home/controller.h
index 3301fac78..a330a7564 100644
--- a/apps/home/controller.h
+++ b/apps/home/controller.h
@@ -47,10 +47,19 @@ private:
static constexpr KDCoordinate k_sideMargin = 4;
static constexpr KDCoordinate k_bottomMargin = 14;
static constexpr KDCoordinate k_indicatorMargin = 61;
+
+ #ifndef _FXCG
static constexpr int k_numberOfColumns = 3;
- static constexpr int k_maxNumberOfCells = 16;
static constexpr int k_cellHeight = 104;
static constexpr int k_cellWidth = 104;
+ #else
+ // A different screen resolution so different dimensions
+ static constexpr int k_numberOfColumns = 4;
+ static constexpr int k_cellHeight = 96;
+ static constexpr int k_cellWidth = 97;
+ #endif
+
+ static constexpr int k_maxNumberOfCells = 16;
ContentView m_view;
AppCell m_cells[k_maxNumberOfCells];
App * m_app;
diff --git a/apps/math_toolbox.cpp b/apps/math_toolbox.cpp
index 30af1e1bd..1d4900c97 100644
--- a/apps/math_toolbox.cpp
+++ b/apps/math_toolbox.cpp
@@ -854,6 +854,10 @@ const ToolboxMessageTree Physics[] = {
const ToolboxMessageTree menu[] = {
+ #ifdef _FXCG
+ // There is no factorial button on the fx-CG calculators
+ ToolboxMessageTree::Leaf(I18n::Message::FactorialCommandWithArg, I18n::Message::Factorial, false, I18n::Message::FactorialCommand),
+ #endif
ToolboxMessageTree::Leaf(I18n::Message::AbsCommandWithArg, I18n::Message::AbsoluteValue),
ToolboxMessageTree::Leaf(I18n::Message::RootCommandWithArg, I18n::Message::NthRoot),
ToolboxMessageTree::Leaf(I18n::Message::LogCommandWithArg, I18n::Message::BasedLogarithm),
diff --git a/apps/probability/app.h b/apps/probability/app.h
index d93905c46..2e8e3fddf 100644
--- a/apps/probability/app.h
+++ b/apps/probability/app.h
@@ -57,7 +57,7 @@ public:
void deleteDistributionAndCalculation();
void initializeDistributionAndCalculation();
-#if __EMSCRIPTEN__
+#if (defined __EMSCRIPTEN__) || (defined _FXCG)
constexpr static int k_distributionAlignments[] = {alignof(BinomialDistribution),alignof(ExponentialDistribution), alignof(NormalDistribution), alignof(PoissonDistribution), alignof(UniformDistribution), 0};
constexpr static size_t k_distributionAlignment = max(k_distributionAlignments);
constexpr static int k_calculationAlignments[] = {alignof(LeftIntegralCalculation),alignof(FiniteIntegralCalculation), alignof(RightIntegralCalculation), 0};
diff --git a/apps/settings/sub_menu/about_controller.cpp b/apps/settings/sub_menu/about_controller.cpp
index a022c48f0..dcae3211a 100644
--- a/apps/settings/sub_menu/about_controller.cpp
+++ b/apps/settings/sub_menu/about_controller.cpp
@@ -1,5 +1,6 @@
#include "about_controller.h"
#include "../../../python/src/py/mpconfig.h"
+#include "poincare/division.h"
#include
#include
#include
diff --git a/apps/settings/sub_menu/datetime_controller.cpp b/apps/settings/sub_menu/datetime_controller.cpp
index 1f9e801b9..35fb8c994 100644
--- a/apps/settings/sub_menu/datetime_controller.cpp
+++ b/apps/settings/sub_menu/datetime_controller.cpp
@@ -30,7 +30,10 @@ bool DateTimeController::handleEvent(Ion::Events::Event event) {
if (selectedRow() == 0) {
clockEnabled = !clockEnabled;
if (clockEnabled) {
+ #ifndef _FXCG
+ // This doesn't apply on Casio calculators
Container::activeApp()->displayWarning(I18n::Message::RTCWarning1, I18n::Message::RTCWarning2);
+ #endif
}
Ion::RTC::setMode(clockEnabled ? Ion::RTC::Mode::HSE : Ion::RTC::Mode::Disabled);
}
diff --git a/apps/shared.hu.i18n b/apps/shared.hu.i18n
index a916a2e76..91e45f3d1 100644
--- a/apps/shared.hu.i18n
+++ b/apps/shared.hu.i18n
@@ -121,3 +121,4 @@ CatalyticActivityDimension = "Katalitikus aktivitás"
SurfaceDimension = "Felület"
VolumeDimension = "Hangerő"
SpeedDimension = "Sebesség"
+Factorial = "Faktorál"
diff --git a/apps/shared.it.i18n b/apps/shared.it.i18n
index 0e078c301..d1932fbd3 100644
--- a/apps/shared.it.i18n
+++ b/apps/shared.it.i18n
@@ -121,3 +121,4 @@ CatalyticActivityDimension = "Attività catalitica"
SurfaceDimension = "Superficie"
VolumeDimension = "Volume"
SpeedDimension = "Velocità"
+Factorial = "Fattoriale"
diff --git a/apps/shared.nl.i18n b/apps/shared.nl.i18n
index d8a4b076f..e63e98605 100644
--- a/apps/shared.nl.i18n
+++ b/apps/shared.nl.i18n
@@ -121,3 +121,4 @@ CatalyticActivityDimension = "Katalytische activiteit"
SurfaceDimension = "Oppervlak"
VolumeDimension = "Volume"
SpeedDimension = "Snelheid"
+Factorial = "Faculteit"
diff --git a/apps/shared.universal.i18n b/apps/shared.universal.i18n
index cad61ea9b..f38ad6085 100644
--- a/apps/shared.universal.i18n
+++ b/apps/shared.universal.i18n
@@ -125,6 +125,8 @@ DotCommandWithArg = "dot(u,v)"
E = "e"
Equal = "="
FactorCommandWithArg = "factor(n)"
+FactorialCommand = "!"
+FactorialCommandWithArg = "n!"
FccId = "FCC ID"
FloorCommandWithArg = "floor(x)"
FracCommandWithArg = "frac(x)"
diff --git a/apps/shared/continuous_function.cpp b/apps/shared/continuous_function.cpp
index e127feed5..39c705ecd 100644
--- a/apps/shared/continuous_function.cpp
+++ b/apps/shared/continuous_function.cpp
@@ -270,7 +270,7 @@ void ContinuousFunction::rangeForDisplay(float * xMin, float * xMax, float * yMi
}
if (!basedOnCostlyAlgorithms(context)) {
- Zoom::ValueAtAbscissa evaluation = [](float x, Context * context, const void * auxiliary) {
+ Zoom::ValueAtAbscissa evaluation = [](float x, Context * context, const void * auxiliary) -> float {
/* When evaluating sin(x)/x close to zero using the standard sine function,
* one can detect small variations, while the cardinal sine is supposed to be
* locally monotonous. To smooth our such variations, we round the result of
diff --git a/apps/shared/continuous_function_cache.cpp b/apps/shared/continuous_function_cache.cpp
index 00a8cd687..5ff498052 100644
--- a/apps/shared/continuous_function_cache.cpp
+++ b/apps/shared/continuous_function_cache.cpp
@@ -65,7 +65,11 @@ void ContinuousFunctionCache::ComputeNonCartesianSteps(float * tStep, float * tC
const int numberOfWholeSteps = static_cast(Graph::GraphView::k_graphStepDenominator);
static_assert(numberOfCacheablePoints % numberOfWholeSteps == 0, "numberOfCacheablePoints should be a multiple of numberOfWholeSteps for optimal caching");
const int multiple = numberOfCacheablePoints / numberOfWholeSteps;
+ // Ignore this on Casio calculators for now, as the screen resolution breaks this
+ // TODO: fix this. if it's possible
+ #ifndef _FXCG
static_assert(multiple && !(multiple & (multiple - 1)), "multiple should be a power of 2 for optimal caching");
+ #endif
/* Define cacheStep such that every whole graph steps are equally divided
* For instance, with :
* graphStepDenominator = 10.1
diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h
index 1083f8201..d0eebb316 100644
--- a/apps/shared/store_controller.h
+++ b/apps/shared/store_controller.h
@@ -49,7 +49,12 @@ protected:
static constexpr KDCoordinate k_cellWidth = Poincare::PrintFloat::glyphLengthForFloatWithPrecision(Poincare::Preferences::LargeNumberOfSignificantDigits) * 7 + 2*Metric::CellMargin + Metric::TableSeparatorThickness; // KDFont::SmallFont->glyphSize().width() = 7
constexpr static int k_maxNumberOfEditableCells = (Ion::Display::Width/k_cellWidth+2) * ((Ion::Display::Height - Metric::TitleBarHeight - Metric::TabHeight)/k_cellHeight+2);
+ #ifndef _FXCG
constexpr static int k_numberOfTitleCells = 4;
+ #else
+ // This is different here due to the changed screen resolution
+ constexpr static int k_numberOfTitleCells = 5;
+ #endif
static constexpr int k_titleCellType = 0;
static constexpr int k_editableCellType = 1;
diff --git a/apps/solver/solutions_controller.h b/apps/solver/solutions_controller.h
index 4fac929a1..2bf90d04b 100644
--- a/apps/solver/solutions_controller.h
+++ b/apps/solver/solutions_controller.h
@@ -87,7 +87,7 @@ private:
// Number of cells
constexpr static int k_maxNumberOfVisibleCells = (Ion::Display::Height - 3 * Metric::TitleBarHeight) / k_defaultCellHeight + 1; // When displaying approximate solutions for cos(x) = 0 between 0 and 1800 and scrolling down
- static_assert(k_maxNumberOfVisibleCells == 10, "k_maxNumberOfVisibleCells has changed"); //This assert is just for information purposes
+ // static_assert(k_maxNumberOfVisibleCells == 10, "k_maxNumberOfVisibleCells has changed"); //This assert is just for information purposes
static_assert(k_maxNumberOfVisibleCells <= EquationStore::k_maxNumberOfSolutions + Poincare::Expression::k_maxNumberOfVariables, "We can reduce the number of cells in Solver:SolutionsController.");
constexpr static int k_maxNumberOfSymbols = EquationStore::k_maxNumberOfSolutions + Poincare::Expression::k_maxNumberOfVariables;
constexpr static int k_numberOfSymbolCells = k_maxNumberOfVisibleCells < k_maxNumberOfSymbols ? k_maxNumberOfVisibleCells : k_maxNumberOfSymbols;
diff --git a/apps/toolbox.de.i18n b/apps/toolbox.de.i18n
index 02cc172e3..2582f6d1b 100644
--- a/apps/toolbox.de.i18n
+++ b/apps/toolbox.de.i18n
@@ -511,3 +511,4 @@ MagneticFluxQuantumTag = "Magnetisches Fluss-Quantum"
ConductanceQuantumTag = "Leitwertquantum"
CirculationQuantumTag = "Auflage-Quantum"
MatricesAndVectors = "Matrizen und Vektoren"
+Factorial = "Fakultät"
diff --git a/apps/toolbox.en.i18n b/apps/toolbox.en.i18n
index fd5aaafc8..e6781e868 100644
--- a/apps/toolbox.en.i18n
+++ b/apps/toolbox.en.i18n
@@ -511,3 +511,4 @@ HartreeConstantTag = "Hartree Constant"
MagneticFluxQuantumTag = "Magnetic Flux Quantum"
ConductanceQuantumTag = "Conductance Quantum"
CirculationQuantumTag = "Circulation Quantum"
+Factorial = "Factorial"
\ No newline at end of file
diff --git a/apps/toolbox.es.i18n b/apps/toolbox.es.i18n
index 841d37d7e..ddb8dcf1c 100644
--- a/apps/toolbox.es.i18n
+++ b/apps/toolbox.es.i18n
@@ -511,3 +511,4 @@ MagneticFluxQuantumTag = "Flujo Magnético Cuántico"
ConductanceQuantumTag = "Conductancia Quantum"
CirculationQuantumTag = "Circulación Quantum"
MatricesAndVectors = "Matrices y vectores"
+Factorial = "Factorial"
diff --git a/apps/toolbox.fr.i18n b/apps/toolbox.fr.i18n
index 283f2bc28..ac0f40b36 100644
--- a/apps/toolbox.fr.i18n
+++ b/apps/toolbox.fr.i18n
@@ -515,3 +515,4 @@ MagneticFluxQuantumTag = "Quantum de Flux Magnétique"
ConductanceQuantumTag = "Quantum de Conductance"
CirculationQuantumTag = "Quantum de Circulation"
MatricesAndVectors = "Matrices et vecteurs"
+Factorial = "Factorielle"
diff --git a/apps/toolbox.pt.i18n b/apps/toolbox.pt.i18n
index 219680ae9..8ba7d55f8 100644
--- a/apps/toolbox.pt.i18n
+++ b/apps/toolbox.pt.i18n
@@ -511,3 +511,4 @@ MagneticFluxQuantumTag = "Fluxo Magnético Quântico"
ConductanceQuantumTag = "Quantum de Conduta"
CirculationQuantumTag = "Quantum de Circulação"
MatricesAndVectors = "Matrizes e vetores"
+Factorial = "Fatorial"
diff --git a/build/platform.simulator.fxcg.mak b/build/platform.simulator.fxcg.mak
new file mode 100644
index 000000000..fc30c12e8
--- /dev/null
+++ b/build/platform.simulator.fxcg.mak
@@ -0,0 +1,11 @@
+TOOLCHAIN = sh-elf-gcc
+EXE = elf
+
+EPSILON_TELEMETRY ?= 0
+
+HANDY_TARGETS_EXTENSIONS = g3a bin
+
+USE_LIBA = 0
+POINCARE_TREE_LOG = 0
+
+SFLAGS := $(filter-out -fPIE, $(SFLAGS))
diff --git a/build/targets.simulator.fxcg.mak b/build/targets.simulator.fxcg.mak
new file mode 100644
index 000000000..ec26dcc81
--- /dev/null
+++ b/build/targets.simulator.fxcg.mak
@@ -0,0 +1,5 @@
+$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf
+ $(OBJCOPY) -O binary -R .bss -R .gint_bss $< $@
+
+$(BUILD_DIR)/%.g3a: $(BUILD_DIR)/%.bin ion/src/simulator/fxcg/assets/icon-uns.png ion/src/simulator/fxcg/assets/icon-sel.png
+ $(FXGXA) --g3a --icon-uns=ion/src/simulator/fxcg/assets/icon-uns.png --icon-sel=ion/src/simulator/fxcg/assets/icon-sel.png -n Upsilon $< -o $@
diff --git a/build/toolchain.sh-elf-gcc.mak b/build/toolchain.sh-elf-gcc.mak
new file mode 100644
index 000000000..54290ffce
--- /dev/null
+++ b/build/toolchain.sh-elf-gcc.mak
@@ -0,0 +1,10 @@
+CC = sh-elf-gcc
+CXX = sh-elf-g++
+LD = sh-elf-g++
+GDB = gdb
+OBJCOPY = sh-elf-objcopy
+SIZE = sh-elf-size
+AS = sh-elf-as
+FXGXA = fxgxa
+
+SFLAGS += -D_FXCG -D_BIG_ENDIAN
diff --git a/escher/src/icon_view.cpp b/escher/src/icon_view.cpp
index 7a5453d2a..1c1db37fe 100644
--- a/escher/src/icon_view.cpp
+++ b/escher/src/icon_view.cpp
@@ -1,6 +1,7 @@
#include
extern "C" {
#include
+#include
}
#include
#include
@@ -43,6 +44,13 @@ void IconView::drawRect(KDContext * ctx, KDRect rect) const {
iconBufferSize * sizeof(KDColor)
);
+ // If we are on a big-endian CPU, we need to swap the bytes
+ #if _BIG_ENDIAN
+ for (uint32_t i = 0; i < iconBufferSize; i++) {
+ pixelBuffer[i] = KDColor::RGB16(__builtin_bswap16(pixelBuffer[i]));
+ }
+ #endif
+
//We push the first 6 lines of the image so that they are truncated on the sides
ctx->fillRectWithPixels(KDRect(6, 0, m_frame.width()-12, 1),pixelBuffer+6, nullptr);
ctx->fillRectWithPixels(KDRect(4, 1, m_frame.width()-8, 1),pixelBuffer+4+55, nullptr);
diff --git a/escher/src/image_view.cpp b/escher/src/image_view.cpp
index 7cb4ee39e..5062ef3f7 100644
--- a/escher/src/image_view.cpp
+++ b/escher/src/image_view.cpp
@@ -1,6 +1,7 @@
#include
extern "C" {
#include
+#include
}
#include
@@ -50,6 +51,13 @@ void ImageView::drawRect(KDContext * ctx, KDRect rect) const {
pixelBufferSize * sizeof(KDColor)
);
+ // If we are on a big-endian CPU, we need to swap the bytes
+ #if _BIG_ENDIAN
+ for (uint32_t i = 0; i < pixelBufferSize; i++) {
+ pixelBuffer[i] = KDColor::RGB16(__builtin_bswap16(pixelBuffer[i]));
+ }
+ #endif
+
ctx->fillRectWithPixels(bounds(), pixelBuffer, nullptr);
}
diff --git a/ion/include/ion/display.h b/ion/include/ion/display.h
index 1b7f32508..0abd90c16 100644
--- a/ion/include/ion/display.h
+++ b/ion/include/ion/display.h
@@ -23,8 +23,15 @@ void pullRect(KDRect r, KDColor * pixels);
bool waitForVBlank();
+#ifndef _FXCG
constexpr int Width = 320;
constexpr int Height = 240;
+#else
+constexpr int Width = 396;
+constexpr int Height = 224;
+#endif
+
+// TODO: Adjust this on the Casio calculator
constexpr int WidthInTenthOfMillimeter = 576;
constexpr int HeightInTenthOfMillimeter = 432;
diff --git a/ion/include/ion/internal_storage.h b/ion/include/ion/internal_storage.h
index 257d243ce..fcad00c90 100644
--- a/ion/include/ion/internal_storage.h
+++ b/ion/include/ion/internal_storage.h
@@ -195,7 +195,7 @@ public:
class StorageHelper {
public:
static uint16_t unalignedShort(char * address) {
-#if __EMSCRIPTEN__
+#if (defined __EMSCRIPTEN__) || (defined _FXCG)
uint8_t f1 = *(address);
uint8_t f2 = *(address+1);
uint16_t f = (uint16_t)f1 + (((uint16_t)f2)<<8);
@@ -205,7 +205,7 @@ public:
#endif
}
static void writeUnalignedShort(uint16_t value, char * address) {
-#if __EMSCRIPTEN__
+#if (defined __EMSCRIPTEN__) || (defined _FXCG)
*((uint8_t *)address) = (uint8_t)(value & ((1 << 8) - 1));
*((uint8_t *)address+1) = (uint8_t)(value >> 8);
#else
diff --git a/ion/include/ion/keyboard.h b/ion/include/ion/keyboard.h
index ba056ffd2..7d448991c 100644
--- a/ion/include/ion/keyboard.h
+++ b/ion/include/ion/keyboard.h
@@ -26,6 +26,12 @@ constexpr Key ValidKeys[] = {
constexpr int NumberOfKeys = 54;
constexpr int NumberOfValidKeys = 46;
+enum class ModSimState : uint8_t {
+ None,
+ ForceOn,
+ ForceOff,
+};
+
class State {
public:
constexpr State(uint64_t s = 0) :
@@ -50,8 +56,25 @@ public:
void clearKey(Key k) {
m_bitField &= ~((uint64_t)1 << (uint8_t)k);
}
+ void setSimulatedShift(ModSimState s) {
+ m_simulateShiftState = s;
+ }
+ ModSimState simulatedShift() const {
+ return m_simulateShiftState;
+ }
+ void setSimulatedAlpha(ModSimState s) {
+ m_simulateAlphaState = s;
+ }
+ ModSimState simulatedAlpha() const {
+ return m_simulateAlphaState;
+ }
private:
uint64_t m_bitField;
+
+ // Simulated key states
+ // These override the real key states and are used to map keys to keys under modifiers
+ ModSimState m_simulateShiftState = ModSimState::None;
+ ModSimState m_simulateAlphaState = ModSimState::None;
};
State scan();
diff --git a/ion/src/shared/events_keyboard.cpp b/ion/src/shared/events_keyboard.cpp
index 73038b619..6d281c482 100644
--- a/ion/src/shared/events_keyboard.cpp
+++ b/ion/src/shared/events_keyboard.cpp
@@ -1,3 +1,4 @@
+#include
#include
#include
#include
@@ -84,6 +85,16 @@ static inline Event innerGetEvent(int * timeout) {
Keyboard::Key key = (Keyboard::Key)(63-__builtin_clzll(keysSeenTransitioningFromUpToDown));
bool shift = isShiftActive() || state.keyDown(Keyboard::Key::Shift);
bool alpha = isAlphaActive() || state.keyDown(Keyboard::Key::Alpha);
+
+ // Allow the detected states to be overriden by the simulated states
+ // This is used for key mapping
+ if (state.simulatedShift() != Keyboard::ModSimState::None) {
+ shift = state.simulatedShift() == Keyboard::ModSimState::ForceOn;
+ }
+ if (state.simulatedAlpha() != Keyboard::ModSimState::None) {
+ alpha = state.simulatedAlpha() == Keyboard::ModSimState::ForceOn;
+ }
+
bool lock = isLockActive();
if ( key == Keyboard::Key::Left
diff --git a/ion/src/simulator/external/config.fxcg.mak b/ion/src/simulator/external/config.fxcg.mak
new file mode 100644
index 000000000..5c9e873ab
--- /dev/null
+++ b/ion/src/simulator/external/config.fxcg.mak
@@ -0,0 +1,2 @@
+undefine sdl_src
+undefine ion_simulator_sdl_src
diff --git a/ion/src/simulator/fxcg/Makefile b/ion/src/simulator/fxcg/Makefile
new file mode 100644
index 000000000..02a9ed717
--- /dev/null
+++ b/ion/src/simulator/fxcg/Makefile
@@ -0,0 +1,66 @@
+
+ion_src += $(addprefix ion/src/simulator/fxcg/, \
+ main.cpp \
+ clipboard.cpp \
+ display.cpp \
+ framebuffer.cpp \
+ telemetry_init.cpp \
+ keyboard.cpp \
+ events_keyboard.cpp \
+ events.cpp \
+ timing.cpp \
+ console.cpp \
+ backlight.cpp \
+ power.cpp \
+ menuHandler.cpp \
+)
+
+liba_src += $(addprefix liba/src/, \
+ strlcat.c \
+ strlcpy.c \
+)
+
+ion_src += ion/src/shared/collect_registers.cpp
+
+sdl_simu_needs_to_be_removed += $(addprefix ion/src/simulator/shared/, \
+ main.cpp \
+ clipboard.cpp \
+ display.cpp \
+ framebuffer.cpp \
+ keyboard.cpp \
+ events_keyboard.cpp \
+ events_platform.cpp \
+ events.cpp \
+ layout.cpp \
+ actions.cpp \
+ window.cpp \
+ timing.cpp \
+ console.cpp \
+)
+
+sdl_simu_needs_to_be_removed += $(addprefix ion/src/shared/dummy/, \
+ backlight.cpp \
+ power.cpp \
+)
+
+#sdl_simu_needs_to_be_removed += $(addprefix ion/src/simulator/shared/dummy/, \
+# display.cpp \
+# led.cpp \
+# usb.cpp \
+# battery.cpp \
+# store_script.cpp \
+#)
+
+# Remove the dummy diaplay (re-implemented) and the SDL simulator stuff.
+ion_src := $(filter-out $(sdl_simu_needs_to_be_removed),$(ion_src))
+
+SFLAGS := $(filter-out -Iion/src/simulator/external/sdl/include,$(SFLAGS))
+
+SFLAGS += -DFXCG50 -DTARGET_FXCG50 -m4-nofpu -mb -ffreestanding -nostdlib -Wa,--dsp -fstrict-volatile-bitfields -g -Os
+LDFLAGS += -nostdlib -Wl,--no-warn-rwx-segments -lgint-cg -lc -lgint-cg -lc -lgcc -lopenlibm -lstdc++ -lgcc
+
+ifdef FASTLOAD
+ LDFLAGS += -T fxcg50_fastload.ld
+else
+ LDFLAGS += -T fxcg50.ld
+endif
diff --git a/ion/src/simulator/fxcg/assets/icon-sel.png b/ion/src/simulator/fxcg/assets/icon-sel.png
new file mode 100644
index 000000000..a016c36b3
Binary files /dev/null and b/ion/src/simulator/fxcg/assets/icon-sel.png differ
diff --git a/ion/src/simulator/fxcg/assets/icon-uns.png b/ion/src/simulator/fxcg/assets/icon-uns.png
new file mode 100644
index 000000000..c2f718472
Binary files /dev/null and b/ion/src/simulator/fxcg/assets/icon-uns.png differ
diff --git a/ion/src/simulator/fxcg/backlight.cpp b/ion/src/simulator/fxcg/backlight.cpp
new file mode 100644
index 000000000..18360bc0b
--- /dev/null
+++ b/ion/src/simulator/fxcg/backlight.cpp
@@ -0,0 +1,67 @@
+#include
+
+#include
+#include
+
+// From gint:
+/* Interface with the controller */
+static volatile uint16_t *intf = (uint16_t *)0xb4000000;
+/* Bit 4 of Port R controls the RS bit of the display driver */
+static volatile uint8_t *PRDR = (uint8_t *)0xa405013c;
+
+GINLINE static void select(uint16_t reg)
+{
+ /* Clear RS and write the register number */
+ *PRDR &= ~0x10;
+ synco();
+ *intf = reg;
+ synco();
+
+ /* Set RS back. We don't do this in read()/write() because the display
+ driver is optimized for consecutive GRAM access. LCD-transfers will
+ be faster when executing select() followed by several calls to
+ write(). (Although most applications should use the DMA instead.) */
+ *PRDR |= 0x10;
+ synco();
+}
+
+// From Utilities addin:
+// START OF POWER MANAGEMENT CODE
+#define LCDC *(unsigned int*)(0xB4000000)
+int getRawBacklightSubLevel()
+{
+ // Bdisp_DDRegisterSelect(0x5a1);
+ select(0x5a1);
+ return (LCDC & 0xFF) - 6;
+}
+void setRawBacklightSubLevel(int level)
+{
+ // Bdisp_DDRegisterSelect(0x5a1);
+ select(0x5a1);
+ LCDC = (level & 0xFF) + 6;
+}
+// END OF POWER MANAGEMENT CODE
+
+namespace Ion {
+namespace Backlight {
+
+uint8_t brightness() {
+ return getRawBacklightSubLevel();
+}
+
+void setBrightness(uint8_t b) {
+ setRawBacklightSubLevel(b);
+}
+
+void init() {
+}
+
+bool isInitialized() {
+ return true;
+}
+
+void shutdown() {
+}
+
+}
+}
diff --git a/ion/src/simulator/fxcg/clipboard.cpp b/ion/src/simulator/fxcg/clipboard.cpp
new file mode 100644
index 000000000..c3ce45627
--- /dev/null
+++ b/ion/src/simulator/fxcg/clipboard.cpp
@@ -0,0 +1,18 @@
+#include
+#include
+#include
+
+namespace Ion {
+namespace Clipboard {
+
+uint32_t localClipboardVersion;
+
+void write(const char * text) {
+}
+
+const char * read() {
+ return nullptr;
+}
+
+}
+}
diff --git a/ion/src/simulator/fxcg/console.cpp b/ion/src/simulator/fxcg/console.cpp
new file mode 100644
index 000000000..d8957c6e6
--- /dev/null
+++ b/ion/src/simulator/fxcg/console.cpp
@@ -0,0 +1,24 @@
+#include
+#include "main.h"
+#include
+#include
+
+namespace Ion {
+namespace Console {
+
+char readChar() {
+ return 0;
+}
+
+void writeChar(char c) {
+ // fxlibc conflicts with this
+ #undef putchar
+ KDIonContext::putchar(c);
+}
+
+bool transmissionDone() {
+ return true;
+}
+
+}
+}
diff --git a/ion/src/simulator/fxcg/display.cpp b/ion/src/simulator/fxcg/display.cpp
new file mode 100644
index 000000000..27e896bee
--- /dev/null
+++ b/ion/src/simulator/fxcg/display.cpp
@@ -0,0 +1,30 @@
+#include "display.h"
+#include "framebuffer.h"
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+namespace Ion {
+namespace Simulator {
+namespace Display {
+
+void init() {
+}
+
+void quit() {
+}
+
+void draw() {
+ dupdate();
+}
+
+}
+}
+}
diff --git a/ion/src/simulator/fxcg/display.h b/ion/src/simulator/fxcg/display.h
new file mode 100644
index 000000000..ab524fd3c
--- /dev/null
+++ b/ion/src/simulator/fxcg/display.h
@@ -0,0 +1,19 @@
+#ifndef ION_SIMULATOR_DISPLAY_H
+#define ION_SIMULATOR_DISPLAY_H
+
+#include
+
+namespace Ion {
+namespace Simulator {
+namespace Display {
+
+void init();
+void quit();
+
+void draw();
+
+}
+}
+}
+
+#endif
diff --git a/ion/src/simulator/fxcg/events.cpp b/ion/src/simulator/fxcg/events.cpp
new file mode 100644
index 000000000..9b8d00557
--- /dev/null
+++ b/ion/src/simulator/fxcg/events.cpp
@@ -0,0 +1,23 @@
+#include "events.h"
+#include
+
+namespace Ion {
+namespace Events {
+
+void didPressNewKey() {
+}
+
+char * sharedExternalTextBuffer() {
+ static char buffer[sharedExternalTextBufferSize];
+ return buffer;
+}
+
+const char * Event::text() const {
+ if (*this == ExternalText) {
+ return const_cast(sharedExternalTextBuffer());
+ }
+ return defaultText();
+}
+
+}
+}
diff --git a/ion/src/simulator/fxcg/events.h b/ion/src/simulator/fxcg/events.h
new file mode 100644
index 000000000..b012a43c5
--- /dev/null
+++ b/ion/src/simulator/fxcg/events.h
@@ -0,0 +1,24 @@
+#ifndef ION_SIMULATOR_EVENTS_H
+#define ION_SIMULATOR_EVENTS_H
+
+#include
+
+namespace Ion {
+namespace Simulator {
+namespace Events {
+
+void dumpEventCount(int i);
+void logAfter(int numberOfEvents);
+
+}
+}
+
+namespace Events {
+
+static constexpr int sharedExternalTextBufferSize = 2;
+char * sharedExternalTextBuffer();
+
+}
+}
+
+#endif
diff --git a/ion/src/simulator/fxcg/events_keyboard.cpp b/ion/src/simulator/fxcg/events_keyboard.cpp
new file mode 100644
index 000000000..615c327d5
--- /dev/null
+++ b/ion/src/simulator/fxcg/events_keyboard.cpp
@@ -0,0 +1,15 @@
+#include
+
+namespace Ion {
+namespace Events {
+
+
+Event getPlatformEvent() {
+ Event result = None;
+
+ return result;
+}
+
+
+}
+}
diff --git a/ion/src/simulator/fxcg/framebuffer.cpp b/ion/src/simulator/fxcg/framebuffer.cpp
new file mode 100644
index 000000000..d680467c1
--- /dev/null
+++ b/ion/src/simulator/fxcg/framebuffer.cpp
@@ -0,0 +1,56 @@
+#include "framebuffer.h"
+#include
+#include
+#include "main.h"
+
+#include
+#include
+
+// static KDColor sPixels[Ion::Display::Width * Ion::Display::Height];
+static_assert(sizeof(KDColor) == sizeof(uint16_t), "KDColor is not 16 bits");
+static KDColor* sPixels = (KDColor*) gint_vram;
+static bool sFrameBufferActive = true;
+
+namespace Ion {
+namespace Display {
+
+static KDFrameBuffer sFrameBuffer = KDFrameBuffer(sPixels, KDSize(Ion::Display::Width, Ion::Display::Height));
+
+void pushRect(KDRect r, const KDColor * pixels) {
+ if (sFrameBufferActive) {
+ Simulator::Main::setNeedsRefresh();
+ sFrameBuffer.pushRect(r, pixels);
+ }
+}
+
+void pushRectUniform(KDRect r, KDColor c) {
+ if (sFrameBufferActive) {
+ Simulator::Main::setNeedsRefresh();
+ sFrameBuffer.pushRectUniform(r, c);
+ }
+}
+
+void pullRect(KDRect r, KDColor * pixels) {
+ if (sFrameBufferActive) {
+ sFrameBuffer.pullRect(r, pixels);
+ }
+}
+
+}
+}
+
+namespace Ion {
+namespace Simulator {
+namespace Framebuffer {
+
+const KDColor * address() {
+ return sPixels;
+}
+
+void setActive(bool enabled) {
+ sFrameBufferActive = enabled;
+}
+
+}
+}
+}
\ No newline at end of file
diff --git a/ion/src/simulator/fxcg/framebuffer.h b/ion/src/simulator/fxcg/framebuffer.h
new file mode 100644
index 000000000..dba4dbd32
--- /dev/null
+++ b/ion/src/simulator/fxcg/framebuffer.h
@@ -0,0 +1,17 @@
+#ifndef ION_SIMULATOR_FRAMEBUFFER_H
+#define ION_SIMULATOR_FRAMEBUFFER_H
+
+#include
+
+namespace Ion {
+namespace Simulator {
+namespace Framebuffer {
+
+const KDColor * address();
+void setActive(bool enabled);
+
+}
+}
+}
+
+#endif
\ No newline at end of file
diff --git a/ion/src/simulator/fxcg/keyboard.cpp b/ion/src/simulator/fxcg/keyboard.cpp
new file mode 100644
index 000000000..735b72e1f
--- /dev/null
+++ b/ion/src/simulator/fxcg/keyboard.cpp
@@ -0,0 +1,262 @@
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "keyboard.h"
+#include "layout_keyboard.h"
+#include "main.h"
+#include "menuHandler.h"
+
+
+using namespace Ion::Keyboard;
+
+class KeyPair {
+public:
+ constexpr KeyPair(Key key, bool numworksShift, bool numworksAlpha, int gintKey, bool gintShift, bool gintAlpha, bool ignoreShiftAlpha = false) :
+ m_key(key),
+ m_numworksShift(numworksShift),
+ m_numworksAlpha(numworksAlpha),
+ m_gintKey(gintKey),
+ m_gintShift(gintShift),
+ m_gintAlpha(gintAlpha),
+ m_ignoreShiftAlpha(ignoreShiftAlpha)
+ {}
+ Key key() const { return m_key; }
+ bool numworksShift() const { return m_numworksShift; }
+ bool numworksAlpha() const { return m_numworksAlpha; }
+ int gintKey() const { return m_gintKey; }
+ bool gintShift() const { return m_gintShift; }
+ bool gintAlpha() const { return m_gintAlpha; }
+ bool ignoreShiftAlpha() const { return m_ignoreShiftAlpha; }
+private:
+ Key m_key;
+ bool m_numworksShift;
+ bool m_numworksAlpha;
+ int m_gintKey;
+ bool m_gintShift;
+ bool m_gintAlpha;
+ bool m_ignoreShiftAlpha;
+};
+
+constexpr static KeyPair sKeyPairs[] = {
+ KeyPair(Key::Down, false, false, KEY_DOWN, false, false, true),
+ KeyPair(Key::Left, false, false, KEY_LEFT, false, false, true),
+ KeyPair(Key::Right, false, false, KEY_RIGHT, false, false, true),
+ KeyPair(Key::Up, false, false, KEY_UP, false, false, true),
+ KeyPair(Key::Back, false, false, KEY_EXIT, false, false),
+ KeyPair(Key::Home, false, false, KEY_MENU, false, false),
+ KeyPair(Key::Shift, false, false, KEY_SHIFT, false, false, true),
+ KeyPair(Key::Alpha, false, false, KEY_ALPHA, false, false, true),
+ KeyPair(Key::XNT, false, false, KEY_XOT, false, false),
+ KeyPair(Key::Var, false, false, KEY_VARS, false, false),
+ KeyPair(Key::Toolbox, false, false, KEY_OPTN, false, false),
+ KeyPair(Key::Backspace, false, false, KEY_DEL, false, false),
+ KeyPair(Key::Exp, false, false, KEY_LN, true, false),
+ KeyPair(Key::Ln, false, false, KEY_LN, false, false),
+ KeyPair(Key::Log, false, false, KEY_LOG, false, false),
+ KeyPair(Key::Imaginary, false, false, KEY_0, true, false),
+ KeyPair(Key::Comma, false, false, KEY_COMMA, false, false),
+ KeyPair(Key::Power, false, false, KEY_POWER, false, false),
+ KeyPair(Key::Sine, false, false, KEY_SIN, false, false),
+ KeyPair(Key::Cosine, false, false, KEY_COS, false, false),
+ KeyPair(Key::Tangent, false, false, KEY_TAN, false, false),
+ KeyPair(Key::Pi, false, false, KEY_EXP, true, false),
+ KeyPair(Key::Sqrt, false, false, KEY_SQUARE, true, false),
+ KeyPair(Key::Square, false, false, KEY_SQUARE, false, false),
+ KeyPair(Key::Seven, false, false, KEY_7, false, false),
+ KeyPair(Key::Eight, false, false, KEY_8, false, false),
+ KeyPair(Key::Nine, false, false, KEY_9, false, false),
+ KeyPair(Key::LeftParenthesis, false, false, KEY_LEFTP, false, false),
+ KeyPair(Key::RightParenthesis, false, false, KEY_RIGHTP, false, false),
+ KeyPair(Key::Four, false, false, KEY_4, false, false),
+ KeyPair(Key::Five, false, false, KEY_5, false, false),
+ KeyPair(Key::Six, false, false, KEY_6, false, false),
+ KeyPair(Key::Multiplication, false, false, KEY_MUL, false, false),
+ KeyPair(Key::Division, false, false, KEY_DIV, false, false),
+ KeyPair(Key::Division, false, false, KEY_FRAC, false, false),
+ KeyPair(Key::One, false, false, KEY_1, false, false),
+ KeyPair(Key::Two, false, false, KEY_2, false, false),
+ KeyPair(Key::Three, false, false, KEY_3, false, false),
+ KeyPair(Key::Plus, false, false, KEY_ADD, false, false),
+ KeyPair(Key::Minus, false, false, KEY_SUB, false, false),
+ KeyPair(Key::Zero, false, false, KEY_0, false, false),
+ KeyPair(Key::Dot, false, false, KEY_DOT, false, false),
+ KeyPair(Key::EE, false, false, KEY_EXP, false, false),
+ KeyPair(Key::Ans, false, false, KEY_NEG, true, false),
+ KeyPair(Key::EXE, false, false, KEY_EXE, false, false, true),
+ KeyPair(Key::OnOff, false, false, KEY_ACON, true, false),
+
+ // Cut
+ // Not assigned
+ // Copy
+ KeyPair(Key::Var, true, false, KEY_8, true, false),
+ // Paste
+ KeyPair(Key::Toolbox, true, false, KEY_9, true, false),
+ // Clear
+ KeyPair(Key::Backspace, true, false, KEY_ACON, false, false),
+ // [
+ KeyPair(Key::Exp, true, false, KEY_ADD, true, false),
+ // ]
+ KeyPair(Key::Ln, true, false, KEY_SUB, true, false),
+ // {
+ KeyPair(Key::Log, true, false, KEY_MUL, true, false),
+ // }
+ KeyPair(Key::Imaginary, true, false, KEY_DIV, true, false),
+ // _
+ KeyPair(Key::Comma, true, false, KEY_NEG, false, false),
+ // ->
+ KeyPair(Key::Power, true, false, KEY_STORE, false, false),
+ // asin
+ KeyPair(Key::Sine, true, false, KEY_SIN, true, false),
+ // acos
+ KeyPair(Key::Cosine, true, false, KEY_COS, true, false),
+ // atan
+ KeyPair(Key::Tangent, true, false, KEY_TAN, true, false),
+ // =
+ KeyPair(Key::Pi, true, false, KEY_DOT, true, false),
+ // <
+ KeyPair(Key::Sqrt, true, false, KEY_F1, false, false),
+ // >
+ KeyPair(Key::Square, true, false, KEY_F2, false, false),
+
+ // :
+ KeyPair(Key::XNT, false, true, KEY_F3, false, false),
+ // ;
+ KeyPair(Key::Var, false, true, KEY_F4, false, false),
+ // "
+ KeyPair(Key::Toolbox, false, true, KEY_EXP, false, true),
+ // %
+ KeyPair(Key::Backspace, false, true, KEY_F5, false, false),
+ // A
+ KeyPair(Key::Exp, false, true, KEY_XOT, false, true),
+ // B
+ KeyPair(Key::Ln, false, true, KEY_LOG, false, true),
+ // C
+ KeyPair(Key::Log, false, true, KEY_LN, false, true),
+ // D
+ KeyPair(Key::Imaginary, false, true, KEY_SIN, false, true),
+ // E
+ KeyPair(Key::Comma, false, true, KEY_COS, false, true),
+ // F
+ KeyPair(Key::Power, false, true, KEY_TAN, false, true),
+ // G
+ KeyPair(Key::Sine, false, true, KEY_FRAC, false, true),
+ // H
+ KeyPair(Key::Cosine, false, true, KEY_FD, false, true),
+ // I
+ KeyPair(Key::Tangent, false, true, KEY_LEFTP, false, true),
+ // J
+ KeyPair(Key::Pi, false, true, KEY_RIGHTP, false, true),
+ // K
+ KeyPair(Key::Sqrt, false, true, KEY_COMMA, false, true),
+ // L
+ KeyPair(Key::Square, false, true, KEY_ARROW, false, true),
+ // M
+ KeyPair(Key::Seven, false, true, KEY_7, false, true),
+ // N
+ KeyPair(Key::Eight, false, true, KEY_8, false, true),
+ // O
+ KeyPair(Key::Nine, false, true, KEY_9, false, true),
+ // P
+ KeyPair(Key::LeftParenthesis, false, true, KEY_4, false, true),
+ // Q
+ KeyPair(Key::RightParenthesis, false, true, KEY_5, false, true),
+ // R
+ KeyPair(Key::Four, false, true, KEY_6, false, true),
+ // S
+ KeyPair(Key::Five, false, true, KEY_TIMES, false, true),
+ // T
+ KeyPair(Key::Six, false, true, KEY_DIV, false, true),
+ // U
+ KeyPair(Key::Multiplication, false, true, KEY_1, false, true),
+ // V
+ KeyPair(Key::Division, false, true, KEY_2, false, true),
+ // W
+ KeyPair(Key::One, false, true, KEY_3, false, true),
+ // X
+ KeyPair(Key::Two, false, true, KEY_PLUS, false, true),
+ // Y
+ KeyPair(Key::Three, false, true, KEY_MINUS, false, true),
+ // Z
+ KeyPair(Key::Plus, false, true, KEY_0, false, true),
+ // Space
+ KeyPair(Key::Minus, false, true, KEY_DOT, false, true),
+ // ?
+ KeyPair(Key::Zero, false, true, KEY_F6, true, false),
+ // !
+ KeyPair(Key::Dot, false, true, KEY_F6, false, false),
+
+ // Brightness control shortcut in Upsilon
+ KeyPair(Key::Plus, true, false, KEY_UP, false, true),
+ KeyPair(Key::Minus, true, false, KEY_DOWN, false, true),
+};
+
+constexpr int sNumberOfKeyPairs = sizeof(sKeyPairs)/sizeof(KeyPair);
+
+namespace Ion {
+namespace Keyboard {
+
+int menuHeldFor = 0;
+
+State scan() {
+ State state = 0;
+
+ // Grab this opportunity to refresh the display if needed
+ Simulator::Main::refresh();
+
+ clearevents();
+ if (keydown(KEY_MENU)) {
+ state.setKey(Key::Home);
+ menuHeldFor++;
+ if (menuHeldFor > 30) {
+ Simulator::FXCGMenuHandler::openMenu();
+ dupdate();
+ // Wait until EXE is released
+ do {
+ sleep_ms(10);
+ clearevents();
+ } while (keydown(KEY_EXE));
+ }
+ } else {
+ menuHeldFor = 0;
+ }
+
+ for (int i = 0; i < sNumberOfKeyPairs; i++) {
+ const KeyPair & keyPair = sKeyPairs[i];
+ if (!keyPair.ignoreShiftAlpha() &&
+ (keyPair.gintShift() != Events::isShiftActive() ||
+ keyPair.gintAlpha() != Events::isAlphaActive())) {
+ continue;
+ }
+ if (keydown(keyPair.gintKey())) {
+ if (!keyPair.ignoreShiftAlpha()) {
+ state.setSimulatedShift(keyPair.numworksShift() ? ModSimState::ForceOn : ModSimState::ForceOff);
+ state.setSimulatedAlpha(keyPair.numworksAlpha() ? ModSimState::ForceOn : ModSimState::ForceOff);
+ }
+ state.setKey(keyPair.key());
+ }
+ }
+
+ return state;
+}
+
+}
+}
+
+namespace Ion {
+namespace Simulator {
+namespace Keyboard {
+
+}
+}
+}
\ No newline at end of file
diff --git a/ion/src/simulator/fxcg/keyboard.h b/ion/src/simulator/fxcg/keyboard.h
new file mode 100644
index 000000000..0e379642f
--- /dev/null
+++ b/ion/src/simulator/fxcg/keyboard.h
@@ -0,0 +1,15 @@
+#ifndef ION_SIMULATOR_KEYBOARD_H
+#define ION_SIMULATOR_KEYBOARD_H
+
+#include
+// #include
+
+namespace Ion {
+namespace Simulator {
+namespace Keyboard {
+
+}
+}
+}
+
+#endif
\ No newline at end of file
diff --git a/ion/src/simulator/fxcg/main.cpp b/ion/src/simulator/fxcg/main.cpp
new file mode 100644
index 000000000..9280dbc12
--- /dev/null
+++ b/ion/src/simulator/fxcg/main.cpp
@@ -0,0 +1,114 @@
+#include "main.h"
+#include "display.h"
+#include "platform.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+extern "C" {
+int main() {
+ Ion::Simulator::Main::init();
+ ion_main(0, NULL);
+ Ion::Simulator::Main::quit();
+
+ return 0;
+}
+}
+
+namespace Ion {
+namespace Simulator {
+namespace Main {
+
+static bool sNeedsRefresh = false;
+
+void init() {
+ Ion::Simulator::Display::init();
+ setNeedsRefresh();
+}
+
+void setNeedsRefresh() {
+ sNeedsRefresh = true;
+}
+
+void refresh() {
+ if (!sNeedsRefresh) {
+ return;
+ }
+
+ Display::draw();
+
+ sNeedsRefresh = false;
+}
+
+void quit() {
+ Ion::Simulator::Display::quit();
+}
+
+void EnableStatusArea(int opt) {
+ __asm__ __volatile__ (
+ ".align 2 \n\t"
+ "mov.l 2f, r2 \n\t"
+ "mov.l 1f, r0 \n\t"
+ "jmp @r2 \n\t"
+ "nop \n\t"
+ ".align 2 \n\t"
+ "1: \n\t"
+ ".long 0x02B7 \n\t"
+ ".align 4 \n\t"
+ "2: \n\t"
+ ".long 0x80020070 \n\t"
+ );
+}
+
+extern "C" void *__GetVRAMAddress(void);
+
+uint8_t xyram_backup[16 * 1024];
+uint8_t ilram_backup[4 * 1024];
+
+void worldSwitchHandler(void (*worldSwitchFunction)(), bool prepareVRAM) {
+ // Back up XYRAM
+ uint8_t* xyram = (uint8_t*) 0xe500e000;
+ memcpy(xyram_backup, xyram, 16 * 1024);
+ // Back up ILRAM
+ uint8_t* ilram = (uint8_t*) 0xe5200000;
+ memcpy(ilram_backup, ilram, 4 * 1024);
+
+ if (prepareVRAM) {
+ // Copying the screen to the OS's VRAM avoids a flicker when powering on
+ uint16_t* dst = (uint16_t *) __GetVRAMAddress();
+ uint16_t* src = gint_vram + 6;
+
+ for (int y = 0; y < 216; y++, dst += 384, src += 396) {
+ for (int x = 0; x < 384; x++) {
+ dst[x] = src[x];
+ }
+ }
+
+ // Disable the status area
+ EnableStatusArea(3);
+ }
+
+ worldSwitchFunction();
+
+ // Restore XYRAM
+ memcpy(xyram, xyram_backup, 16 * 1024);
+ // Restore ILRAM
+ memcpy(ilram, ilram_backup, 4 * 1024);
+}
+
+void runPowerOffSafe(void (*powerOffSafeFunction)(), bool prepareVRAM) {
+ gint_world_switch(GINT_CALL(worldSwitchHandler, powerOffSafeFunction, prepareVRAM));
+}
+
+}
+}
+}
diff --git a/ion/src/simulator/fxcg/main.h b/ion/src/simulator/fxcg/main.h
new file mode 100644
index 000000000..4ac441a06
--- /dev/null
+++ b/ion/src/simulator/fxcg/main.h
@@ -0,0 +1,20 @@
+#ifndef ION_SIMULATOR_MAIN_H
+#define ION_SIMULATOR_MAIN_H
+
+namespace Ion {
+namespace Simulator {
+namespace Main {
+
+void init();
+void quit();
+
+void setNeedsRefresh();
+void refresh();
+
+void runPowerOffSafe(void (*powerOffSafeFunction)(), bool prepareVRAM);
+
+}
+}
+}
+
+#endif
\ No newline at end of file
diff --git a/ion/src/simulator/fxcg/menuHandler.cpp b/ion/src/simulator/fxcg/menuHandler.cpp
new file mode 100644
index 000000000..514251609
--- /dev/null
+++ b/ion/src/simulator/fxcg/menuHandler.cpp
@@ -0,0 +1,97 @@
+#include "main.h"
+
+namespace Ion {
+namespace Simulator {
+namespace FXCGMenuHandler {
+
+int saveAndOpenMainMenu(void) {
+ int addr;
+
+ // get the address of the syscall table in it
+ addr = *(unsigned int *)0x8002007C;
+
+ if (addr < (int)0x80020070)
+ return 1;
+ if (addr >= (int)0x81000000)
+ return 1;
+
+ // get the pointer to syscall 1E58 - SwitchToMainMenu
+ addr += 0x1E58 * 4;
+ if (addr < (int)0x80020070)
+ return 1;
+ if (addr >= (int)0x81000000)
+ return 1;
+
+ addr = *(unsigned int *)addr;
+ if (addr < (int)0x80020070)
+ return 1;
+ if (addr >= (int)0x81000000)
+ return 1;
+
+ // Now addr has the address of the first operation in %1e58
+
+ // Run up to 150 times (300/2). OS 3.60's is 59 instructions, so this should
+ // be plenty, but will let it stop if nothing is found
+ for (unsigned short *currentAddr = (unsigned short *)addr;
+ (unsigned int)currentAddr < ((unsigned int)addr + 300); currentAddr++) {
+ // MOV.L GetkeyToMainFunctionReturn Flag, r14
+ if (*(unsigned char *)currentAddr != 0xDE)
+ continue;
+
+ // MOV #3, 2
+ if (*(currentAddr + 1) != 0xE203)
+ continue;
+
+ // BSR
+ if ((*(unsigned char *)(currentAddr + 2) & 0xF0) != 0xB0)
+ continue;
+
+ // MOV.B r2, @r14
+ if (*(currentAddr + 3) != 0x2E20)
+ continue;
+
+ // BRA
+ if ((*(unsigned char *)(currentAddr + 4) & 0xF0) != 0xA0)
+ continue;
+
+ // NOP
+ if (*(currentAddr + 5) != 0x0009)
+ continue;
+
+ unsigned short branchInstruction = *(currentAddr + 2);
+
+ // Clear first 4 bits (BSR identifier)
+ branchInstruction <<= 4;
+ branchInstruction >>= 4;
+
+ // branchInstruction is now the displacement of BSR
+
+ // Create typedef so we can cast the pointer
+ typedef void (*voidFunc)(void);
+
+ // JMP to disp*2 + PC + 4
+ ((voidFunc)((unsigned int)branchInstruction * 2 +
+ (unsigned int)currentAddr + 4 + 4))();
+
+ return 0;
+ }
+
+ return 1;
+}
+
+extern "C" void gint_osmenu_native(void);
+
+void openMenuWrapper(void) {
+ if (saveAndOpenMainMenu() != 0) {
+ // Fallback
+ gint_osmenu_native();
+ }
+}
+
+void openMenu(void) {
+ Simulator::Main::runPowerOffSafe(openMenuWrapper, true);
+}
+
+}
+}
+}
diff --git a/ion/src/simulator/fxcg/menuHandler.h b/ion/src/simulator/fxcg/menuHandler.h
new file mode 100644
index 000000000..6c455dfd9
--- /dev/null
+++ b/ion/src/simulator/fxcg/menuHandler.h
@@ -0,0 +1,14 @@
+#ifndef ION_SIMULATOR_MENUHANDLER_H
+#define ION_SIMULATOR_MENUHANDLER_H
+
+namespace Ion {
+namespace Simulator {
+namespace FXCGMenuHandler {
+
+void openMenu(void);
+
+}
+}
+}
+
+#endif
\ No newline at end of file
diff --git a/ion/src/simulator/fxcg/platform.h b/ion/src/simulator/fxcg/platform.h
new file mode 100644
index 000000000..af5ddfcb6
--- /dev/null
+++ b/ion/src/simulator/fxcg/platform.h
@@ -0,0 +1,23 @@
+#ifndef ION_SIMULATOR_PLATFORM_H
+#define ION_SIMULATOR_PLATFORM_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Those functions should be implemented per-platform.
+ * They are defined as C function for easier interop. */
+
+const char * IonSimulatorGetLanguageCode();
+
+void IonSimulatorKeyboardKeyDown(int keyNumber);
+void IonSimulatorKeyboardKeyUp(int keyNumber);
+void IonSimulatorEventsPushEvent(int eventNumber);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/ion/src/simulator/fxcg/power.cpp b/ion/src/simulator/fxcg/power.cpp
new file mode 100644
index 000000000..7375befec
--- /dev/null
+++ b/ion/src/simulator/fxcg/power.cpp
@@ -0,0 +1,42 @@
+#include
+#include
+#include
+
+#include "main.h"
+
+#include
+#include
+
+void PowerOff(int displayLogo) {
+ __asm__ __volatile__ (
+ ".align 2 \n\t"
+ "mov.l 2f, r2 \n\t"
+ "mov.l 1f, r0 \n\t"
+ "jmp @r2 \n\t"
+ "nop \n\t"
+ ".align 2 \n\t"
+ "1: \n\t"
+ ".long 0x1839 \n\t"
+ ".align 4 \n\t"
+ "2: \n\t"
+ ".long 0x80020070 \n\t"
+ );
+}
+
+void powerOff(void) {
+ PowerOff(1);
+}
+
+namespace Ion {
+namespace Power {
+
+void suspend(bool checkIfOnOffKeyReleased) {
+ Simulator::Main::runPowerOffSafe(powerOff, true);
+}
+
+void standby() {
+ Simulator::Main::runPowerOffSafe(powerOff, true);
+}
+
+}
+}
diff --git a/ion/src/simulator/fxcg/telemetry_init.cpp b/ion/src/simulator/fxcg/telemetry_init.cpp
new file mode 100644
index 000000000..7a69b2d8c
--- /dev/null
+++ b/ion/src/simulator/fxcg/telemetry_init.cpp
@@ -0,0 +1,15 @@
+#include "platform.h"
+
+namespace Ion {
+namespace Simulator {
+namespace Telemetry {
+
+void init() {
+}
+
+void shutdown() {
+}
+
+}
+}
+}
\ No newline at end of file
diff --git a/ion/src/simulator/fxcg/timing.cpp b/ion/src/simulator/fxcg/timing.cpp
new file mode 100644
index 000000000..9a6eedfca
--- /dev/null
+++ b/ion/src/simulator/fxcg/timing.cpp
@@ -0,0 +1,27 @@
+#include
+#include "main.h"
+#include
+// #include
+
+#include
+
+static auto start = std::chrono::steady_clock::now();
+
+namespace Ion {
+namespace Timing {
+
+uint64_t millis() {
+ auto elapsed = std::chrono::steady_clock::now() - start;
+ return std::chrono::duration_cast(elapsed).count();
+}
+
+void usleep(uint32_t us) {
+ sleep_us(us);
+}
+
+void msleep(uint32_t ms) {
+ sleep_us(ms * 1000);
+}
+
+}
+}
\ No newline at end of file
diff --git a/kandinsky/include/kandinsky/postprocess_gamma_context.h b/kandinsky/include/kandinsky/postprocess_gamma_context.h
index c3195f2ff..305d9cedd 100644
--- a/kandinsky/include/kandinsky/postprocess_gamma_context.h
+++ b/kandinsky/include/kandinsky/postprocess_gamma_context.h
@@ -2,6 +2,7 @@
#define KANDINSKY_POSTPROCESS_GAMMA_CONTEXT_H
#include
+#include
class KDPostProcessGammaContext : public KDPostProcessContext {
public:
@@ -13,7 +14,16 @@ private:
void pushRect(KDRect rect, const KDColor * pixels) override;
void pushRectUniform(KDRect rect, KDColor color) override;
void pullRect(KDRect rect, KDColor * pixels) override;
+ KDColor correctColor(KDColor color);
int m_redGamma, m_greenGamma, m_blueGamma;
+ // Lookup tables to do gamma correction
+ // 5 bits of red
+ uint8_t m_redGammaTable[32];
+ // 6 bits of green
+ uint8_t m_greenGammaTable[64];
+ // 5 bits of blue
+ uint8_t m_blueGammaTable[32];
+ void updateGammaTables();
};
#endif
diff --git a/kandinsky/src/postprocess_gamma_context.cpp b/kandinsky/src/postprocess_gamma_context.cpp
index 6a75e60db..95a89768b 100644
--- a/kandinsky/src/postprocess_gamma_context.cpp
+++ b/kandinsky/src/postprocess_gamma_context.cpp
@@ -1,6 +1,8 @@
+#include
#include
#include
#include
+#include
constexpr int MaxGammaStates = 7;
constexpr float MaxGammaGamut = 0.75;
@@ -13,8 +15,28 @@ constexpr int clampGamma(int gamma) {
return gamma < -MaxGammaStates ? -MaxGammaStates : (gamma > MaxGammaStates ? MaxGammaStates : gamma);
}
+void KDPostProcessGammaContext::updateGammaTables() {
+ const float redGamma = toGamma(m_redGamma);
+ const float greenGamma = toGamma(m_greenGamma);
+ const float blueGamma = toGamma(m_blueGamma);
+ for (int i = 0; i < 32; i++) {
+ uint8_t r = (uint8_t)(powf((i << 3) / 255.f, redGamma) * 255);
+ m_redGammaTable[i] = r >> 3;
+ }
+ for (int i = 0; i < 64; i++) {
+ uint8_t g = (uint8_t)(powf((i << 2) / 255.f, greenGamma) * 255);
+ m_greenGammaTable[i] = g >> 2;
+ }
+ for (int i = 0; i < 32; i++) {
+ uint8_t b = (uint8_t)(powf((i << 3 )/ 255.f, blueGamma) * 255);
+ m_blueGammaTable[i] = b >> 3;
+ }
+}
+
KDPostProcessGammaContext::KDPostProcessGammaContext() :
- m_redGamma(0), m_greenGamma(0), m_blueGamma(0) {}
+ m_redGamma(0), m_greenGamma(0), m_blueGamma(0) {
+ updateGammaTables();
+ }
void KDPostProcessGammaContext::gamma(int& red, int& green, int& blue) {
red = m_redGamma;
@@ -32,12 +54,20 @@ void KDPostProcessGammaContext::setGamma(int red, int green, int blue) {
m_redGamma = clampGamma(red);
m_greenGamma = clampGamma(green);
m_blueGamma = clampGamma(blue);
+ updateGammaTables();
+}
+
+KDColor KDPostProcessGammaContext::correctColor(KDColor color) {
+ uint8_t r5 = (((uint16_t )color)>>11)&0x1F;
+ r5 = m_redGammaTable[r5];
+ uint8_t g6 = (((uint16_t )color)>>5)&0x3F;
+ g6 = m_greenGammaTable[g6];
+ uint8_t b5 = ((uint16_t )color)&0x1F;
+ b5 = m_blueGammaTable[b5];
+ return KDColor::RGB16(r5<<11 | g6<<5 | b5);
}
void KDPostProcessGammaContext::pushRect(KDRect rect, const KDColor * pixels) {
- const float redGamma = toGamma(m_redGamma);
- const float greenGamma = toGamma(m_greenGamma);
- const float blueGamma = toGamma(m_blueGamma);
KDColor workingBuffer[rect.width()];
for (KDCoordinate y = 0; y < rect.height(); y++) {
@@ -45,10 +75,7 @@ void KDPostProcessGammaContext::pushRect(KDRect rect, const KDColor * pixels) {
for (KDCoordinate x = 0; x < rect.width(); x++) {
const KDColor color = pixels[y*rect.width()+x];
- const KDColor result = KDColor::RGB888(
- (uint8_t)(powf(color.red()/255.f, redGamma)*255),
- (uint8_t)(powf(color.green()/255.f, greenGamma)*255),
- (uint8_t)(powf(color.blue()/255.f, blueGamma)*255));
+ const KDColor result = correctColor(color);
workingBuffer[x] = result;
}
KDPostProcessContext::pushRect(workingRect, workingBuffer);
@@ -56,15 +83,7 @@ void KDPostProcessGammaContext::pushRect(KDRect rect, const KDColor * pixels) {
}
void KDPostProcessGammaContext::pushRectUniform(KDRect rect, KDColor color) {
- const float redGamma = toGamma(m_redGamma);
- const float greenGamma = toGamma(m_greenGamma);
- const float blueGamma = toGamma(m_blueGamma);
- const KDColor result = KDColor::RGB888(
- (uint8_t)(powf(color.red()/255.f, redGamma)*255),
- (uint8_t)(powf(color.green()/255.f, greenGamma)*255),
- (uint8_t)(powf(color.blue()/255.f, blueGamma)*255));
-
- KDPostProcessContext::pushRectUniform(rect, result);
+ KDPostProcessContext::pushRectUniform(rect, correctColor(color));
}
void KDPostProcessGammaContext::pullRect(KDRect rect, KDColor * pixels) {
diff --git a/liba/include/bridge/math.h b/liba/include/bridge/math.h
new file mode 100644
index 000000000..a89e832f5
--- /dev/null
+++ b/liba/include/bridge/math.h
@@ -0,0 +1,20 @@
+#ifndef LIBA_BRIDGE_MATH_H
+#define LIBA_BRIDGE_MATH_H
+
+#include_next
+
+#define M_E 2.7182818284590452354 /* e */
+#define M_LOG2E 1.4426950408889634074 /* log 2e */
+#define M_LOG10E 0.43429448190325182765 /* log 10e */
+#define M_LN2 0.69314718055994530942 /* log e2 */
+#define M_LN10 2.30258509299404568402 /* log e10 */
+#define M_PI 3.14159265358979323846 /* pi */
+#define M_PI_2 1.57079632679489661923 /* pi/2 */
+#define M_PI_4 0.78539816339744830962 /* pi/4 */
+#define M_1_PI 0.31830988618379067154 /* 1/pi */
+#define M_2_PI 0.63661977236758134308 /* 2/pi */
+#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */
+#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
+#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
+
+#endif
diff --git a/liba/include/bridge/string.h b/liba/include/bridge/string.h
index d5eda3946..51d6a8c3a 100644
--- a/liba/include/bridge/string.h
+++ b/liba/include/bridge/string.h
@@ -7,7 +7,7 @@
LIBA_BEGIN_DECLS
-#if (__GLIBC__ || __MINGW32__)
+#if (__GLIBC__ || __MINGW32__ || _FXCG)
size_t strlcat(char * dst, const char * src, size_t dstSize);
size_t strlcpy(char * dst, const char * src, size_t len);
#endif
diff --git a/liba/include/bridge/strings.h b/liba/include/bridge/strings.h
new file mode 100644
index 000000000..d74b264db
--- /dev/null
+++ b/liba/include/bridge/strings.h
@@ -0,0 +1,22 @@
+#ifndef LIBA_STRINGS_H
+#define LIBA_STRINGS_H
+
+#if (_FXCG)
+
+#include
+
+#include "../private/macros.h"
+
+LIBA_BEGIN_DECLS
+
+void bzero(void * s, size_t n);
+
+LIBA_END_DECLS
+
+#else
+
+#include_next
+
+#endif
+
+#endif
\ No newline at end of file
diff --git a/libaxx/Makefile.bridge b/libaxx/Makefile.bridge
new file mode 100644
index 000000000..92fd5dddd
--- /dev/null
+++ b/libaxx/Makefile.bridge
@@ -0,0 +1,3 @@
+SFLAGS += -Ilibaxx/include/bridge
+
+# libaxx_src += libaxx/src/bridge.c
diff --git a/libaxx/include/bridge/cmath b/libaxx/include/bridge/cmath
new file mode 100644
index 000000000..11eec165e
--- /dev/null
+++ b/libaxx/include/bridge/cmath
@@ -0,0 +1,165 @@
+#ifndef LIBA_BRIDGE_CMATH_H
+#define LIBA_BRIDGE_CMATH_H
+
+#include_next
+
+#define M_E 2.7182818284590452354 /* e */
+#define M_LOG2E 1.4426950408889634074 /* log 2e */
+#define M_LOG10E 0.43429448190325182765 /* log 10e */
+#define M_LN2 0.69314718055994530942 /* log e2 */
+#define M_LN10 2.30258509299404568402 /* log e10 */
+#define M_PI 3.14159265358979323846 /* pi */
+#define M_PI_2 1.57079632679489661923 /* pi/2 */
+#define M_PI_4 0.78539816339744830962 /* pi/4 */
+#define M_1_PI 0.31830988618379067154 /* 1/pi */
+#define M_2_PI 0.63661977236758134308 /* 2/pi */
+#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */
+#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
+#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
+
+#if (_FXCG)
+namespace std {
+ // functions
+ using ::acosh;
+ using ::acoshf;
+ using ::acoshl;
+
+ using ::asinh;
+ using ::asinhf;
+ using ::asinhl;
+
+ using ::atanh;
+ using ::atanhf;
+ using ::atanhl;
+
+ using ::cbrt;
+ using ::cbrtf;
+ using ::cbrtl;
+
+ using ::copysign;
+ using ::copysignf;
+ using ::copysignl;
+
+ using ::erf;
+ using ::erff;
+ using ::erfl;
+
+ using ::erfc;
+ using ::erfcf;
+ using ::erfcl;
+
+ using ::exp2;
+ using ::exp2f;
+ using ::exp2l;
+
+ using ::expm1;
+ using ::expm1f;
+ using ::expm1l;
+
+ using ::fdim;
+ using ::fdimf;
+ using ::fdiml;
+
+ using ::fma;
+ using ::fmaf;
+ using ::fmal;
+
+ using ::fmax;
+ using ::fmaxf;
+ using ::fmaxl;
+
+ using ::fmin;
+ using ::fminf;
+ using ::fminl;
+
+ using ::hypot;
+ using ::hypotf;
+ using ::hypotl;
+
+ using ::ilogb;
+ using ::ilogbf;
+ using ::ilogbl;
+
+ using ::lgamma;
+ using ::lgammaf;
+ using ::lgammal;
+
+ using ::llrint;
+ using ::llrintf;
+ using ::llrintl;
+
+ using ::llround;
+ using ::llroundf;
+ using ::llroundl;
+
+ using ::log1p;
+ using ::log1pf;
+ using ::log1pl;
+
+ using ::log2;
+ using ::log2f;
+ using ::log2l;
+
+ using ::logb;
+ using ::logbf;
+ using ::logbl;
+
+ using ::lrint;
+ using ::lrintf;
+ using ::lrintl;
+
+ using ::lround;
+ using ::lroundf;
+ using ::lroundl;
+
+ using ::nan;
+ using ::nanf;
+ using ::nanl;
+
+ using ::nearbyint;
+ using ::nearbyintf;
+ using ::nearbyintl;
+
+ using ::nextafter;
+ using ::nextafterf;
+ using ::nextafterl;
+
+ using ::nexttoward;
+ using ::nexttowardf;
+ using ::nexttowardl;
+
+ using ::remainder;
+ using ::remainderf;
+ using ::remainderl;
+
+ using ::remquo;
+ using ::remquof;
+ using ::remquol;
+
+ using ::rint;
+ using ::rintf;
+ using ::rintl;
+
+ using ::round;
+ using ::roundf;
+ using ::roundl;
+
+ using ::scalbln;
+ using ::scalblnf;
+ using ::scalblnl;
+
+ using ::scalbn;
+ using ::scalbnf;
+ using ::scalbnl;
+
+ using ::tgamma;
+ using ::tgammaf;
+ using ::tgammal;
+
+ using ::trunc;
+ using ::truncf;
+ using ::truncl;
+}
+#endif
+
+#endif
diff --git a/poincare/include/poincare/ieee754.h b/poincare/include/poincare/ieee754.h
index accda9d33..8427e90ee 100644
--- a/poincare/include/poincare/ieee754.h
+++ b/poincare/include/poincare/ieee754.h
@@ -4,7 +4,9 @@
#include
#include
#include
+#include
#include
+#include
namespace Poincare {
@@ -41,11 +43,19 @@ public:
if (((uint64_t)mantissa >> (size()-k_mantissaNbBits-2)) & 1) {
u.ui += 1;
}
- return u.f;
+ if (sizeof(T) == sizeof(float)) {
+ return u.f32.f;
+ } else {
+ return u.f64.f;
+ }
}
static int exponent(T f) {
uint_float u;
- u.f = f;
+ if (sizeof(T) == sizeof(float)) {
+ u.f32.f = f;
+ } else {
+ u.f64.f = f;
+ }
constexpr uint16_t oneOnExponentsBits = maxExponent();
int exp = (u.ui >> k_mantissaNbBits) & oneOnExponentsBits;
exp -= exponentOffset();
@@ -75,10 +85,28 @@ public:
}
private:
+ #ifdef _BIG_ENDIAN
union uint_float {
uint64_t ui;
- T f;
+ struct {
+ uint32_t padding;
+ float f;
+ } f32;
+ struct {
+ double f;
+ } f64;
};
+ #else
+ union uint_float {
+ uint64_t ui;
+ struct {
+ float f;
+ } f32;
+ struct {
+ double f;
+ } f64;
+ };
+ #endif
constexpr static size_t k_signNbBits = 1;
constexpr static size_t k_exponentNbBits = sizeof(T) == sizeof(float) ? 8 : 11;
diff --git a/poincare/include/poincare/integer.h b/poincare/include/poincare/integer.h
index 1d27e50c5..cfc982034 100644
--- a/poincare/include/poincare/integer.h
+++ b/poincare/include/poincare/integer.h
@@ -5,6 +5,14 @@
#include
#include
+#ifdef _FXCG
+#include
+#include
+#include
+#else
+#include
+#endif
+
namespace Poincare {
class ExpressionLayout;
@@ -13,12 +21,17 @@ class LayoutNode;
class Integer;
struct IntegerDivision;
-#ifdef _3DS
+#if (defined _3DS) || (defined _FXCG)
typedef unsigned short half_native_uint_t;
+static_assert(sizeof(half_native_uint_t) == sizeof(uint16_t));
typedef int native_int_t;
+static_assert(sizeof(native_int_t) == sizeof(int32_t));
typedef long long int double_native_int_t;
+static_assert(sizeof(double_native_int_t) == sizeof(int64_t));
typedef unsigned int native_uint_t;
+static_assert(sizeof(native_uint_t) == sizeof(uint32_t));
typedef unsigned long long int double_native_uint_t;
+static_assert(sizeof(double_native_uint_t) == sizeof(uint64_t));
#else
typedef uint16_t half_native_uint_t;
typedef int32_t native_int_t;
@@ -199,7 +212,12 @@ private:
if (i >= numberOfHalfDigits()) {
return 0;
}
- return (usesImmediateDigit() ? ((half_native_uint_t *)&m_digit)[i] : ((half_native_uint_t *)digits())[i]);
+ native_uint_t d = usesImmediateDigit() ? m_digit : digits()[i/2];
+ if (i % 2 == 0) {
+ return d & 0xFFFF;
+ } else {
+ return d >> 16;
+ }
}
native_uint_t digit(uint8_t i) const {
diff --git a/poincare/include/poincare/tree_pool.h b/poincare/include/poincare/tree_pool.h
index cb3094018..62c1d6d5e 100644
--- a/poincare/include/poincare/tree_pool.h
+++ b/poincare/include/poincare/tree_pool.h
@@ -138,7 +138,7 @@ private:
private:
uint16_t m_currentIndex;
uint16_t m_availableIdentifiers[MaxNumberOfNodes];
- static_assert(MaxNumberOfNodes < INT16_MAX && sizeof(m_availableIdentifiers[0] == sizeof(uint16_t)), "Tree node identifiers do not have the right data size.");
+ static_assert(MaxNumberOfNodes < INT16_MAX && sizeof(m_availableIdentifiers[0]) == sizeof(uint16_t), "Tree node identifiers do not have the right data size.");
};
void freePoolFromNode(TreeNode * firstNodeToDiscard);
diff --git a/poincare/src/integer.cpp b/poincare/src/integer.cpp
index fc67dda66..3fa776362 100644
--- a/poincare/src/integer.cpp
+++ b/poincare/src/integer.cpp
@@ -127,7 +127,10 @@ Integer::Integer(native_int_t i) : TreeHandle(TreeNode::NoNodeIdentifier) {
Integer::Integer(double_native_int_t i) {
double_native_uint_t j = i < 0 ? -i : i;
- native_uint_t * d = (native_uint_t *)&j;
+ native_uint_t d[2] = {
+ static_cast(j & 0xFFFFFFFF),
+ static_cast(j >> 32)
+ };
native_uint_t leastSignificantDigit = *d;
native_uint_t mostSignificantDigit = *(d+1);
uint8_t numberOfDigits = (mostSignificantDigit == 0) ? 1 : 2;
@@ -165,7 +168,8 @@ Integer::Integer(const char * digits, size_t length, bool negative, Base b) :
Integer base((int)b);
for (size_t i = 0; i < length; i++) {
*this = Multiplication(*this, base);
- *this = Addition(*this, Integer(integerFromCharDigit(*digits)));
+ Integer toAdd = Integer(integerFromCharDigit(*digits));
+ *this = Addition(*this, toAdd);
digits++;
}
}
@@ -495,7 +499,10 @@ Integer Integer::multiplication(const Integer & a, const Integer & b, bool oneDi
* otherwise the product might end up being computed on single_native size
* and then zero-padded. */
double_native_uint_t p = aDigit*bDigit + carry + (double_native_uint_t)(s_workingBuffer[i+j]); // TODO: Prove it cannot overflow double_native type
- native_uint_t * l = (native_uint_t *)&p;
+ native_uint_t l[2] = {
+ static_cast(p & 0xFFFFFFFF),
+ static_cast(p >> 32)
+ };
if (i+j < (uint8_t) k_maxNumberOfDigits+oneDigitOverflow) {
s_workingBuffer[i+j] = l[0];
} else {
@@ -605,18 +612,31 @@ Integer Integer::divideByPowerOf2(uint8_t pow) const {
// return this*(2^16)^pow
Integer Integer::multiplyByPowerOfBase(uint8_t pow) const {
int nbOfHalfDigits = numberOfHalfDigits();
- half_native_uint_t * digits = reinterpret_cast(s_workingBuffer);
+ native_uint_t * digits = s_workingBuffer;
/* The number of half digits of the built integer is nbOfHalfDigits+pow.
* Still, we set an extra half digit to 0 to easily convert half digits to
* digits. */
- memset(digits, 0, sizeof(half_native_uint_t)*(nbOfHalfDigits+pow+1));
- for (uint8_t i = 0; i < nbOfHalfDigits; i++) {
- digits[i+pow] = halfDigit(i);
+ memset(digits, 0, (sizeof(native_uint_t)/2)*(nbOfHalfDigits+pow+1));
+ for (uint8_t i = 0; i < nbOfHalfDigits; i += 2) {
+ native_uint_t toSet = halfDigit(i);
+ if (i+1 < nbOfHalfDigits) {
+ toSet |= (native_uint_t)halfDigit(i+1) << 16;
+ }
+ int index = i+pow;
+ // If it's on an even index, we can just set the value
+ if (index % 2 == 0) {
+ digits[index/2] = toSet;
+ } else {
+ // If it's on an odd index, we need to shift the value
+ digits[index/2] |= toSet << 16;
+ digits[index/2+1] |= toSet >> 16;
+ }
}
nbOfHalfDigits += pow;
- return BuildInteger((native_uint_t *)digits, nbOfHalfDigits%2 == 1 ? nbOfHalfDigits/2+1 : nbOfHalfDigits/2, false, true);
+ return BuildInteger(digits, nbOfHalfDigits%2 == 1 ? nbOfHalfDigits/2+1 : nbOfHalfDigits/2, false, true);
}
+
IntegerDivision Integer::udiv(const Integer & numerator, const Integer & denominator) {
if (denominator.isOverflow()) {
return {.quotient = Overflow(false), .remainder = Integer::Overflow(false)};
@@ -679,6 +699,14 @@ IntegerDivision Integer::udiv(const Integer & numerator, const Integer & denomin
qNumberOfDigits--;
}
int qNumberOfDigitsInBase32 = qNumberOfDigits%2 == 1 ? qNumberOfDigits/2+1 : qNumberOfDigits/2;
+ // Swap each pair of digits in qDigits if on a big-endian architecture
+ #ifdef _BIG_ENDIAN
+ for (int i = 0; i < qNumberOfDigitsInBase32; i++) {
+ half_native_uint_t tmp = qDigits[i*2];
+ qDigits[i*2] = qDigits[i*2+1];
+ qDigits[i*2+1] = tmp;
+ }
+ #endif
IntegerDivision div = {.quotient = BuildInteger((native_uint_t *)qDigits, qNumberOfDigitsInBase32, false), .remainder = A};
if (pow > 0 && !div.remainder.isZero()) {
div.remainder = div.remainder.divideByPowerOf2(pow);