mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
Merge branch 'master' into python_turtle
This commit is contained in:
@@ -17,5 +17,6 @@ matrix:
|
||||
|
||||
os: linux
|
||||
script:
|
||||
- set -e
|
||||
- make clean && make epsilon.$EXT test.$EXT
|
||||
- if [ "$PLATFORM" = "blackbox" ]; then ./test.$EXT; PLATFORM=blackbox make integration_tests; fi
|
||||
|
||||
@@ -136,6 +136,10 @@ bool AppsContainer::dispatchEvent(Ion::Events::Event event) {
|
||||
if (event == Ion::Events::USBEnumeration) {
|
||||
if (Ion::USB::isPlugged()) {
|
||||
App::Snapshot * activeSnapshot = (activeApp() == nullptr ? appSnapshotAtIndex(0) : activeApp()->snapshot());
|
||||
/* Just after a software update, the battery timer does not have time to
|
||||
* fire before the calculator enters DFU mode. As the DFU mode blocks the
|
||||
* event loop, we update the battery state "manually" here. */
|
||||
updateBatteryState();
|
||||
switchTo(usbConnectedAppSnapshot());
|
||||
Ion::USB::DFU();
|
||||
switchTo(activeSnapshot);
|
||||
@@ -237,9 +241,10 @@ void AppsContainer::run() {
|
||||
}
|
||||
|
||||
bool AppsContainer::updateBatteryState() {
|
||||
if (m_window.updateBatteryLevel() ||
|
||||
m_window.updateIsChargingState() ||
|
||||
m_window.updatePluggedState()) {
|
||||
bool batteryLevelUpdated = m_window.updateBatteryLevel();
|
||||
bool pluggedStateUpdated = m_window.updatePluggedState();
|
||||
bool chargingStateUpdated = m_window.updateIsChargingState();
|
||||
if (batteryLevelUpdated || pluggedStateUpdated || chargingStateUpdated) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -257,7 +262,7 @@ void AppsContainer::displayExamModePopUp(bool activate) {
|
||||
void AppsContainer::shutdownDueToLowBattery() {
|
||||
while (Ion::Battery::level() == Ion::Battery::Charge::EMPTY) {
|
||||
m_emptyBatteryWindow.redraw(true);
|
||||
Ion::msleep(3000);
|
||||
Ion::Timing::msleep(3000);
|
||||
Ion::Power::suspend();
|
||||
}
|
||||
window()->redraw(true);
|
||||
|
||||
@@ -21,14 +21,6 @@ const uint8_t tickMask[BatteryView::k_tickHeight][BatteryView::k_tickWidth] = {
|
||||
|
||||
};
|
||||
|
||||
|
||||
BatteryView::BatteryView() :
|
||||
m_chargeState(Ion::Battery::Charge::SOMEWHERE_INBETWEEN),
|
||||
m_isCharging(false),
|
||||
m_isPlugged(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool BatteryView::setChargeState(Ion::Battery::Charge chargeState) {
|
||||
if (chargeState != m_chargeState) {
|
||||
m_chargeState = chargeState;
|
||||
@@ -62,28 +54,41 @@ KDColor s_tickWorkingBuffer[BatteryView::k_tickHeight*BatteryView::k_tickWidth];
|
||||
void BatteryView::drawRect(KDContext * ctx, KDRect rect) const {
|
||||
/* We draw from left to right. The middle part representing the battery
|
||||
*'content' depends on the charge */
|
||||
|
||||
// Draw the left part
|
||||
ctx->fillRect(KDRect(0, 0, k_elementWidth, k_batteryHeight), KDColorWhite);
|
||||
|
||||
// Draw the middle part
|
||||
constexpr KDCoordinate batteryInsideX = k_elementWidth+k_separatorThickness;
|
||||
constexpr KDCoordinate batteryInsideWidth = k_batteryWidth-3*k_elementWidth-2*k_separatorThickness;
|
||||
if (m_isCharging) {
|
||||
ctx->fillRect(KDRect(k_elementWidth+k_separatorThickness, 0, k_batteryWidth-3*k_elementWidth-2*k_separatorThickness, k_batteryHeight), Palette::YellowLight);
|
||||
// Charging: Yellow background with flash
|
||||
ctx->fillRect(KDRect(batteryInsideX, 0, batteryInsideWidth, k_batteryHeight), Palette::YellowLight);
|
||||
KDRect frame((k_batteryWidth-k_flashWidth)/2, 0, k_flashWidth, k_flashHeight);
|
||||
ctx->blendRectWithMask(frame, KDColorWhite, (const uint8_t *)flashMask, s_flashWorkingBuffer);
|
||||
}
|
||||
if (!m_isCharging && m_isPlugged && m_chargeState == Ion::Battery::Charge::FULL) {
|
||||
ctx->fillRect(KDRect(k_elementWidth+k_separatorThickness, 0, k_batteryWidth-3*k_elementWidth-2*k_separatorThickness, k_batteryHeight), KDColorWhite);
|
||||
KDRect frame((k_batteryWidth-k_tickWidth)/2, (k_batteryHeight-k_tickHeight)/2, k_tickWidth, k_tickHeight);
|
||||
ctx->blendRectWithMask(frame, Palette::YellowDark, (const uint8_t *)tickMask, s_tickWorkingBuffer);
|
||||
}
|
||||
if (!m_isCharging && m_chargeState == Ion::Battery::Charge::LOW) {
|
||||
ctx->fillRect(KDRect(k_elementWidth+k_separatorThickness, 0, 2*k_elementWidth, k_batteryHeight), Palette::LowBattery);
|
||||
} else if (m_chargeState == Ion::Battery::Charge::LOW) {
|
||||
assert(!m_isPlugged);
|
||||
// Low: Quite empty battery
|
||||
ctx->fillRect(KDRect(batteryInsideX, 0, 2*k_elementWidth, k_batteryHeight), Palette::LowBattery);
|
||||
ctx->fillRect(KDRect(3*k_elementWidth+k_separatorThickness, 0, k_batteryWidth-5*k_elementWidth-2*k_separatorThickness, k_batteryHeight), Palette::YellowLight);
|
||||
} else if (m_chargeState == Ion::Battery::Charge::SOMEWHERE_INBETWEEN) {
|
||||
assert(!m_isPlugged);
|
||||
// Middle: Half full battery
|
||||
constexpr KDCoordinate middleChargeWidth = batteryInsideWidth/2;
|
||||
ctx->fillRect(KDRect(batteryInsideX, 0, middleChargeWidth, k_batteryHeight), KDColorWhite);
|
||||
ctx->fillRect(KDRect(batteryInsideX+middleChargeWidth, 0, middleChargeWidth, k_batteryHeight), Palette::YellowLight);
|
||||
} else {
|
||||
assert(m_chargeState == Ion::Battery::Charge::FULL);
|
||||
// Full but not plugged: Full battery
|
||||
ctx->fillRect(KDRect(batteryInsideX, 0, batteryInsideWidth, k_batteryHeight), KDColorWhite);
|
||||
if (m_isPlugged) {
|
||||
// Plugged and full: Full battery with tick
|
||||
KDRect frame((k_batteryWidth-k_tickWidth)/2, (k_batteryHeight-k_tickHeight)/2, k_tickWidth, k_tickHeight);
|
||||
ctx->blendRectWithMask(frame, Palette::YellowDark, (const uint8_t *)tickMask, s_tickWorkingBuffer);
|
||||
}
|
||||
}
|
||||
if (!m_isCharging && m_chargeState == Ion::Battery::Charge::SOMEWHERE_INBETWEEN) {
|
||||
ctx->fillRect(KDRect(k_elementWidth+k_separatorThickness, 0, (k_batteryWidth-3*k_elementWidth-2*k_separatorThickness)/2, k_batteryHeight), KDColorWhite);
|
||||
ctx->fillRect(KDRect(k_elementWidth+k_separatorThickness+(k_batteryWidth-3*k_elementWidth-2*k_separatorThickness)/2, 0, (k_batteryWidth-3*k_elementWidth-2*k_separatorThickness)/2, k_batteryHeight), Palette::YellowLight);
|
||||
}
|
||||
if (!m_isCharging && !m_isPlugged && m_chargeState == Ion::Battery::Charge::FULL) {
|
||||
ctx->fillRect(KDRect(k_elementWidth+k_separatorThickness, 0, k_batteryWidth-3*k_elementWidth-2*k_separatorThickness, k_batteryHeight), KDColorWhite);
|
||||
}
|
||||
|
||||
// Draw the right part
|
||||
ctx->fillRect(KDRect(k_batteryWidth-2*k_elementWidth, 0, k_elementWidth, k_batteryHeight), KDColorWhite);
|
||||
ctx->fillRect(KDRect(k_batteryWidth-k_elementWidth, (k_batteryHeight-k_capHeight)/2, k_elementWidth, k_capHeight), KDColorWhite);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
|
||||
class BatteryView : public TransparentView {
|
||||
public:
|
||||
BatteryView();
|
||||
BatteryView() :
|
||||
m_chargeState(Ion::Battery::Charge::SOMEWHERE_INBETWEEN),
|
||||
m_isCharging(false),
|
||||
m_isPlugged(false)
|
||||
{}
|
||||
bool setChargeState(Ion::Battery::Charge chargeState);
|
||||
bool setIsCharging(bool isCharging);
|
||||
bool setIsPlugged(bool isPlugged);
|
||||
|
||||
@@ -108,6 +108,12 @@ void PreferencesController::willDisplayCellForIndex(HighlightCell * cell, int in
|
||||
myCell->setLayout(layoutForPreferences(m_messageTreeModel->children(index)->label()));
|
||||
}
|
||||
|
||||
KDCoordinate PreferencesController::rowHeight(int j) {
|
||||
/* We cheat for the Writing format subcontroller, because the Edition2D layout
|
||||
* needs more vertical space. */
|
||||
return GenericSubController::rowHeight(j) + (m_messageTreeModel->label() == I18n::Message::EditionMode ? 2 : 0);
|
||||
}
|
||||
|
||||
void PreferencesController::setPreferenceWithValueIndex(I18n::Message message, int valueIndex) {
|
||||
Preferences * preferences = Preferences::sharedPreferences();
|
||||
if (message == I18n::Message::AngleUnit) {
|
||||
|
||||
@@ -13,6 +13,7 @@ public:
|
||||
HighlightCell * reusableCell(int index, int type) override;
|
||||
int reusableCellCount(int type) override;
|
||||
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
|
||||
KDCoordinate rowHeight(int j) override;
|
||||
protected:
|
||||
constexpr static int k_totalNumberOfCell = 2;
|
||||
private:
|
||||
|
||||
@@ -47,9 +47,8 @@ bool HistogramController::handleEvent(Ion::Events::Event event) {
|
||||
return MultipleDataViewController::handleEvent(event);
|
||||
}
|
||||
|
||||
void HistogramController::didBecomeFirstResponder() {
|
||||
MultipleDataViewController::didBecomeFirstResponder();
|
||||
|
||||
void HistogramController::viewWillAppear() {
|
||||
MultipleDataViewController::viewWillAppear();
|
||||
uint32_t storeChecksum = m_store->storeChecksum();
|
||||
if (*m_storeVersion != storeChecksum) {
|
||||
*m_storeVersion = storeChecksum;
|
||||
@@ -67,8 +66,6 @@ void HistogramController::didBecomeFirstResponder() {
|
||||
initBarSelection();
|
||||
reloadBannerView();
|
||||
}
|
||||
HistogramView * selectedHistogramView = static_cast<HistogramView *>(m_view.dataViewAtIndex(selectedSeriesIndex()));
|
||||
selectedHistogramView->setHighlight(m_store->startOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex), m_store->endOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex));
|
||||
}
|
||||
|
||||
void HistogramController::willExitResponderChain(Responder * nextFirstResponder) {
|
||||
@@ -80,6 +77,11 @@ void HistogramController::willExitResponderChain(Responder * nextFirstResponder)
|
||||
MultipleDataViewController::willExitResponderChain(nextFirstResponder);
|
||||
}
|
||||
|
||||
void HistogramController::highlightSelection() {
|
||||
HistogramView * selectedHistogramView = static_cast<HistogramView *>(m_view.dataViewAtIndex(selectedSeriesIndex()));
|
||||
selectedHistogramView->setHighlight(m_store->startOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex), m_store->endOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex));
|
||||
}
|
||||
|
||||
Responder * HistogramController::tabController() const {
|
||||
return (parentResponder()->parentResponder()->parentResponder()->parentResponder());
|
||||
}
|
||||
|
||||
@@ -19,17 +19,18 @@ public:
|
||||
|
||||
// ViewController
|
||||
const char * title() override;
|
||||
void viewWillAppear() override;
|
||||
MultipleDataView * multipleDataView() override { return &m_view; }
|
||||
|
||||
// Responder
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
void didBecomeFirstResponder() override;
|
||||
void willExitResponderChain(Responder * nextFirstResponder) override;
|
||||
private:
|
||||
constexpr static int k_maxNumberOfBarsPerWindow = 100;
|
||||
constexpr static int k_maxIntervalLegendLength = 33;
|
||||
constexpr static int k_maxLegendLength = 13;
|
||||
constexpr static int k_maxNumberOfCharacters = 30;
|
||||
void highlightSelection() override;
|
||||
Responder * tabController() const override;
|
||||
void reloadBannerView() override;
|
||||
void initRangeParameters();
|
||||
|
||||
@@ -5,6 +5,11 @@ using namespace Shared;
|
||||
|
||||
namespace Statistics {
|
||||
|
||||
void MultipleDataView::setDisplayBanner(bool display) {
|
||||
m_displayBanner = display;
|
||||
layoutBanner();
|
||||
}
|
||||
|
||||
void MultipleDataView::reload() {
|
||||
layoutSubviews();
|
||||
for (int i = 0; i < Store::k_numberOfSeries; i++) {
|
||||
@@ -72,7 +77,7 @@ void MultipleDataView::layoutDataSubviews() {
|
||||
int numberDataSubviews = m_store->numberOfNonEmptySeries();
|
||||
assert(numberDataSubviews > 0);
|
||||
KDCoordinate bannerHeight = bannerFrame().height();
|
||||
KDCoordinate subviewHeight = (bounds().height() - bannerHeight)/numberDataSubviews;
|
||||
KDCoordinate subviewHeight = (bounds().height() - bannerHeight)/numberDataSubviews + 1; // +1 to make sure that all pixel rows are drawn
|
||||
int displayedSubviewIndex = 0;
|
||||
for (int i = 0; i < Store::k_numberOfSeries; i++) {
|
||||
if (!m_store->seriesIsEmpty(i)) {
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
virtual int seriesOfSubviewAtIndex(int index) = 0;
|
||||
|
||||
// Display
|
||||
void setDisplayBanner(bool display) { m_displayBanner = display; }
|
||||
void setDisplayBanner(bool display);
|
||||
virtual void reload();
|
||||
|
||||
// View
|
||||
|
||||
@@ -27,10 +27,8 @@ Responder * MultipleDataViewController::defaultController() {
|
||||
}
|
||||
|
||||
void MultipleDataViewController::viewWillAppear() {
|
||||
multipleDataView()->setDisplayBanner(true);
|
||||
if (*m_selectedSeriesIndex < 0) {
|
||||
if (*m_selectedSeriesIndex < 0 || m_store->sumOfOccurrences(*m_selectedSeriesIndex) == 0) {
|
||||
*m_selectedSeriesIndex = multipleDataView()->seriesOfSubviewAtIndex(0);
|
||||
multipleDataView()->selectDataView(*m_selectedSeriesIndex);
|
||||
}
|
||||
reloadBannerView();
|
||||
multipleDataView()->reload();
|
||||
@@ -45,8 +43,8 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) {
|
||||
*m_selectedSeriesIndex = multipleDataView()->seriesOfSubviewAtIndex(currentSelectedSubview+1);
|
||||
*m_selectedBarIndex = MultipleDataView::k_defaultSelectedBar;
|
||||
multipleDataView()->selectDataView(*m_selectedSeriesIndex);
|
||||
highlightSelection();
|
||||
reloadBannerView();
|
||||
app()->setFirstResponder(this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -58,7 +56,7 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) {
|
||||
*m_selectedSeriesIndex = multipleDataView()->seriesOfSubviewAtIndex(currentSelectedSubview-1);
|
||||
*m_selectedBarIndex = MultipleDataView::k_defaultSelectedBar;
|
||||
multipleDataView()->selectDataView(*m_selectedSeriesIndex);
|
||||
app()->setFirstResponder(this);
|
||||
highlightSelection();
|
||||
} else {
|
||||
app()->setFirstResponder(tabController());
|
||||
}
|
||||
@@ -73,26 +71,18 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void MultipleDataViewController::didBecomeFirstResponder() {
|
||||
void MultipleDataViewController::didEnterResponderChain(Responder * firstResponder) {
|
||||
assert(*m_selectedSeriesIndex >= 0);
|
||||
multipleDataView()->setDisplayBanner(true);
|
||||
if (*m_selectedSeriesIndex < 0 || m_store->sumOfOccurrences(*m_selectedSeriesIndex) == 0) {
|
||||
if (*m_selectedSeriesIndex >= 0) {
|
||||
multipleDataView()->deselectDataView(*m_selectedSeriesIndex);
|
||||
}
|
||||
*m_selectedSeriesIndex = multipleDataView()->seriesOfSubviewAtIndex(0);
|
||||
multipleDataView()->selectDataView(*m_selectedSeriesIndex);
|
||||
multipleDataView()->reload();
|
||||
} else {
|
||||
multipleDataView()->dataViewAtIndex(*m_selectedSeriesIndex)->selectMainView(true);
|
||||
}
|
||||
multipleDataView()->selectDataView(*m_selectedSeriesIndex);
|
||||
highlightSelection();
|
||||
}
|
||||
|
||||
void MultipleDataViewController::willExitResponderChain(Responder * nextFirstResponder) {
|
||||
if (nextFirstResponder == nullptr || nextFirstResponder == tabController()) {
|
||||
if (*m_selectedSeriesIndex >= 0) {
|
||||
multipleDataView()->dataViewAtIndex(*m_selectedSeriesIndex)->selectMainView(false);
|
||||
multipleDataView()->setDisplayBanner(false);
|
||||
}
|
||||
assert(*m_selectedSeriesIndex >= 0);
|
||||
multipleDataView()->deselectDataView(*m_selectedSeriesIndex);
|
||||
multipleDataView()->setDisplayBanner(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,9 +24,10 @@ public:
|
||||
|
||||
// Responder
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
void didBecomeFirstResponder() override;
|
||||
void didEnterResponderChain(Responder * previousFirstResponder) override;
|
||||
void willExitResponderChain(Responder * nextFirstResponder) override;
|
||||
protected:
|
||||
virtual void highlightSelection() {}
|
||||
virtual Responder * tabController() const = 0;
|
||||
virtual void reloadBannerView() = 0;
|
||||
virtual bool moveSelectionHorizontally(int deltaIndex) = 0;
|
||||
|
||||
@@ -124,7 +124,8 @@ double Store::minValueForAllSeries() const {
|
||||
|
||||
double Store::maxValue(int series) const {
|
||||
double max = -DBL_MAX;
|
||||
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {
|
||||
int numberOfPairs = numberOfPairsOfSeries(series);
|
||||
for (int k = 0; k < numberOfPairs; k++) {
|
||||
if (m_data[series][0][k] > max && m_data[series][1][k] > 0) {
|
||||
max = m_data[series][0][k];
|
||||
}
|
||||
@@ -134,7 +135,8 @@ double Store::maxValue(int series) const {
|
||||
|
||||
double Store::minValue(int series) const {
|
||||
double min = DBL_MAX;
|
||||
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {
|
||||
int numberOfPairs = numberOfPairsOfSeries(series);
|
||||
for (int k = 0; k < numberOfPairs; k++) {
|
||||
if (m_data[series][0][k] < min && m_data[series][1][k] > 0) {
|
||||
min = m_data[series][0][k];
|
||||
}
|
||||
@@ -183,7 +185,8 @@ double Store::median(int series) const {
|
||||
|
||||
double Store::sum(int series) const {
|
||||
double result = 0;
|
||||
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {
|
||||
int numberOfPairs = numberOfPairsOfSeries(series);
|
||||
for (int k = 0; k < numberOfPairs; k++) {
|
||||
result += m_data[series][0][k]*m_data[series][1][k];
|
||||
}
|
||||
return result;
|
||||
@@ -191,7 +194,8 @@ double Store::sum(int series) const {
|
||||
|
||||
double Store::squaredValueSum(int series) const {
|
||||
double result = 0;
|
||||
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {
|
||||
int numberOfPairs = numberOfPairsOfSeries(series);
|
||||
for (int k = 0; k < numberOfPairs; k++) {
|
||||
result += m_data[series][0][k]*m_data[series][0][k]*m_data[series][1][k];
|
||||
}
|
||||
return result;
|
||||
@@ -233,7 +237,8 @@ double Store::defaultValue(int series, int i, int j) const {
|
||||
|
||||
double Store::sumOfValuesBetween(int series, double x1, double x2) const {
|
||||
double result = 0;
|
||||
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {
|
||||
int numberOfPairs = numberOfPairsOfSeries(series);
|
||||
for (int k = 0; k < numberOfPairs; k++) {
|
||||
if (m_data[series][0][k] < x2 && x1 <= m_data[series][0][k]) {
|
||||
result += m_data[series][1][k];
|
||||
}
|
||||
@@ -246,22 +251,31 @@ double Store::sortedElementAtCumulatedFrequency(int series, double k, bool creat
|
||||
assert(k >= 0.0 && k <= 1.0);
|
||||
double totalNumberOfElements = sumOfOccurrences(series);
|
||||
double numberOfElementsAtFrequencyK = totalNumberOfElements * k;
|
||||
|
||||
double bufferValues[numberOfPairsOfSeries(series)];
|
||||
memcpy(bufferValues, m_data[series][0], numberOfPairsOfSeries(series)*sizeof(double));
|
||||
int numberOfPairs = numberOfPairsOfSeries(series);
|
||||
double bufferValues[numberOfPairs];
|
||||
memcpy(bufferValues, m_data[series][0], numberOfPairs*sizeof(double));
|
||||
int sortedElementIndex = 0;
|
||||
double cumulatedNumberOfElements = 0.0;
|
||||
while (cumulatedNumberOfElements < numberOfElementsAtFrequencyK-DBL_EPSILON) {
|
||||
sortedElementIndex = minIndex(bufferValues, numberOfPairsOfSeries(series));
|
||||
sortedElementIndex = minIndex(bufferValues, numberOfPairs);
|
||||
bufferValues[sortedElementIndex] = DBL_MAX;
|
||||
cumulatedNumberOfElements += m_data[series][1][sortedElementIndex];
|
||||
}
|
||||
|
||||
if (createMiddleElement && std::fabs(cumulatedNumberOfElements - numberOfElementsAtFrequencyK) < DBL_EPSILON) {
|
||||
int nextElementIndex = minIndex(bufferValues, numberOfPairsOfSeries(series));
|
||||
/* There is an element of cumulated frequency k, so the result is the mean
|
||||
* between this element and the next element (in terms of cumulated
|
||||
* frequency) that has a non-null frequency. */
|
||||
int nextElementIndex = minIndex(bufferValues, numberOfPairs);
|
||||
while (m_data[series][1][nextElementIndex] == 0 && bufferValues[nextElementIndex] != DBL_MAX) {
|
||||
bufferValues[nextElementIndex] = DBL_MAX;
|
||||
nextElementIndex = minIndex(bufferValues, numberOfPairs);
|
||||
}
|
||||
if (bufferValues[nextElementIndex] != DBL_MAX) {
|
||||
return (m_data[series][0][sortedElementIndex] + m_data[series][0][nextElementIndex]) / 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
return m_data[series][0][sortedElementIndex];
|
||||
}
|
||||
|
||||
|
||||
@@ -41,14 +41,17 @@ void assert_data_statictics_equal_to(double n[], double v[], int numberOfData, d
|
||||
}
|
||||
|
||||
QUIZ_CASE(data_statistics) {
|
||||
|
||||
/* 1 2 3 4
|
||||
* 1 1 1 1 */
|
||||
double n1[4] = {1.0, 2.0, 3.0, 4.0};
|
||||
double v1[4] = {1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
constexpr int listLength1 = 4;
|
||||
double n1[listLength1] = {1.0, 2.0, 3.0, 4.0};
|
||||
double v1[listLength1] = {1.0, 1.0, 1.0, 1.0};
|
||||
assert_data_statictics_equal_to(
|
||||
n1,
|
||||
v1,
|
||||
4,
|
||||
listLength1,
|
||||
/* sumOfOccurrences */ 4.0,
|
||||
/* maxValue */ 4.0,
|
||||
/* minValue */ 1.0,
|
||||
@@ -68,12 +71,13 @@ QUIZ_CASE(data_statistics) {
|
||||
/* 1 2 3 4 5 6 7 8 9 10 11
|
||||
* 1 1 1 1 1 1 1 1 1 1 1 */
|
||||
|
||||
double n2[11] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0};
|
||||
double v2[11] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
|
||||
constexpr int listLength2 = 11;
|
||||
double n2[listLength2] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0};
|
||||
double v2[listLength2] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
|
||||
assert_data_statictics_equal_to(
|
||||
n2,
|
||||
v2,
|
||||
11,
|
||||
listLength2,
|
||||
/* sumOfOccurrences */ 11.0,
|
||||
/* maxValue */ 11.0,
|
||||
/* minValue */ 1.0,
|
||||
@@ -92,12 +96,13 @@ QUIZ_CASE(data_statistics) {
|
||||
/* 1 2 3 4 5 6 7 8 9 10 11 12
|
||||
* 1 1 1 1 1 1 1 1 1 1 1 1 */
|
||||
|
||||
double n3[12] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0};
|
||||
double v3[12] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
|
||||
constexpr int listLength3 = 13;
|
||||
double n3[listLength3] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0};
|
||||
double v3[listLength3] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
|
||||
assert_data_statictics_equal_to(
|
||||
n3,
|
||||
v3,
|
||||
12,
|
||||
listLength3,
|
||||
/* sumOfOccurrences */ 12.0,
|
||||
/* maxValue */ 12.0,
|
||||
/* minValue */ 1.0,
|
||||
@@ -115,12 +120,14 @@ QUIZ_CASE(data_statistics) {
|
||||
|
||||
/* 1 2 3 5 10
|
||||
* 0.2 0.05 0.3 0.0001 0.4499 */
|
||||
double n4[5] = {1.0, 2.0, 3.0, 5.0, 10.0};
|
||||
double v4[5] = {0.2, 0.05, 0.3, 0.0001, 0.4499};
|
||||
|
||||
constexpr int listLength4 = 5;
|
||||
double n4[listLength4] = {1.0, 2.0, 3.0, 5.0, 10.0};
|
||||
double v4[listLength4] = {0.2, 0.05, 0.3, 0.0001, 0.4499};
|
||||
assert_data_statictics_equal_to(
|
||||
n4,
|
||||
v4,
|
||||
5,
|
||||
listLength4,
|
||||
/* sumOfOccurrences */ 1.0,
|
||||
/* maxValue */ 10.0,
|
||||
/* minValue */ 1.0,
|
||||
@@ -138,12 +145,14 @@ QUIZ_CASE(data_statistics) {
|
||||
|
||||
/* 1 -2 3 5 10
|
||||
* 0.4 0.00005 0.9 0.4 0.5 */
|
||||
double n5[5] = {1.0, -2.0, 3.0, 5.0, 10.0};
|
||||
double v5[5] = {0.4, 0.00005, 0.9, 0.4, 0.5};
|
||||
|
||||
constexpr int listLength5 = 5;
|
||||
double n5[listLength5] = {1.0, -2.0, 3.0, 5.0, 10.0};
|
||||
double v5[listLength5] = {0.4, 0.00005, 0.9, 0.4, 0.5};
|
||||
assert_data_statictics_equal_to(
|
||||
n5,
|
||||
v5,
|
||||
5,
|
||||
listLength5,
|
||||
/* sumOfOccurrences */ 2.2,
|
||||
/* maxValue */ 10.0,
|
||||
/* minValue */ -2.0,
|
||||
@@ -161,12 +170,14 @@ QUIZ_CASE(data_statistics) {
|
||||
|
||||
/* -7 -10 12 5 -2
|
||||
* 4 5 3 1 9 */
|
||||
double n6[6] = {-7.0, -10.0, 1.0, 2.0, 5.0, -2.0};
|
||||
double v6[6] = {4.0, 5.0, 3.0, 0.5, 1.0, 9.0};
|
||||
|
||||
constexpr int listLength6 = 6;
|
||||
double n6[listLength6] = {-7.0, -10.0, 1.0, 2.0, 5.0, -2.0};
|
||||
double v6[listLength6] = {4.0, 5.0, 3.0, 0.5, 1.0, 9.0};
|
||||
assert_data_statictics_equal_to(
|
||||
n6,
|
||||
v6,
|
||||
6,
|
||||
listLength6,
|
||||
/* sumOfOccurrences */ 22.5,
|
||||
/* maxValue */ 5.0,
|
||||
/* minValue */ -10.0,
|
||||
@@ -184,12 +195,14 @@ QUIZ_CASE(data_statistics) {
|
||||
|
||||
/* 1 1 1 10 3 -1 3
|
||||
* 1 1 1 0 0 0 1 */
|
||||
double n7[7] = {1.0, 1.0, 1.0, 10.0, 3.0, -1.0, 3.0};
|
||||
double v7[7] = {1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0};
|
||||
|
||||
constexpr int listLength7 = 7;
|
||||
double n7[listLength7] = {1.0, 1.0, 1.0, 10.0, 3.0, -1.0, 3.0};
|
||||
double v7[listLength7] = {1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0};
|
||||
assert_data_statictics_equal_to(
|
||||
n7,
|
||||
v7,
|
||||
7,
|
||||
listLength7,
|
||||
/* sumOfOccurrences */ 4.0,
|
||||
/* maxValue */ 3.0,
|
||||
/* minValue */ 1.0,
|
||||
@@ -205,6 +218,30 @@ QUIZ_CASE(data_statistics) {
|
||||
/* sum */ 6.0,
|
||||
/* squaredValueSum */ 12.0);
|
||||
|
||||
/* 1 2 3 4
|
||||
* 0 1 0 1 */
|
||||
|
||||
constexpr int listLength8 = 4;
|
||||
double n8[listLength8] = {1.0, 2.0, 3.0, 4.0};
|
||||
double v8[listLength8] = {0.0, 1.0, 0.0, 1.0};
|
||||
assert_data_statictics_equal_to(
|
||||
n8,
|
||||
v8,
|
||||
listLength8,
|
||||
/* sumOfOccurrences */ 2.0,
|
||||
/* maxValue */ 4.0,
|
||||
/* minValue */ 2.0,
|
||||
/* range */ 2.0,
|
||||
/* mean */ 3.0,
|
||||
/* variance */ 1.0,
|
||||
/* standardDeviation */ 1.0,
|
||||
/* sampleStandardDeviation */ 1.414,
|
||||
/* firstQuartile */ 2.0,
|
||||
/* thirdQuartile */ 4.0,
|
||||
/* quartileRange */ 2.0,
|
||||
/* median */ 3.0,
|
||||
/* sum */ 6.0,
|
||||
/* squaredValueSum */ 20.0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ libepsilon_%.o: $(libepsilon_objs) $(app_objs) $(app_image_objs) ion/src/blackbo
|
||||
@echo "LD $@"
|
||||
$(Q) $(LD) $^ $(LDFLAGS) -r -s -o $@
|
||||
|
||||
compare: ion/src/blackbox/compare.o libepsilon_first.o libepsilon_second.o
|
||||
compare: ion/src/blackbox/compare.o
|
||||
@echo "LD $@"
|
||||
$(Q) $(LD) $^ $(LDFLAGS) -L. -o $@
|
||||
$(Q) $(LD) $^ libepsilon_first.o libepsilon_second.o $(LDFLAGS) -L. -o $@
|
||||
|
||||
# Integration tests
|
||||
|
||||
@@ -48,3 +48,13 @@ else
|
||||
epsilon_fuzz:
|
||||
@echo "Fuzzing requires TOOLCHAIN=afl"
|
||||
endif
|
||||
|
||||
.PHONY: compare_fuzz
|
||||
ifeq ($(TOOLCHAIN),afl)
|
||||
compare_fuzz: compare
|
||||
@echo "FUZZ $<"
|
||||
@afl-fuzz -t 3000 -i tests -o afl ./compare
|
||||
else
|
||||
compare_fuzz:
|
||||
@echo "Fuzzing requires TOOLCHAIN=afl"
|
||||
endif
|
||||
|
||||
@@ -5,8 +5,9 @@ LD = emcc
|
||||
EMSCRIPTEN_ASYNC_SYMBOLS = \
|
||||
SAFE_HEAP_LOAD \
|
||||
SAFE_HEAP_STORE \
|
||||
_IonEventsEmscriptenKeyDown \
|
||||
_IonEventsEmscriptenKeyUp \
|
||||
_IonEventsEmscriptenPushEvent \
|
||||
_IonEventsEmscriptenPushKey \
|
||||
__Z8ion_mainiPPc \
|
||||
__ZN10Invocation7performEPv \
|
||||
__ZN11MicroPython20ExecutionEnvironment7runCodeEPKc \
|
||||
@@ -27,6 +28,7 @@ __ZN3Ion6Events5EventC2Ei \
|
||||
__ZN3Ion6Events5EventC2ENS_8Keyboard3KeyEbb \
|
||||
__ZN3Ion6Events8getEventEPi \
|
||||
__ZN3Ion6EventsL16sleepWithTimeoutEiPi \
|
||||
__ZN3Ion8Keyboard4scanEv \
|
||||
__ZN4Code14MenuController21openConsoleWithScriptENS_6ScriptE \
|
||||
__ZN4Code14MenuController23didBecomeFirstResponderEv \
|
||||
__ZN4Code14MenuController28openConsoleWithScriptAtIndexEi \
|
||||
@@ -50,7 +52,7 @@ __ZN9Container3runEv \
|
||||
__ZN9Container8switchToEPN3App8SnapshotE \
|
||||
__ZN9TextField11handleEventEN3Ion6Events5EventE \
|
||||
__ZN9TextField18privateHandleEventEN3Ion6Events5EventE \
|
||||
__ZThn28_N4Code17ConsoleController25textFieldDidFinishEditingEP9TextFieldPKcN3Ion6Events5EventE \
|
||||
__ZThn32_N4Code17ConsoleController25textFieldDidFinishEditingEP9TextFieldPKcN3Ion6Events5EventE \
|
||||
__ZThn28_N6Button11handleEventEN3Ion6Events5EventE \
|
||||
__ZThn32_N4Code17ConsoleController9inputTextEPKc \
|
||||
__ZThn36_N4Code17ConsoleController9inputTextEPKc \
|
||||
@@ -61,6 +63,9 @@ _do_load_from_lexer \
|
||||
_fun_bc_call \
|
||||
_fun_builtin_var_call \
|
||||
_main \
|
||||
_micropython_port_interruptible_msleep \
|
||||
_micropython_port_should_interrupt \
|
||||
_micropython_port_vm_hook_loop \
|
||||
_mp_builtin___import__ \
|
||||
_mp_builtin_input \
|
||||
_mp_call_function_0 \
|
||||
@@ -68,7 +73,8 @@ _mp_call_function_n_kw \
|
||||
_mp_execute_bytecode \
|
||||
_mp_hal_input \
|
||||
_mp_import_name \
|
||||
_mp_parse_compile_execute
|
||||
_mp_parse_compile_execute \
|
||||
_msleep
|
||||
|
||||
EMTERPRETIFY_WHITELIST = $(foreach sym,$(EMSCRIPTEN_ASYNC_SYMBOLS),"$(sym)",)END
|
||||
EMFLAGS = -s PRECISE_F32=1 -s EMTERPRETIFY=1 -s EMTERPRETIFY_ASYNC=1 -s EMTERPRETIFY_WHITELIST='[$(EMTERPRETIFY_WHITELIST:,END=)]'
|
||||
@@ -82,4 +88,4 @@ endif
|
||||
EMFLAGS += -s MODULARIZE=1 -s 'EXPORT_NAME="Epsilon"'
|
||||
|
||||
SFLAGS += $(EMFLAGS)
|
||||
LDFLAGS += $(EMFLAGS) -Oz -s EXPORTED_FUNCTIONS='["_main", "_IonEventsEmscriptenPushKey", "_IonEventsEmscriptenPushEvent", "_IonSoftwareVersion", "_IonPatchLevel"]'
|
||||
LDFLAGS += $(EMFLAGS) -Oz -s EXPORTED_FUNCTIONS='["_main", "_IonEventsEmscriptenKeyDown", "_IonEventsEmscriptenKeyUp", "_IonEventsEmscriptenPushEvent", "_IonSoftwareVersion", "_IonPatchLevel"]'
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <escher/container.h>
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
Container::Container() :
|
||||
RunLoop(),
|
||||
|
||||
@@ -58,6 +58,9 @@ bool WarningController::handleEvent(Ion::Events::Event event) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (event == Ion::Events::USBPlug || event == Ion::Events::USBEnumeration) {
|
||||
return false;
|
||||
}
|
||||
app()->dismissModalViewController();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <ion/led.h>
|
||||
#include <ion/power.h>
|
||||
#include <ion/storage.h>
|
||||
#include <ion/timing.h>
|
||||
#include <ion/usb.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
@@ -25,9 +26,6 @@ void ion_main(int argc, char * argv[]);
|
||||
|
||||
namespace Ion {
|
||||
|
||||
void msleep(long ms);
|
||||
void usleep(long us);
|
||||
|
||||
const char * serialNumber();
|
||||
const char * softwareVersion();
|
||||
const char * patchLevel();
|
||||
|
||||
@@ -49,6 +49,12 @@ public:
|
||||
return (m_bitField>>(uint8_t)k) & 1;
|
||||
}
|
||||
operator uint64_t() const { return m_bitField; }
|
||||
void setKey(Key k) {
|
||||
m_bitField |= (uint64_t)1 << (uint8_t)k;
|
||||
}
|
||||
void clearKey(Key k) {
|
||||
m_bitField &= ~((uint64_t)1 << (uint8_t)k);
|
||||
}
|
||||
private:
|
||||
uint64_t m_bitField;
|
||||
};
|
||||
|
||||
19
ion/include/ion/timing.h
Normal file
19
ion/include/ion/timing.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef ION_TIMING_H
|
||||
#define ION_TIMING_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Timing {
|
||||
|
||||
void usleep(uint32_t us);
|
||||
void msleep(uint32_t ms);
|
||||
|
||||
/* millis is the number of milliseconds ellapsed since a random epoch.
|
||||
* On the device, epoch is the boot time. */
|
||||
uint64_t millis();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -12,6 +12,7 @@ objs += $(addprefix ion/src/shared/, \
|
||||
events.o \
|
||||
power.o \
|
||||
random.o \
|
||||
timing.o \
|
||||
dummy/backlight.o \
|
||||
dummy/battery.o \
|
||||
dummy/events_modifier.o \
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
/* Compare two Epsilon versions
|
||||
*
|
||||
* git checkout first_hash
|
||||
* make -j8 PLATFORM=blackbox clean libepsilon_first.dylib
|
||||
* git checkout second_hash
|
||||
* make -j8 PLATFORM=blackbox clean libepsilon_second.dylib
|
||||
* make PLATFORM=blackbox compare
|
||||
* This tool compares the frames step-by-step of scenarios played on two
|
||||
* different epsilon versions, and shows the first frame where pixels differ
|
||||
* between the versions.
|
||||
*
|
||||
* To use it, first create the two epsilon versions to compare, in a library
|
||||
* format:
|
||||
* git checkout first_hash
|
||||
* make -j8 PLATFORM=blackbox clean libepsilon_first.o
|
||||
* git checkout second_hash
|
||||
* make -j8 PLATFORM=blackbox clean libepsilon_second.o
|
||||
*
|
||||
* To compare the versions on a given scenario:
|
||||
* make -j8 PLATFORM=blackbox compare
|
||||
* ./compare path/to/scenario
|
||||
* To fuzz over scenarios that are in a folder named "tests":
|
||||
* make -j8 PLATFORM=blackbox TOOLCHAIN=afl compare_fuzz
|
||||
*/
|
||||
|
||||
#undef EPSILON_LIB_PREFIX
|
||||
@@ -49,4 +60,5 @@ int main(int argc, char * argv[]) {
|
||||
|
||||
first.join();
|
||||
second.join();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <stdint.h>
|
||||
#include <ion.h>
|
||||
|
||||
void Ion::msleep(long ms) {
|
||||
void Ion::Timing::msleep(uint32_t ms) {
|
||||
}
|
||||
|
||||
void Ion::Timing::usleep(uint32_t us) {
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ objs += $(addprefix ion/src/device/, \
|
||||
sd_card.o\
|
||||
stack.o\
|
||||
swd.o \
|
||||
timing.o \
|
||||
usb.o \
|
||||
wakeup.o \
|
||||
)
|
||||
|
||||
@@ -45,12 +45,12 @@ void shutdown() {
|
||||
|
||||
void suspend() {
|
||||
GPIOC.ODR()->set(6, false);
|
||||
msleep(3); // Might not need to be blocking
|
||||
Timing::msleep(3); // Might not need to be blocking
|
||||
}
|
||||
|
||||
void resume() {
|
||||
GPIOC.ODR()->set(6, true);
|
||||
usleep(50);
|
||||
Timing::usleep(50);
|
||||
uint8_t level = sLevel;
|
||||
sLevel = 0xF;
|
||||
setLevel(level);
|
||||
@@ -74,9 +74,9 @@ uint8_t level() {
|
||||
void sendPulses(int n) {
|
||||
for (int i=0; i<n; i++) {
|
||||
GPIOC.ODR()->set(6, false);
|
||||
usleep(20);
|
||||
Timing::usleep(20);
|
||||
GPIOC.ODR()->set(6, true);
|
||||
usleep(20);
|
||||
Timing::usleep(20);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ void Suspend(const char * input) {
|
||||
return;
|
||||
}
|
||||
reply(sOK);
|
||||
Ion::msleep(100);
|
||||
Ion::Timing::msleep(100);
|
||||
Ion::Power::suspend();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "rt0.h"
|
||||
#include "isr.h"
|
||||
extern const void * _stack_start;
|
||||
|
||||
/* Interrupt Service Routines are void->void functions */
|
||||
@@ -28,7 +28,7 @@ ISR InitialisationVector[INITIALISATION_VECTOR_SIZE]
|
||||
0, // DebugMonitor service routine,
|
||||
0, // Reserved
|
||||
0, // PendSV service routine,
|
||||
0, // SysTick service routine
|
||||
isr_systick, // SysTick service routine
|
||||
0, // WWDG service routine
|
||||
0, // PVD service routine
|
||||
0, // TampStamp service routine
|
||||
|
||||
16
ion/src/device/boot/isr.h
Normal file
16
ion/src/device/boot/isr.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef ION_DEVICE_BOOT_ISR_H
|
||||
#define ION_DEVICE_BOOT_ISR_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void start();
|
||||
void abort();
|
||||
void isr_systick();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,10 +1,9 @@
|
||||
extern "C" {
|
||||
#include "rt0.h"
|
||||
}
|
||||
#include "isr.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ion.h>
|
||||
#include "../device.h"
|
||||
#include "../timing.h"
|
||||
#include "../console.h"
|
||||
|
||||
typedef void (*cxx_constructor)();
|
||||
@@ -92,3 +91,7 @@ void start() {
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
void __attribute__((interrupt)) isr_systick() {
|
||||
Ion::Timing::Device::MillisElapsed++;
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#ifndef ION_DEVICE_BOOT_RT0_H
|
||||
#define ION_DEVICE_BOOT_RT0_H
|
||||
|
||||
void start();
|
||||
void abort();
|
||||
|
||||
#endif
|
||||
@@ -63,7 +63,7 @@ void shutdown() {
|
||||
bool peerConnected() {
|
||||
RxPin.group().PUPDR()->setPull(RxPin.pin(), GPIO::PUPDR::Pull::Down);
|
||||
RxPin.group().MODER()->setMode(RxPin.pin(), GPIO::MODER::Mode::Input);
|
||||
msleep(1);
|
||||
Timing::msleep(1);
|
||||
bool result = RxPin.group().IDR()->get(RxPin.pin());
|
||||
RxPin.group().PUPDR()->setPull(RxPin.pin(), GPIO::PUPDR::Pull::None);
|
||||
RxPin.group().MODER()->setMode(RxPin.pin(), GPIO::MODER::Mode::AlternateFunction);
|
||||
|
||||
@@ -24,21 +24,6 @@ extern "C" {
|
||||
|
||||
// Public Ion methods
|
||||
|
||||
/* TODO: The delay methods 'msleep' and 'usleep' are currently dependent on the
|
||||
* optimizations chosen by the compiler. To prevent that and to gain in
|
||||
* precision, we could use the controller cycle counter (Systick). */
|
||||
|
||||
void Ion::msleep(long ms) {
|
||||
for (volatile long i=0; i<8852*ms; i++) {
|
||||
__asm volatile("nop");
|
||||
}
|
||||
}
|
||||
void Ion::usleep(long us) {
|
||||
for (volatile long i=0; i<9*us; i++) {
|
||||
__asm volatile("nop");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Ion::crc32(const uint32_t * data, size_t length) {
|
||||
bool initialCRCEngineState = RCC.AHB1ENR()->getCRCEN();
|
||||
RCC.AHB1ENR()->setCRCEN(true);
|
||||
@@ -113,6 +98,23 @@ void initMPU() {
|
||||
MPU.CTRL()->setENABLE(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
void initSysTick() {
|
||||
// CPU clock is 96 MHz, and systick clock source is divided by 8
|
||||
// To get 1 ms systick overflow we need to reset it to
|
||||
// 96 000 000 (Hz) / 8 / 1 000 (ms/s) - 1 (because the counter resets *after* counting to 0)
|
||||
CM4.SYST_RVR()->setRELOAD(11999);
|
||||
CM4.SYST_CVR()->setCURRENT(0);
|
||||
CM4.SYST_CSR()->setCLKSOURCE(CM4::SYST_CSR::CLKSOURCE::AHB_DIV8);
|
||||
CM4.SYST_CSR()->setTICKINT(true);
|
||||
CM4.SYST_CSR()->setENABLE(true);
|
||||
}
|
||||
|
||||
void shutdownSysTick() {
|
||||
CM4.SYST_CSR()->setENABLE(false);
|
||||
CM4.SYST_CSR()->setTICKINT(false);
|
||||
}
|
||||
|
||||
void coreReset() {
|
||||
// Perform a full core reset
|
||||
CM4.AIRCR()->requestReset();
|
||||
@@ -184,9 +186,11 @@ void initPeripherals() {
|
||||
#endif
|
||||
Console::Device::init();
|
||||
SWD::Device::init();
|
||||
initSysTick();
|
||||
}
|
||||
|
||||
void shutdownPeripherals(bool keepLEDAwake) {
|
||||
shutdownSysTick();
|
||||
SWD::Device::shutdown();
|
||||
Console::Device::shutdown();
|
||||
#if USE_SD_CARD
|
||||
|
||||
@@ -11,6 +11,10 @@ void initFPU();
|
||||
#if 0
|
||||
void initMPU();
|
||||
#endif
|
||||
|
||||
void initSysTick();
|
||||
void shutdownSysTick();
|
||||
|
||||
void coreReset();
|
||||
void jumpReset();
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ void initGPIO() {
|
||||
TearingEffectPin.group().MODER()->setMode(TearingEffectPin.pin(), GPIO::MODER::Mode::Input);
|
||||
TearingEffectPin.group().PUPDR()->setPull(TearingEffectPin.pin(), GPIO::PUPDR::Pull::None);
|
||||
|
||||
msleep(120);
|
||||
Timing::msleep(120);
|
||||
}
|
||||
|
||||
|
||||
@@ -247,10 +247,10 @@ void shutdownFSMC() {
|
||||
|
||||
void initPanel() {
|
||||
send_command(Command::Reset);
|
||||
msleep(5);
|
||||
Timing::msleep(5);
|
||||
|
||||
send_command(Command::SleepOut);
|
||||
msleep(5);
|
||||
Timing::msleep(5);
|
||||
|
||||
send_command(Command::PixelFormatSet, 0x05);
|
||||
send_command(Command::TearingEffectLineOn, 0x00);
|
||||
@@ -262,7 +262,7 @@ void initPanel() {
|
||||
void shutdownPanel() {
|
||||
send_command(Command::DisplayOff);
|
||||
send_command(Command::SleepIn);
|
||||
msleep(5);
|
||||
Timing::msleep(5);
|
||||
}
|
||||
|
||||
void setDrawingArea(KDRect r, Orientation o) {
|
||||
|
||||
@@ -6,11 +6,11 @@ namespace Events {
|
||||
|
||||
static bool sleepWithTimeout(int duration, int * timeout) {
|
||||
if (*timeout >= duration) {
|
||||
msleep(duration);
|
||||
Timing::msleep(duration);
|
||||
*timeout -= duration;
|
||||
return false;
|
||||
} else {
|
||||
msleep(*timeout);
|
||||
Timing::msleep(*timeout);
|
||||
*timeout = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ inline void activateRow(uint8_t row) {
|
||||
Device::RowGPIO.ODR()->setBitRange(9, 0, rowState);
|
||||
|
||||
// TODO: 100 us seems to work, but wasn't really calculated
|
||||
usleep(100);
|
||||
Timing::usleep(100);
|
||||
}
|
||||
|
||||
inline bool columnIsActive(uint8_t column) {
|
||||
|
||||
@@ -40,14 +40,39 @@ public:
|
||||
REGS_BOOL_FIELD(SLEEPDEEP, 2);
|
||||
};
|
||||
|
||||
class SYST_CSR : public Register32 {
|
||||
public:
|
||||
enum class CLKSOURCE : uint8_t {
|
||||
AHB_DIV8 = 0,
|
||||
AHB = 1
|
||||
};
|
||||
REGS_BOOL_FIELD(COUNTFLAG, 16);
|
||||
REGS_TYPE_FIELD(CLKSOURCE, 2, 2);
|
||||
REGS_BOOL_FIELD(TICKINT, 1);
|
||||
REGS_BOOL_FIELD(ENABLE, 0);
|
||||
};
|
||||
|
||||
class SYST_RVR : public Register32 {
|
||||
public:
|
||||
REGS_FIELD(RELOAD, uint32_t, 23, 0);
|
||||
};
|
||||
|
||||
class SYST_CVR : public Register32 {
|
||||
public:
|
||||
REGS_FIELD(CURRENT, uint32_t, 23, 0);
|
||||
};
|
||||
|
||||
constexpr CM4() {};
|
||||
REGS_REGISTER_AT(VTOR, 0x08);
|
||||
REGS_REGISTER_AT(AIRCR, 0x0C);
|
||||
REGS_REGISTER_AT(SCR, 0x10);
|
||||
REGS_REGISTER_AT(CPACR, 0x88);
|
||||
REGS_REGISTER_AT(SYST_CSR, 0x10);
|
||||
REGS_REGISTER_AT(SYST_RVR, 0x14);
|
||||
REGS_REGISTER_AT(SYST_CVR, 0x18);
|
||||
REGS_REGISTER_AT(VTOR, 0xD08);
|
||||
REGS_REGISTER_AT(AIRCR, 0xD0C);
|
||||
REGS_REGISTER_AT(SCR, 0xD10);
|
||||
REGS_REGISTER_AT(CPACR, 0xD88);
|
||||
private:
|
||||
constexpr uint32_t Base() const {
|
||||
return 0xE000ED00;
|
||||
return 0xE000E000;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ typedef Register<uint32_t> Register32;
|
||||
typedef Register<uint64_t> 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) <= 2, "Invalid size"); setBitRange(high, low, static_cast<uint16_t>(v)); };
|
||||
#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<uint32_t>(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)
|
||||
|
||||
36
ion/src/device/timing.cpp
Normal file
36
ion/src/device/timing.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "timing.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Timing {
|
||||
|
||||
/* TODO: The delay methods 'msleep' and 'usleep' are currently dependent on the
|
||||
* optimizations chosen by the compiler. To prevent that and to gain in
|
||||
* precision, we could use the controller cycle counter (Systick). */
|
||||
|
||||
void msleep(uint32_t ms) {
|
||||
for (volatile uint32_t i=0; i<8852*ms; i++) {
|
||||
__asm volatile("nop");
|
||||
}
|
||||
}
|
||||
void usleep(uint32_t us) {
|
||||
for (volatile uint32_t i=0; i<9*us; i++) {
|
||||
__asm volatile("nop");
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t millis() {
|
||||
return Ion::Timing::Device::MillisElapsed;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace Ion {
|
||||
namespace Timing {
|
||||
namespace Device {
|
||||
|
||||
volatile uint64_t MillisElapsed = 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
16
ion/src/device/timing.h
Normal file
16
ion/src/device/timing.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef ION_DEVICE_TIMING_H
|
||||
#define ION_DEVICE_TIMING_H
|
||||
|
||||
#include <ion/timing.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Timing {
|
||||
namespace Device {
|
||||
|
||||
extern volatile uint64_t MillisElapsed;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -50,6 +50,7 @@ dfu_objs += ion/src/device/device.o
|
||||
dfu_objs += ion/src/device/usb.o
|
||||
dfu_objs += ion/src/device/base64.o
|
||||
dfu_objs += ion/src/device/flash.o
|
||||
dfu_objs += ion/src/device/timing.o
|
||||
|
||||
ion/src/device/usb/dfu.elf: LDSCRIPT = ion/src/device/usb/dfu.ld
|
||||
ion/src/device/usb/dfu.elf: $(usb_objs) $(dfu_objs)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <ion/usb.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <ion/src/device/device.h>
|
||||
|
||||
extern char _stack_end;
|
||||
extern char _dfu_bootloader_flash_start;
|
||||
@@ -47,7 +48,12 @@ void DFU() {
|
||||
|
||||
memcpy(dfu_bootloader_ram_start, &_dfu_bootloader_flash_start, dfu_bootloader_size);
|
||||
|
||||
/* 4- Jump to DFU bootloader code. We made sure in the linker script that the
|
||||
/* 4- Disable all interrupts
|
||||
* The interrupt service routines live in the Flash and could be overwritten
|
||||
* by garbage during a firmware upgrade opration, so we disable them. */
|
||||
Device::shutdownSysTick();
|
||||
|
||||
/* 5- Jump to DFU bootloader code. We made sure in the linker script that the
|
||||
* first function we want to call is at the beginning of the DFU code. */
|
||||
|
||||
PollFunctionPointer dfu_bootloader_entry = reinterpret_cast<PollFunctionPointer>(dfu_bootloader_ram_start);
|
||||
@@ -63,7 +69,10 @@ void DFU() {
|
||||
|
||||
dfu_bootloader_entry(true);
|
||||
|
||||
/* 5- That's all. The DFU bootloader on the stack is now dead code that will
|
||||
/* 5- Restore interrupts */
|
||||
Device::initSysTick();
|
||||
|
||||
/* 6- That's all. The DFU bootloader on the stack is now dead code that will
|
||||
* be overwritten when the stack grows. */
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ objs += $(addprefix ion/src/shared/, \
|
||||
events_modifier.o \
|
||||
power.o \
|
||||
random.o \
|
||||
timing.o \
|
||||
dummy/backlight.o \
|
||||
dummy/battery.o \
|
||||
dummy/fcc_id.o \
|
||||
|
||||
@@ -52,33 +52,45 @@ private:
|
||||
|
||||
static Queue<Ion::Events::Event, 1024> sEventQueue;
|
||||
|
||||
void IonEventsEmscriptenPushKey(int keyNumber) {
|
||||
Ion::Keyboard::State sKeyboardState;
|
||||
|
||||
void IonEventsEmscriptenKeyDown(int keyNumber) {
|
||||
Ion::Keyboard::Key key = static_cast<Ion::Keyboard::Key>(keyNumber);
|
||||
sKeyboardState.setKey(key);
|
||||
/* Note: This uses the *current* modifier state to generate the event. If some
|
||||
* other modifiers were in the queue before, those won't be taken into account
|
||||
* when the event corresponding to this key is dequeued.
|
||||
* In practice, this should not happen because we push keys one by one. */
|
||||
Ion::Events::Event event = Ion::Events::Event((Ion::Keyboard::Key)keyNumber, Ion::Events::isShiftActive(), Ion::Events::isAlphaActive());
|
||||
Ion::Events::Event event = Ion::Events::Event(key, Ion::Events::isShiftActive(), Ion::Events::isAlphaActive());
|
||||
sEventQueue.enqueue(event);
|
||||
}
|
||||
|
||||
void IonEventsEmscriptenKeyUp(int keyNumber) {
|
||||
Ion::Keyboard::Key key = static_cast<Ion::Keyboard::Key>(keyNumber);
|
||||
sKeyboardState.clearKey(key);
|
||||
}
|
||||
|
||||
void IonEventsEmscriptenPushEvent(int eventNumber) {
|
||||
sEventQueue.enqueue(Ion::Events::Event(eventNumber));
|
||||
}
|
||||
|
||||
Ion::Keyboard::State Ion::Keyboard::scan() {
|
||||
// FIXME
|
||||
// On the Emscripten platform, scan() is used in :
|
||||
// - shouldInterrupt(), from interruptHelper.h
|
||||
// - apps_container.cpp
|
||||
// - apps/hardware_test/keyboard_test_controller.cpp
|
||||
// We would like to check if there is a Back event that would interrupt the
|
||||
// Python or Poincare computation, but it is quite difficult to get because
|
||||
// the runLoop is blocking in JavaScript and Events do not get pushed in
|
||||
// sEvent.
|
||||
// We still need to override the dummy/events_keyboard.cpp function, which
|
||||
// returns that all keys are always pressed and thus interrupts Python
|
||||
// computations after 20000 calls.
|
||||
return 0;
|
||||
/* The following call to emscripten_sleep gives the JS VM a chance to do a run
|
||||
* loop iteration. This in turns gives the browser an opportunity to call the
|
||||
* IonEventsEmscriptenPushKey function, therefore modifying the sKeyboardState
|
||||
* global variable before it is returned by this Ion::Keyboard::scan.
|
||||
* On Emterpreter-async, emscripten_sleep is actually a wrapper around the JS
|
||||
* function setTimeout, which can be called with a value of zero. Doing so
|
||||
* puts the callback at the end of the queue of callbacks to be processed. */
|
||||
emscripten_sleep(0);
|
||||
|
||||
/* Grab this opporunity to refresh the display. In practice, this routine is
|
||||
* called from micropython_port_vm_hook_loop once in a while, so this gives us
|
||||
* an opportunity to refresh the display during the execution of a
|
||||
* long-running Python script. */
|
||||
Ion::Display::Emscripten::refresh();
|
||||
|
||||
return sKeyboardState;
|
||||
}
|
||||
|
||||
namespace Ion {
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
#include <ion/events.h>
|
||||
|
||||
extern "C" {
|
||||
void IonEventsEmscriptenPushKey(int keyNumber);
|
||||
void IonEventsEmscriptenKeyUp(int keyNumber);
|
||||
void IonEventsEmscriptenKeyDown(int keyNumber);
|
||||
void IonEventsEmscriptenPushEvent(int eventNumber);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "display.h"
|
||||
#include "events_keyboard.h"
|
||||
#include "../../../apps/global_preferences.h"
|
||||
#include <emscripten.h>
|
||||
|
||||
extern "C" {
|
||||
const char * IonSoftwareVersion();
|
||||
@@ -19,5 +20,10 @@ int main(int argc, char * argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Ion::msleep(long ms) {
|
||||
void Ion::Timing::msleep(uint32_t ms) {
|
||||
emscripten_sleep(ms);
|
||||
}
|
||||
|
||||
void Ion::Timing::usleep(uint32_t us) {
|
||||
emscripten_sleep(us/1000);
|
||||
}
|
||||
|
||||
@@ -268,8 +268,11 @@ function screenshot() {
|
||||
var spans = document.querySelectorAll(".calculator .keyboard span");
|
||||
for (var i=0; i< spans.length; i++) {
|
||||
var span = spans[i];
|
||||
span.addEventListener("click", function(e) {
|
||||
Module._IonEventsEmscriptenPushKey(this.getAttribute("data-key"));
|
||||
span.addEventListener("mousedown", function(e) {
|
||||
Module._IonEventsEmscriptenKeyDown(this.getAttribute("data-key"));
|
||||
});
|
||||
span.addEventListener("mouseup", function(e) {
|
||||
Module._IonEventsEmscriptenKeyUp(this.getAttribute("data-key"));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,14 @@ static uint32_t crc32(uint32_t crc, uint8_t data) {
|
||||
|
||||
uint32_t Ion::crc32(const uint32_t * data, size_t length) {
|
||||
const uint8_t * dataByte = (const uint8_t *)data;
|
||||
size_t byteLength = length*sizeof(uint32_t)/sizeof(uint8_t);
|
||||
size_t uint32ByteLength = sizeof(uint32_t)/sizeof(uint8_t);
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
for (size_t i=0; i<byteLength; i++) {
|
||||
// scan byte by byte to avoid alignment issue when building for emscripten platform
|
||||
crc = ::crc32(crc, dataByte[i]);
|
||||
for (int i = 0; i < (int)length; i++) {
|
||||
// FIXME: Assumes little-endian byte order!
|
||||
for (int j = uint32ByteLength-1; j >= 0; j--) {
|
||||
// scan byte by byte to avoid alignment issue when building for emscripten platform
|
||||
crc = ::crc32(crc, dataByte[i*uint32ByteLength+j]);
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
9
ion/src/shared/timing.cpp
Normal file
9
ion/src/shared/timing.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <ion.h>
|
||||
#include <chrono>
|
||||
|
||||
static auto start = std::chrono::steady_clock::now();
|
||||
|
||||
uint64_t Ion::Timing::millis() {
|
||||
auto elapsed = std::chrono::steady_clock::now() - start;
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
|
||||
}
|
||||
@@ -13,6 +13,7 @@ objs += $(addprefix ion/src/shared/, \
|
||||
events_modifier.o \
|
||||
power.o \
|
||||
random.o \
|
||||
timing.o \
|
||||
dummy/backlight.o \
|
||||
dummy/battery.o \
|
||||
dummy/fcc_id.o \
|
||||
|
||||
@@ -104,7 +104,7 @@ Ion::Events::Event Ion::Events::getEvent(int * timeout) {
|
||||
|
||||
#include <chrono>
|
||||
|
||||
void Ion::msleep(long ms) {
|
||||
void Ion::Timing::msleep(uint32_t ms) {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
while (true) {
|
||||
sDisplay->redraw();
|
||||
@@ -116,3 +116,16 @@ void Ion::msleep(long ms) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Ion::Timing::usleep(uint32_t us) {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
while (true) {
|
||||
sDisplay->redraw();
|
||||
Fl::wait(0);
|
||||
auto elapsed = std::chrono::high_resolution_clock::now() - start;
|
||||
long long microseconds = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
|
||||
if (microseconds >= us) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@ int __aeabi_d2iz(aeabi_double_t x) {
|
||||
return f64_to_i32_r_minMag(f64(x), 0);
|
||||
}
|
||||
|
||||
unsigned int __aeabi_d2uiz(aeabi_double_t x) {
|
||||
return f64_to_i32_r_minMag(f64(x), 0);
|
||||
}
|
||||
|
||||
aeabi_double_t __aeabi_i2d(int i) {
|
||||
return d(i32_to_f64(i));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ public:
|
||||
|
||||
// CharLayout
|
||||
virtual void setChar(char c) { m_char = c; }
|
||||
char character() const { return m_char; }
|
||||
const KDFont * font() const { return m_font; }
|
||||
void setFont(const KDFont * font) { m_font = font; }
|
||||
|
||||
@@ -24,6 +25,7 @@ public:
|
||||
void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) override;
|
||||
void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) override;
|
||||
int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;
|
||||
bool isChar() const override { return true; }
|
||||
bool isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const override;
|
||||
|
||||
// TreeNode
|
||||
@@ -55,8 +57,10 @@ private:
|
||||
|
||||
class CharLayout final : public Layout {
|
||||
public:
|
||||
CharLayout(const CharLayoutNode * n) : Layout(n) {}
|
||||
CharLayout(char c, const KDFont * font = KDFont::LargeFont);
|
||||
const KDFont * font() const { return const_cast<CharLayout *>(this)->node()->font(); }
|
||||
char character() const {return const_cast<CharLayout *>(this)->node()->character();}
|
||||
private:
|
||||
using Layout::node;
|
||||
CharLayoutNode * node() { return static_cast<CharLayoutNode *>(Layout::node());}
|
||||
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
bool isMatrix() const { return const_cast<Layout *>(this)->node()->isMatrix(); }
|
||||
bool isVerticalOffset() const { return const_cast<Layout *>(this)->node()->isVerticalOffset(); }
|
||||
bool isLeftParenthesis() const { return const_cast<Layout *>(this)->node()->isLeftParenthesis(); }
|
||||
bool isChar() const { return const_cast<Layout *>(this)->node()->isChar(); }
|
||||
bool isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const { return const_cast<Layout *>(this)->node()->isCollapsable(numberOfOpenParenthesis, goingLeft); }
|
||||
int leftCollapsingAbsorbingChildIndex() const { return const_cast<Layout *>(this)->node()->leftCollapsingAbsorbingChildIndex(); }
|
||||
int rightCollapsingAbsorbingChildIndex() const { return const_cast<Layout *>(this)->node()->rightCollapsingAbsorbingChildIndex(); }
|
||||
|
||||
@@ -103,6 +103,7 @@ public:
|
||||
virtual bool isRightBracket() const { return false; }
|
||||
virtual bool isEmpty() const { return false; }
|
||||
virtual bool isMatrix() const { return false; }
|
||||
virtual bool isChar() const { return false; }
|
||||
virtual bool hasUpperLeftIndex() const { return false; }
|
||||
virtual char XNTChar() const {
|
||||
LayoutNode * p = parent();
|
||||
|
||||
@@ -32,17 +32,35 @@ int CharLayoutNode::serialize(char * buffer, int bufferSize, Preferences::PrintF
|
||||
}
|
||||
|
||||
bool CharLayoutNode::isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const {
|
||||
if (*numberOfOpenParenthesis <= 0
|
||||
&& (m_char == '+'
|
||||
|| m_char == '-'
|
||||
if (*numberOfOpenParenthesis <= 0) {
|
||||
if (m_char == '+'
|
||||
|| m_char == '*'
|
||||
|| m_char == Ion::Charset::MultiplicationSign
|
||||
|| m_char == Ion::Charset::MiddleDot
|
||||
|| m_char == Ion::Charset::Sto
|
||||
|| m_char == '='
|
||||
|| m_char == ','))
|
||||
{
|
||||
return false;
|
||||
|| m_char == ',')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_char == '-') {
|
||||
/* If the expression is like 3E-200, we want '-' to be collapsable.
|
||||
* Otherwise, '-' is not collapsable. */
|
||||
Layout thisRef = CharLayout(this);
|
||||
Layout parent = thisRef.parent();
|
||||
if (!parent.isUninitialized()) {
|
||||
int indexOfThis = parent.indexOfChild(thisRef);
|
||||
if (indexOfThis > 0) {
|
||||
Layout leftBrother = parent.childAtIndex(indexOfThis-1);
|
||||
if (leftBrother.isChar()
|
||||
&& static_cast<CharLayout&>(leftBrother).character() == Ion::Charset::Exponent)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -53,7 +71,7 @@ KDSize CharLayoutNode::computeSize() {
|
||||
}
|
||||
|
||||
KDCoordinate CharLayoutNode::computeBaseline() {
|
||||
return (m_font->glyphSize().height()+1)/2; //TODO +1 ?
|
||||
return m_font->glyphSize().height()/2;
|
||||
}
|
||||
|
||||
void CharLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) {
|
||||
|
||||
@@ -69,7 +69,7 @@ QUIZ_CASE(poincare_complex_to_expression) {
|
||||
|
||||
assert_parsed_expression_evaluates_to<double>("2+3*I", "3.60555127546*X^(0.982793723247*I)", Radian, Polar, 12);
|
||||
assert_parsed_expression_evaluates_to<double>("3.60555127546*X^(0.982793723247*I)", "2+3*I", Radian, Cartesian, 12);
|
||||
assert_parsed_expression_evaluates_to<float>("12.04159457879229548012824103*X^(1.4876550949*I)", "1+12*I", Radian, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<float>("12.04159457879229548012824103*X^(1.4876550949*I)", "1+12*I", Radian, Cartesian, 5);
|
||||
assert_parsed_expression_evaluates_to<float>("-2E20+2E20*I", "(-2E20)+2E20*I");
|
||||
assert_parsed_expression_evaluates_to<float>("-2E20+2E20*I", "2.828427E20*X^(2.356194*I)", Radian, Polar);
|
||||
assert_parsed_expression_evaluates_to<double>("1E155-1E155*I", "1E155-1E155*I");
|
||||
|
||||
@@ -163,7 +163,7 @@ QUIZ_CASE(poincare_function_evaluate) {
|
||||
#if MATRICES_ARE_DEFINED
|
||||
assert_parsed_expression_evaluates_to<float>("inverse([[1,2,3][4,5,-6][7,8,9]])", "[[-1.2917,-0.083333,0.375][1.0833,0.16667,-0.25][0.041667,-0.083333,0.041667]]", Degree, Cartesian, 5); // inverse is not precise enough to display 7 significative digits
|
||||
assert_parsed_expression_evaluates_to<double>("inverse([[1,2,3][4,5,-6][7,8,9]])", "[[-1.2916666666667,-8.3333333333333E-2,0.375][1.0833333333333,1.6666666666667E-1,-0.25][4.1666666666667E-2,-8.3333333333333E-2,4.1666666666667E-2]]");
|
||||
assert_parsed_expression_evaluates_to<float>("inverse([[I,23-2I,3*I][4+I,5*I,6][7,8*I+2,9]])", "[[(-0.01183)-0.0455*I,(-0.5005)-0.727*I,0.3185+0.4886*I][0.04095+0.00364*I,0.04004-0.02184*I,(-0.02548)+0.0009099*I][0.003336-0.00182*I,0.3609+0.5347*I,(-0.1301)-0.3576*I]]", Degree, Cartesian, 4); // inverse is not precise enough to display 7 significative digits
|
||||
assert_parsed_expression_evaluates_to<float>("inverse([[I,23-2I,3*I][4+I,5*I,6][7,8*I+2,9]])", "[[(-0.0118)-0.0455*I,(-0.5)-0.727*I,0.318+0.489*I][0.0409+0.00364*I,0.04-0.0218*I,(-0.0255)+0.00091*I][0.00334-0.00182*I,0.361+0.535*I,(-0.13)-0.358*I]]", Degree, Cartesian, 3); // inverse is not precise enough to display 7 significative digits
|
||||
assert_parsed_expression_evaluates_to<double>("inverse([[I,23-2I,3*I][4+I,5*I,6][7,8*I+2,9]])", "[[(-0.0118289353958)-0.0454959053685*I,(-0.500454959054)-0.727024567789*I,0.31847133758+0.488626023658*I][0.0409463148317+3.63967242948E-3*I,0.0400363967243-0.0218380345769*I,(-0.0254777070064)+9.0991810737E-4*I][3.33636639369E-3-1.81983621474E-3*I,0.36093418259+0.534728541098*I,(-0.130118289354)-0.357597816197*I]]", Degree, Cartesian, 12); // FIXME: inverse is not precise enough to display 14 significative digits
|
||||
#endif
|
||||
|
||||
|
||||
@@ -227,14 +227,15 @@ QUIZ_CASE(poincare_trigo_evaluate) {
|
||||
// On R*i
|
||||
assert_parsed_expression_evaluates_to<double>("tanh(43*I)", "-1.4983873388552*I", Radian);
|
||||
// Tangent-style
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(P*I/2)", "undef", Radian);
|
||||
// FIXME: this depends on the libm implementation and does not work on travis/appveyor servers
|
||||
/*assert_parsed_expression_evaluates_to<float>("tanh(P*I/2)", "undef", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(5*P*I/2)", "undef", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(7*P*I/2)", "undef", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(8*P*I/2)", "0", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(9*P*I/2)", "undef", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(9*P*I/2)", "undef", Radian);*/
|
||||
// On C
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(I-4)", "(-1.000279)+0.0006102409*I", Radian);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(I-4)", "(-1.000279)+0.0006102409*I", Degree);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(I-4)", "(-1.00028)+0.000610241*I", Radian, Cartesian, 6);
|
||||
assert_parsed_expression_evaluates_to<float>("tanh(I-4)", "(-1.00028)+0.000610241*I", Degree, Cartesian, 6);
|
||||
|
||||
/* acosh: [-1,1] -> R*i
|
||||
* ]-inf,-1[ -> Pi*i+R (even on real)
|
||||
|
||||
@@ -129,10 +129,12 @@ port_objs += $(addprefix python/port/,\
|
||||
port.o \
|
||||
builtins.o\
|
||||
helpers.o \
|
||||
modkandinsky.o \
|
||||
modkandinsky_impl.o \
|
||||
modturtle.o \
|
||||
modturtle_impl.o \
|
||||
mod/kandinsky/modkandinsky.o \
|
||||
mod/kandinsky/modkandinsky_table.o \
|
||||
mod/time/modtime.o \
|
||||
mod/time/modtime_table.o \
|
||||
mphalport.o \
|
||||
)
|
||||
|
||||
|
||||
@@ -65,6 +65,11 @@ Q(isvisible)
|
||||
Q(pencolor)
|
||||
Q(reset)
|
||||
|
||||
// utime QSTRs
|
||||
Q(time)
|
||||
Q(sleep)
|
||||
Q(monotonic)
|
||||
|
||||
// MicroPython QSTRs
|
||||
Q()
|
||||
Q(*)
|
||||
|
||||
@@ -4,16 +4,35 @@ extern "C" {
|
||||
#include "mphalport.h"
|
||||
}
|
||||
|
||||
void micropython_port_should_interrupt() {
|
||||
void micropython_port_vm_hook_loop() {
|
||||
/* This function is called very frequently by the MicroPython engine. We grab
|
||||
* this opportunity to interrupt execution and/or refresh the display on
|
||||
* platforms that need it. */
|
||||
|
||||
/* Doing too many things here slows down Python execution quite a lot. So we
|
||||
* only do things once in a while and return as soon as possible otherwise. */
|
||||
static int c = 0;
|
||||
c++;
|
||||
if (c%20000 != 0) {
|
||||
|
||||
c = (c + 1) % 20000;
|
||||
if (c != 0) {
|
||||
return;
|
||||
}
|
||||
c = 0;
|
||||
Ion::Keyboard::State scan = Ion::Keyboard::scan();
|
||||
if (scan.keyDown((Ion::Keyboard::Key)mp_interrupt_char)) {
|
||||
|
||||
/* Check if the user asked for an interruption from the keyboard */
|
||||
if (micropython_port_should_interrupt()) {
|
||||
mp_keyboard_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
bool micropython_port_should_interrupt() {
|
||||
Ion::Keyboard::State scan = Ion::Keyboard::scan();
|
||||
Ion::Keyboard::Key interruptKey = static_cast<Ion::Keyboard::Key>(mp_interrupt_char);
|
||||
return scan.keyDown(interruptKey);
|
||||
}
|
||||
|
||||
void micropython_port_interruptible_msleep(uint32_t delay) {
|
||||
uint32_t start = Ion::Timing::millis();
|
||||
while (Ion::Timing::millis() - start < delay && !micropython_port_should_interrupt()) {
|
||||
Ion::Timing::msleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* should_interrupt effectively does something once every 20000 calls. It checks
|
||||
* if a key is down to raise an interruption flag. */
|
||||
void micropython_port_should_interrupt();
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void micropython_port_vm_hook_loop();
|
||||
bool micropython_port_should_interrupt();
|
||||
void micropython_port_interruptible_msleep(uint32_t delay);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ extern "C" {
|
||||
* the stackViewController and forces the window to redraw itself.
|
||||
* KDIonContext::sharedContext is set to the frame of the last object drawn. */
|
||||
|
||||
mp_obj_t kandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue) {
|
||||
mp_obj_t modkandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue) {
|
||||
return
|
||||
MP_OBJ_NEW_SMALL_INT(
|
||||
KDColor::RGB888(
|
||||
@@ -21,14 +21,14 @@ mp_obj_t kandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue) {
|
||||
);
|
||||
}
|
||||
|
||||
mp_obj_t kandinsky_get_pixel(mp_obj_t x, mp_obj_t y) {
|
||||
mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y) {
|
||||
KDColor c = KDIonContext::sharedContext()->getPixel(
|
||||
KDPoint(mp_obj_get_int(x), mp_obj_get_int(y))
|
||||
);
|
||||
return MP_OBJ_NEW_SMALL_INT(c);
|
||||
}
|
||||
|
||||
mp_obj_t kandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color) {
|
||||
mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color) {
|
||||
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
|
||||
KDIonContext::sharedContext()->setPixel(
|
||||
KDPoint(mp_obj_get_int(x), mp_obj_get_int(y)),
|
||||
@@ -37,7 +37,7 @@ mp_obj_t kandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color) {
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t kandinsky_draw_string(mp_obj_t text, mp_obj_t x, mp_obj_t y) {
|
||||
mp_obj_t modkandinsky_draw_string(mp_obj_t text, mp_obj_t x, mp_obj_t y) {
|
||||
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
|
||||
KDIonContext::sharedContext()->drawString(
|
||||
mp_obj_str_get_str(text),
|
||||
6
python/port/mod/kandinsky/modkandinsky.h
Normal file
6
python/port/mod/kandinsky/modkandinsky.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <py/obj.h>
|
||||
|
||||
mp_obj_t modkandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue);
|
||||
mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y);
|
||||
mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color);
|
||||
mp_obj_t modkandinsky_draw_string(mp_obj_t text, mp_obj_t x, mp_obj_t y);
|
||||
21
python/port/mod/kandinsky/modkandinsky_table.c
Normal file
21
python/port/mod/kandinsky/modkandinsky_table.c
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "modkandinsky.h"
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_color_obj, modkandinsky_color);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(modkandinsky_get_pixel_obj, modkandinsky_get_pixel);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_set_pixel_obj, modkandinsky_set_pixel);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_draw_string_obj, modkandinsky_draw_string);
|
||||
|
||||
STATIC const mp_rom_map_elem_t modkandinsky_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_kandinsky) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_color), (mp_obj_t)&modkandinsky_color_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_get_pixel), (mp_obj_t)&modkandinsky_get_pixel_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_set_pixel), (mp_obj_t)&modkandinsky_set_pixel_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_draw_string), (mp_obj_t)&modkandinsky_draw_string_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(modkandinsky_module_globals, modkandinsky_module_globals_table);
|
||||
|
||||
const mp_obj_module_t modkandinsky_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&modkandinsky_module_globals,
|
||||
};
|
||||
20
python/port/mod/time/modtime.cpp
Normal file
20
python/port/mod/time/modtime.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
extern "C" {
|
||||
#include "modtime.h"
|
||||
}
|
||||
#include <ion/timing.h>
|
||||
#include "../../helpers.h"
|
||||
#include <py/smallint.h>
|
||||
#include <py/runtime.h>
|
||||
|
||||
mp_obj_t modtime_sleep(mp_obj_t seconds_o) {
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
micropython_port_interruptible_msleep(1000 * mp_obj_get_float(seconds_o));
|
||||
#else
|
||||
micropython_port_interruptible_msleep(1000 * mp_obj_get_int(seconds_o));
|
||||
#endif
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modtime_monotonic() {
|
||||
return mp_obj_new_float(Ion::Timing::millis() / 1000.0);
|
||||
}
|
||||
4
python/port/mod/time/modtime.h
Normal file
4
python/port/mod/time/modtime.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#include <py/obj.h>
|
||||
|
||||
mp_obj_t modtime_sleep(mp_obj_t seconds_o);
|
||||
mp_obj_t modtime_monotonic();
|
||||
17
python/port/mod/time/modtime_table.c
Normal file
17
python/port/mod/time/modtime_table.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "modtime.h"
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(modtime_sleep_obj, modtime_sleep);
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(modtime_monotonic_obj, modtime_monotonic);
|
||||
|
||||
STATIC const mp_rom_map_elem_t modtime_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_time) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&modtime_sleep_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_monotonic), MP_ROM_PTR(&modtime_monotonic_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(modtime_module_globals, modtime_module_globals_table);
|
||||
|
||||
const mp_obj_module_t modtime_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&modtime_module_globals,
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
#include "py/obj.h"
|
||||
#include "py/mphal.h"
|
||||
#include "modkandinsky.h"
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(kandinsky_color_obj, kandinsky_color);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(kandinsky_get_pixel_obj, kandinsky_get_pixel);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(kandinsky_set_pixel_obj, kandinsky_set_pixel);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(kandinsky_draw_string_obj, kandinsky_draw_string);
|
||||
|
||||
STATIC const mp_rom_map_elem_t kandinsky_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_kandinsky) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_color), (mp_obj_t)&kandinsky_color_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_get_pixel), (mp_obj_t)&kandinsky_get_pixel_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_set_pixel), (mp_obj_t)&kandinsky_set_pixel_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_draw_string), (mp_obj_t)&kandinsky_draw_string_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(kandinsky_module_globals, kandinsky_module_globals_table);
|
||||
|
||||
const mp_obj_module_t kandinsky_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&kandinsky_module_globals,
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
#include "py/obj.h"
|
||||
|
||||
/*
|
||||
* kandinsky.color(12,0,233);
|
||||
* kandinsky.getPixel(x, y);
|
||||
* kandinsky.setPixel(x, y, color);
|
||||
* kandinsky.drawString(text, x, y);
|
||||
*/
|
||||
|
||||
mp_obj_t kandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue);
|
||||
mp_obj_t kandinsky_get_pixel(mp_obj_t x, mp_obj_t y);
|
||||
mp_obj_t kandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color);
|
||||
mp_obj_t kandinsky_draw_string(mp_obj_t text, mp_obj_t x, mp_obj_t y);
|
||||
@@ -55,7 +55,7 @@ void draw_turtle() {
|
||||
|
||||
if (t_mileage > 1000) {
|
||||
if (t_speed > 0) {
|
||||
Ion::msleep(8 * (8 - t_speed));
|
||||
Ion::Timing::msleep(8 * (8 - t_speed));
|
||||
t_mileage -= 1000;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
// (This scheme won't work if we want to mix Thumb and normal ARM code.)
|
||||
#define MICROPY_MAKE_POINTER_CALLABLE(p) (p)
|
||||
|
||||
#define MICROPY_VM_HOOK_LOOP micropython_port_should_interrupt();
|
||||
#define MICROPY_VM_HOOK_LOOP micropython_port_vm_hook_loop();
|
||||
|
||||
typedef intptr_t mp_int_t; // must be pointer size
|
||||
typedef uintptr_t mp_uint_t; // must be pointer size
|
||||
@@ -106,9 +106,12 @@ typedef long mp_off_t;
|
||||
|
||||
#define MP_STATE_PORT MP_STATE_VM
|
||||
|
||||
extern const struct _mp_obj_module_t kandinsky_module;
|
||||
extern const struct _mp_obj_module_t modkandinsky_module;
|
||||
extern const struct _mp_obj_module_t modtime_module;
|
||||
extern const struct _mp_obj_module_t turtle_module;
|
||||
|
||||
#define MICROPY_PORT_BUILTIN_MODULES \
|
||||
{ MP_ROM_QSTR(MP_QSTR_kandinsky), MP_ROM_PTR(&kandinsky_module) }, \
|
||||
{ MP_ROM_QSTR(MP_QSTR_turtle), MP_ROM_PTR(&turtle_module) }
|
||||
{ MP_ROM_QSTR(MP_QSTR_kandinsky), MP_ROM_PTR(&modkandinsky_module) }, \
|
||||
{ MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&modtime_module) }, \
|
||||
{ MP_ROM_QSTR(MP_QSTR_turtle), MP_ROM_PTR(&turtle_module) }, \
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ static inline void ion_main_inner() {
|
||||
quiz_print("ALL TESTS FINISHED");
|
||||
#if !QUIZ_USE_CONSOLE
|
||||
while (1) {
|
||||
Ion::msleep(1000);
|
||||
Ion::Timing::msleep(1000);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -56,10 +56,10 @@ void ion_main(int argc, char * argv[]) {
|
||||
#if POINCARE_TREE_LOG
|
||||
Poincare::TreePool::sharedPool()->log();
|
||||
#endif
|
||||
assert(false);
|
||||
quiz_assert(false);
|
||||
#if !QUIZ_USE_CONSOLE
|
||||
while (1) {
|
||||
Ion::msleep(1000);
|
||||
Ion::Timing::msleep(1000);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user