diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index d38794afc..7b53519b9 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -14,7 +14,8 @@ jobs: build-device-n0100: runs-on: ubuntu-latest steps: - - run: sudo apt-get install binutils-arm-none-eabi build-essential gcc-arm-none-eabi imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config + - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config + - uses: numworks/setup-arm-toolchain@v1 - uses: actions/checkout@v1 - run: make -j2 MODEL=n0100 epsilon.dfu - run: make -j2 MODEL=n0100 epsilon.onboarding.dfu @@ -30,7 +31,8 @@ jobs: build-device-n0110: runs-on: ubuntu-latest steps: - - run: sudo apt-get install binutils-arm-none-eabi build-essential gcc-arm-none-eabi imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config + - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config + - uses: numworks/setup-arm-toolchain@v1 - uses: actions/checkout@v1 - run: make -j2 epsilon.dfu - run: make -j2 epsilon.onboarding.dfu diff --git a/apps/Makefile b/apps/Makefile index 43cf2c95a..828352cfc 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -92,6 +92,10 @@ $(call object_for,$(all_app_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.gene apps_tests_src = $(app_calculation_test_src) $(app_probability_test_src) $(app_regression_test_src) $(app_sequence_test_src) $(app_shared_test_src) $(app_statistics_test_src) $(app_solver_test_src) +apps_tests_src += $(addprefix apps/,\ + global_preferences.cpp \ +) + # Configure variants apps_all_src = $(app_src) apps_all_src += $(apps_launch_default_src) $(apps_launch_on_boarding_src diff --git a/apps/apps_container.cpp b/apps/apps_container.cpp index 597cadcce..c4a77a547 100644 --- a/apps/apps_container.cpp +++ b/apps/apps_container.cpp @@ -201,8 +201,8 @@ bool AppsContainer::processEvent(Ion::Events::Event event) { // Warning: if the window is dirtied, you need to call window()->redraw() if (event == Ion::Events::USBPlug) { if (Ion::USB::isPlugged()) { - if (GlobalPreferences::sharedGlobalPreferences()->examMode()) { - displayExamModePopUp(false); + if (GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { + displayExamModePopUp(GlobalPreferences::ExamMode::Off); window()->redraw(); } else { Ion::USB::enable(); @@ -251,8 +251,8 @@ void AppsContainer::run() { * and it is visible when reflashing a N0100 (there is some noise on the * screen before the logo appears). */ Ion::Display::pushRectUniform(screenRect, KDColorWhite); - if (GlobalPreferences::sharedGlobalPreferences()->examMode()) { - activateExamMode(); + if (GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { + activateExamMode(GlobalPreferences::sharedGlobalPreferences()->examMode()); } refreshPreferences(); @@ -313,8 +313,8 @@ void AppsContainer::reloadTitleBarView() { m_window.reloadTitleBarView(); } -void AppsContainer::displayExamModePopUp(bool activate) { - m_examPopUpController.setActivatingExamMode(activate); +void AppsContainer::displayExamModePopUp(GlobalPreferences::ExamMode mode) { + m_examPopUpController.setTargetExamMode(mode); s_activeApp->displayModalViewController(&m_examPopUpController, 0.f, 0.f, Metric::ExamPopUpTopMargin, Metric::PopUpRightMargin, Metric::ExamPopUpBottomMargin, Metric::PopUpLeftMargin); } @@ -329,7 +329,7 @@ void AppsContainer::shutdownDueToLowBattery() { } while (Ion::Battery::level() == Ion::Battery::Charge::EMPTY) { Ion::Backlight::setBrightness(0); - if (!GlobalPreferences::sharedGlobalPreferences()->examMode()) { + if (!GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { /* Unless the LED is lit up for the exam mode, switch off the LED. IF the * low battery event happened during the Power-On Self-Test, a LED might * have stayed lit up. */ @@ -362,7 +362,8 @@ void AppsContainer::redrawWindow(bool force) { m_window.redraw(force); } -void AppsContainer::activateExamMode() { +void AppsContainer::activateExamMode(GlobalPreferences::ExamMode examMode) { + assert(examMode == GlobalPreferences::ExamMode::Standard || examMode == GlobalPreferences::ExamMode::Dutch); reset(); Preferences * preferences = Preferences::sharedPreferences(); switch ((int)preferences->colorOfLED()) { @@ -379,6 +380,14 @@ void AppsContainer::activateExamMode() { Ion::LED::setColor(KDColorYellow); break; } + /* The Dutch exam mode LED is supposed to be orange but we can only make + * blink "pure" colors: with RGB leds on or off (as the PWM is used for + * blinking). The closest "pure" color is Yellow. Moreover, Orange LED is + * already used when the battery is charging. Using yellow, we can assert + * that the yellow LED only means that Dutch exam mode is on and avoid + * confusing states when the battery is charging and states when the Dutch + * exam mode is on. */ + // Ion::LED::setColor(examMode == GlobalPreferences::ExamMode::Dutch ? KDColorYellow : KDColorRed); Ion::LED::setBlinking(1000, 0.1f); } diff --git a/apps/apps_container.h b/apps/apps_container.h index da9250aed..814e642a1 100644 --- a/apps/apps_container.h +++ b/apps/apps_container.h @@ -13,6 +13,7 @@ #include "exam_pop_up_controller_delegate.h" #include "battery_timer.h" #include "suspend_timer.h" +#include "global_preferences.h" #include "backlight_dimming_timer.h" #include "shared/global_context.h" #include "on_boarding/pop_up_controller.h" @@ -41,12 +42,12 @@ public: bool updateBatteryState(); void refreshPreferences(); void reloadTitleBarView(); - void displayExamModePopUp(bool activate); + void displayExamModePopUp(GlobalPreferences::ExamMode mode); void shutdownDueToLowBattery(); void setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus newStatus); OnBoarding::PopUpController * promptController(); void redrawWindow(bool force = false); - void activateExamMode(); + void activateExamMode(GlobalPreferences::ExamMode examMode); // Exam pop-up controller delegate void examDeactivatingPopUpIsDismissed() override; // Ion::StorageDelegate diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 25b71a71d..711408b3e 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -1,5 +1,6 @@ #include "calculation.h" #include "../shared/poincare_helpers.h" +#include "../global_preferences.h" #include #include #include @@ -123,7 +124,9 @@ Calculation::DisplayOutput Calculation::displayOutput(Context * context) { } if (shouldOnlyDisplayExactOutput()) { m_displayOutput = DisplayOutput::ExactOnly; - } else if (input().recursivelyMatches( + // Force all results to be ApproximateOnly in Dutch exam mode + } else if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Dutch || + input().recursivelyMatches( [](const Expression e, Context * c) { constexpr int approximateOnlyTypesCount = 9; /* If the input contains the following types, we only display the diff --git a/apps/exam_pop_up_controller.cpp b/apps/exam_pop_up_controller.cpp index 72661159e..f35afe420 100644 --- a/apps/exam_pop_up_controller.cpp +++ b/apps/exam_pop_up_controller.cpp @@ -10,14 +10,14 @@ using namespace Poincare; ExamPopUpController::ExamPopUpController(ExamPopUpControllerDelegate * delegate) : ViewController(nullptr), m_contentView(this), - m_isActivatingExamMode(false), + m_targetExamMode(GlobalPreferences::ExamMode::Unknown), m_delegate(delegate) { } -void ExamPopUpController::setActivatingExamMode(bool activatingExamMode) { - m_isActivatingExamMode = activatingExamMode; - m_contentView.setMessages(activatingExamMode); +void ExamPopUpController::setTargetExamMode(GlobalPreferences::ExamMode mode) { + m_targetExamMode = mode; + m_contentView.setMessagesForExamMode(mode); } View * ExamPopUpController::view() { @@ -25,7 +25,7 @@ View * ExamPopUpController::view() { } void ExamPopUpController::viewDidDisappear() { - if (m_isActivatingExamMode == false) { + if (m_targetExamMode == GlobalPreferences::ExamMode::Off) { m_delegate->examDeactivatingPopUpIsDismissed(); } } @@ -53,13 +53,15 @@ ExamPopUpController::ContentView::ContentView(Responder * parentResponder) : }, parentResponder), KDFont::SmallFont), m_okButton(parentResponder, I18n::Message::Ok, Invocation([](void * context, void * sender) { ExamPopUpController * controller = (ExamPopUpController *)context; - GlobalPreferences::sharedGlobalPreferences()->setExamMode(controller->isActivatingExamMode()); + GlobalPreferences::ExamMode mode = controller->targetExamMode(); + assert(mode != GlobalPreferences::ExamMode::Unknown); + GlobalPreferences::sharedGlobalPreferences()->setExamMode(mode); AppsContainer * container = AppsContainer::sharedAppsContainer(); - if (controller->isActivatingExamMode()) { - container->activateExamMode(); - } else { + if (mode == GlobalPreferences::ExamMode::Off) { Ion::LED::setColor(KDColorBlack); Ion::LED::updateColorWithPlugAndCharge(); + } else { + container->activateExamMode(mode); } container->refreshPreferences(); Container::activeApp()->dismissModalViewController(); @@ -89,15 +91,20 @@ int ExamPopUpController::ContentView::selectedButton() { return 1; } -void ExamPopUpController::ContentView::setMessages(bool activingExamMode) { - if (activingExamMode) { +void ExamPopUpController::ContentView::setMessagesForExamMode(GlobalPreferences::ExamMode mode) { + if (mode == GlobalPreferences::ExamMode::Off) { + m_messageTextView1.setMessage(I18n::Message::ExitExamMode1); + m_messageTextView2.setMessage(I18n::Message::ExitExamMode2); + m_messageTextView3.setMessage(I18n::Message::Default); + } else if (mode == GlobalPreferences::ExamMode::Standard) { m_messageTextView1.setMessage(I18n::Message::ActiveExamModeMessage1); m_messageTextView2.setMessage(I18n::Message::ActiveExamModeMessage2); m_messageTextView3.setMessage(I18n::Message::ActiveExamModeMessage3); } else { - m_messageTextView1.setMessage(I18n::Message::ExitExamMode1); - m_messageTextView2.setMessage(I18n::Message::ExitExamMode2); - m_messageTextView3.setMessage(I18n::Message::Default); + assert(mode == GlobalPreferences::ExamMode::Dutch); + m_messageTextView1.setMessage(I18n::Message::ActiveDutchExamModeMessage1); + m_messageTextView2.setMessage(I18n::Message::ActiveDutchExamModeMessage2); + m_messageTextView3.setMessage(I18n::Message::ActiveDutchExamModeMessage3); } } diff --git a/apps/exam_pop_up_controller.h b/apps/exam_pop_up_controller.h index 3bff108f7..79ca4d6c9 100644 --- a/apps/exam_pop_up_controller.h +++ b/apps/exam_pop_up_controller.h @@ -3,6 +3,7 @@ #include #include "exam_pop_up_controller_delegate.h" +#include "global_preferences.h" class HighContrastButton : public Button { public: @@ -13,8 +14,8 @@ public: class ExamPopUpController : public ViewController { public: ExamPopUpController(ExamPopUpControllerDelegate * delegate); - void setActivatingExamMode(bool activingExamMode); - bool isActivatingExamMode() const { return m_isActivatingExamMode; } + void setTargetExamMode(GlobalPreferences::ExamMode mode); + GlobalPreferences::ExamMode targetExamMode() const { return m_targetExamMode; } // View Controller View * view() override; void viewDidDisappear() override; @@ -28,7 +29,7 @@ private: void drawRect(KDContext * ctx, KDRect rect) const override; void setSelectedButton(int selectedButton); int selectedButton(); - void setMessages(bool activingExamMode); + void setMessagesForExamMode(GlobalPreferences::ExamMode mode); private: constexpr static KDCoordinate k_buttonMargin = 10; constexpr static KDCoordinate k_buttonHeight = 20; @@ -45,7 +46,7 @@ private: MessageTextView m_messageTextView3; }; ContentView m_contentView; - bool m_isActivatingExamMode; + GlobalPreferences::ExamMode m_targetExamMode; ExamPopUpControllerDelegate * m_delegate; }; diff --git a/apps/global_preferences.cpp b/apps/global_preferences.cpp index b78102ba9..c6644f5fd 100644 --- a/apps/global_preferences.cpp +++ b/apps/global_preferences.cpp @@ -5,20 +5,26 @@ GlobalPreferences * GlobalPreferences::sharedGlobalPreferences() { return &globalPreferences; } -bool GlobalPreferences::examMode() const { +GlobalPreferences::ExamMode GlobalPreferences::examMode() const { if (m_examMode == ExamMode::Unknown) { - m_examMode = (ExamMode)Ion::ExamMode::FetchExamMode(); + uint8_t mode = Ion::ExamMode::FetchExamMode(); + assert(mode >= 0 && mode < 3); // mode can be cast in ExamMode (Off, Standard or Dutch) + m_examMode = (ExamMode)mode; } - assert((int)m_examMode == 0 || (int)m_examMode == 1); - return (bool)m_examMode; + return m_examMode; } -void GlobalPreferences::setExamMode(bool activateExamMode) { - if (((bool)examMode()) == activateExamMode) { +void GlobalPreferences::setExamMode(ExamMode mode) { + ExamMode currentMode = examMode(); + if (currentMode == mode) { return; } - Ion::ExamMode::ToggleExamMode(); - m_examMode = (ExamMode)activateExamMode; + assert(mode != ExamMode::Unknown); + int8_t deltaMode = (int8_t)mode - (int8_t)currentMode; + deltaMode = deltaMode < 0 ? deltaMode + 3 : deltaMode; + assert(deltaMode > 0); + Ion::ExamMode::IncrementExamMode(deltaMode); + m_examMode = mode; } void GlobalPreferences::setBrightnessLevel(int brightnessLevel) { diff --git a/apps/global_preferences.h b/apps/global_preferences.h index 3e3c75feb..4e266c63b 100644 --- a/apps/global_preferences.h +++ b/apps/global_preferences.h @@ -5,11 +5,18 @@ class GlobalPreferences { public: + enum class ExamMode : int8_t { + Unknown = -1, + Off = 0, + Standard = 1, + Dutch = 2 + }; static GlobalPreferences * sharedGlobalPreferences(); I18n::Language language() const { return m_language; } void setLanguage(I18n::Language language) { m_language = language; } - bool examMode() const; - void setExamMode(bool activateExamMode); + bool isInExamMode() const { return (int8_t)examMode() > 0; } + ExamMode examMode() const; + void setExamMode(ExamMode examMode); bool showPopUp() const { return m_showPopUp; } void setShowPopUp(bool showPopUp) { m_showPopUp = showPopUp; } int brightnessLevel() const { return m_brightnessLevel; } @@ -22,13 +29,8 @@ private: m_showPopUp(true), m_brightnessLevel(Ion::Backlight::MaxBrightness) {} I18n::Language m_language; - enum class ExamMode : uint8_t { - Deactivate = 0, - Activate = 1, - Unknown = 2 - }; - static_assert((uint8_t)GlobalPreferences::ExamMode::Deactivate == 0, "GlobalPreferences::setExamMode and examMode() are not right"); - static_assert((uint8_t)GlobalPreferences::ExamMode::Activate == 1, "GlobalPreferences::setExamMode and examMode() are not right"); + static_assert((int8_t)GlobalPreferences::ExamMode::Off == 0, "GlobalPreferences::isInExamMode() is not right"); + static_assert((int8_t)GlobalPreferences::ExamMode::Unknown < 0, "GlobalPreferences::isInExamMode() is not right"); mutable ExamMode m_examMode; bool m_showPopUp; int m_brightnessLevel; diff --git a/apps/home/base.de.i18n b/apps/home/base.de.i18n index dbfdc7c34..e3ac17694 100644 --- a/apps/home/base.de.i18n +++ b/apps/home/base.de.i18n @@ -1,2 +1,5 @@ Apps = "Anwendungen" AppsCapital = "OMEGA" +AppsCapital = "ANWENDUNGEN" +ForbidenAppInExamMode1 = "This application is" +ForbidenAppInExamMode2 = "forbidden in exam mode" diff --git a/apps/home/base.en.i18n b/apps/home/base.en.i18n index f86319c3b..45c8bb6cd 100644 --- a/apps/home/base.en.i18n +++ b/apps/home/base.en.i18n @@ -1,2 +1,5 @@ Apps = "Applications" AppsCapital = "OMEGA" +AppsCapital = "APPLICATIONS" +ForbidenAppInExamMode1 = "This application is" +ForbidenAppInExamMode2 = "forbidden in exam mode" diff --git a/apps/home/base.es.i18n b/apps/home/base.es.i18n index cfad4a8e3..921b3b308 100644 --- a/apps/home/base.es.i18n +++ b/apps/home/base.es.i18n @@ -1,2 +1,5 @@ Apps = "Aplicaciones" AppsCapital = "OMEGA" +AppsCapital = "APLICACIONES" +ForbidenAppInExamMode1 = "This application is" +ForbidenAppInExamMode2 = "forbidden in exam mode" diff --git a/apps/home/base.fr.i18n b/apps/home/base.fr.i18n index f86319c3b..45c8bb6cd 100644 --- a/apps/home/base.fr.i18n +++ b/apps/home/base.fr.i18n @@ -1,2 +1,5 @@ Apps = "Applications" AppsCapital = "OMEGA" +AppsCapital = "APPLICATIONS" +ForbidenAppInExamMode1 = "This application is" +ForbidenAppInExamMode2 = "forbidden in exam mode" diff --git a/apps/home/base.pt.i18n b/apps/home/base.pt.i18n index bdd4d1f63..12bc7a302 100644 --- a/apps/home/base.pt.i18n +++ b/apps/home/base.pt.i18n @@ -1,2 +1,5 @@ Apps = "Aplicações" AppsCapital = "OMEGA" +AppsCapital = "APLICAÇÕES" +ForbidenAppInExamMode1 = "This application is" +ForbidenAppInExamMode2 = "forbidden in exam mode" diff --git a/apps/home/controller.cpp b/apps/home/controller.cpp index cd2919556..3062e6485 100644 --- a/apps/home/controller.cpp +++ b/apps/home/controller.cpp @@ -58,9 +58,14 @@ Controller::Controller(Responder * parentResponder, SelectableTableViewDataSourc bool Controller::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::OK || event == Ion::Events::EXE) { AppsContainer * container = AppsContainer::sharedAppsContainer(); - bool switched = container->switchTo(container->appSnapshotAtIndex(selectionDataSource()->selectedRow()*k_numberOfColumns+selectionDataSource()->selectedColumn()+1)); - assert(switched); - (void) switched; // Silence compilation warning about unused variable. + ::App::Snapshot * selectedSnapshot = container->appSnapshotAtIndex(selectionDataSource()->selectedRow()*k_numberOfColumns+selectionDataSource()->selectedColumn()+1); + if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Dutch && selectedSnapshot->descriptor()->name() == I18n::Message::CodeApp) { + App::app()->displayWarning(I18n::Message::ForbidenAppInExamMode1, I18n::Message::ForbidenAppInExamMode2); + } else { + bool switched = container->switchTo(selectedSnapshot); + assert(switched); + (void) switched; // Silence compilation warning about unused variable. + } return true; } diff --git a/apps/on_boarding/logo_controller.cpp b/apps/on_boarding/logo_controller.cpp index 91ea5173e..13df87e4d 100644 --- a/apps/on_boarding/logo_controller.cpp +++ b/apps/on_boarding/logo_controller.cpp @@ -49,8 +49,8 @@ void LogoController::viewDidDisappear() { Ion::LED::setColor(m_previousLEDColor); /* TODO: instead of setting again the exam mode, put the previous led color * AND BLINKING.*/ - if (GlobalPreferences::sharedGlobalPreferences()->examMode()) { - AppsContainer::sharedAppsContainer()->activateExamMode(); + if (GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { + AppsContainer::sharedAppsContainer()->activateExamMode(GlobalPreferences::sharedGlobalPreferences()->examMode()); } } ViewController::viewDidDisappear(); diff --git a/apps/on_boarding/power_on_self_test.cpp b/apps/on_boarding/power_on_self_test.cpp index dde93f04c..5bdeb1807 100644 --- a/apps/on_boarding/power_on_self_test.cpp +++ b/apps/on_boarding/power_on_self_test.cpp @@ -20,7 +20,7 @@ KDColor PowerOnSelfTest::Perform() { resultColor = KDColorRed; } } else { - resultColor = KDColorBlue; + resultColor = KDColorPurple; } Ion::LED::setColor(resultColor); return previousLEDColor; diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n index d86c25dc5..048f88912 100644 --- a/apps/settings/base.de.i18n +++ b/apps/settings/base.de.i18n @@ -9,6 +9,7 @@ ComplexFormat = "Komplex" ExamMode = "Testmodus" ActivateExamMode = "Starten Testmodus" ExamModeActive = "Wieder starten Testmodus" +ActivateDutchExamMode = "Activate Dutch exam mode" About = "Über" Degrees = "Grad " Gradians = "Gone " diff --git a/apps/settings/base.en.i18n b/apps/settings/base.en.i18n index 1d710c354..b9c411b26 100644 --- a/apps/settings/base.en.i18n +++ b/apps/settings/base.en.i18n @@ -9,6 +9,7 @@ ComplexFormat = "Complex format" ExamMode = "Exam mode" ActivateExamMode = "Activate exam mode" ExamModeActive = "Reactivate exam mode" +ActivateDutchExamMode = "Activate Dutch exam mode" About = "About" Degrees = "Degrees " Gradians = "Gradians " diff --git a/apps/settings/base.es.i18n b/apps/settings/base.es.i18n index a75ad3912..bd100e830 100644 --- a/apps/settings/base.es.i18n +++ b/apps/settings/base.es.i18n @@ -9,6 +9,7 @@ ComplexFormat = "Forma compleja" ExamMode = "Modo examen" ActivateExamMode = "Activar el modo examen" ExamModeActive = "Reactivar el modo examen" +ActivateDutchExamMode = "Activate Dutch exam mode" About = "Acerca" Degrees = "Grados " Gradians = "Gradianes " diff --git a/apps/settings/base.fr.i18n b/apps/settings/base.fr.i18n index 511a0ae2a..3e87a529c 100644 --- a/apps/settings/base.fr.i18n +++ b/apps/settings/base.fr.i18n @@ -9,6 +9,7 @@ ComplexFormat = "Forme complexe" ExamMode = "Mode examen" ActivateExamMode = "Activer le mode examen" ExamModeActive = "Réactiver le mode examen" +ActivateDutchExamMode = "Activate Dutch exam mode" About = "À propos" Degrees = "Degrés " Gradians = "Grades " diff --git a/apps/settings/base.pt.i18n b/apps/settings/base.pt.i18n index 2f1ccf928..cfff892d8 100644 --- a/apps/settings/base.pt.i18n +++ b/apps/settings/base.pt.i18n @@ -9,6 +9,7 @@ ComplexFormat = "Complexos" ExamMode = "Modo de exame" ActivateExamMode = "Ativar o modo de exame" ExamModeActive = "Reativar o modo de exame" +ActivateDutchExamMode = "Activate Dutch exam mode" About = "Acerca" Degrees = "Graus " Gradians = "Grados " diff --git a/apps/settings/main_controller_prompt_beta.cpp b/apps/settings/main_controller_prompt_beta.cpp index 5bbc5209d..57c4ed980 100644 --- a/apps/settings/main_controller_prompt_beta.cpp +++ b/apps/settings/main_controller_prompt_beta.cpp @@ -14,7 +14,7 @@ constexpr SettingsMessageTree s_symbolChildren[4] = {SettingsMessageTree(I18n::M //sub-menus constexpr SettingsMessageTree s_modelMathOptionsChildren[5] = {SettingsMessageTree(I18n::Message::AngleUnit, s_modelAngleChildren, 3), SettingsMessageTree(I18n::Message::DisplayMode, s_modelFloatDisplayModeChildren, 4), SettingsMessageTree(I18n::Message::EditionMode, s_modelEditionModeChildren, 2), SettingsMessageTree(I18n::Message::ComplexFormat, s_modelComplexFormatChildren, 3), SettingsMessageTree(I18n::Message::SymbolMultiplication, s_symbolChildren, 4)}; -constexpr SettingsMessageTree s_modelExamChildren[3] = {SettingsMessageTree(I18n::Message::LEDColor, s_ledColorChildren, 4), SettingsMessageTree(I18n::Message::SymbolicEnabled), SettingsMessageTree(I18n::Message::ActivateExamMode)}; +constexpr SettingsMessageTree s_modelExamChildren[3] = {SettingsMessageTree(I18n::Message::LEDColor, s_ledColorChildren, 4), SettingsMessageTree(I18n::Message::SymbolicEnabled), SettingsMessageTree(I18n::Message::ActivateExamMode), SettingsMessageTree(I18n::Message::ActivateDutchExamMode)}; constexpr SettingsMessageTree s_accessibilityChildren[6] = {SettingsMessageTree(I18n::Message::AccessibilityInvertColors), SettingsMessageTree(I18n::Message::AccessibilityMagnify),SettingsMessageTree(I18n::Message::AccessibilityGamma),SettingsMessageTree(I18n::Message::AccessibilityGammaRed),SettingsMessageTree(I18n::Message::AccessibilityGammaGreen),SettingsMessageTree(I18n::Message::AccessibilityGammaBlue)}; #ifdef USERNAME constexpr SettingsMessageTree s_modelAboutChildren[7] = {SettingsMessageTree(I18n::Message::Username), SettingsMessageTree(I18n::Message::SoftwareVersion), SettingsMessageTree(I18n::Message::CustomSoftwareVersion), SettingsMessageTree(I18n::Message::MicroPythonVersion), SettingsMessageTree(I18n::Message::SerialNumber), SettingsMessageTree(I18n::Message::FccId), SettingsMessageTree(I18n::Message::Contributors, s_contributorsChildren, 5)}; @@ -26,7 +26,7 @@ constexpr SettingsMessageTree s_modelMenu[] = {SettingsMessageTree(I18n::Message::MathOptions, s_modelMathOptionsChildren, 5), SettingsMessageTree(I18n::Message::Brightness), SettingsMessageTree(I18n::Message::Language), - SettingsMessageTree(I18n::Message::ExamMode, s_modelExamChildren, 3), + SettingsMessageTree(I18n::Message::ExamMode, s_modelExamChildren, 4), SettingsMessageTree(I18n::Message::BetaPopUp), SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren, 6), #ifdef USERNAME diff --git a/apps/settings/main_controller_prompt_none.cpp b/apps/settings/main_controller_prompt_none.cpp index 7ff074116..3289d680f 100644 --- a/apps/settings/main_controller_prompt_none.cpp +++ b/apps/settings/main_controller_prompt_none.cpp @@ -15,7 +15,7 @@ constexpr SettingsMessageTree s_symbolChildren[4] = {SettingsMessageTree(I18n::M //sub-menus constexpr SettingsMessageTree s_modelMathOptionsChildren[5] = {SettingsMessageTree(I18n::Message::AngleUnit, s_modelAngleChildren, 3), SettingsMessageTree(I18n::Message::DisplayMode, s_modelFloatDisplayModeChildren, 4), SettingsMessageTree(I18n::Message::EditionMode, s_modelEditionModeChildren, 2), SettingsMessageTree(I18n::Message::ComplexFormat, s_modelComplexFormatChildren, 3), SettingsMessageTree(I18n::Message::SymbolMultiplication, s_symbolChildren, 4)}; -constexpr SettingsMessageTree s_modelExamChildren[3] = {SettingsMessageTree(I18n::Message::LEDColor, s_ledColorChildren, 4), SettingsMessageTree(I18n::Message::SymbolicEnabled), SettingsMessageTree(I18n::Message::ActivateExamMode)}; +constexpr SettingsMessageTree s_modelExamChildren[3] = {SettingsMessageTree(I18n::Message::LEDColor, s_ledColorChildren, 4), SettingsMessageTree(I18n::Message::SymbolicEnabled), SettingsMessageTree(I18n::Message::ActivateExamMode), SettingsMessageTree(I18n::Message::ActivateDutchExamMode)}; constexpr SettingsMessageTree s_accessibilityChildren[6] = {SettingsMessageTree(I18n::Message::AccessibilityInvertColors), SettingsMessageTree(I18n::Message::AccessibilityMagnify),SettingsMessageTree(I18n::Message::AccessibilityGamma),SettingsMessageTree(I18n::Message::AccessibilityGammaRed),SettingsMessageTree(I18n::Message::AccessibilityGammaGreen),SettingsMessageTree(I18n::Message::AccessibilityGammaBlue)}; #ifdef USERNAME constexpr SettingsMessageTree s_modelAboutChildren[7] = {SettingsMessageTree(I18n::Message::Username), SettingsMessageTree(I18n::Message::SoftwareVersion), SettingsMessageTree(I18n::Message::CustomSoftwareVersion), SettingsMessageTree(I18n::Message::MicroPythonVersion), SettingsMessageTree(I18n::Message::SerialNumber), SettingsMessageTree(I18n::Message::FccId), SettingsMessageTree(I18n::Message::Contributors, s_contributorsChildren, 5)}; @@ -27,7 +27,7 @@ constexpr SettingsMessageTree s_modelMenu[] = {SettingsMessageTree(I18n::Message::MathOptions, s_modelMathOptionsChildren, 5), SettingsMessageTree(I18n::Message::Brightness), SettingsMessageTree(I18n::Message::Language), - SettingsMessageTree(I18n::Message::ExamMode, s_modelExamChildren, 3), + SettingsMessageTree(I18n::Message::ExamMode, s_modelExamChildren, 4), SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren, 6), #ifdef USERNAME SettingsMessageTree(I18n::Message::About, s_modelAboutChildren, 7)}; diff --git a/apps/settings/main_controller_prompt_update.cpp b/apps/settings/main_controller_prompt_update.cpp index 9a7b82685..784dcd4cc 100644 --- a/apps/settings/main_controller_prompt_update.cpp +++ b/apps/settings/main_controller_prompt_update.cpp @@ -14,7 +14,7 @@ constexpr SettingsMessageTree s_symbolChildren[4] = {SettingsMessageTree(I18n::M //sub-menus constexpr SettingsMessageTree s_modelMathOptionsChildren[5] = {SettingsMessageTree(I18n::Message::AngleUnit, s_modelAngleChildren, 3), SettingsMessageTree(I18n::Message::DisplayMode, s_modelFloatDisplayModeChildren, 4), SettingsMessageTree(I18n::Message::EditionMode, s_modelEditionModeChildren, 2), SettingsMessageTree(I18n::Message::ComplexFormat, s_modelComplexFormatChildren, 3), SettingsMessageTree(I18n::Message::SymbolMultiplication, s_symbolChildren, 4)}; -constexpr SettingsMessageTree s_modelExamChildren[3] = {SettingsMessageTree(I18n::Message::LEDColor, s_ledColorChildren, 4), SettingsMessageTree(I18n::Message::SymbolicEnabled), SettingsMessageTree(I18n::Message::ActivateExamMode)}; +constexpr SettingsMessageTree s_modelExamChildren[3] = {SettingsMessageTree(I18n::Message::LEDColor, s_ledColorChildren, 4), SettingsMessageTree(I18n::Message::SymbolicEnabled), SettingsMessageTree(I18n::Message::ActivateExamMode), SettingsMessageTree(I18n::Message::ActivateDutchExamMode)}; constexpr SettingsMessageTree s_accessibilityChildren[6] = {SettingsMessageTree(I18n::Message::AccessibilityInvertColors), SettingsMessageTree(I18n::Message::AccessibilityMagnify),SettingsMessageTree(I18n::Message::AccessibilityGamma),SettingsMessageTree(I18n::Message::AccessibilityGammaRed),SettingsMessageTree(I18n::Message::AccessibilityGammaGreen),SettingsMessageTree(I18n::Message::AccessibilityGammaBlue)}; #ifdef USERNAME constexpr SettingsMessageTree s_modelAboutChildren[7] = {SettingsMessageTree(I18n::Message::Username), SettingsMessageTree(I18n::Message::SoftwareVersion), SettingsMessageTree(I18n::Message::CustomSoftwareVersion), SettingsMessageTree(I18n::Message::MicroPythonVersion), SettingsMessageTree(I18n::Message::SerialNumber), SettingsMessageTree(I18n::Message::FccId), SettingsMessageTree(I18n::Message::Contributors, s_contributorsChildren, 5)}; @@ -26,7 +26,7 @@ constexpr SettingsMessageTree s_modelMenu[] = {SettingsMessageTree(I18n::Message::MathOptions, s_modelMathOptionsChildren, 5), SettingsMessageTree(I18n::Message::Brightness), SettingsMessageTree(I18n::Message::Language), - SettingsMessageTree(I18n::Message::ExamMode, s_modelExamChildren, 3), + SettingsMessageTree(I18n::Message::ExamMode, s_modelExamChildren, 4), SettingsMessageTree(I18n::Message::UpdatePopUp), SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren, 6), #ifdef USERNAME diff --git a/apps/settings/sub_menu/exam_mode_controller.cpp b/apps/settings/sub_menu/exam_mode_controller.cpp index 5e4bbf10b..1d6e98a9a 100644 --- a/apps/settings/sub_menu/exam_mode_controller.cpp +++ b/apps/settings/sub_menu/exam_mode_controller.cpp @@ -1,6 +1,7 @@ #include "exam_mode_controller.h" #include "../../global_preferences.h" #include "../../apps_container.h" +#include #include #include #include @@ -17,17 +18,31 @@ ExamModeController::ExamModeController(Responder * parentResponder) : m_examModeCell(I18n::Message::Default, KDFont::LargeFont), m_ledCell(KDFont::LargeFont, KDFont::SmallFont), m_symbolicCell(I18n::Message::SymbolicEnabled, KDFont::LargeFont) + m_cell{MessageTableCell(I18n::Message::ExamModeActive, KDFont::LargeFont), MessageTableCell(I18n::Message::ActivateDutchExamMode, KDFont::LargeFont)} { } -void ExamModeController::didEnterResponderChain(Responder * previousFirstResponder) { - m_selectableTableView.reloadData(); +int ExamModeController::initialSelectedRow() const { + int row = selectedRow(); + if (row >= numberOfRows()) { + return numberOfRows()-1; + } else if (row < 0) { + return 0; + } + return row; } bool ExamModeController::handleEvent(Ion::Events::Event event) { I18n::Message childLabel = m_messageTreeModel->children(selectedRow())->label(); if (event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Right) { - if (GlobalPreferences::sharedGlobalPreferences()->examMode()) { + if (GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { + // If the exam mode is already on, this re-activate the same exam mode + mode = GlobalPreferences::sharedGlobalPreferences()->examMode(); + if (childLabel == I18n::Message::ActivateExamMode || childLabel == I18n::Message::ExamModeActive) + AppsContainer::sharedAppsContainer()->displayExamModePopUp(true); + return true; + } else if (childLabel == I18n::Message::ActivateDutchExamMode) { + mode = GlobalPreferences::ExamMode::Dutch if (childLabel == I18n::Message::ActivateExamMode || childLabel == I18n::Message::ExamModeActive) AppsContainer::sharedAppsContainer()->displayExamModePopUp(true); return true; @@ -53,6 +68,13 @@ bool ExamModeController::handleEvent(Ion::Events::Event event) { return GenericSubController::handleEvent(event); } +int ExamModeController::numberOfRows() const { + if (GlobalPreferences::sharedGlobalPreferences()->language() != I18n::Language::EN || GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { + return 1; + } + return GenericSubController::numberOfRows(); +} + HighlightCell * ExamModeController::reusableCell(int index, int type) { assert(index == 0); if (type == 0) { @@ -76,14 +98,21 @@ int ExamModeController::reusableCellCount(int type) { assert(false); return 0; } + /*assert(type == 0); + assert(index >= 0 && index < k_maxNumberOfCells); + return &m_cell[index];*/ } +/*int ExamModeController::reusableCellCount(int type) { + return k_maxNumberOfCells; +}*/ + void ExamModeController::willDisplayCellForIndex(HighlightCell * cell, int index) { Preferences * preferences = Preferences::sharedPreferences(); GenericSubController::willDisplayCellForIndex(cell, index); I18n::Message thisLabel = m_messageTreeModel->children(index)->label(); - if (GlobalPreferences::sharedGlobalPreferences()->examMode() && (thisLabel == I18n::Message::ActivateExamMode || thisLabel == I18n::Message::ExamModeActive)) { + if (GlobalPreferences::sharedGlobalPreferences()->isInExamMode() && (thisLabel == I18n::Message::ActivateExamMode || thisLabel == I18n::Message::ExamModeActive)) { MessageTableCell * myCell = (MessageTableCell *)cell; myCell->setMessage(I18n::Message::ExamModeActive); } diff --git a/apps/settings/sub_menu/exam_mode_controller.h b/apps/settings/sub_menu/exam_mode_controller.h index f348414c4..3177bf437 100644 --- a/apps/settings/sub_menu/exam_mode_controller.h +++ b/apps/settings/sub_menu/exam_mode_controller.h @@ -9,17 +9,23 @@ namespace Settings { class ExamModeController : public GenericSubController { public: ExamModeController(Responder * parentResponder); - void didEnterResponderChain(Responder * previousFirstResponder) override; bool handleEvent(Ion::Events::Event event) override; + int numberOfRows() const override; HighlightCell * reusableCell(int index, int type) override; int reusableCellCount(int type) override; void willDisplayCellForIndex(HighlightCell * cell, int index) override; int typeAtLocation(int i, int j) override; private: +<<<<<<< HEAD MessageTableCell m_examModeCell; MessageTableCellWithChevronAndMessage m_ledCell; PreferencesController m_preferencesController; MessageTableCellWithSwitch m_symbolicCell; +======= + int initialSelectedRow() const override; + static constexpr int k_maxNumberOfCells = 2; + MessageTableCell m_cell[k_maxNumberOfCells]; +>>>>>>> upstream/master }; } diff --git a/apps/settings/sub_menu/generic_sub_controller.cpp b/apps/settings/sub_menu/generic_sub_controller.cpp index 96c0a39a4..67af6c918 100644 --- a/apps/settings/sub_menu/generic_sub_controller.cpp +++ b/apps/settings/sub_menu/generic_sub_controller.cpp @@ -26,8 +26,12 @@ View * GenericSubController::view() { return &m_selectableTableView; } +void GenericSubController::didEnterResponderChain(Responder * previousFirstResponder) { + selectCellAtLocation(0, initialSelectedRow()); + m_selectableTableView.reloadData(); +} + void GenericSubController::didBecomeFirstResponder() { - selectCellAtLocation(0, 0); Container::activeApp()->setFirstResponder(&m_selectableTableView); } @@ -75,10 +79,6 @@ void GenericSubController::setMessageTreeModel(const MessageTree * messageTreeMo m_messageTreeModel = (MessageTree *)messageTreeModel; } -void GenericSubController::viewWillAppear() { - m_selectableTableView.reloadData(); -} - void GenericSubController::viewDidDisappear() { m_selectableTableView.deselectTable(); } diff --git a/apps/settings/sub_menu/generic_sub_controller.h b/apps/settings/sub_menu/generic_sub_controller.h index 039d25ede..bcb700779 100644 --- a/apps/settings/sub_menu/generic_sub_controller.h +++ b/apps/settings/sub_menu/generic_sub_controller.h @@ -11,6 +11,7 @@ public: GenericSubController(Responder * parentResponder); const char * title() override; View * view() override; + void didEnterResponderChain(Responder * previousFirstResponder) override; void didBecomeFirstResponder() override; bool handleEvent(Ion::Events::Event event) override; int numberOfRows() const override; @@ -20,10 +21,10 @@ public: virtual int typeAtLocation(int i, int j) override; void willDisplayCellForIndex(HighlightCell * cell, int index) override; void setMessageTreeModel(const MessageTree * messageTreeModel); - void viewWillAppear() override; void viewDidDisappear() override; protected: StackViewController * stackController() const; + virtual int initialSelectedRow() const { return 0; } constexpr static KDCoordinate k_topBottomMargin = 13; SelectableTableView m_selectableTableView; MessageTree * m_messageTreeModel; diff --git a/apps/settings/sub_menu/preferences_controller.cpp b/apps/settings/sub_menu/preferences_controller.cpp index 1417a1c0a..11e6364c8 100644 --- a/apps/settings/sub_menu/preferences_controller.cpp +++ b/apps/settings/sub_menu/preferences_controller.cpp @@ -23,7 +23,6 @@ PreferencesController::PreferencesController(Responder * parentResponder) : } void PreferencesController::didBecomeFirstResponder() { - selectCellAtLocation(0, valueIndexForPreference(m_messageTreeModel->label())); Container::activeApp()->setFirstResponder(&m_selectableTableView); } @@ -188,7 +187,7 @@ void PreferencesController::setPreferenceWithValueIndex(I18n::Message message, i } } -int PreferencesController::valueIndexForPreference(I18n::Message message) { +int PreferencesController::valueIndexForPreference(I18n::Message message) const { Preferences * preferences = Preferences::sharedPreferences(); if (message == I18n::Message::AngleUnit) { return (int)preferences->angleUnit(); diff --git a/apps/settings/sub_menu/preferences_controller.h b/apps/settings/sub_menu/preferences_controller.h index 01f2a66a6..56ea131b6 100644 --- a/apps/settings/sub_menu/preferences_controller.h +++ b/apps/settings/sub_menu/preferences_controller.h @@ -18,9 +18,10 @@ protected: constexpr static int k_totalNumberOfCell = 4; private: constexpr static const KDFont * k_layoutFont = KDFont::SmallFont; + int initialSelectedRow() const override { return valueIndexForPreference(m_messageTreeModel->label()); } Poincare::Layout layoutForPreferences(I18n::Message message); void setPreferenceWithValueIndex(I18n::Message message, int valueIndex); - int valueIndexForPreference(I18n::Message message); + int valueIndexForPreference(I18n::Message message) const; MessageTableCellWithExpression m_cells[k_totalNumberOfCell]; }; diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index 386c32c9a..e697ad584 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -2,6 +2,9 @@ ActivateDeactivate = "Aktivieren/Deaktivieren" ActiveExamModeMessage1 = "Alle Ihre Daten werden " ActiveExamModeMessage2 = "gelöscht, wenn Sie den " ActiveExamModeMessage3 = "Testmodus einschalten." +ActiveDutchExamModeMessage1 = "All your data will be deleted when" +ActiveDutchExamModeMessage2 = "you activate the exam mode. Python" +ActiveDutchExamModeMessage3 = "application will be unavailable." Axis = "Achsen" Cancel = "Abbrechen" ClearColumn = "Spalte löschen" diff --git a/apps/shared.en.i18n b/apps/shared.en.i18n index abefd01cf..b951a2a8b 100644 --- a/apps/shared.en.i18n +++ b/apps/shared.en.i18n @@ -2,6 +2,9 @@ ActivateDeactivate = "Turn on/off" ActiveExamModeMessage1 = "All your data will be " ActiveExamModeMessage2 = "deleted when you activate " ActiveExamModeMessage3 = "the exam mode." +ActiveDutchExamModeMessage1 = "All your data will be deleted when" +ActiveDutchExamModeMessage2 = "you activate the exam mode. Python" +ActiveDutchExamModeMessage3 = "application will be unavailable." Axis = "Axes" Cancel = "Cancel" ClearColumn = "Clear column" diff --git a/apps/shared.es.i18n b/apps/shared.es.i18n index fb670a6a9..226b1d80c 100644 --- a/apps/shared.es.i18n +++ b/apps/shared.es.i18n @@ -2,6 +2,9 @@ ActivateDeactivate = "Activar/Desactivar" ActiveExamModeMessage1 = "Todos sus datos se " ActiveExamModeMessage2 = "eliminaran al activar " ActiveExamModeMessage3 = "el modo examen." +ActiveDutchExamModeMessage1 = "All your data will be deleted when" +ActiveDutchExamModeMessage2 = "you activate the exam mode. Python" +ActiveDutchExamModeMessage3 = "application will be unavailable." Axis = "Ejes" Cancel = "Cancelar" ClearColumn = "Borrar la columna" diff --git a/apps/shared.fr.i18n b/apps/shared.fr.i18n index 30c3c7f1c..ceec0e270 100644 --- a/apps/shared.fr.i18n +++ b/apps/shared.fr.i18n @@ -2,6 +2,9 @@ ActivateDeactivate = "Activer/Désactiver" ActiveExamModeMessage1 = "Toutes vos données seront " ActiveExamModeMessage2 = "supprimées si vous activez " ActiveExamModeMessage3 = "le mode examen." +ActiveDutchExamModeMessage1 = "All your data will be deleted when" +ActiveDutchExamModeMessage2 = "you activate the exam mode. Python" +ActiveDutchExamModeMessage3 = "application will be unavailable." Axis = "Axes" Cancel = "Annuler" ClearColumn = "Effacer la colonne" diff --git a/apps/shared.pt.i18n b/apps/shared.pt.i18n index be721eb4e..cbef151b3 100644 --- a/apps/shared.pt.i18n +++ b/apps/shared.pt.i18n @@ -2,6 +2,9 @@ ActivateDeactivate = "Activar/Desactivar" ActiveExamModeMessage1 = "Todos os seus dados serão " ActiveExamModeMessage2 = "apagados se você ligar " ActiveExamModeMessage3 = "o modo de exame." +ActiveDutchExamModeMessage1 = "All your data will be deleted when" +ActiveDutchExamModeMessage2 = "you activate the exam mode. Python" +ActiveDutchExamModeMessage3 = "application will be unavailable." Axis = "Eixos" Cancel = "Cancelar" ClearColumn = "Excluir coluna" diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index ea3112b53..0691c77c0 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -1,6 +1,7 @@ #include "equation_store.h" #include "../constant.h" #include "../shared/poincare_helpers.h" +#include "../global_preferences.h" #include #include @@ -193,6 +194,8 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { } } // Create the results' layouts + // In Dutch exam mode, display only approximate solutions + bool forbidExactSolutions = GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Dutch; int solutionIndex = 0; int initialNumberOfSolutions = m_numberOfSolutions <= k_maxNumberOfExactSolutions ? m_numberOfSolutions : -1; // We iterate through the solutions and the potential delta @@ -211,7 +214,9 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { char approximateBuffer[::Constant::MaxSerializedExpressionSize]; m_exactSolutionExactLayouts[solutionIndex].serializeForParsing(exactBuffer, ::Constant::MaxSerializedExpressionSize); m_exactSolutionApproximateLayouts[solutionIndex].serializeForParsing(approximateBuffer, ::Constant::MaxSerializedExpressionSize); - m_exactSolutionIdentity[solutionIndex] = strcmp(exactBuffer, approximateBuffer) == 0; + /* Cheat: declare exact and approximate solutions to be identical in + * Dutch exam mode to display only the approximate solutions. */ + m_exactSolutionIdentity[solutionIndex] = forbidExactSolutions || strcmp(exactBuffer, approximateBuffer) == 0; if (!m_exactSolutionIdentity[solutionIndex]) { char buffer[::Constant::MaxSerializedExpressionSize]; m_exactSolutionEquality[solutionIndex] = exactSolutions[i].isEqualToItsApproximationLayout(exactSolutionsApproximations[i], buffer, ::Constant::MaxSerializedExpressionSize, preferences->complexFormat(), preferences->angleUnit(), preferences->displayMode(), preferences->numberOfSignificantDigits(), context); diff --git a/apps/title_bar_view.cpp b/apps/title_bar_view.cpp index 8e64ba19e..6c71ea337 100644 --- a/apps/title_bar_view.cpp +++ b/apps/title_bar_view.cpp @@ -71,7 +71,7 @@ void TitleBarView::layoutSubviews() { m_preferenceView.setFrame(KDRect(Metric::TitleBarExternHorizontalMargin, 0, m_preferenceView.minimalSizeForOptimalDisplay().width(), bounds().height())); KDSize batterySize = m_batteryView.minimalSizeForOptimalDisplay(); m_batteryView.setFrame(KDRect(bounds().width() - batterySize.width() - Metric::TitleBarExternHorizontalMargin, (bounds().height()- batterySize.height())/2, batterySize)); - if (GlobalPreferences::sharedGlobalPreferences()->examMode()) { + if (GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { m_examModeIconView.setFrame(KDRect(k_examIconMargin, (bounds().height() - k_examIconHeight)/2, k_examIconWidth, k_examIconHeight)); } else { m_examModeIconView.setFrame(KDRectZero); diff --git a/build/targets.device.mak b/build/targets.device.mak index 99e47264f..3a54b7554 100644 --- a/build/targets.device.mak +++ b/build/targets.device.mak @@ -60,22 +60,3 @@ $(BUILD_DIR)/bench.flash.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/$(MODEL)/interna bench_src = $(ion_xip_src) $(liba_src) $(kandinsky_src) $(poincare_src) $(libaxx_src) $(app_shared_src) $(ion_target_device_bench_src) $(BUILD_DIR)/bench.ram.$(EXE): $(call object_for,$(bench_src)) $(BUILD_DIR)/bench.flash.$(EXE): $(call object_for,$(bench_src)) - -.PHONY: %.two_binaries -%.two_binaries: %.elf - @echo "Building an internal and an external binary for $<" - $(Q) $(OBJCOPY) -O binary -j .text.external -j .rodata.external -j .exam_mode_buffer $< $(basename $<).external.bin - $(Q) $(OBJCOPY) -O binary -R .text.external -R .rodata.external -R .exam_mode_buffer $< $(basename $<).internal.bin - @echo "Padding $(basename $<).external.bin and $(basename $<).internal.bin" - $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).external.bin - $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).internal.bin - -.PHONY: binpack -binpack: $(BUILD_DIR)/flasher.light.bin $(BUILD_DIR)/epsilon.onboarding.two_binaries - rm -rf $(BUILD_DIR)/binpack - mkdir -p $(BUILD_DIR)/binpack - cp $(BUILD_DIR)/flasher.light.bin $(BUILD_DIR)/binpack - cp $(BUILD_DIR)/epsilon.onboarding.internal.bin $(BUILD_DIR)/epsilon.onboarding.external.bin $(BUILD_DIR)/binpack - cd $(BUILD_DIR) && for binary in flasher.light.bin epsilon.onboarding.internal.bin epsilon.onboarding.external.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done - cd $(BUILD_DIR) && tar cvfz binpack-$(MODEL)-`git rev-parse HEAD | head -c 7`.tgz binpack/* - diff --git a/build/targets.device.n0110.mak b/build/targets.device.n0110.mak index 3abc930e4..7c22b862b 100644 --- a/build/targets.device.n0110.mak +++ b/build/targets.device.n0110.mak @@ -19,3 +19,31 @@ $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_ex sleep 2; \ fi $(Q) $(PYTHON) build/device/dfu.py -u $(word 1,$^) + +.PHONY: %.two_binaries +%.two_binaries: %.elf + @echo "Building an internal and an external binary for $<" + $(Q) $(OBJCOPY) -O binary -j .text.external -j .rodata.external -j .exam_mode_buffer $(BUILD_DIR)/$< $(BUILD_DIR)/$(basename $<).external.bin + $(Q) $(OBJCOPY) -O binary -R .text.external -R .rodata.external -R .exam_mode_buffer $(BUILD_DIR)/$< $(BUILD_DIR)/$(basename $<).internal.bin + @echo "Padding $(basename $<).external.bin and $(basename $<).internal.bin" + $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).external.bin + $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).internal.bin + +.PHONY: binpack +binpack: + rm -rf build/binpack + mkdir -p build/binpack + make clean + make -j8 $(BUILD_DIR)/flasher.light.bin + cp $(BUILD_DIR)/flasher.light.bin build/binpack + make clean + make -j8 $(BUILD_DIR)/bench.flash.bin + make -j8 $(BUILD_DIR)/bench.ram.bin + cp $(BUILD_DIR)/bench.ram.bin $(BUILD_DIR)/bench.flash.bin build/binpack + make clean + make -j8 $(BUILD_DIR)/epsilon.onboarding.update.two_binaries + cp $(BUILD_DIR)/epsilon.onboarding.update.internal.bin $(BUILD_DIR)/epsilon.onboarding.update.external.bin build/binpack + make clean + cd build && for binary in flasher.light.bin bench.flash.bin bench.ram.bin epsilon.onboarding.internal.bin epsilon.onboarding.external.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done + cd build && tar cvfz binpack-`git rev-parse HEAD | head -c 7`.tgz binpack + rm -rf build/binpack diff --git a/escher/include/escher/responder.h b/escher/include/escher/responder.h index cf84840bb..b0918b5fc 100644 --- a/escher/include/escher/responder.h +++ b/escher/include/escher/responder.h @@ -5,16 +5,17 @@ class Responder { public: - Responder(Responder * parentResponder); + Responder(Responder * parentResponder) : m_parentResponder(parentResponder) {} virtual bool handleEvent(Ion::Events::Event event) { return false; }; // Default implementation does nothing - virtual void didBecomeFirstResponder(); - virtual void willResignFirstResponder(); - virtual void didEnterResponderChain(Responder * previousFirstResponder); - virtual void willExitResponderChain(Responder * nextFirstResponder); - Responder * parentResponder() const; + virtual void didBecomeFirstResponder() {} + virtual void willResignFirstResponder() {} + virtual void didEnterResponderChain(Responder * previousFirstResponder) {} + virtual void willExitResponderChain(Responder * nextFirstResponder) {} + Responder * parentResponder() const { return m_parentResponder; } Responder * commonAncestorWith(Responder * responder); - void setParentResponder(Responder * responder); + void setParentResponder(Responder * responder) { m_parentResponder = responder; } private: + bool hasAncestor(Responder * responder) const; Responder * m_parentResponder; }; diff --git a/escher/src/responder.cpp b/escher/src/responder.cpp index 34cbd96a0..72f12c85e 100644 --- a/escher/src/responder.cpp +++ b/escher/src/responder.cpp @@ -2,62 +2,29 @@ #include #include -Responder::Responder(Responder * parentResponder) : - m_parentResponder(parentResponder) -{ -} - -Responder * Responder::parentResponder() const { - return m_parentResponder; -} - -void Responder::setParentResponder(Responder * responder) { - m_parentResponder = responder; -} - -void Responder::didBecomeFirstResponder() { -} - -void Responder::willResignFirstResponder() { -} - -void Responder::didEnterResponderChain(Responder * previousFirstResponder) { -} - -void Responder::willExitResponderChain(Responder * nextFirstResponder) { -} Responder * Responder::commonAncestorWith(Responder * responder) { if (responder == nullptr) { return nullptr; } - if (this == responder) { - return this; + Responder * p = this; + while (p != nullptr) { + if (responder->hasAncestor(p)) { + return p; + } + p = p->parentResponder(); } - Responder * rootResponder = this; - while (rootResponder->parentResponder() != responder && rootResponder->parentResponder() != nullptr) { - rootResponder = rootResponder->parentResponder(); - } - if (rootResponder->parentResponder() == responder) { - return responder; - } - rootResponder = responder; - while (rootResponder->parentResponder() != this && rootResponder->parentResponder() != nullptr) { - rootResponder = rootResponder->parentResponder(); - } - if (rootResponder->parentResponder() == this) { - return this; - } - Responder * r = nullptr; - if (parentResponder()) { - r = parentResponder()->commonAncestorWith(responder); - } - Responder * s = nullptr; - if (responder->parentResponder()) { - s = commonAncestorWith(responder->parentResponder()); - } - if (r) { - return r; - } - return s; + return nullptr; +} + +bool Responder::hasAncestor(Responder * responder) const { + assert(responder != nullptr); + Responder * p = const_cast(this); + while (p != nullptr) { + if (p == responder) { + return true; + } + p = p->parentResponder(); + } + return false; } diff --git a/ion/include/ion/exam_mode.h b/ion/include/ion/exam_mode.h index ef0c5c92b..801dd4d1e 100644 --- a/ion/include/ion/exam_mode.h +++ b/ion/include/ion/exam_mode.h @@ -1,11 +1,15 @@ #ifndef ION_EXAM_MODE_H #define ION_EXAM_MODE_H +extern "C" { +#include +} + namespace Ion { namespace ExamMode { -bool FetchExamMode(); -void ToggleExamMode(); +uint8_t FetchExamMode(); +void IncrementExamMode(uint8_t delta); } } diff --git a/ion/src/device/Makefile b/ion/src/device/Makefile index 5adb13861..161218a49 100644 --- a/ion/src/device/Makefile +++ b/ion/src/device/Makefile @@ -19,20 +19,5 @@ ion_src += $(addprefix ion/src/shared/, \ ION_DEVICE_SFLAGS = -Iion/src/device/$(MODEL) -Iion/src/device/shared -$(call object_for,$(ion_device_src) $(dfu_src) $(ion_target_device_flasher_light_src) $(ion_target_device_flasher_verbose_src) $(usb_src) $(ion_target_device_bench_src) $(ion_device_dfu_xip_src) $(ion_device_dfu_relocated_src) $(ion_console_uart_src)): SFLAGS += $(ION_DEVICE_SFLAGS) +$(call object_for,$(sort $(ion_device_src) $(dfu_src) $(ion_target_device_flasher_light_src) $(ion_target_device_flasher_verbose_src) $(usb_src) $(ion_target_device_bench_src) $(ion_device_dfu_xip_src) $(ion_device_dfu_relocated_src) $(ion_console_uart_src))): SFLAGS += $(ION_DEVICE_SFLAGS) ion_src += $(ion_device_src) - -# When using the register.h C++ file in production mode, we expect the compiler -# to completely inline all bit manipulations. For some reason, if we build using -# the -Os optimization flag, GCC doesn't inline everything and and ends up -# emitting calls to aeabi_llsl for 64-bits registers. This is very sub-optimal -# so we're enforcing -O3 for this specific file. - -ifneq ($(DEBUG),1) -ifneq ($(COMPILER),llvm) -$(BUILD_DIR)/ion/src/device/led.o: SFLAGS+=-O3 -$(BUILD_DIR)/ion/src/device/console.o: SFLAGS+=-O3 -$(BUILD_DIR)/ion/src/device/display.o: SFLAGS+=-O3 -$(BUILD_DIR)/ion/src/device/swd.o: SFLAGS+=-O3 -endif -endif diff --git a/ion/src/device/n0100/drivers/config/external_flash.h b/ion/src/device/n0100/drivers/config/external_flash.h index 79d39f768..2fa895a21 100644 --- a/ion/src/device/n0100/drivers/config/external_flash.h +++ b/ion/src/device/n0100/drivers/config/external_flash.h @@ -22,7 +22,10 @@ using namespace Regs; constexpr static uint32_t StartAddress = 0xFFFFFFFF; constexpr static uint32_t EndAddress = 0xFFFFFFFF; -constexpr static int NumberOfSectors = 0; +constexpr static int NumberOf4KSectors = 0; +constexpr static int NumberOf32KSectors = 0; +constexpr static int NumberOf64KSectors = 0; +constexpr static int NumberOfSectors = NumberOf4KSectors + NumberOf32KSectors + NumberOf64KSectors; constexpr static AFGPIOPin Pins[] = {}; } diff --git a/ion/src/device/n0110/drivers/cache.cpp b/ion/src/device/n0110/drivers/cache.cpp index af907386f..0d16f5261 100644 --- a/ion/src/device/n0110/drivers/cache.cpp +++ b/ion/src/device/n0110/drivers/cache.cpp @@ -7,43 +7,39 @@ namespace Cache { using namespace Regs; void privateCleanInvalidateDisableDCache(bool clean, bool invalidate, bool disable) { + // Select Level 1 data cache CORTEX.CSSELR()->set(0); dsb(); - // Associativity = 6 - - uint32_t sets = CORTEX.CCSIDR()->getNUMSETS(); - uint32_t ways = CORTEX.CCSIDR()->getASSOCIATIVITY(); - + // Disable D-Cache if (disable) { CORTEX.CCR()->setDC(false); dsb(); } - do { - uint32_t w = ways; - do { - if (clean) { - if (invalidate) { - class CORTEX::DCCISW dccisw; - dccisw.setSET(sets); - dccisw.setWAY(w); - CORTEX.DCCISW()->set(dccisw); - } else { - class CORTEX::DCCSW dccsw; - dccsw.setSET(sets); - dccsw.setWAY(w); - CORTEX.DCCSW()->set(dccsw); - } - } else if (invalidate) { - class CORTEX::DCISW dcisw; - dcisw.setSET(sets); - dcisw.setWAY(w); - CORTEX.DCISW()->set(dcisw); - } - __asm volatile("nop"); - } while (w-- != 0); - } while (sets-- != 0); + // Pick the right DC??SW register according to invalidate/disable parameters + volatile CORTEX::DCSW * target = nullptr; + if (clean && invalidate) { + target = CORTEX.DCCISW(); + } else if (clean) { + target = CORTEX.DCCSW(); + } else { + assert(invalidate); + target = CORTEX.DCISW(); + } + + class CORTEX::CCSIDR ccsidr = CORTEX.CCSIDR()->get(); + uint32_t sets = ccsidr.getNUMSETS(); + uint32_t ways = ccsidr.getASSOCIATIVITY(); + + for (int set = sets; set >= 0; set--) { + for (int way = ways; way >= 0; way--) { + class CORTEX::DCSW dcsw; + dcsw.setSET(set); + dcsw.setWAY(way); + target->set(dcsw); + } + } dsb(); isb(); @@ -69,7 +65,7 @@ void cleanDCache() { void enableDCache() { invalidateDCache(); - CORTEX.CCR()->setDC(true); + CORTEX.CCR()->setDC(true); // Enable D-cache dsb(); isb(); } @@ -81,14 +77,14 @@ void disableDCache() { void invalidateICache() { dsb(); isb(); - CORTEX.ICIALLU()->set(0); + CORTEX.ICIALLU()->set(0); // Invalidate I-cache dsb(); isb(); } void enableICache() { invalidateICache(); - CORTEX.CCR()->setIC(true); + CORTEX.CCR()->setIC(true); // Enable I-cache dsb(); isb(); } @@ -96,8 +92,10 @@ void enableICache() { void disableICache() { dsb(); isb(); - CORTEX.CCR()->setIC(false); - invalidateICache(); + CORTEX.CCR()->setIC(false); // Disable I-cache + CORTEX.ICIALLU()->set(0); // Invalidate I-cache + dsb(); + isb(); } diff --git a/ion/src/device/n0110/drivers/config/external_flash.h b/ion/src/device/n0110/drivers/config/external_flash.h index 98c3ef9f0..f245dca20 100644 --- a/ion/src/device/n0110/drivers/config/external_flash.h +++ b/ion/src/device/n0110/drivers/config/external_flash.h @@ -22,7 +22,12 @@ using namespace Regs; constexpr static uint32_t StartAddress = 0x90000000; constexpr static uint32_t EndAddress = 0x90800000; -constexpr static int NumberOfSectors = 128; + +constexpr static int NumberOf4KSectors = 8; +constexpr static int NumberOf32KSectors = 1; +constexpr static int NumberOf64KSectors = 128 - 1; +constexpr static int NumberOfSectors = NumberOf4KSectors + NumberOf32KSectors + NumberOf64KSectors; + constexpr static AFGPIOPin Pins[] = { AFGPIOPin(GPIOB, 2, GPIO::AFR::AlternateFunction::AF9, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast), AFGPIOPin(GPIOB, 6, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast), diff --git a/ion/src/device/n0110/drivers/config/usb.h b/ion/src/device/n0110/drivers/config/usb.h index 7477f0846..bbd3fa5a1 100644 --- a/ion/src/device/n0110/drivers/config/usb.h +++ b/ion/src/device/n0110/drivers/config/usb.h @@ -14,7 +14,7 @@ constexpr static AFGPIOPin VbusPin = AFGPIOPin(GPIOA, 9, GPIO::AFR::AlternateFun constexpr static AFGPIOPin DmPin = AFGPIOPin(GPIOA, 11, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast); constexpr static AFGPIOPin DpPin = AFGPIOPin(GPIOA, 12, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast); -constexpr static const char * InterfaceStringDescriptor = "@Flash/0x08000000/04*016Kg/0x90000000/64*064Kg,64*064Kg"; +constexpr static const char * InterfaceStringDescriptor = "@Flash/0x08000000/04*016Kg/0x90000000/08*004Kg,01*032Kg,63*064Kg,64*064Kg"; } } diff --git a/ion/src/device/n0110/drivers/led.cpp b/ion/src/device/n0110/drivers/led.cpp index e24aed2ea..a63b35189 100644 --- a/ion/src/device/n0110/drivers/led.cpp +++ b/ion/src/device/n0110/drivers/led.cpp @@ -9,7 +9,7 @@ namespace LED { KDColor updateColorWithPlugAndCharge() { KDColor ledColor = getColor(); - if (GlobalPreferences::sharedGlobalPreferences()->examMode()) { // If exam mode is on, we do not update the LED with the plugged/charging state + if (GlobalPreferences::sharedGlobalPreferences()->isInExamMode() { // If exam mode is on, we do not update the LED with the plugged/charging state if (USB::isPlugged()) { ledColor = Battery::isCharging() ? KDColorOrange : KDColorGreen; } else { diff --git a/ion/src/device/shared/drivers/exam_mode.cpp b/ion/src/device/shared/drivers/exam_mode.cpp index 19b62b4df..a4aa20143 100644 --- a/ion/src/device/shared/drivers/exam_mode.cpp +++ b/ion/src/device/shared/drivers/exam_mode.cpp @@ -3,14 +3,14 @@ #include "flash.h" #include -namespace Ion { -namespace ExamMode { - extern "C" { extern char _exam_mode_buffer_start; extern char _exam_mode_buffer_end; } +namespace Ion { +namespace ExamMode { + char ones[Config::ExamModeBufferSize] __attribute__((section(".exam_mode_buffer"))) __attribute__((used)) @@ -18,33 +18,24 @@ char ones[Config::ExamModeBufferSize] /* The exam mode is written in flash so that it is resilient to resets. * We erase the dedicated flash sector (all bits written to 1) and, upon - * activating or deactivating the exam mode we write one bit to 0. To determine - * if we are in exam mode, we count the number of leading 0 bits. If it is even, - * the exam mode is deactivated, if it is odd, the exam mode is activated. */ + * deactivating or activating standard or Dutch exam mode we write one or two + * bits to 0. To determine in which exam mode we are, we count the number of + * leading 0 bits. If it is equal to: + * - 0[3]: the exam mode is off; + * - 1[3]: the standard exam mode is activated; + * - 2[3]: the Dutch exam mode is activated. */ /* significantExamModeAddress returns the first uint32_t * in the exam mode - * flash sector that does not point to 0. If this flash sector has only 0s, it - * is erased (to 1) and significantExamModeAddress returns the start of the - * sector. */ + * flash sector that does not point to 0. If this flash sector has only 0s or + * if it has only one 1, it is erased (to 1) and significantExamModeAddress + * returns the start of the sector. */ -uint32_t * SignificantExamModeAddress() { - uint32_t * persitence_start = (uint32_t *)&_exam_mode_buffer_start; - uint32_t * persitence_end = (uint32_t *)&_exam_mode_buffer_end; - while (persitence_start < persitence_end && *persitence_start == 0x0) { - // Skip even number of zero bits - persitence_start++; - } - if (persitence_start == persitence_end) { - assert(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_buffer_start) >= 0); - Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_buffer_start)); - return (uint32_t *)&_exam_mode_buffer_start; - } - return persitence_start; -} +constexpr static size_t numberOfBitsInByte = 8; -size_t firstOneBit(int i, size_t size) { +// if i = 0b000011101, firstOneBitInByte(i) returns 5 +size_t numberOfBitsAfterLeadingZeroes(int i) { int minShift = 0; - int maxShift = size; + int maxShift = numberOfBitsInByte; while (maxShift > minShift+1) { int shift = (minShift + maxShift)/2; int shifted = i >> shift; @@ -57,23 +48,72 @@ size_t firstOneBit(int i, size_t size) { return maxShift; } -bool FetchExamMode() { - uint32_t * readingAddress = SignificantExamModeAddress(); - size_t numberOfLeading0 = 32 - firstOneBit(*readingAddress, 32); - return numberOfLeading0 % 2 == 1; +uint8_t * SignificantExamModeAddress() { + uint32_t * persitence_start_32 = (uint32_t *)&_exam_mode_buffer_start; + uint32_t * persitence_end_32 = (uint32_t *)&_exam_mode_buffer_end; + assert(persitence_end_32 - persitence_start_32 % 4 == 0); + while (persitence_start_32 < persitence_end_32 && *persitence_start_32 == 0x0) { + // Scan by groups of 32 bits to reach first non-zero bit + persitence_start_32++; + } + uint8_t * persitence_start_8 = (uint8_t *)persitence_start_32; + uint8_t * persitence_end_8 = (uint8_t *)persitence_end_32; + while (persitence_start_8 < persitence_end_8 && *persitence_start_8 == 0x0) { + // Scan by groups of 8 bits to reach first non-zero bit + persitence_start_8++; + } + if (persitence_start_8 == persitence_end_8 + // we can't toggle from 0[3] to 2[3] when there is only one 1 bit in the whole sector + || (persitence_start_8 + 1 == persitence_end_8 && *persitence_start_8 == 1)) { + assert(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_buffer_start) >= 0); + Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_buffer_start)); + return (uint8_t *)&_exam_mode_buffer_start; + } + + return persitence_start_8; } -void ToggleExamMode() { - uint32_t * writingAddress = SignificantExamModeAddress(); +uint8_t FetchExamMode() { + uint8_t * readingAddress = SignificantExamModeAddress(); + // Count the number of 0[3] before reading address + uint32_t nbOfZerosBefore = ((readingAddress - (uint8_t *)&_exam_mode_buffer_start) * numberOfBitsInByte) % 3; + // Count the number of 0[3] at reading address + size_t numberOfLeading0 = (numberOfBitsInByte - numberOfBitsAfterLeadingZeroes(*readingAddress)) % 3; + return (nbOfZerosBefore + numberOfLeading0) % 3; +} + +void IncrementExamMode(uint8_t delta) { + assert(delta == 1 || delta == 2); + uint8_t * writingAddress = SignificantExamModeAddress(); assert(*writingAddress != 0); - // Compute the new value with one bit switched - uint8_t numberOfLeadingZeroes = 32 - firstOneBit(*writingAddress, 32); - /* When writing in flash, we can only switch a 1 to a 0. If we want to switch - * the fifth bit in a byte, we can thus write "11110111". */ - uint32_t newValue = ~(1 << (31 - numberOfLeadingZeroes)); + size_t nbOfTargetedOnes = numberOfBitsAfterLeadingZeroes(*writingAddress); + + // Compute the new value with delta bits switched to 0. + /* We write in 2 bytes instead of 1, in case there was only one bit + * left to 1 in writingAddress. */ + nbOfTargetedOnes += numberOfBitsInByte; + nbOfTargetedOnes -= delta; + constexpr size_t newValueSize = sizeof(uint16_t)/sizeof(uint8_t); + uint8_t newValue[newValueSize]; + if (nbOfTargetedOnes > numberOfBitsInByte) { + size_t nbOfTargetedOnesInFirstByte = nbOfTargetedOnes - numberOfBitsInByte; + assert(nbOfTargetedOnesInFirstByte <= numberOfBitsInByte); + newValue[0] = ((uint16_t)1 << nbOfTargetedOnesInFirstByte) - 1; + newValue[1] = 0xFF; + } else { + assert(nbOfTargetedOnes <= numberOfBitsInByte); + newValue[0] = 0; + newValue[1] = ((uint16_t)1 << nbOfTargetedOnes) - 1; + } // Write the value in flash - Ion::Device::Flash::WriteMemory((uint8_t *)writingAddress, (uint8_t *)&newValue, sizeof(uint32_t)); + /* As the number of changed bits is capped by 2, if *writingAddress has more + * than one remaining 1 bit, we know we toggle bits only in the first byte of + * newValue. We can settle for writing one byte instead of two. */ + size_t writtenFlash = *writingAddress == 1 ? sizeof(uint16_t) : sizeof(uint8_t); + /* Avoid writing out of sector */ + assert(writingAddress < (uint8_t *)&_exam_mode_buffer_end - 1 || (writingAddress == (uint8_t *)&_exam_mode_buffer_end - 1 && writtenFlash == 1)); + Ion::Device::Flash::WriteMemory(writingAddress, newValue, writtenFlash); } } diff --git a/ion/src/device/shared/drivers/external_flash.cpp b/ion/src/device/shared/drivers/external_flash.cpp index 10404c7c2..a2b6cfe59 100644 --- a/ion/src/device/shared/drivers/external_flash.cpp +++ b/ion/src/device/shared/drivers/external_flash.cpp @@ -71,6 +71,8 @@ enum class Command : uint8_t { Reset = 0x99, // Erase the whole chip or a 64-Kbyte block as being "1" ChipErase = 0xC7, + Erase4KbyteBlock = 0x20, + Erase32KbyteBlock = 0x52, Erase64KbyteBlock = 0xD8, SetReadParameters = 0xC0, DeepPowerDown = 0xB9, @@ -79,6 +81,8 @@ enum class Command : uint8_t { }; static constexpr uint8_t NumberOfAddressBitsIn64KbyteBlock = 16; +static constexpr uint8_t NumberOfAddressBitsIn32KbyteBlock = 15; +static constexpr uint8_t NumberOfAddressBitsIn4KbyteBlock = 12; class ExternalFlashStatusRegister { public: @@ -367,10 +371,23 @@ void shutdown() { } int SectorAtAddress(uint32_t address) { + /* WARNING: this code assumes that the flash sectors are of increasing size: + * first all 4K sectors, then all 32K sectors, and finally all 64K sectors. */ int i = address >> NumberOfAddressBitsIn64KbyteBlock; - if (i >= Config::NumberOfSectors) { + if (i > Config::NumberOf64KSectors) { return -1; } + if (i >= 1) { + return Config::NumberOf4KSectors + Config::NumberOf32KSectors + i - 1; + } + i = address >> NumberOfAddressBitsIn32KbyteBlock; + if (i >= 1) { + i = Config::NumberOf4KSectors + i - 1; + assert(i >= 0 && i <= Config::NumberOf32KSectors); + return i; + } + i = address >> NumberOfAddressBitsIn4KbyteBlock; + assert(i <= Config::NumberOf4KSectors); return i; } @@ -408,7 +425,22 @@ void __attribute__((noinline)) EraseSector(int i) { unlockFlash(); send_command(Command::WriteEnable); wait(); - send_write_command(Command::Erase64KbyteBlock, reinterpret_cast(i << NumberOfAddressBitsIn64KbyteBlock), nullptr, 0); + /* WARNING: this code assumes that the flash sectors are of increasing size: + * first all 4K sectors, then all 32K sectors, and finally all 64K sectors. */ + if (i < Config::NumberOf4KSectors) { + send_write_command(Command::Erase4KbyteBlock, reinterpret_cast(i << NumberOfAddressBitsIn4KbyteBlock), nullptr, 0); + } else if (i < Config::NumberOf4KSectors + Config::NumberOf32KSectors) { + /* If the sector is the number Config::NumberOf4KSectors, we want to write + * at the address 1 << NumberOfAddressBitsIn32KbyteBlock, hence the formula + * (i - Config::NumberOf4KSectors + 1). */ + send_write_command(Command::Erase32KbyteBlock, reinterpret_cast((i - Config::NumberOf4KSectors + 1) << NumberOfAddressBitsIn32KbyteBlock), nullptr, 0); + } else { + /* If the sector is the number + * Config::NumberOf4KSectors - Config::NumberOf32KSectors, we want to write + * at the address 1 << NumberOfAddressBitsIn32KbyteBlock, hence the formula + * (i - Config::NumberOf4KSectors - Config::NumberOf32KSectors + 1). */ + send_write_command(Command::Erase64KbyteBlock, reinterpret_cast((i - Config::NumberOf4KSectors - Config::NumberOf32KSectors + 1) << NumberOfAddressBitsIn64KbyteBlock), nullptr, 0); + } wait(); set_as_memory_mapped(); } diff --git a/ion/src/device/shared/drivers/external_flash.h b/ion/src/device/shared/drivers/external_flash.h index 8a0ad3b62..f2c1ddd6f 100644 --- a/ion/src/device/shared/drivers/external_flash.h +++ b/ion/src/device/shared/drivers/external_flash.h @@ -15,7 +15,12 @@ * 2^7 64KiB blocks 0x..0000 - 0x..FFFF * 2^7 * 2 32KiB blocks 0x..0000 - 0x..7FFF or 0x..8000 - 0x..FFFF * 2^7 * 2 * 2^3 4KiB blocks 0x...000 - 0x...FFF - * 2^7 * 2 * 2^3 * 2^4 256B pages 0x....00 - 0x....FF */ + * 2^7 * 2 * 2^3 * 2^4 256B pages 0x....00 - 0x....FF + * + * To be able to erase a small sector for the exam mode, we say that the flash + * is cut into 8 + 1 + 2^7-1 = 136 sectors: 8 sectors of 4Kb, 1 sector of 32Kb + * and 2^7-1 sectors of 64Kb. These sectors are the smallest erasable units. If + * need be, we can define more sectors to erase even more finely the flash. */ namespace Ion { namespace Device { diff --git a/ion/src/device/shared/drivers/reset.cpp b/ion/src/device/shared/drivers/reset.cpp index d69b2e102..ef6053570 100644 --- a/ion/src/device/shared/drivers/reset.cpp +++ b/ion/src/device/shared/drivers/reset.cpp @@ -12,7 +12,13 @@ using namespace Regs; void core() { // Perform a full core reset + Ion::Device::Cache::dsb(); // Complete all memory accesses CORTEX.AIRCR()->requestReset(); + Ion::Device::Cache::dsb(); + // Wait until reset + while (true) { + asm("nop"); + } } /* We isolate the jump code that needs to be executed from the internal @@ -41,8 +47,7 @@ void __attribute__((noinline)) internalFlashJump(uint32_t jumpIsrVectorAddress) void jump(uint32_t jumpIsrVectorAddress) { // Disable cache before reset - Ion::Device::Cache::disableDCache(); - Ion::Device::Cache::disableICache(); + Ion::Device::Cache::disable(); /* Shutdown all clocks and periherals to mimic a hardware reset. */ Board::shutdownPeripherals(); diff --git a/ion/src/device/shared/regs/cortex.h b/ion/src/device/shared/regs/cortex.h index b89351ebe..2c71c0fa2 100644 --- a/ion/src/device/shared/regs/cortex.h +++ b/ion/src/device/shared/regs/cortex.h @@ -72,6 +72,7 @@ public: #if REGS_CORTEX_CONFIG_CACHE class CCSIDR : public Register32 { public: + using Register32::Register32; REGS_FIELD(ASSOCIATIVITY, uint16_t, 12, 3); REGS_FIELD(NUMSETS, uint16_t, 27, 13); }; @@ -100,25 +101,20 @@ public: using Register32::Register32; }; - class DCISW : public Register32 { + class DCSW : public Register32 { public: - DCISW() : Register32(0) {} + DCSW() : Register32(0) {} REGS_FIELD(SET, uint16_t, 13, 5); REGS_FIELD(WAY, uint8_t, 31, 30); }; - class DCCSW : public Register32 { - public: - DCCSW() : Register32(0) {} - REGS_FIELD(SET, uint16_t, 13, 5); - REGS_FIELD(WAY, uint8_t, 31, 30); + class DCISW : public DCSW { }; - class DCCISW : public Register32 { - public: - DCCISW() : Register32(0) {} - REGS_FIELD(SET, uint16_t, 13, 5); - REGS_FIELD(WAY, uint8_t, 31, 30); + class DCCSW : public DCSW { + }; + + class DCCISW : public DCSW { }; #endif diff --git a/ion/src/device/shared/regs/register.h b/ion/src/device/shared/regs/register.h index cac002a6b..12adc8a28 100644 --- a/ion/src/device/shared/regs/register.h +++ b/ion/src/device/shared/regs/register.h @@ -4,6 +4,8 @@ #include #include +#define always_inline __attribute__((always_inline)) + namespace Ion { namespace Device { namespace Regs { @@ -22,7 +24,7 @@ public: T get() volatile { return m_value; } - void setBitRange(uint8_t high, uint8_t low, T value) volatile { + always_inline void setBitRange(uint8_t high, uint8_t low, T value) volatile { m_value = bit_range_set_value(high, low, m_value, value); } T getBitRange(uint8_t high, uint8_t low) volatile { @@ -57,8 +59,8 @@ typedef Register Register64; } } -#define REGS_FIELD_R(name,type,high,low) type get##name() volatile { return (type)getBitRange(high,low); }; -#define REGS_FIELD_W(name,type,high,low) void set##name(type v) volatile { static_assert(sizeof(type) <= 4, "Invalid size"); setBitRange(high, low, static_cast(v)); }; +#define REGS_FIELD_R(name,type,high,low) always_inline type get##name() volatile { return (type)getBitRange(high,low); }; +#define REGS_FIELD_W(name,type,high,low) always_inline void set##name(type v) volatile { static_assert(sizeof(type) <= 4, "Invalid size"); setBitRange(high, low, static_cast(v)); }; #define REGS_FIELD(name,type,high,low) REGS_FIELD_R(name,type,high,low); REGS_FIELD_W(name,type,high,low); #define REGS_TYPE_FIELD(name,high,low) REGS_FIELD(name,name,high,low) #define REGS_BOOL_FIELD(name,bit) REGS_FIELD(name,bool,bit,bit) diff --git a/ion/src/shared/dummy/exam_mode.cpp b/ion/src/shared/dummy/exam_mode.cpp index 02d78e591..e9a32a539 100644 --- a/ion/src/shared/dummy/exam_mode.cpp +++ b/ion/src/shared/dummy/exam_mode.cpp @@ -3,11 +3,11 @@ namespace Ion { namespace ExamMode { -bool FetchExamMode() { +uint8_t FetchExamMode() { return false; } -void ToggleExamMode() { +void IncrementExamMode(uint8_t delta) { } } diff --git a/kandinsky/include/kandinsky/color.h b/kandinsky/include/kandinsky/color.h index 8e1cafa50..1ac9b91dc 100644 --- a/kandinsky/include/kandinsky/color.h +++ b/kandinsky/include/kandinsky/color.h @@ -46,5 +46,6 @@ constexpr KDColor KDColorGreen = KDColor::RGB24(0x00FF00); constexpr KDColor KDColorBlue = KDColor::RGB24(0x0000FF); constexpr KDColor KDColorYellow = KDColor::RGB24(0xFFFF00); constexpr KDColor KDColorOrange = KDColor::RGB24(0xFF9900); +constexpr KDColor KDColorPurple = KDColor::RGB24(0xFF00DD); #endif diff --git a/poincare/include/poincare/approximation_helper.h b/poincare/include/poincare/approximation_helper.h index b079d6a62..169143faf 100644 --- a/poincare/include/poincare/approximation_helper.h +++ b/poincare/include/poincare/approximation_helper.h @@ -9,6 +9,7 @@ namespace Poincare { namespace ApproximationHelper { + template int PositiveIntegerApproximationIfPossible(const ExpressionNode * expression, bool * isUndefined, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); template std::complex TruncateRealOrImaginaryPartAccordingToArgument(std::complex c); template using ComplexCompute = Complex(*)(const std::complex, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); diff --git a/poincare/src/approximation_helper.cpp b/poincare/src/approximation_helper.cpp index 2d7eafc39..32f1f88ad 100644 --- a/poincare/src/approximation_helper.cpp +++ b/poincare/src/approximation_helper.cpp @@ -15,6 +15,18 @@ template T absMod(T a, T b) { return result > b/2 ? b-result : result; } +static inline int absInt(int x) { return x < 0 ? -x : x; } + +template int ApproximationHelper::PositiveIntegerApproximationIfPossible(const ExpressionNode * expression, bool * isUndefined, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + Evaluation evaluation = expression->approximate(T(), context, complexFormat, angleUnit); + T scalar = evaluation.toScalar(); + if (std::isnan(scalar) || scalar != (int)scalar) { + *isUndefined = true; + return 0; + } + return absInt((int)scalar); +} + template std::complex ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex c) { T arg = std::arg(c); T precision = 10*Expression::Epsilon(); @@ -92,6 +104,8 @@ template MatrixComplex ApproximationHelper::ElementWiseOnComplexM return matrix; } +template int Poincare::ApproximationHelper::PositiveIntegerApproximationIfPossible(Poincare::ExpressionNode const*, bool*, Poincare::Context*, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit); +template int Poincare::ApproximationHelper::PositiveIntegerApproximationIfPossible(Poincare::ExpressionNode const*, bool*, Poincare::Context*, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit); template std::complex Poincare::ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex); template std::complex Poincare::ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex); template Poincare::Evaluation Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute compute); diff --git a/poincare/src/great_common_divisor.cpp b/poincare/src/great_common_divisor.cpp index 51afde0c1..d9e9faec0 100644 --- a/poincare/src/great_common_divisor.cpp +++ b/poincare/src/great_common_divisor.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -27,22 +28,20 @@ Expression GreatCommonDivisorNode::shallowReduce(ReductionContext reductionConte template Evaluation GreatCommonDivisorNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { - Evaluation f1Input = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); - Evaluation f2Input = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); - T f1 = f1Input.toScalar(); - T f2 = f2Input.toScalar(); - if (std::isnan(f1) || std::isnan(f2) || f1 != (int)f1 || f2 != (int)f2) { + bool isUndefined = false; + int a = ApproximationHelper::PositiveIntegerApproximationIfPossible(childAtIndex(0), &isUndefined, context, complexFormat, angleUnit); + int b = ApproximationHelper::PositiveIntegerApproximationIfPossible(childAtIndex(1), &isUndefined, context, complexFormat, angleUnit); + if (isUndefined) { return Complex::Undefined(); } - int a = (int)f2; - int b = (int)f1; - if (f1 > f2) { + if (b > a) { + int temp = b; b = a; - a = (int)f1; + a = temp; } int r = 0; while((int)b!=0){ - r = a - ((int)(a/b))*b; + r = a - (a/b)*b; a = b; b = r; } diff --git a/poincare/src/least_common_multiple.cpp b/poincare/src/least_common_multiple.cpp index c94547809..cb356e338 100644 --- a/poincare/src/least_common_multiple.cpp +++ b/poincare/src/least_common_multiple.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -27,26 +28,24 @@ Expression LeastCommonMultipleNode::shallowReduce(ReductionContext reductionCont template Evaluation LeastCommonMultipleNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { - Evaluation f1Input = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); - Evaluation f2Input = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); - T f1 = f1Input.toScalar(); - T f2 = f2Input.toScalar(); - if (std::isnan(f1) || std::isnan(f2) || f1 != (int)f1 || f2 != (int)f2) { + bool isUndefined = false; + int a = ApproximationHelper::PositiveIntegerApproximationIfPossible(childAtIndex(0), &isUndefined, context, complexFormat, angleUnit); + int b = ApproximationHelper::PositiveIntegerApproximationIfPossible(childAtIndex(1), &isUndefined, context, complexFormat, angleUnit); + if (isUndefined) { return Complex::Undefined(); } - if (f1 == 0.0f || f2 == 0.0f) { + if (a == 0 || b == 0) { return Complex::Builder(0.0); } - int a = (int)f2; - int b = (int)f1; - if (f1 > f2) { + if (b > a) { + int temp = b; b = a; - a = (int)f1; + a = temp; } int product = a*b; int r = 0; while((int)b!=0){ - r = a - ((int)(a/b))*b; + r = a - (a/b)*b; a = b; b = r; } diff --git a/poincare/test/approximation.cpp b/poincare/test/approximation.cpp index bf8ff4a3e..d6573c8d7 100644 --- a/poincare/test/approximation.cpp +++ b/poincare/test/approximation.cpp @@ -269,12 +269,18 @@ QUIZ_CASE(poincare_approximation_function) { assert_expression_approximates_to("gcd(234,394)", "2"); assert_expression_approximates_to("gcd(234,394)", "2"); + assert_expression_approximates_to("gcd(-234,394)", "2"); + assert_expression_approximates_to("gcd(234,-394)", "2"); + assert_expression_approximates_to("gcd(-234,-394)", "2"); assert_expression_approximates_to("im(2+3𝐢)", "3"); assert_expression_approximates_to("im(2+3𝐢)", "3"); assert_expression_approximates_to("lcm(234,394)", "46098"); assert_expression_approximates_to("lcm(234,394)", "46098"); + assert_expression_approximates_to("lcm(-234,394)", "46098"); + assert_expression_approximates_to("lcm(234,-394)", "46098"); + assert_expression_approximates_to("lcm(-234,-394)", "46098"); assert_expression_approximates_to("int(x,x, 1, 2)", "1.5"); assert_expression_approximates_to("int(x,x, 1, 2)", "1.5");