diff --git a/Makefile b/Makefile
index fa4c53c50c7..f6d55abc7e5 100644
--- a/Makefile
+++ b/Makefile
@@ -92,6 +92,7 @@ help:
@echo " make PLATFORM=simulator TARGET=web"
@echo " make PLATFORM=simulator TARGET=windows"
@echo " make PLATFORM=simulator TARGET=3ds"
+ @echo " make PLATFORM=simulator TARGET=nspire"
.PHONY: doc
doc:
diff --git a/README.fr.md b/README.fr.md
index 58555c9c808..2a1e4fa2700 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -141,6 +141,29 @@ Vous pouvez ensuite copier epsilon.3dsx sur une carte SD pour l'exécuter depuis
+
+ Simulateur TI-Nspire
+
+***Mes excuses, mais cette section est traduite automatiquement. Merci de nous aider à le localiser.***
+
+Tout d'abord, construisez et installez [Ndless SDK](https://hackspire.org/index.php/C_and_assembly_development_introduction);
+
+Compilez ensuite Upsilon en spécifiant la cible:
+
+```bash
+make clean
+make PLATFORM=simulator TARGET=nspire -j4
+```
+
+Copiez epsilon.tns sur Nspire Touchpad ou CX avec [Ndless](https://ndless.me) installé et exécutez-le à partir de Mes documents.
+
+Veuillez noter que l'exécutable est au [format Zehn](https://hackspire.org//index.php?title=Zehn) donc
+Ndless 3.1 ou une version ultérieure est requise.
+
+La méthode de mappage du clavier n'est pas parfaite et est susceptible de changer.
+
+
+
Si vous avez besoin d'aide, n'hésitez pas à rejoindre notre serveur discord : https://discord.gg/Q9buEMduXG
@@ -177,9 +200,10 @@ Vous pouvez essayer Epsilon depuis votre navigateur sur le [simulateur en ligne]
## Licence
-NumWorks est une marque déposée de NumWorks SAS, 24 Rue Godot de Mauroy, 75009 Paris, France.
-Nintendo est Nintendo 3DS sont des marques déposées de Nintendo of America Inc, 4600 150th Ave NE, Redmond, WA 98052, Etats-Unis.
-NumWorks SAS et Nintendo of America Inc ne sont en aucun cas associés avec ce projet.
+NumWorks est une marque déposée de NumWorks SAS, 24 Rue Godot de Mauroy, 75009 Paris, France.
+Nintendo est Nintendo 3DS sont des marques déposées de Nintendo of America Inc, 4600 150th Ave NE, Redmond, WA 98052, Etats-Unis.
+NumWorks SAS et Nintendo of America Inc ne sont en aucun cas associés avec ce projet.
+TI-Nspire est une marque déposée de Texas Instruments, Inc.
* NumWorks Epsilon est disponible sous [Lisense CC BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode).
* Omega est disponible sous [Lisense CC BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode).
diff --git a/README.md b/README.md
index fb5f3144c39..3694c47052f 100644
--- a/README.md
+++ b/README.md
@@ -328,6 +328,27 @@ You can then put epsilon.3dsx on a SD card to run it from the HBC or use 3dslink
+
+ TI-Nspire Simulator
+
+First, build and install [Ndless SDK](https://hackspire.org/index.php/C_and_assembly_development_introduction);
+
+Then compile Upsilon specifying the target:
+
+```bash
+make clean
+make PLATFORM=simulator TARGET=nspire -j4
+```
+
+Copy epsilon.tns to either Nspire Touchpad or CX with [Ndless](https://ndless.me) installed and run it from My Documents.
+
+Please note that the executable is in [Zehn format](https://hackspire.org//index.php?title=Zehn) thus
+Ndless 3.1 or a later version is required.
+
+Keyboard mapping method is not perfect and is subject to change.
+
+
+
Important: Don't forget the `--recursive` tag, because Upsilon relies on submodules.
@@ -371,9 +392,10 @@ You can try Epsilon straight from your browser in the [online simulator](https:/
## License
-NumWorks is a registered trademark of NumWorks SAS, 24 Rue Godot de Mauroy, 75009 Paris, France.
-Nintendo and Nintendo 3DS are registered trademarks of Nintendo of America Inc, 4600 150th Ave NE, Redmond, WA 98052, USA.
-NumWorks SAS and Nintendo of America Inc aren't associated in any shape or form with this project.
+NumWorks is a registered trademark of NumWorks SAS, 24 Rue Godot de Mauroy, 75009 Paris, France.
+Nintendo and Nintendo 3DS are registered trademarks of Nintendo of America Inc, 4600 150th Ave NE, Redmond, WA 98052, USA.
+NumWorks SAS and Nintendo of America Inc aren't associated in any shape or form with this project.
+TI-Nspire is a registered trademark of Texas Instruments, Inc.
* NumWorks Epsilon is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode).
* Omega is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode).
diff --git a/build/platform.simulator.nspire.mak b/build/platform.simulator.nspire.mak
new file mode 100644
index 00000000000..ebb4fefd03c
--- /dev/null
+++ b/build/platform.simulator.nspire.mak
@@ -0,0 +1,6 @@
+TOOLCHAIN = nspire-gcc
+EXE = elf
+
+EPSILON_TELEMETRY ?= 0
+
+HANDY_TARGETS_EXTENSIONS = tns
diff --git a/build/targets.simulator.nspire.mak b/build/targets.simulator.nspire.mak
new file mode 100644
index 00000000000..30fd3cf1c8d
--- /dev/null
+++ b/build/targets.simulator.nspire.mak
@@ -0,0 +1,4 @@
+ZEHNFLAGS = --name "epsilon"
+
+$(BUILD_DIR)/%.tns: $(BUILD_DIR)/%.elf
+ $(GENZEHN) --input $^ --output $@ $(ZEHNFLAGS)
diff --git a/build/toolchain.nspire-gcc.mak b/build/toolchain.nspire-gcc.mak
new file mode 100644
index 00000000000..86cab35916b
--- /dev/null
+++ b/build/toolchain.nspire-gcc.mak
@@ -0,0 +1,10 @@
+CC = nspire-gcc
+CXX = nspire-g++
+LD = nspire-ld
+GDB = arm-none-eabi-gdb
+OBJCOPY = arm-none-eabi-objcopy
+SIZE = arm-none-eabi-size
+AS = nspire-as
+GENZEHN = genzehn
+
+SFLAGS += -U__STRICT_ANSI__ -D_NSPIRE -marm
diff --git a/ion/src/simulator/external/README.md b/ion/src/simulator/external/README.md
index 3d0a55bc4ad..af8c45c628a 100644
--- a/ion/src/simulator/external/README.md
+++ b/ion/src/simulator/external/README.md
@@ -1,6 +1,8 @@
The sdl directory contains a snapshot of the SDL source code at revision
13105:db9d0d0b7ebc
+The n2DLib directory contains a fork of n2DLib at https://github.com/gameblabla/n2DLib with prefix 'GRAFX_' attached to macro 'min' and 'max'
+
Note that parts of SDL source code are also copied into the path
ion/src/simulator/android/src/java because Gradle expects Java code to live
there...
diff --git a/ion/src/simulator/external/config.nspire.mak b/ion/src/simulator/external/config.nspire.mak
new file mode 100644
index 00000000000..f570062782f
--- /dev/null
+++ b/ion/src/simulator/external/config.nspire.mak
@@ -0,0 +1,9 @@
+
+undefine sdl_src
+undefine ion_simulator_sdl_src
+
+sdl_src += $(addprefix ion/src/simulator/external/n2DLib/, \
+ n2DLib.c \
+)
+
+SFLAGS += -Iion/src/simulator/external/n2DLib
diff --git a/ion/src/simulator/external/n2DLib/README.md b/ion/src/simulator/external/n2DLib/README.md
new file mode 100644
index 00000000000..97639dff14e
--- /dev/null
+++ b/ion/src/simulator/external/n2DLib/README.md
@@ -0,0 +1,345 @@
+n2DLib documentation
+====================
+
+n2DLib is a C graphics library for the TI-Nspire calculators intended to be used with the Ndless 3rd-party jailbreak.
+
+The lib uses buffering to display smoothly ; that means that all drawing is done to a separate buffer in memory which is then copied to the screen.
+
+All colors, unless specified, must be in R5G6B5 format, meaning a 16-bits number organized this way :
+
+Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
+------|----|----|----|----|----|----|---|---|---|---|---|---|---|---|---|---
+Color | R | R | R | R | R | G | G | G | G | G | G | B | B | B | B | B
+
+n2DLib uses a variation on the NTI image format to store images. All images must be arrays of unsigned short organized this way :
+
+Entry | 0 | 1 | 2 | 3-...
+------|----------------|----------------|-----------------|----------
+Use | Width in pixels|Height in pixels|Transparent color|Image data
+
+n2DLib also offers the Rect structure that has four members : x, y, w and h. You're free to use it for various purposes.
+
+About fixed-point numbers
+=========================
+
+In addition to graphical commands, n2DLib offers a fast alternative to floating-point numbers that is 24.8 fixed-point numbers. Those numbers are integers whose bits 0-7 are treated like a decimal part, and whose bits 8-24 are treated like an integer part. That means 256 is actually 1.0 when used with the appropriate commands. FIxed-point angles are written in binary angles, meaning a full period is [0, 256].
+
+Since fixed-point numbers are special numbers, they can't interact normally with integers. The following array describes what you can and can't do normally :
+
+ |integer|fixed-point|
+-----------|-------|-----------|
+integer | +-*/ | * |
+fixed-point| */ | +- |
+
+You need a special routine to :
+add an integer to a fixed-point number
+divide an integer by a fixed-point number
+multiply a fixed-point number by another fixed-point number
+divide a fixed-point number by another fixed-point number
+
+See the "Math routines" part of the doc to find how to do all of this.
+
+Using the key detection functions
+=================================
+
+n2DLib provides additional key detection functions to those already provided by Ndless, and aims for full compatibility with those latters. That's why n2DLib uses Ndless's t_key struct :
+
+```C
+typedef struct {
+int row, col, tpad_row, tpad_col;
+tpad_arrow_t tpad_arrow;
+} t_key;
+```
+
+Each key is stored depending on its position on the Nspire's keypad, row and column. You can see two different versions for each variable ; that's because the clickpad keypad and the touchpad keypad have different keymappings. This is taken care of by n2DLib internally, so you don't have to worry about it.
+
+For more info about how the keypad(s) work(s), see http://hackspire.unsads.com/wiki/index.php/Keypads .
+
+See the "Key detection functions" part of the doc to see what you can do. You don't necessarily need these routines unless you want to do specific things like custom keybinding, since what Ndless provides is enough for most cases .
+
+Summary of commands
+===================
+
+GRAPHICAL ROUTINES
+==================
+
+```C
+void clearBufferB()
+```
+
+Fills the buffer with black color.
+
+```C
+void clearBufferW()
+```
+
+Fills the buffer with white color.
+
+```C
+void clearBuffer(unsigned short c)
+```
+
+Fills the buffer with the given color.
+
+```C
+unsigned short getPixel(unsigned short *src, unsigned int x, unsigned int y)
+```
+
+Returns the color of a given pixel in an image, or the transparent color of this image if the coordinates given are out-of-bounds.
+
+```C
+void deinitBuffering()
+```
+
+Frees memory used by the buffering functionalities. Call this as the very last instruction of your program (excepting return).
+
+```C
+void drawLine(int x1, int y1, int x2, int y2, unsigned short c)
+```
+
+Draws a line between two points of the given color.
+
+```C
+void drawPolygon(unsigned short c, int pointsNb, ...)
+```
+
+Draws a wired polygon of the given color using a list of pointsNb coordinates (first x, then y).
+
+```C
+void drawSprite(unsigned short *src, unsigned int _x, unsigned int _y)
+```
+
+Draws an image to the given coordinates.
+
+```C
+void drawSpritePart(unsigned short *src, unsigned int _x, unsigned int _y, Rect* part)
+```
+
+Draws part of an image to the given coordinates. The routine draws what of the sprite is in the rectangle given by Rect* part.
+
+```C
+void drawSpriteRotated(unsigned short* source, Rect* sr, Rect* rc, Fixed angle)
+```
+
+Draws a sprite rotated by a given angle at the coordinates given by Rect* sr, so that the rotation of the sprite is performed around the point Rect *rc, which is relative to the sprite itself. The center of the rotation will always be displayed at the coordinates Rect* sr. For example, if rc->x and rc->y are half the sprite's width and height, the sprite will be rotated around its center. You can also pass NULL as Rect* rc to rotate the image around its center.
+
+```C
+void drawSpriteScaled(unsigned short* source, Rect* info)
+```
+
+Draws a sprite by scaling it so that it fits perfectly in the rectangle given by Rect* info.
+
+```C
+void fillCircle(int x, int y, int radius, unsigned short c)
+```
+
+Fills a circle of the given color.
+
+```C
+void fillEllipse(int x, int y, int w, int h, unsigned short c)
+```
+
+Fills an ellipse of the given size with the given color.
+
+```C
+void fillRect(int x, int y, int w, int h, unsigned short c)
+```
+
+Fills a rectangle of the given dimensions with the given color ; does clipping.
+
+```C
+void initBuffering()
+```
+
+Initializes the buffering functionalities. Call this as the very first instruction of your program if you want to use n2DLib's buffering.
+
+```C
+void setPixel(unsigned int x, unsigned int y, unsigned short c)
+```
+
+Sets a pixel to the given color after verifying the pixel is actually in the screen's dimensions.
+
+```C
+void setPixelRGB(unsigned int x, unsigned int y, unsigned char r, unsigned char g, unsigned char b)
+```
+
+Sets a pixel to the given color after verifying the pixel is actually in the screen's dimensions and using three color components.
+
+```C
+void setPixelUnsafe(unsigned int x, unsigned int y, unsigned short c)
+```
+
+Sets a pixel to the given color, but does not make sure the pixel is in the screen's dimensions. Faster than setPixel, but use only if you know you can't draw out of the screen.
+
+```C
+void updateScreen()
+```
+
+Copies the content of the buffer to the screen. This does not clear the buffer.
+
+TEXT ROUTINES
+=============
+
+```C
+void drawChar(int *x, int *y, int margin, char ch, unsigned short fc, unsigned short olc)
+```
+
+Draws a single character at the given position with the given front and outline color using n2DLib's built-in font. Does clipping and supports newlines with \n. When \n is passed as ch, the function resets the X value to the passed margin value and Y goes to newline. X and Y are modified like a cursor position would.
+
+```C
+void drawDecimal(int *x, int *y, int n, unsigned short fc, unsigned short olc)
+```
+
+Draws a signed integer at the given position with the given front and outline color using n2DLib's built-in font. Does clipping. X and Y are modified like a cursor position would. Use this as a fast way to display integers only.
+
+```C
+void drawString(int *x, int *y, int margin, const char *str, unsigned short fc, unsigned short olc)
+```
+
+Draws a string of characters at the given position with the given front and outline color using n2DLib's built-in font. Does clipping and supports newlines with \n. When \n is encountered in the string, the function resets the X value to the passed margin value and Y goes to newline. X and Y are modified like a cursor position would. Use this as a fast way to display strings only.
+
+```C
+void drawStringF(int *x, int *y, int margin, unsigned short fc, unsigned short olc, const char *s, ...)
+```
+
+Draws a string of characters at the given position with the given front and outline color using n2DLib's built-in font. Does clipping and supports newlines with \n and printf-like arguments. When \n is encountered in the string, the function resets the X value to the passed margin value and Y goes to newline. X and Y are modified like a cursor position would.
+
+```C
+int stringWidth(const char* s)
+```
+
+Returns the width of the string in pixels when using n2DLib's built-in font.
+
+```C
+int numberWitdh(int n)
+```
+
+Returns the width of the decimal in pixels when drawn to the screen using n2DLib's built-in font.
+
+
+MATH ROUTINES
+=============
+
+```C
+Type clamp(Type x, Type lowerBound, Type upperBound)
+```
+
+Returns x if it's in the interval formed by [lowerBound, upperBound], else returns the corresponding bound.
+
+```C
+Fixed fixcos(Fixed angle)
+```
+
+Returns the cosinus of a binary angle in fixed-point format.
+
+```C
+Fixed fixdiv(Fixed a, Fixed b)
+```
+
+Performs a division between two fixed-point numbers.
+
+```C
+Fixed fixmul(Fixed a, Fixed b)
+```
+
+Performs a multiplication between two fixed-point numbers.
+
+```C
+Fixed fixsin(Fixed angle)
+```
+
+Returns the sinus of a binary angle in fixed-point format.
+
+```C
+int fixtoi(Fixed f)
+```
+
+Turns a fixed-point number into an integer.
+
+```C
+void getBoundingBox(int x, int y, int w, int h, int cx, int cy, Fixed angle, Rect *out)
+```
+
+Calculates the bounds of the box that has the following properties :
+- Has top-left corner at coordinates (x, y)
+- Has width w and height h
+- Rotates by a given angle around the point (cx, cy)
+Writes the resulting box in "out". The resulting box is the smallest straight rectangle that contains all points of the input.
+
+```C
+int interpolatePathFixed(Fixed x[], Fixed y[], int t[], int nbPoints, Rect *out)
+```
+
+Given arrays of `nbPoints` X coordinates, Y coordinates and times, the function generates a curve that passes by the given points after the corresponding number of iterations. For every iteration, the calculated coordinates are written to `Rect *out`'s x and y components. The function returns 1 when the last point has been reached by the curve, and 0 otherwise. This version uses n2DLib's fixed point numbers, and thus is very fast but has limited accuracy.
+
+```C
+int interpolatePathFloat(float x[], float y[], int t[], int nbPoints, Rect *out)
+```
+
+Given arrays of `nbPoints` X coordinates, Y coordinates and times, the function generates a curve that passes by the given points after the corresponding number of iterations. For every iteration, the calculated coordinates are written to `Rect *out`'s x and y components. The function returns 1 when the last point has been reached by the curve, and 0 otherwise. This version uses floating-point numbers, and thus is much slower than `interpolatePathFixed` but has far better accuracy.
+
+```C
+Fixed itofix(int i)
+```
+
+Turns an integer into a fixed-point number.
+
+```C
+void rotate(int x, int y, int cx, int cy, Fixed angle, Rect* out)
+```
+
+Rotates the 2D point (x, y) around the 2D point (cx, cy) by the given angle, and stores the resulting point in "out" (w and h not modified).
+
+```C
+int sign(x)
+```
+
+Returns 1 if x is negative, 0 otherwise.
+
+KEY DETECTION FUNCTIONS
+=======================
+
+```C
+int get_key_pressed(t_key* report)
+```
+
+Detects if any key is being pressed ; if so, fills report with the corresponding data and returns 1. If no key is being pressed, fills report with _KEY_DUMMY and returns 0. NOTE : this doesn't detect the touchpad's arrow keys.
+
+```C
+int isKey(t_key k1, t_key k2)
+```
+
+Returns 1 if both keys match, or 0 if they don't. NOTE : this is very useful for example when comparing a key that has been filled with get_key_pressed() with an Ndless KEY_NSPIRE_ constant.
+
+HARDWARE TIMERS
+===============
+The TI-Nspire calculators have 2 hardware timers that allows for accurate timing of any task. Each timer runs at 32 kHz. n2DLib provides a precise use for them, which is as follows :
+- Every 0.000030517578125 second (roughly 30 µs, that is 32 768 times per second), each timer's value is decreased by 1.
+- When a timer reaches 0, it stops decreasing until you reload a value in it.
+To sum up, when a timer reads 0, it means the amount of time you passed has run out.
+
+Everything here is by aeTIos and Streetwalrus from http://www.omnimaga.org/. Greetings to them !
+
+```C
+void timer_init(unsigned timer)
+```
+
+Initialises the first timer (unsigned timer = 0) or the second timer (unsigned timer = 1). Any other parameter than 0 or 1 will crash your calc badly. This MUST be called ONCE for every timer you use in your program or your timing will be invalid.
+
+```C
+void timer_restore(unsigned timer)
+```
+
+Restores the initial state of a timer. You MUST call this for every timer that you initialised via timer_init.
+
+```C
+void timer_load(unsigned timer, unsigned value)
+```
+
+Loads a value into a timer. This value will be decremented by the timer each tick until it reaches 0. Note that a timer will freeze until its value is non-zero.
+
+```C
+unsigned timer_read(unsigned timer)
+```C
+
+Reads a value from a timer. If it reads 0, it will always read as 0 until you load a new value into the timer.
\ No newline at end of file
diff --git a/ion/src/simulator/external/n2DLib/example.cpp b/ion/src/simulator/external/n2DLib/example.cpp
new file mode 100644
index 00000000000..2eb7e41ba13
--- /dev/null
+++ b/ion/src/simulator/external/n2DLib/example.cpp
@@ -0,0 +1,112 @@
+#include "n2DLib.h"
+
+#define UNUSED(var) (void)var
+
+int main(int argc, char *argv[])
+{
+ UNUSED(argc);
+ UNUSED(argv);
+ Fixed theta = 0;
+ Rect spr, nullRect = {0, 0, 0, 0};
+ Rect pt1, pt2;
+ Fixed ax[] = { 300, 260, 220, 280 }, ay[] = { 10, 120 , 30, 80 }, at[] = { 0, 16, 24, 36 };
+ float fax[] = { 200., 160., 120., 180. }, fay[] = { 10., 120. , 30., 80. };
+
+ int x, y, i;
+
+ enable_relative_paths(argv);
+
+ unsigned short *sprite = loadBMP("example.bmp.tns", 0xf81f);
+ if(!sprite)
+ {
+ printf("Error while loading sprite. Make sure you have example.bmp.tns in the same directory\n");
+ return 0;
+ }
+
+ initBuffering();
+ clearBufferB();
+
+ spr.x = 160;
+ spr.y = 120;
+
+ t_key quitKey;
+
+ x = y = 0;
+
+ drawString(&x, &y, 0, "What key to exit the program ?", 0xffff, 0);
+ updateScreen();
+
+ while(!get_key_pressed(&quitKey));
+ wait_no_key_pressed();
+
+ clearBufferB();
+
+ while(!isKeyPressed(quitKey))
+ {
+ fillCircle(160, 120, 80, 0xf800);
+ fillEllipse(160, 120, 70, 60, 0x001f);
+ drawPolygon(0x07e0, 4, 140, 100, 180, 100, 180, 140, 140, 140);
+ drawHLine(80, -10, 330, 0xf800);
+ drawHLine(160, -10, 330, 0xf800);
+ drawVLine(106, -10, 250, 0xf800);
+ drawVLine(212, -10, 250, 0xf800);
+
+ fillRect(-10, -10, 20, 20, 0xf800);
+ fillRect(310, -10, 20, 20, 0x07e0);
+ fillRect(310, 230, 20, 20, 0x001f);
+ fillRect(-10, 230, 20, 20, 0xffff);
+
+ x = 0;
+ y = 120;
+
+ drawString(&x, &y, x, "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqsrtuvwxyz\n0123456789\n", 0, 0xffff);
+ drawDecimal(&x, &y, -2134567841, 0x07e0, 0xf800);
+ x = 0;
+ y += 8;
+ drawStringF(&x, &y, x, 0x07e0, 0xf800, "Test ; number : %d\nstring : %s", 42, "the game");
+
+ spr.w = theta / 2;
+ spr.h = theta / 2;
+ drawSpriteScaled(sprite, &spr, 0, 0); // don't flash
+ drawSpriteRotated(sprite, &spr, &nullRect, theta, theta < 128, 0xffff); // occasionally flash
+ theta++;
+ theta &= 255;
+
+ updateScreen();
+ }
+
+ clearBufferB();
+
+ wait_no_key_pressed();
+
+ while(!isKeyPressed(quitKey))
+ {
+ for(i = 0; i < 4; i++)
+ {
+ fillRect(ax[i]-2, ay[i]-2, 5, 5, 0xf800);
+ fillRect(fax[i]-2, fay[i]-2, 5, 5, 0xf800);
+ }
+
+ interpolatePathFloat(fax, fay, at, 4, &pt1);
+
+ while(!interpolatePathFloat(fax, fay, at, 4, &pt2))
+ {
+ drawLine(pt1.x, pt1.y, pt2.x, pt2.y, 0x07f0);
+ pt1 = pt2;
+ }
+ drawLine(pt1.x, pt1.y, pt2.x, pt2.y, 0x07f0);
+
+ interpolatePathFixed(ax, ay, at, 4, &pt1);
+
+ while(!interpolatePathFixed(ax, ay, at, 4, &pt2))
+ {
+ drawLine(pt1.x, pt1.y, pt2.x, pt2.y, 0xffff);
+ pt1 = pt2;
+ }
+ drawLine(pt1.x, pt1.y, pt2.x, pt2.y, 0xffff);
+ updateScreen();
+ }
+ deinitBuffering();
+
+ return 0;
+}
diff --git a/ion/src/simulator/external/n2DLib/n2DLib.c b/ion/src/simulator/external/n2DLib/n2DLib.c
new file mode 100644
index 00000000000..ab55b57b16b
--- /dev/null
+++ b/ion/src/simulator/external/n2DLib/n2DLib.c
@@ -0,0 +1,859 @@
+#include
+#include "n2DLib.h"
+#include "n2DLib_font.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* *
+ * Buffering *
+ * */
+
+uint16_t *BUFF_BASE_ADDRESS;
+
+void initBuffering()
+{
+ uint8_t init_scr;
+
+ BUFF_BASE_ADDRESS = (uint16_t*)malloc(BUFF_BYTES_SIZE);
+ if(!BUFF_BASE_ADDRESS)
+ {
+ lcd_init(SCR_TYPE_INVALID);
+ exit(0);
+ }
+
+ init_scr = lcd_init(SCR_320x240_565);
+ if (init_scr == 0)
+ {
+ free(BUFF_BASE_ADDRESS);
+ lcd_init(SCR_TYPE_INVALID);
+ exit(0);
+ }
+}
+
+void updateScreen()
+{
+ lcd_blit(BUFF_BASE_ADDRESS, SCR_320x240_565);
+}
+
+void deinitBuffering()
+{
+ if (BUFF_BASE_ADDRESS) free(BUFF_BASE_ADDRESS);
+ lcd_init(SCR_TYPE_INVALID);
+}
+
+
+/* *
+ * Hardware timers *
+ * */
+// Everything on HW timers is by aeTIos and Streetwalrus from http://www.omnimaga.org/
+
+#define TIMER 0x900D0000
+unsigned timer_ctl_bkp[2], timer_load_bkp[2];
+
+void timer_init(unsigned timer)
+{
+ /* Timer configuration :
+ - timers not freezed
+ - timers decreasing
+ - timers count to 0 and stop until a new value is loaded
+ */
+ if (has_colors)
+ {
+ volatile unsigned *timer_ctl = (unsigned *) (TIMER + 0x08 + 0x20 * timer);
+ volatile unsigned *timer_load = (unsigned *) (TIMER + 0x20 * timer);
+
+ timer_ctl_bkp[timer] = *timer_ctl;
+ timer_load_bkp[timer] = *timer_load;
+
+ *timer_ctl &= ~(1 << 7);
+ *timer_ctl = 0b01100011;
+ *timer_ctl |= (1 << 7);
+ }
+ else
+ {
+ volatile unsigned *timer_ctl = (unsigned *) (TIMER + 0x08 + 0x0C * timer);
+ volatile unsigned *timer_divider = (unsigned *) (TIMER + 0x04 + 0x0C * timer);
+
+ timer_ctl_bkp[timer] = *timer_ctl;
+ timer_load_bkp[timer] = *timer_divider;
+
+ *timer_ctl = 0;
+ *timer_divider = 0;
+ }
+}
+
+void timer_restore(unsigned timer)
+{
+ if (has_colors)
+ {
+ volatile unsigned *timer_ctl = (unsigned *) (TIMER + 0x08 + 0x20 * timer);
+ volatile unsigned *timer_load = (unsigned *) (TIMER + 0x20 * timer);
+
+ *timer_ctl &= ~(1 << 7);
+ *timer_ctl = timer_ctl_bkp[timer] & ~(1 << 7);
+ *timer_load = timer_load_bkp[timer];
+ *timer_ctl = timer_ctl_bkp[timer];
+ }
+ else
+ {
+ volatile unsigned *timer_ctl = (unsigned *) (TIMER + 0x08 + 0x0C * timer);
+ volatile unsigned *timer_divider = (unsigned *) (TIMER + 0x04 + 0x0C * timer);
+ volatile unsigned *timer_value = (unsigned *)(TIMER + 0x0C * timer);
+
+ *timer_ctl = timer_ctl_bkp[timer];
+ *timer_divider = timer_load_bkp[timer];
+ *timer_value = 32;
+ }
+}
+
+void timer_load(unsigned timer, unsigned value)
+{
+ if (has_colors)
+ {
+ volatile unsigned *timer_load = (unsigned *) (TIMER + 0x20 * timer);
+ *timer_load = value;
+ }
+ else
+ {
+ volatile unsigned *timer_value = (unsigned *) (TIMER + 0x0C * timer);
+ *timer_value = value;
+ }
+}
+
+unsigned timer_read(unsigned timer)
+{
+ if (has_colors)
+ {
+ volatile unsigned *timer_value = (unsigned *) (TIMER + 0x04 + 0x20 * timer);
+ return *timer_value;
+ }
+ else
+ {
+ volatile unsigned *timer_value = (unsigned *) (TIMER + 0x0C * timer);
+ return *timer_value;
+ }
+}
+
+/* *
+ * Maths *
+ * */
+
+ /*
+Example:
+2.5 * 3.5 :
+ xdec = 128
+ ydec = 128
+ xint = 2
+ yint = 3
+2.5 * 3 = 8.75 :
+ rdec = 192
+ rint = 8
+*/
+
+inline Fixed fixmul(Fixed x, Fixed y)
+{
+ // x = (xint << 8)+ xdec, y = (yint << 8)+ ydec
+ Fixed xdec = x & 0xff, ydec = y & 0xff, xint = x >> 8, yint = y >> 8;
+ // Like (x * y) >> 8 ; a bit slower but without any risk of overflow (noticeable when squaring and cubing)
+ return ((xint * yint) << 8) + xint * ydec + xdec * yint + ((xdec * ydec) >> 8);
+}
+
+Fixed fixcos(Fixed angle)
+{
+ static Fixed cosLUT[] = { 256, 255, 255, 255, 254, 254, 253, 252, 251, 249, 248, 246, 244, 243, 241, 238, 236, 234, 231, 228, 225, 222, 219, 216, 212, 209, 205, 201, 197, 193, 189, 185, 181, 176, 171, 167, 162, 157, 152, 147, 142, 136, 131, 126, 120, 115, 109, 103, 97, 92, 86, 80, 74, 68, 62, 56, 49, 43, 37, 31, 25, 18, 12, 6, 0, -6, -12, -18, -25, -31, -37, -43, -49, -56, -62, -68, -74, -80, -86, -92, -97, -103, -109, -115, -120, -126, -131, -136, -142, -147, -152, -157, -162, -167, -171, -176, -181, -185, -189, -193, -197, -201, -205, -209, -212, -216, -219, -222, -225, -228, -231, -234, -236, -238, -241, -243, -244, -246, -248, -249, -251, -252, -253, -254, -254, -255, -255, -255, -256, -255, -255, -255, -254, -254, -253, -252, -251, -249, -248, -246, -244, -243, -241, -238, -236, -234, -231, -228, -225, -222, -219, -216, -212, -209, -205, -201, -197, -193, -189, -185, -181, -176, -171, -167, -162, -157, -152, -147, -142, -136, -131, -126, -120, -115, -109, -103, -97, -92, -86, -80, -74, -68, -62, -56, -49, -43, -37, -31, -25, -18, -12, -6, 0, 6, 12, 18, 25, 31, 37, 43, 49, 56, 62, 68, 74, 80, 86, 92, 97, 103, 109, 115, 120, 126, 131, 136, 142, 147, 152, 157, 162, 167, 171, 176, 181, 185, 189, 193, 197, 201, 205, 209, 212, 216, 219, 222, 225, 228, 231, 234, 236, 238, 241, 243, 244, 246, 248, 249, 251, 252, 253, 254, 254, 255, 255, 255 };
+ return cosLUT[angle & 0xff];
+}
+
+inline void rotate(int x, int y, int cx, int cy, Fixed angle, Rect *out)
+{
+ x -= cx;
+ y -= cy;
+ out->x = fixtoi(fixmul(itofix(x), fixcos(angle)) + fixmul(itofix(y), fixsin(angle))) + cx;
+ out->y = fixtoi(fixmul(itofix(x), -fixsin(angle)) + fixmul(itofix(y), fixcos(angle))) + cy;
+}
+
+void getBoundingBox(int x, int y, int w, int h, int cx, int cy, Fixed angle, Rect *out)
+{
+ Rect tl, tr, bl, br;
+ rotate(x, y, cx, cy, angle, &tl);
+ rotate(x + w, y, cx, cy, angle, &tr);
+ rotate(x, y + h, cx, cy, angle, &bl);
+ rotate(x + w, y + h, cx, cy, angle, &br);
+ out->x = GRAFX_min(GRAFX_min(GRAFX_min(tl.x, tr.x), bl.x), br.x);
+ out->y = GRAFX_min(GRAFX_min(GRAFX_min(tl.y, tr.y), bl.y), br.y);
+ out->w = GRAFX_max(GRAFX_max(GRAFX_max(tl.x, tr.x), bl.x), br.x) - out->x;
+ out->h = GRAFX_max(GRAFX_max(GRAFX_max(tl.y, tr.y), bl.y), br.y) - out->y;
+}
+
+inline int sq(int x)
+{
+ return x * x;
+}
+
+inline Fixed fixsq(Fixed x)
+{
+ return fixmul(x, x);
+}
+
+inline int cube(int x)
+{
+ return x * x * x;
+}
+
+inline Fixed fixcube(Fixed x)
+{
+ return fixmul(fixmul(x, x), x);
+}
+
+Fixed *pathX, *pathY;
+int *pathT;
+int curT;
+int nbPts;
+// Uses Lagrange's interpolation polynomial
+// Returns whether or not the curve reached the last point
+// Uses n2DLib's fixed-point numbers, very fast but not very accurate
+int interpolatePathFixed(Fixed _x[], Fixed _y[], int _t[], int nb, Rect *out)
+{
+ static int init = 1;
+ int factor, rx = 0, ry = 0;
+ int i, j;
+ if(init)
+ {
+ pathX = _x; pathY = _y; pathT = _t;
+ curT = 0;
+ init = 0;
+ nbPts = nb;
+ }
+ // One polynomial gives X, the other gives Y
+ // Both are a function of T
+ for(i = 0; i < nbPts; i++)
+ {
+ factor = itofix(1);
+ for(j = 0; j < nbPts; j++)
+ if(i != j)
+ factor = fixdiv(fixmul(factor, itofix(curT - pathT[j])), itofix(pathT[i] - pathT[j]));
+ rx += fixmul(itofix(pathX[i]), factor);
+ ry += fixmul(itofix(pathY[i]), factor);
+ }
+
+ out->x = fixtoi(rx);
+ out->y = fixtoi(ry);
+ if(curT == pathT[nbPts - 1])
+ {
+ init = 1;
+ return 1;
+ }
+ curT++;
+ return 0;
+}
+
+float *fpathX, *fpathY;
+// Same with C floats, much slower but much more accurate
+int interpolatePathFloat(float _x[], float _y[], int _t[], int nb, Rect *out)
+{
+ static int init = 1;
+ float factor, rx = 0., ry = 0.;
+ int i, j;
+ if(init)
+ {
+ fpathX = _x; fpathY = _y; pathT = _t;
+ curT = 0;
+ init = 0;
+ nbPts = nb;
+ }
+
+ for(i = 0; i < nbPts; i++)
+ {
+ factor = 1.;
+ for(j = 0; j < nbPts; j++)
+ if(i != j)
+ factor = factor * (curT - pathT[j]) / (pathT[i] - pathT[j]);
+ rx += fpathX[i] * factor;
+ ry += fpathY[i] * factor;
+ }
+
+ out->x = (int)rx;
+ out->y = (int)ry;
+ if(curT == pathT[nbPts - 1])
+ {
+ init = 1;
+ return 1;
+ }
+ curT++;
+ return 0;
+}
+
+/* *
+ * Graphics *
+ * */
+
+void clearBufferB()
+{
+ int i;
+ for(i = 0; i < 160 * 240; i++)
+ ((unsigned int*)BUFF_BASE_ADDRESS)[i] = 0;
+}
+
+void clearBufferW()
+{
+ int i;
+ for(i = 0; i < 160 * 240; i++)
+ ((unsigned int*)BUFF_BASE_ADDRESS)[i] = 0xffffffff;
+}
+
+void clearBuffer(unsigned short c)
+{
+ int i;
+ unsigned int ci = (c << 16) | c;
+ for(i = 0; i < 160 * 240; i++)
+ *((unsigned int*)BUFF_BASE_ADDRESS + i) = ci;
+}
+
+inline unsigned short getPixel(const unsigned short *src, unsigned int x, unsigned int y)
+{
+ if(x < src[0] && y < src[1])
+ return src[x + y * src[0] + 3];
+ else
+ return src[2];
+}
+
+inline void setPixelUnsafe(unsigned int x, unsigned int y, unsigned short c)
+{
+ *((unsigned short*)BUFF_BASE_ADDRESS + x + y * 320) = c;
+}
+
+inline void setPixel(unsigned int x, unsigned int y, unsigned short c)
+{
+ if(x < 320 && y < 240)
+ *((unsigned short*)BUFF_BASE_ADDRESS + x + y * 320) = c;
+}
+
+inline void setPixelRGB(unsigned int x, unsigned int y, unsigned char r, unsigned char g, unsigned char b)
+{
+ if(x < 320 && y < 240)
+ *((unsigned short*)BUFF_BASE_ADDRESS + x + y * 320) = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
+}
+
+void drawHLine(int y, int x1, int x2, unsigned short c)
+{
+ unsigned int _x1, _x2;
+ if((x1 & x2) >> 31 || x1 + x2 >= 640 || (unsigned)y > 239)
+ {
+ return;
+ }
+
+ if(x1 < x2)
+ {
+ _x1 = GRAFX_max(x1, 0);
+ _x2 = GRAFX_min(x2, 319);
+ }
+ else
+ {
+ _x1 = GRAFX_max(x2, 0);
+ _x2 = GRAFX_min(x1, 319);
+ }
+ for(; _x1 <= _x2; _x1++)
+ setPixelUnsafe(_x1, y, c);
+}
+
+void drawVLine(int x, int y1, int y2, unsigned short c)
+{
+ unsigned int _y1, _y2;
+ if((y1 & y2) >> 31 || y1 + y2 >= 480 || (unsigned)x > 319)
+ {
+ return;
+ }
+
+ if(y1 < y2)
+ {
+ _y1 = GRAFX_max(y1, 0);
+ _y2 = GRAFX_min(y2, 239);
+ }
+ else
+ {
+ _y1 = GRAFX_max(y2, 0);
+ _y2 = GRAFX_min(y1, 239);
+ }
+ for(; _y1 <= _y2; _y1++)
+ setPixelUnsafe(x, _y1, c);
+}
+
+void fillRect(int x, int y, int w, int h, unsigned short c)
+{
+ unsigned int _x = GRAFX_max(x, 0), _y = GRAFX_max(y, 0), _w = GRAFX_min(320 - _x, w - _x + x), _h = GRAFX_min(240 - _y, h - _y + y), i, j;
+ if(_x < 320 && _y < 240)
+ {
+ for(j = _y; j < _y + _h; j++)
+ for(i = _x; i < _x + _w; i++)
+ setPixelUnsafe(i, j, c);
+ }
+}
+
+void drawSprite(const unsigned short *src, int _x, int _y, int flash, unsigned short flashColor)
+{
+ int x, y, w = src[0] + _x, h = src[1] + _y, c = 3;
+ for(y = _y; y < h; y++)
+ {
+ for(x = _x; x < w; x++, c++)
+ {
+ if(src[c] != src[2])
+ setPixel(x, y, flash ? flashColor : src[c]);
+ }
+ if(y > 239) break;
+ }
+}
+
+void drawSpritePart(const unsigned short *src, int _x, int _y, const Rect* part, int flash, unsigned short flashColor)
+{
+ unsigned short c;
+ int x, y, w = part->w + _x, h = part->h + _y, z = part->x, t = part->y;
+ for(y = _y; y < h; y++, t++)
+ {
+ for(x = _x, z = part->x; x < w; x++, z++)
+ {
+ c = getPixel(src, z, t);
+ if(c != src[2])
+ setPixel(x, y, flash ? flashColor : c);
+ if(x > 319) break;
+ }
+ if(y > 239) break;
+ }
+}
+
+void drawSpriteScaled(const unsigned short* source, const Rect* info, int flash, unsigned short flashColor)
+{
+ Fixed dx = itofix(source[0]) / info->w;
+ Fixed dy = itofix(source[1]) / info->h;
+ int x, y, _x = info->x + info->w / 2, _y = info->y + info->h / 2;
+ Fixed tx = 0, ty = 0;
+ unsigned short c;
+
+ for(y = info->y - info->h / 2; y < _y; y++, ty += dy)
+ {
+ for(x = info->x - info->w / 2, tx = 0; x < _x; x++, tx += dx)
+ {
+ c = getPixel(source, fixtoi(tx), fixtoi(ty));
+ if(c != source[2])
+ setPixel(x, y, flash ? flashColor : c);
+ if(x > 319) break;
+ }
+ if(y > 239) break;
+ }
+}
+
+void drawSpriteRotated(const unsigned short* source, const Rect* sr, const Rect* rc, Fixed angle, int flash, unsigned short flashColor)
+{
+ Rect defaultRect = { source[0] / 2, source[1] / 2, 0, 0 };
+ Rect fr;
+ unsigned short currentPixel;
+ Fixed dX = fixcos(angle), dY = fixsin(angle);
+
+ if(rc == NULL)
+ rc = &defaultRect;
+
+ getBoundingBox(-rc->x, -rc->y, source[0], source[1], 0, 0, angle, &fr);
+ fr.x += sr->x;
+ fr.y += sr->y;
+ fr.w += fr.x;
+ fr.h += fr.y;
+
+ Rect cp, lsp, cdrp;
+
+ // Feed fixed-point to get fixed-point
+ rotate(itofix(fr.x - sr->x), itofix(fr.y - sr->y), 0, 0, -angle, &lsp);
+
+ for(cp.y = fr.y; cp.y <= fr.h; cp.y++)
+ {
+ cdrp.x = lsp.x;
+ cdrp.y = lsp.y;
+
+ for(cp.x = fr.x; cp.x <= fr.w; cp.x++)
+ {
+ if(cp.x >= 0 && cp.x < 320 && cp.y >= 0 && cp.y < 240)
+ {
+ currentPixel = getPixel(source, fixtoi(cdrp.x) + rc->x, fixtoi(cdrp.y) + rc->y);
+ if(currentPixel != source[2])
+ {
+ setPixelUnsafe(cp.x, cp.y, flash ? flashColor : currentPixel);
+ }
+ }
+ cdrp.x += dX;
+ cdrp.y += dY;
+ }
+
+ lsp.x -= dY;
+ lsp.y += dX;
+ }
+}
+
+/* *
+ * Geometry *
+ * */
+
+void drawLine(int x1, int y1, int x2, int y2, unsigned short c)
+{
+ int dx = abs(x2-x1);
+ int dy = abs(y2-y1);
+ int sx = (x1 < x2)?1:-1;
+ int sy = (y1 < y2)?1:-1;
+ int err = dx-dy;
+ int e2;
+
+ while (!(x1 == x2 && y1 == y2))
+ {
+ setPixel(x1,y1,c);
+ e2 = 2*err;
+ if (e2 > -dy)
+ {
+ err = err - dy;
+ x1 = x1 + sx;
+ }
+ if (e2 < dx)
+ {
+ err = err + dx;
+ y1 = y1 + sy;
+ }
+ }
+}
+
+void drawPolygon(unsigned short c, int pointsNb, ...)
+// color, ,
+{
+ // the number of arguments in the <...> must be even
+ int i;
+ int *pointsList = (int *)malloc(pointsNb * 2 * sizeof(int));
+
+ if (!pointsList) return;
+
+ va_list ap;
+ int cur_arg = 1;
+
+ va_start(ap, pointsNb);
+
+ for (i = 0; i < pointsNb * 2; i++)
+ {
+ cur_arg = va_arg(ap, int);
+ *(pointsList + i) = cur_arg;
+ }
+
+ for (i = 0; i < pointsNb * 2 - 2; i += 2)
+ {
+ drawLine(*(pointsList + i), *(pointsList + i + 1), *(pointsList + i + 2), *(pointsList + i + 3), c);
+ }
+ drawLine(*(pointsList + pointsNb*2 - 2), *(pointsList + pointsNb*2 - 1), *(pointsList), *(pointsList + 1), c);
+ va_end(ap);
+ free(pointsList);
+}
+
+void fillCircle(int x, int y, int radius, unsigned short c)
+{
+ int i,j;
+ for(j=-radius; j<=radius; j++)
+ for(i=-radius; i<=radius; i++)
+ if(i*i+j*j <= radius*radius)
+ setPixel(x + i, y + j, c);
+}
+
+/* /!\ for circle and ellispe, the x and y must be the center of the shape, not the top-left point /!\ */
+
+void fillEllipse(int x, int y, int w, int h, unsigned short c)
+{
+ int i,j;
+ for(j=-h; j<=h; j++)
+ for(i=-w; i<=w; i++)
+ if(i*i*h*h+j*j*w*w <= h*h*w*w)
+ setPixel(x + i, y + j, c);
+}
+
+/* *
+ * Text *
+ * */
+
+int isOutlinePixel(unsigned char* charfont, int x, int y)
+{
+ int xis0 = !x, xis7 = x == 7, yis0 = !y, yis7 = y == 7;
+
+ if(xis0)
+ {
+ if(yis0)
+ {
+ return !(*charfont & 0x80) && ((*charfont & 0x40) || (charfont[1] & 0x80) || (charfont[1] & 0x40));
+ }
+ else if(yis7)
+ {
+ return !(charfont[7] & 0x80) && ((charfont[7] & 0x40) || (charfont[6] & 0x80) || (charfont[6] & 0x40));
+ }
+ else
+ {
+ return !(charfont[y] & 0x80) && (
+ (charfont[y - 1] & 0x80) || (charfont[y - 1] & 0x40) ||
+ (charfont[y] & 0x40) ||
+ (charfont[y + 1] & 0x80) || (charfont[y + 1] & 0x40));
+ }
+ }
+ else if(xis7)
+ {
+ if(yis0)
+ {
+ return !(*charfont & 0x01) && ((*charfont & 0x02) || (charfont[1] & 0x01) || (charfont[1] & 0x02));
+ }
+ else if(yis7)
+ {
+ return !(charfont[7] & 0x01) && ((charfont[7] & 0x02) || (charfont[6] & 0x01) || (charfont[6] & 0x02));
+ }
+ else
+ {
+ return !(charfont[y] & 0x01) && (
+ (charfont[y - 1] & 0x01) || (charfont[y - 1] & 0x02) ||
+ (charfont[y] & 0x02) ||
+ (charfont[y + 1] & 0x01) || (charfont[y + 1] & 0x02));
+ }
+ }
+ else
+ {
+ char b = 1 << (7 - x);
+ if(yis0)
+ {
+ return !(*charfont & b) && (
+ (*charfont & (b << 1)) || (*charfont & (b >> 1)) ||
+ (charfont[1] & (b << 1)) || (charfont[1] & b) || (charfont[1] & (b >> 1)));
+ }
+ else if(yis7)
+ {
+ return !(charfont[7] & b) && (
+ (charfont[7] & (b << 1)) || (charfont[7] & (b >> 1)) ||
+ (charfont[6] & (b << 1)) || (charfont[6] & b) || (charfont[6] & (b >> 1)));
+ }
+ else
+ {
+ return !(charfont[y] & b) && (
+ (charfont[y] & (b << 1)) || (charfont[y] & (b >> 1)) ||
+ (charfont[y - 1] & (b << 1)) || (charfont[y - 1] & b) || (charfont[y - 1] & (b >> 1)) ||
+ (charfont[y + 1] & (b << 1)) || (charfont[y + 1] & b) || (charfont[y + 1] & (b >> 1)));
+ }
+ }
+}
+
+void drawChar(int *x, int *y, int margin, char ch, unsigned short fc, unsigned short olc)
+{
+ int i, j;
+ unsigned char *charSprite;
+ if(ch == '\n')
+ {
+ *x = margin;
+ *y += 8;
+ }
+ else if(*y < 239)
+ {
+ charSprite = ch * 8 + n2DLib_font;
+ // Draw charSprite as monochrome 8*8 image using given color
+ for(i = 0; i < 8; i++)
+ {
+ for(j = 7; j >= 0; j--)
+ {
+ if((charSprite[i] >> j) & 1)
+ setPixel(*x + (7 - j), *y + i, fc);
+ else if(isOutlinePixel(charSprite, 7 - j, i))
+ setPixel(*x + (7 - j), *y + i, olc);
+ }
+ }
+ *x += 8;
+ }
+}
+
+void drawString(int *x, int *y, int _x, const char *str, unsigned short fc, unsigned short olc)
+{
+ int i, GRAFX_max = strlen(str) + 1;
+ for(i = 0; i < GRAFX_max; i++)
+ drawChar(x, y, _x, str[i], fc, olc);
+}
+
+void drawDecimal(int *x, int *y, int n, unsigned short fc, unsigned short olc)
+{
+ // Ints are in [-2147483648, 2147483647]
+ // | |
+ int divisor = 1000000000, num, numHasStarted = 0;
+
+ if(n < 0)
+ {
+ drawChar(x, y, 0, '-', fc, olc);
+ n = -n;
+ }
+ while(divisor != 0)
+ {
+ num = n / divisor;
+ if(divisor == 1 || num != 0 || numHasStarted)
+ {
+ numHasStarted = 1;
+ drawChar(x, y, 0, num + '0', fc, olc);
+ }
+ n %= divisor;
+ divisor /= 10;
+ }
+}
+
+void drawStringF(int *x, int *y, int _x, unsigned short fc, unsigned short olc, const char *s, ...)
+{
+ va_list specialArgs;
+ char str[1200] = { 0 };
+ // GRAFX_max nb of chars on-screen
+
+ va_start(specialArgs, s);
+ vsprintf(str, s, specialArgs);
+ drawString(x, y, _x, str, fc, olc);
+ va_end(specialArgs);
+}
+
+int numberWidth(int n)
+{
+ // Ints are in [-2147483648, 2147483647]
+ int divisor = 10, result = 8;
+
+ if(n < 0)
+ {
+ result += 8;
+ n = -n;
+ }
+
+ while(1)
+ {
+ if(n < divisor)
+ return result;
+ result += 8;
+ divisor *= 10;
+ }
+}
+
+int stringWidth(const char* s)
+{
+ int i, result = 0, size = strlen(s);
+ for(i = 0; i < size; i++)
+ {
+ if(s[i] >= 0x20)
+ result += 8;
+ }
+ return result;
+}
+
+/* *
+ * Miscellaneous *
+ * */
+int get_key_pressed(t_key* report)
+{
+ unsigned short rowmap;
+ int col, row;
+ unsigned short *KEY_DATA = (unsigned short*)0x900E0010;
+ int gotKey = 0;
+
+ report->row = report->tpad_row = _KEY_DUMMY_ROW;
+ report->col = report->tpad_col = _KEY_DUMMY_COL;
+ report->tpad_arrow = TPAD_ARROW_NONE;
+
+ // Touchpad and clickpad keyboards have different keymapping
+ for(row = 0; row < 8; row++)
+ {
+ rowmap = has_colors ? KEY_DATA[row] : ~KEY_DATA[row];
+ for(col = 1; col <= 0x400; col <<= 1)
+ {
+ if(rowmap & col)
+ {
+ gotKey = 1;
+ break;
+ }
+ }
+ if(gotKey) break;
+ }
+ if(gotKey)
+ {
+ row *= 2;
+ row += 0x10;
+ if(is_touchpad)
+ {
+ report->tpad_row = row;
+ report->tpad_col = col;
+ }
+ else
+ {
+ report->row = row;
+ report->col = col;
+ }
+ return 1;
+ }
+ else
+ return 0;
+}
+
+inline int isKey(t_key k1, t_key k2)
+{
+ return is_touchpad ? (k1.tpad_arrow == k2.tpad_arrow ? k1.tpad_row == k2.tpad_row && k1.tpad_col == k2.tpad_col : 0 )
+ : k1.row == k2.row && k1.col == k2.col;
+}
+
+// Loads a 24-bits bitmap image into an n2DLib-compatible unsigned short* array
+unsigned short * loadBMP(const char *path, unsigned short transparency)
+{
+ int size, width, height, offset, i, j, padding;
+ uint16_t *returnValue;
+ FILE *temp = fopen(path, "rb");
+
+ if(!temp) return NULL;
+ // Check if the file's 2 first char are BM (indicates bitmap)
+ if(!(fgetc(temp) == 0x42 && fgetc(temp) == 0x4d))
+ {
+ printf("Image is not a bitmap\n");
+ fclose(temp);
+ return NULL;
+ }
+
+ // Check if the file is in 24 bpp
+ fseek(temp, 0x1c, SEEK_SET);
+ if(fgetc(temp) != 24)
+ {
+ printf("Wrong format : bitmap must use 24 bpp\n");
+ fclose(temp);
+ return NULL;
+ }
+
+ // Get the 4-bytes pixel width and height, situated respectively at 0x12 and 0x16
+ fseek(temp, 0x12, SEEK_SET);
+ width = fgetc(temp) | (fgetc(temp) << 8) | (fgetc(temp) << 16) | (fgetc(temp) << 24);
+ //fseek(temp, 0x16, SEEK_SET);
+ height = fgetc(temp) | (fgetc(temp) << 8) | (fgetc(temp) << 16) | (fgetc(temp) << 24);
+ size = width * height + 3; // include header
+
+ // Gets the 4-bytes offset to the start of the pixel table, situated at 0x0a
+ fseek(temp, 0x0a, SEEK_SET);
+ offset = fgetc(temp) | (fgetc(temp) << 8) | (fgetc(temp) << 16) | (fgetc(temp) << 24);
+
+ fseek(temp, offset, SEEK_SET);
+
+ returnValue = malloc(size * sizeof(unsigned short));
+ if(!returnValue)
+ {
+ printf("Couldn't allocate memory\n");
+ fclose(temp);
+ return NULL;
+ }
+ returnValue[0] = width;
+ returnValue[1] = height;
+ returnValue[2] = transparency;
+ padding = 4 - (width * 3 - (width * 3 / 4) * 4);
+ padding %= 4;
+ for(j = height - 1; j >= 0; j--)
+ {
+ for(i = 0; i < width; i++)
+ returnValue[j * width + i + 3] = (unsigned short)((fgetc(temp) >> 3) | ((fgetc(temp) >> 2) << 5) | ((fgetc(temp) >> 3) << 11));
+ // Skip padding for widths that are not a multiple of 4
+ for(i = 0; i < padding; i++)
+ fgetc(temp);
+ }
+
+ fclose(temp);
+ return returnValue;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/ion/src/simulator/external/n2DLib/n2DLib.h b/ion/src/simulator/external/n2DLib/n2DLib.h
new file mode 100644
index 00000000000..7bc2ca715a0
--- /dev/null
+++ b/ion/src/simulator/external/n2DLib/n2DLib.h
@@ -0,0 +1,83 @@
+#ifndef INCLUDE_GRAFX
+#define INCLUDE_GRAFX
+
+#include
+#include
+
+#define GRAFX_min(a, b) ((a) < (b) ? (a) : (b))
+#define GRAFX_max(a, b) ((a) > (b) ? (a) : (b))
+
+typedef int Fixed;
+typedef struct
+{
+ int x;
+ int y;
+ int h;
+ int w;
+} Rect;
+
+#define itofix(x) ((x) << 8)
+#define fixtoi(x) ((x) >> 8)
+#define fixdiv(x, y) (((x) << 8) / (y))
+#define clamp(x, a, b) GRAFX_min(GRAFX_max(x, a), b)
+#define sign(x) ((x) < 0)
+#define fixsin(x) fixcos((x) - 64)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern Fixed fixmul(Fixed x, Fixed y);
+extern Fixed fixcos(Fixed angle);
+extern void rotate(int x, int y, int cx, int cy, Fixed angle, Rect* out);
+extern void getBoundingBox(int x, int y, int w, int h, int cx, int cy, Fixed angle, Rect *out);
+extern int sq(int);
+extern Fixed fixsq(Fixed);
+extern int cube(int);
+extern Fixed fixcube(Fixed);
+extern int interpolatePathFixed(Fixed[], Fixed[], int[], int, Rect*);
+extern int interpolatePathFloat(float[], float[], int[], int, Rect*);
+
+extern void initBuffering();
+extern void updateScreen();
+extern void deinitBuffering();
+extern void timer_init(unsigned);
+extern void timer_restore(unsigned);
+extern void timer_load(unsigned, unsigned);
+extern unsigned timer_read(unsigned);
+extern void clearBufferB();
+extern void clearBufferW();
+extern void clearBuffer(unsigned short);
+extern unsigned short getPixel(const unsigned short*, unsigned int, unsigned int);
+extern void setPixelUnsafe(unsigned int, unsigned int, unsigned short);
+extern void setPixel(unsigned int, unsigned int, unsigned short);
+extern void setPixelRGB(unsigned int, unsigned int, unsigned char, unsigned char, unsigned char);
+extern void drawHLine(int, int, int, unsigned short);
+extern void drawVLine(int, int, int, unsigned short);
+extern void fillRect(int, int, int, int, unsigned short);
+extern void drawSprite(const unsigned short*, int, int, int, unsigned short);
+extern void drawSpritePart(const unsigned short*, int, int, const Rect*, int, unsigned short);
+extern void drawSpriteScaled(const unsigned short*, const Rect*, int, unsigned short);
+extern void drawSpriteRotated(const unsigned short*, const Rect*, const Rect*, Fixed, int, unsigned short);
+extern void drawLine(int, int, int, int, unsigned short);
+extern void drawPolygon(unsigned short, int, ...);
+extern void fillCircle(int, int, int, unsigned short);
+extern void fillEllipse(int, int, int, int, unsigned short);
+extern void drawString(int*, int*, int, const char*, unsigned short, unsigned short);
+extern void drawDecimal(int*, int*, int, unsigned short, unsigned short);
+extern void drawChar(int*, int*, int, char, unsigned short, unsigned short);
+extern void drawStringF(int*, int*, int, unsigned short, unsigned short, const char*, ...);
+extern int numberWidth(int);
+extern int stringWidth(const char*);
+extern int get_key_pressed(t_key*);
+extern int isKey(t_key, t_key);
+
+extern unsigned short * loadBMP(const char*, unsigned short);
+
+#define BUFF_BYTES_SIZE (320*240*2)
+extern unsigned short *BUFF_BASE_ADDRESS;
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/ion/src/simulator/external/n2DLib/n2DLib_font.h b/ion/src/simulator/external/n2DLib/n2DLib_font.h
new file mode 100644
index 00000000000..8f04ed50ff5
--- /dev/null
+++ b/ion/src/simulator/external/n2DLib/n2DLib_font.h
@@ -0,0 +1,259 @@
+static unsigned char n2DLib_font[] = /* NSDL_FONT_VGA */
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 000 (.)
+ 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E, // Char 001 (.)
+ 0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E, // Char 002 (.)
+ 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, // Char 003 (.)
+ 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, // Char 004 (.)
+ 0x38, 0x7C, 0x38, 0xFE, 0xFE, 0x7C, 0x38, 0x7C, // Char 005 (.)
+ 0x10, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x7C, // Char 006 (.)
+ 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00, // Char 007 (.)
+ 0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF, // Char 008 (.)
+ 0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00, // Char 009 (.)
+ 0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF, // Char 010 (.)
+ 0x0F, 0x07, 0x0F, 0x7D, 0xCC, 0xCC, 0xCC, 0x78, // Char 011 (.)
+ 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, // Char 012 (.)
+ 0x3F, 0x33, 0x3F, 0x30, 0x30, 0x70, 0xF0, 0xE0, // Char 013 (.)
+ 0x7F, 0x63, 0x7F, 0x63, 0x63, 0x67, 0xE6, 0xC0, // Char 014 (.)
+ 0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99, // Char 015 (.)
+ 0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00, // Char 016 (.)
+ 0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00, // Char 017 (.)
+ 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x7E, 0x3C, 0x18, // Char 018 (.)
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, // Char 019 (.)
+ 0x7F, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, 0x00, // Char 020 (.)
+ 0x3E, 0x63, 0x38, 0x6C, 0x6C, 0x38, 0xCC, 0x78, // Char 021 (.)
+ 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x00, // Char 022 (.)
+ 0x18, 0x3C, 0x7E, 0x18, 0x7E, 0x3C, 0x18, 0xFF, // Char 023 (.)
+ 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00, // Char 024 (.)
+ 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, // Char 025 (.)
+ 0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00, // Char 026 (.)
+ 0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00, // Char 027 (.)
+ 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00, // Char 028 (.)
+ 0x00, 0x24, 0x66, 0xFF, 0x66, 0x24, 0x00, 0x00, // Char 029 (.)
+ 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x00, 0x00, // Char 030 (.)
+ 0x00, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00, 0x00, // Char 031 (.)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 032 ( )
+ 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, // Char 033 (!)
+ 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 034 (")
+ 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, // Char 035 (#)
+ 0x30, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x30, 0x00, // Char 036 ($)
+ 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00, // Char 037 (%)
+ 0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00, // Char 038 (&)
+ 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 039 (')
+ 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, // Char 040 (()
+ 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, // Char 041 ())
+ 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // Char 042 (*)
+ 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00, // Char 043 (+)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, // Char 044 (,)
+ 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, // Char 045 (-)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, // Char 046 (.)
+ 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, // Char 047 (/)
+ 0x7C, 0xC6, 0xCE, 0xDE, 0xF6, 0xE6, 0x7C, 0x00, // Char 048 (0)
+ 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, // Char 049 (1)
+ 0x78, 0xCC, 0x0C, 0x38, 0x60, 0xCC, 0xFC, 0x00, // Char 050 (2)
+ 0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00, // Char 051 (3)
+ 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00, // Char 052 (4)
+ 0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00, // Char 053 (5)
+ 0x38, 0x60, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, // Char 054 (6)
+ 0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, // Char 055 (7)
+ 0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, // Char 056 (8)
+ 0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0x18, 0x70, 0x00, // Char 057 (9)
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, // Char 058 (:)
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, // Char 059 (;)
+ 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, // Char 060 (<)
+ 0x00, 0x00, 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00, // Char 061 (=)
+ 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, // Char 062 (>)
+ 0x78, 0xCC, 0x0C, 0x18, 0x30, 0x00, 0x30, 0x00, // Char 063 (?)
+ 0x7C, 0xC6, 0xDE, 0xDE, 0xDE, 0xC0, 0x78, 0x00, // Char 064 (@)
+ 0x30, 0x78, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0x00, // Char 065 (A)
+ 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, // Char 066 (B)
+ 0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x3C, 0x00, // Char 067 (C)
+ 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, // Char 068 (D)
+ 0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00, // Char 069 (E)
+ 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00, // Char 070 (F)
+ 0x3C, 0x66, 0xC0, 0xC0, 0xCE, 0x66, 0x3E, 0x00, // Char 071 (G)
+ 0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, // Char 072 (H)
+ 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, // Char 073 (I)
+ 0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, // Char 074 (J)
+ 0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00, // Char 075 (K)
+ 0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, // Char 076 (L)
+ 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00, // Char 077 (M)
+ 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, // Char 078 (N)
+ 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, // Char 079 (O)
+ 0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, // Char 080 (P)
+ 0x78, 0xCC, 0xCC, 0xCC, 0xDC, 0x78, 0x1C, 0x00, // Char 081 (Q)
+ 0xFC, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0xE6, 0x00, // Char 082 (R)
+ 0x78, 0xCC, 0xE0, 0x70, 0x1C, 0xCC, 0x78, 0x00, // Char 083 (S)
+ 0xFC, 0xB4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, // Char 084 (T)
+ 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00, // Char 085 (U)
+ 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, // Char 086 (V)
+ 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00, // Char 087 (W)
+ 0xC6, 0xC6, 0x6C, 0x38, 0x38, 0x6C, 0xC6, 0x00, // Char 088 (X)
+ 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x78, 0x00, // Char 089 (Y)
+ 0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00, // Char 090 (Z)
+ 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, // Char 091 ([)
+ 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, // Char 092 (\)
+ 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, // Char 093 (])
+ 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, // Char 094 (^)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // Char 095 (_)
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 096 (`)
+ 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, // Char 097 (a)
+ 0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0xDC, 0x00, // Char 098 (b)
+ 0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00, // Char 099 (c)
+ 0x1C, 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0x76, 0x00, // Char 100 (d)
+ 0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, // Char 101 (e)
+ 0x38, 0x6C, 0x60, 0xF0, 0x60, 0x60, 0xF0, 0x00, // Char 102 (f)
+ 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, // Char 103 (g)
+ 0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00, // Char 104 (h)
+ 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, // Char 105 (i)
+ 0x0C, 0x00, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, // Char 106 (j)
+ 0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00, // Char 107 (k)
+ 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, // Char 108 (l)
+ 0x00, 0x00, 0xCC, 0xFE, 0xFE, 0xD6, 0xC6, 0x00, // Char 109 (m)
+ 0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, // Char 110 (n)
+ 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, // Char 111 (o)
+ 0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0, // Char 112 (p)
+ 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E, // Char 113 (q)
+ 0x00, 0x00, 0xDC, 0x76, 0x66, 0x60, 0xF0, 0x00, // Char 114 (r)
+ 0x00, 0x00, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x00, // Char 115 (s)
+ 0x10, 0x30, 0x7C, 0x30, 0x30, 0x34, 0x18, 0x00, // Char 116 (t)
+ 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // Char 117 (u)
+ 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, // Char 118 (v)
+ 0x00, 0x00, 0xC6, 0xD6, 0xFE, 0xFE, 0x6C, 0x00, // Char 119 (w)
+ 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, // Char 120 (x)
+ 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, // Char 121 (y)
+ 0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00, // Char 122 (z)
+ 0x1C, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x1C, 0x00, // Char 123 ({)
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, // Char 124 (|)
+ 0xE0, 0x30, 0x30, 0x1C, 0x30, 0x30, 0xE0, 0x00, // Char 125 (})
+ 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 126 (~)
+ 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0x00, // Char 127 (.)
+ 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x18, 0x0C, 0x78, // Char 128 (.)
+ 0x00, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0x7E, 0x00, // Char 129 (.)
+ 0x1C, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, // Char 130 (.)
+ 0x7E, 0xC3, 0x3C, 0x06, 0x3E, 0x66, 0x3F, 0x00, // Char 131 (.)
+ 0xCC, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x7E, 0x00, // Char 132 (.)
+ 0xE0, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x7E, 0x00, // Char 133 (.)
+ 0x30, 0x30, 0x78, 0x0C, 0x7C, 0xCC, 0x7E, 0x00, // Char 134 (.)
+ 0x00, 0x00, 0x78, 0xC0, 0xC0, 0x78, 0x0C, 0x38, // Char 135 (.)
+ 0x7E, 0xC3, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, // Char 136 (.)
+ 0xCC, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, // Char 137 (.)
+ 0xE0, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, // Char 138 (.)
+ 0xCC, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, // Char 139 (.)
+ 0x7C, 0xC6, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 140 (.)
+ 0xE0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, // Char 141 (.)
+ 0xC6, 0x38, 0x6C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, // Char 142 (.)
+ 0x30, 0x30, 0x00, 0x78, 0xCC, 0xFC, 0xCC, 0x00, // Char 143 (.)
+ 0x1C, 0x00, 0xFC, 0x60, 0x78, 0x60, 0xFC, 0x00, // Char 144 (.)
+ 0x00, 0x00, 0x7F, 0x0C, 0x7F, 0xCC, 0x7F, 0x00, // Char 145 (.)
+ 0x3E, 0x6C, 0xCC, 0xFE, 0xCC, 0xCC, 0xCE, 0x00, // Char 146 (.)
+ 0x78, 0xCC, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, // Char 147 (.)
+ 0x00, 0xCC, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, // Char 148 (.)
+ 0x00, 0xE0, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, // Char 149 (.)
+ 0x78, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0x7E, 0x00, // Char 150 (.)
+ 0x00, 0xE0, 0x00, 0xCC, 0xCC, 0xCC, 0x7E, 0x00, // Char 151 (.)
+ 0x00, 0xCC, 0x00, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, // Char 152 (.)
+ 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x18, 0x00, // Char 153 (.)
+ 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, // Char 154 (.)
+ 0x18, 0x18, 0x7E, 0xC0, 0xC0, 0x7E, 0x18, 0x18, // Char 155 (.)
+ 0x38, 0x6C, 0x64, 0xF0, 0x60, 0xE6, 0xFC, 0x00, // Char 156 (.)
+ 0xCC, 0xCC, 0x78, 0xFC, 0x30, 0xFC, 0x30, 0x30, // Char 157 (.)
+ 0xF8, 0xCC, 0xCC, 0xFA, 0xC6, 0xCF, 0xC6, 0xC7, // Char 158 (.)
+ 0x0E, 0x1B, 0x18, 0x3C, 0x18, 0x18, 0xD8, 0x70, // Char 159 (.)
+ 0x1C, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x7E, 0x00, // Char 160 (.)
+ 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, // Char 161 (.)
+ 0x00, 0x1C, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, // Char 162 (.)
+ 0x00, 0x1C, 0x00, 0xCC, 0xCC, 0xCC, 0x7E, 0x00, // Char 163 (.)
+ 0x00, 0xF8, 0x00, 0xF8, 0xCC, 0xCC, 0xCC, 0x00, // Char 164 (.)
+ 0xFC, 0x00, 0xCC, 0xEC, 0xFC, 0xDC, 0xCC, 0x00, // Char 165 (.)
+ 0x3C, 0x6C, 0x6C, 0x3E, 0x00, 0x7E, 0x00, 0x00, // Char 166 (.)
+ 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x7C, 0x00, 0x00, // Char 167 (.)
+ 0x30, 0x00, 0x30, 0x60, 0xC0, 0xCC, 0x78, 0x00, // Char 168 (.)
+ 0x00, 0x00, 0x00, 0xFC, 0xC0, 0xC0, 0x00, 0x00, // Char 169 (.)
+ 0x00, 0x00, 0x00, 0xFC, 0x0C, 0x0C, 0x00, 0x00, // Char 170 (.)
+ 0xC3, 0xC6, 0xCC, 0xDE, 0x33, 0x66, 0xCC, 0x0F, // Char 171 (.)
+ 0xC3, 0xC6, 0xCC, 0xDB, 0x37, 0x6F, 0xCF, 0x03, // Char 172 (.)
+ 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, // Char 173 (.)
+ 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00, // Char 174 (.)
+ 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00, // Char 175 (.)
+ 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, // Char 176 (.)
+ 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, // Char 177 (.)
+ 0xDB, 0x77, 0xDB, 0xEE, 0xDB, 0x77, 0xDB, 0xEE, // Char 178 (.)
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // Char 179 (.)
+ 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, // Char 180 (.)
+ 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, // Char 181 (.)
+ 0x36, 0x36, 0x36, 0x36, 0xF6, 0x36, 0x36, 0x36, // Char 182 (.)
+ 0x00, 0x00, 0x00, 0x00, 0xFE, 0x36, 0x36, 0x36, // Char 183 (.)
+ 0x00, 0x00, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, // Char 184 (.)
+ 0x36, 0x36, 0xF6, 0x06, 0xF6, 0x36, 0x36, 0x36, // Char 185 (.)
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, // Char 186 (.)
+ 0x00, 0x00, 0xFE, 0x06, 0xF6, 0x36, 0x36, 0x36, // Char 187 (.)
+ 0x36, 0x36, 0xF6, 0x06, 0xFE, 0x00, 0x00, 0x00, // Char 188 (.)
+ 0x36, 0x36, 0x36, 0x36, 0xFE, 0x00, 0x00, 0x00, // Char 189 (.)
+ 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x00, 0x00, 0x00, // Char 190 (.)
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, // Char 191 (.)
+ 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, // Char 192 (.)
+ 0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, // Char 193 (.)
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, // Char 194 (.)
+ 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, // Char 195 (.)
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, // Char 196 (.)
+ 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, // Char 197 (.)
+ 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, // Char 198 (.)
+ 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, // Char 199 (.)
+ 0x36, 0x36, 0x37, 0x30, 0x3F, 0x00, 0x00, 0x00, // Char 200 (.)
+ 0x00, 0x00, 0x3F, 0x30, 0x37, 0x36, 0x36, 0x36, // Char 201 (.)
+ 0x36, 0x36, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0x00, // Char 202 (.)
+ 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x36, 0x36, 0x36, // Char 203 (.)
+ 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, // Char 204 (.)
+ 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, // Char 205 (.)
+ 0x36, 0x36, 0xF7, 0x00, 0xF7, 0x36, 0x36, 0x36, // Char 206 (.)
+ 0x18, 0x18, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, // Char 207 (.)
+ 0x36, 0x36, 0x36, 0x36, 0xFF, 0x00, 0x00, 0x00, // Char 208 (.)
+ 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x18, 0x18, 0x18, // Char 209 (.)
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x36, 0x36, 0x36, // Char 210 (.)
+ 0x36, 0x36, 0x36, 0x36, 0x3F, 0x00, 0x00, 0x00, // Char 211 (.)
+ 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x00, 0x00, 0x00, // Char 212 (.)
+ 0x00, 0x00, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, // Char 213 (.)
+ 0x00, 0x00, 0x00, 0x00, 0x3F, 0x36, 0x36, 0x36, // Char 214 (.)
+ 0x36, 0x36, 0x36, 0x36, 0xFF, 0x36, 0x36, 0x36, // Char 215 (.)
+ 0x18, 0x18, 0xFF, 0x18, 0xFF, 0x18, 0x18, 0x18, // Char 216 (.)
+ 0x18, 0x18, 0x18, 0x18, 0xF8, 0x00, 0x00, 0x00, // Char 217 (.)
+ 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18, // Char 218 (.)
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Char 219 (.)
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // Char 220 (.)
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, // Char 221 (.)
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, // Char 222 (.)
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // Char 223 (.)
+ 0x00, 0x00, 0x76, 0xDC, 0xC8, 0xDC, 0x76, 0x00, // Char 224 (.)
+ 0x00, 0x78, 0xCC, 0xF8, 0xCC, 0xF8, 0xC0, 0xC0, // Char 225 (.)
+ 0x00, 0xFC, 0xCC, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, // Char 226 (.)
+ 0x00, 0xFE, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, // Char 227 (.)
+ 0xFC, 0xCC, 0x60, 0x30, 0x60, 0xCC, 0xFC, 0x00, // Char 228 (.)
+ 0x00, 0x00, 0x7E, 0xD8, 0xD8, 0xD8, 0x70, 0x00, // Char 229 (.)
+ 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0xC0, // Char 230 (.)
+ 0x00, 0x76, 0xDC, 0x18, 0x18, 0x18, 0x18, 0x00, // Char 231 (.)
+ 0xFC, 0x30, 0x78, 0xCC, 0xCC, 0x78, 0x30, 0xFC, // Char 232 (.)
+ 0x38, 0x6C, 0xC6, 0xFE, 0xC6, 0x6C, 0x38, 0x00, // Char 233 (.)
+ 0x38, 0x6C, 0xC6, 0xC6, 0x6C, 0x6C, 0xEE, 0x00, // Char 234 (.)
+ 0x1C, 0x30, 0x18, 0x7C, 0xCC, 0xCC, 0x78, 0x00, // Char 235 (.)
+ 0x00, 0x00, 0x7E, 0xDB, 0xDB, 0x7E, 0x00, 0x00, // Char 236 (.)
+ 0x06, 0x0C, 0x7E, 0xDB, 0xDB, 0x7E, 0x60, 0xC0, // Char 237 (.)
+ 0x38, 0x60, 0xC0, 0xF8, 0xC0, 0x60, 0x38, 0x00, // Char 238 (.)
+ 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, // Char 239 (.)
+ 0x00, 0xFC, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0x00, // Char 240 (.)
+ 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0xFC, 0x00, // Char 241 (.)
+ 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xFC, 0x00, // Char 242 (.)
+ 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xFC, 0x00, // Char 243 (.)
+ 0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18, 0x18, 0x18, // Char 244 (.)
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0xD8, 0x70, // Char 245 (.)
+ 0x30, 0x30, 0x00, 0xFC, 0x00, 0x30, 0x30, 0x00, // Char 246 (.)
+ 0x00, 0x76, 0xDC, 0x00, 0x76, 0xDC, 0x00, 0x00, // Char 247 (.)
+ 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, // Char 248 (.)
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, // Char 249 (.)
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, // Char 250 (.)
+ 0x0F, 0x0C, 0x0C, 0x0C, 0xEC, 0x6C, 0x3C, 0x1C, // Char 251 (.)
+ 0x78, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, // Char 252 (.)
+ 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00, // Char 253 (.)
+ 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, // Char 254 (.)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Char 255 (.)
+};
\ No newline at end of file
diff --git a/ion/src/simulator/nspire/Makefile b/ion/src/simulator/nspire/Makefile
new file mode 100644
index 00000000000..a12cf54d094
--- /dev/null
+++ b/ion/src/simulator/nspire/Makefile
@@ -0,0 +1,44 @@
+
+ion_src += $(addprefix ion/src/simulator/nspire/, \
+ main.cpp \
+ clipboard.cpp \
+ display.cpp \
+ framebuffer.cpp \
+ telemetry_init.cpp \
+ keyboard.cpp \
+ events_keyboard.cpp \
+ events.cpp \
+ timing.cpp \
+ console.cpp \
+)
+
+ion_src += ion/src/shared/collect_registers.cpp
+
+sdl_simu_needs_to_be_removed += $(addprefix ion/src/simulator/shared/, \
+ main.cpp \
+ clipboard.cpp \
+ display.cpp \
+ framebuffer.cpp \
+ keyboard.cpp \
+ events_keyboard.cpp \
+ events_platform.cpp \
+ events.cpp \
+ layout.cpp \
+ actions.cpp \
+ window.cpp \
+ timing.cpp \
+ console.cpp \
+)
+
+#sdl_simu_needs_to_be_removed += $(addprefix ion/src/simulator/shared/dummy/, \
+# display.cpp \
+# led.cpp \
+# usb.cpp \
+# battery.cpp \
+# store_script.cpp \
+#)
+
+# Remove the dummy diplay (re-implemented) and the SDL simulator stuff.
+ion_src := $(filter-out $(sdl_simu_needs_to_be_removed),$(ion_src))
+
+SFLAGS := $(filter-out -Iion/src/simulator/external/sdl/include,$(SFLAGS))
diff --git a/ion/src/simulator/nspire/changes.txt b/ion/src/simulator/nspire/changes.txt
new file mode 100644
index 00000000000..1c88cf98c78
--- /dev/null
+++ b/ion/src/simulator/nspire/changes.txt
@@ -0,0 +1,49 @@
+
+comparison with original files:
+
+no changes:
+
+minor changes:
+events.cpp: removed haptics
+events.h: set SDL sharedExternalTextBufferSize to 0, added declaration of dumpEventCount and logAfter (why?)
+framebuffer.cpp: removed comments, changed Window to Main, sFrameBufferActive default to true
+telemetry_init.cpp: changed include path
+
+major changes:
+events_keyboard.cpp: from events_keyboard_platform.cpp
+display.cpp: move framebuffer contents managed by Upsilon to video memory, the concept is the same; void shutdown() renamed to void quit()
+keyboard.cpp: removed headless-related keyDown, keyUp and scanHandlesSDLKey, both share returning (State)state
+keyboard.h: now only contains keyboard image bitmap
+platform.h: WTF
+main.cpp: of course they're different
+
+new file:
+callback.cpp: useless, not called anywhere
+drivers etc.
+
+
+nspire port adaptations from 3DS:
+
+unchanged:
+events.cpp
+events.h
+framebuffer.cpp
+framebuffer.h
+telemetry_init.cpp
+platform.h
+callback.cpp
+clipboard.cpp
+main.h
+
+changed:
+display.h: now #include
+display.cpp: memcpy framebuffer to BUFF_BASE_ADDRESS directly. Since Ndless already has a buffer and it is possible to read back from BUFF_BASE_ADDRESS, why not completely abolish that framebuffer? removed assert.h
+main.cpp: changed windowWidth to 320, useless; moved timing functions to timing.cpp
+callback.cpp: removed
+keyboard.cpp: rewritten based on the original SDL thing
+keyboard.h: trimmed
+events_keyboard.cpp: mostly rewritten
+NOTE: some #include and Makefile changes untracked!
+n2DLib.h & n2DLib.c: prefix 'GRAFX_' attached to macro 'min' and 'max', they were polluting C++ standard libraries!
+timing.cpp: modified based on the original; replace msleep function with the one provided by Ndless
+console.cpp: modified based on the original; removed headless-related code
diff --git a/ion/src/simulator/nspire/clipboard.cpp b/ion/src/simulator/nspire/clipboard.cpp
new file mode 100644
index 00000000000..c3ce45627c9
--- /dev/null
+++ b/ion/src/simulator/nspire/clipboard.cpp
@@ -0,0 +1,18 @@
+#include
+#include
+#include
+
+namespace Ion {
+namespace Clipboard {
+
+uint32_t localClipboardVersion;
+
+void write(const char * text) {
+}
+
+const char * read() {
+ return nullptr;
+}
+
+}
+}
diff --git a/ion/src/simulator/nspire/console.cpp b/ion/src/simulator/nspire/console.cpp
new file mode 100644
index 00000000000..400b67eeb6b
--- /dev/null
+++ b/ion/src/simulator/nspire/console.cpp
@@ -0,0 +1,22 @@
+#include
+#include "main.h"
+#include
+#include
+
+namespace Ion {
+namespace Console {
+
+char readChar() {
+ return 0;
+}
+
+void writeChar(char c) {
+ KDIonContext::putchar(c);
+}
+
+bool transmissionDone() {
+ return true;
+}
+
+}
+}
diff --git a/ion/src/simulator/nspire/display.cpp b/ion/src/simulator/nspire/display.cpp
new file mode 100644
index 00000000000..75141366895
--- /dev/null
+++ b/ion/src/simulator/nspire/display.cpp
@@ -0,0 +1,33 @@
+#include "display.h"
+#include "framebuffer.h"
+#include
+#include
+
+namespace Ion {
+namespace Simulator {
+namespace Display {
+
+void init() {
+ lcd_init(SCR_320x240_565);
+}
+
+void quit() {
+ lcd_init(SCR_TYPE_INVALID);
+}
+
+void draw() {
+ static const bool has_colors_cache = has_colors;
+
+ if (has_colors_cache) {
+ // same as lcd_blit((void*)Framebuffer::address(), SCR_320x240_565)
+ memcpy(REAL_SCREEN_BASE_ADDRESS, (void*)Framebuffer::address(), 320 * 240 * sizeof(uint16_t));
+ } else { // invert screen color if running on classic
+ for (unsigned int i = 0; i < 320 * 240; i++) {
+ reinterpret_cast(REAL_SCREEN_BASE_ADDRESS)[i] = 0xFFFF - Framebuffer::address()[i];
+ }
+ }
+}
+
+}
+}
+}
diff --git a/ion/src/simulator/nspire/display.h b/ion/src/simulator/nspire/display.h
new file mode 100644
index 00000000000..4ad3103658d
--- /dev/null
+++ b/ion/src/simulator/nspire/display.h
@@ -0,0 +1,20 @@
+#ifndef ION_SIMULATOR_DISPLAY_H
+#define ION_SIMULATOR_DISPLAY_H
+
+#include
+#include
+
+namespace Ion {
+namespace Simulator {
+namespace Display {
+
+void init();
+void quit();
+
+void draw();
+
+}
+}
+}
+
+#endif
diff --git a/ion/src/simulator/nspire/events.cpp b/ion/src/simulator/nspire/events.cpp
new file mode 100644
index 00000000000..9b8d0055751
--- /dev/null
+++ b/ion/src/simulator/nspire/events.cpp
@@ -0,0 +1,23 @@
+#include "events.h"
+#include
+
+namespace Ion {
+namespace Events {
+
+void didPressNewKey() {
+}
+
+char * sharedExternalTextBuffer() {
+ static char buffer[sharedExternalTextBufferSize];
+ return buffer;
+}
+
+const char * Event::text() const {
+ if (*this == ExternalText) {
+ return const_cast(sharedExternalTextBuffer());
+ }
+ return defaultText();
+}
+
+}
+}
diff --git a/ion/src/simulator/nspire/events.h b/ion/src/simulator/nspire/events.h
new file mode 100644
index 00000000000..b012a43c5e5
--- /dev/null
+++ b/ion/src/simulator/nspire/events.h
@@ -0,0 +1,24 @@
+#ifndef ION_SIMULATOR_EVENTS_H
+#define ION_SIMULATOR_EVENTS_H
+
+#include
+
+namespace Ion {
+namespace Simulator {
+namespace Events {
+
+void dumpEventCount(int i);
+void logAfter(int numberOfEvents);
+
+}
+}
+
+namespace Events {
+
+static constexpr int sharedExternalTextBufferSize = 2;
+char * sharedExternalTextBuffer();
+
+}
+}
+
+#endif
diff --git a/ion/src/simulator/nspire/events_keyboard.cpp b/ion/src/simulator/nspire/events_keyboard.cpp
new file mode 100644
index 00000000000..9effd1e5b74
--- /dev/null
+++ b/ion/src/simulator/nspire/events_keyboard.cpp
@@ -0,0 +1,108 @@
+#include "main.h"
+#include "keyboard.h"
+#include "platform.h"
+//#include "driver/common.h"
+#include
+#include
+#include "events.h"
+#include
+#include
+#include
+
+class AlphaKeyPair {
+ public:
+ constexpr AlphaKeyPair(char key, char shiftAlphaKey, t_key ndlessKey) :
+ m_key(key),
+ m_shiftAlphaKey(shiftAlphaKey),
+ m_ndlessKey(ndlessKey)
+ {}
+ char key() const { return m_key; }
+ char shiftAlphaKey() const { return m_shiftAlphaKey; }
+ t_key ndlessKey() const { return m_ndlessKey; }
+
+ private:
+ char m_key;
+ char m_shiftAlphaKey;
+ t_key m_ndlessKey;
+};
+
+constexpr static AlphaKeyPair aKeyPairs[] = {
+ AlphaKeyPair('a', 'A', KEY_NSPIRE_A),
+ AlphaKeyPair('b', 'B', KEY_NSPIRE_B),
+ AlphaKeyPair('c', 'C', KEY_NSPIRE_C),
+ AlphaKeyPair('d', 'D', KEY_NSPIRE_D),
+ AlphaKeyPair('e', 'E', KEY_NSPIRE_E),
+ AlphaKeyPair('f', 'F', KEY_NSPIRE_F),
+ AlphaKeyPair('g', 'G', KEY_NSPIRE_G),
+ AlphaKeyPair('h', 'H', KEY_NSPIRE_H),
+ AlphaKeyPair('i', 'I', KEY_NSPIRE_I),
+ AlphaKeyPair('j', 'J', KEY_NSPIRE_J),
+ AlphaKeyPair('k', 'K', KEY_NSPIRE_K),
+ AlphaKeyPair('l', 'L', KEY_NSPIRE_L),
+ AlphaKeyPair('m', 'M', KEY_NSPIRE_M),
+ AlphaKeyPair('n', 'N', KEY_NSPIRE_N),
+ AlphaKeyPair('o', 'O', KEY_NSPIRE_O),
+ AlphaKeyPair('p', 'P', KEY_NSPIRE_P),
+ AlphaKeyPair('q', 'Q', KEY_NSPIRE_Q),
+ AlphaKeyPair('r', 'R', KEY_NSPIRE_R),
+ AlphaKeyPair('s', 'S', KEY_NSPIRE_S),
+ AlphaKeyPair('t', 'T', KEY_NSPIRE_T),
+ AlphaKeyPair('u', 'U', KEY_NSPIRE_U),
+ AlphaKeyPair('v', 'V', KEY_NSPIRE_V),
+ AlphaKeyPair('w', 'W', KEY_NSPIRE_W),
+ AlphaKeyPair('x', 'X', KEY_NSPIRE_X),
+ AlphaKeyPair('y', 'Y', KEY_NSPIRE_Y),
+ AlphaKeyPair('z', 'Z', KEY_NSPIRE_Z),
+ AlphaKeyPair(' ', ' ', KEY_NSPIRE_SPACE)
+};
+
+constexpr int aNumberOfKeyPairs = sizeof(aKeyPairs)/sizeof(AlphaKeyPair);
+
+namespace Ion {
+namespace Events {
+
+
+Event getPlatformEvent() {
+ Event result = None;
+ t_key scanResult;
+ static t_key prevScanResult = {_KEY_DUMMY_ROW, _KEY_DUMMY_COL, _KEY_DUMMY_ROW, _KEY_DUMMY_COL, TPAD_ARROW_NONE};
+ static bool keyDown = false;
+
+ bool getKeyResult = get_key_pressed(&scanResult);
+
+ if (getKeyResult) {
+
+ if (isKey(scanResult, KEY_NSPIRE_SCRATCHPAD)) { // 'Quit' bound to 'Scratchpad'
+ return Termination;
+ }
+
+ if (!isKey(scanResult, prevScanResult)) {
+ keyDown = false;
+ prevScanResult = scanResult;
+ }
+
+ if (!keyDown && !Simulator::Keyboard::scanHandlesKey(scanResult)) { // handle alphabetical keypad press
+ keyDown = true;
+ for (int i = 0; i < aNumberOfKeyPairs; i++) {
+ if (isKey(scanResult, aKeyPairs[i].ndlessKey())) {
+ //strlcpy(sharedExternalTextBuffer(), aKeyPairs[i].key(), sharedExternalTextBufferSize);
+ if (Ion::Events::isShiftActive() && Ion::Events::isAlphaActive()) {
+ sharedExternalTextBuffer()[0] = aKeyPairs[i].shiftAlphaKey();
+ if (!Ion::Events::isLockActive()) Ion::Events::removeShift();
+ } else {
+ sharedExternalTextBuffer()[0] = aKeyPairs[i].key();
+ }
+ sharedExternalTextBuffer()[1] = '\0';
+ return ExternalText;
+ }
+ }
+ }
+
+ } else keyDown = false;
+
+ return result;
+}
+
+
+}
+}
diff --git a/ion/src/simulator/nspire/framebuffer.cpp b/ion/src/simulator/nspire/framebuffer.cpp
new file mode 100644
index 00000000000..881079941ff
--- /dev/null
+++ b/ion/src/simulator/nspire/framebuffer.cpp
@@ -0,0 +1,50 @@
+#include "framebuffer.h"
+#include
+#include "main.h"
+
+static KDColor sPixels[Ion::Display::Width * Ion::Display::Height];
+static bool sFrameBufferActive = true;
+
+namespace Ion {
+namespace Display {
+
+static KDFrameBuffer sFrameBuffer = KDFrameBuffer(sPixels, KDSize(Ion::Display::Width, Ion::Display::Height));
+
+void pushRect(KDRect r, const KDColor * pixels) {
+ if (sFrameBufferActive) {
+ Simulator::Main::setNeedsRefresh();
+ sFrameBuffer.pushRect(r, pixels);
+ }
+}
+
+void pushRectUniform(KDRect r, KDColor c) {
+ if (sFrameBufferActive) {
+ Simulator::Main::setNeedsRefresh();
+ sFrameBuffer.pushRectUniform(r, c);
+ }
+}
+
+void pullRect(KDRect r, KDColor * pixels) {
+ if (sFrameBufferActive) {
+ sFrameBuffer.pullRect(r, pixels);
+ }
+}
+
+}
+}
+
+namespace Ion {
+namespace Simulator {
+namespace Framebuffer {
+
+const KDColor * address() {
+ return sPixels;
+}
+
+void setActive(bool enabled) {
+ sFrameBufferActive = enabled;
+}
+
+}
+}
+}
diff --git a/ion/src/simulator/nspire/framebuffer.h b/ion/src/simulator/nspire/framebuffer.h
new file mode 100644
index 00000000000..e9eed19e26e
--- /dev/null
+++ b/ion/src/simulator/nspire/framebuffer.h
@@ -0,0 +1,18 @@
+#ifndef ION_SIMULATOR_FRAMEBUFFER_H
+#define ION_SIMULATOR_FRAMEBUFFER_H
+
+#include
+
+namespace Ion {
+namespace Simulator {
+namespace Framebuffer {
+
+const KDColor * address();
+void setActive(bool enabled);
+void writeToFile(const char * filename);
+
+}
+}
+}
+
+#endif
diff --git a/ion/src/simulator/nspire/keyboard.cpp b/ion/src/simulator/nspire/keyboard.cpp
new file mode 100644
index 00000000000..c6c74dc12b0
--- /dev/null
+++ b/ion/src/simulator/nspire/keyboard.cpp
@@ -0,0 +1,144 @@
+#include
+#include
+#include
+
+#include "keyboard.h"
+#include "main.h"
+
+using namespace Ion::Keyboard;
+
+class KeyPair {
+public:
+ constexpr KeyPair(Key key, t_key ndlessKey) :
+ m_key(key),
+ m_ndlessKey(ndlessKey)
+ {}
+ Key key() const { return m_key; }
+ t_key ndlessKey() const { return m_ndlessKey; }
+private:
+ Key m_key;
+ t_key m_ndlessKey;
+};
+
+constexpr static KeyPair sKeyPairs[] = {
+ KeyPair(Key::Down, KEY_NSPIRE_DOWN),
+ KeyPair(Key::Up, KEY_NSPIRE_UP),
+ KeyPair(Key::Left, KEY_NSPIRE_LEFT),
+ KeyPair(Key::Right, KEY_NSPIRE_RIGHT),
+ KeyPair(Key::Shift, KEY_NSPIRE_SHIFT),
+ KeyPair(Key::EXE, KEY_NSPIRE_ENTER),
+ KeyPair(Key::EXE, KEY_NSPIRE_RET),
+ KeyPair(Key::Back, KEY_NSPIRE_ESC),
+ KeyPair(Key::Toolbox, KEY_NSPIRE_TAB),
+ KeyPair(Key::Backspace, KEY_NSPIRE_DEL),
+ KeyPair(Key::Home, KEY_NSPIRE_HOME),
+ KeyPair(Key::OK, KEY_NSPIRE_CLICK),
+ KeyPair(Key::One, KEY_NSPIRE_1),
+ KeyPair(Key::Two, KEY_NSPIRE_2),
+ KeyPair(Key::Three, KEY_NSPIRE_3),
+ KeyPair(Key::Four, KEY_NSPIRE_4),
+ KeyPair(Key::Five, KEY_NSPIRE_5),
+ KeyPair(Key::Six, KEY_NSPIRE_6),
+ KeyPair(Key::Seven, KEY_NSPIRE_7),
+ KeyPair(Key::Eight, KEY_NSPIRE_8),
+ KeyPair(Key::Nine, KEY_NSPIRE_9),
+ KeyPair(Key::Zero, KEY_NSPIRE_0),
+ KeyPair(Key::Dot, KEY_NSPIRE_PERIOD),
+ KeyPair(Key::LeftParenthesis, KEY_NSPIRE_LP),
+ KeyPair(Key::RightParenthesis, KEY_NSPIRE_RP),
+ KeyPair(Key::Square, KEY_NSPIRE_SQU),
+ KeyPair(Key::Exp, KEY_NSPIRE_eEXP),
+ KeyPair(Key::Power, KEY_NSPIRE_EXP),
+ KeyPair(Key::Comma, KEY_NSPIRE_COMMA),
+ KeyPair(Key::Pi, KEY_NSPIRE_PI),
+ KeyPair(Key::Multiplication, KEY_NSPIRE_MULTIPLY),
+ KeyPair(Key::Division, KEY_NSPIRE_DIVIDE),
+ KeyPair(Key::Plus, KEY_NSPIRE_PLUS),
+ KeyPair(Key::Minus, KEY_NSPIRE_MINUS),
+ KeyPair(Key::EE, KEY_NSPIRE_EE),
+ KeyPair(Key::Shift, KEY_NSPIRE_SHIFT),
+ KeyPair(Key::Alpha, KEY_NSPIRE_CTRL),
+ KeyPair(Key::Var, KEY_NSPIRE_VAR)
+};
+
+constexpr int sNumberOfKeyPairs = sizeof(sKeyPairs)/sizeof(KeyPair);
+
+namespace Ion {
+namespace Keyboard {
+
+static bool getTPArrow(t_key* result) {
+ touchpad_report_t tpReport;
+
+ result->row = result->tpad_row = _KEY_DUMMY_ROW;
+ result->col = result->tpad_col = _KEY_DUMMY_COL;
+ result->tpad_arrow = TPAD_ARROW_NONE;
+
+ if (!touchpad_scan(&tpReport)) {
+ //result->tpad_arrow = (tpad_arrow_t)tpReport.arrow;
+ // nasty workaround for n2DLib isKey()
+ switch (tpReport.arrow) {
+ case TPAD_ARROW_UP:
+ *result = KEY_NSPIRE_UP;
+ break;
+ case TPAD_ARROW_DOWN:
+ *result = KEY_NSPIRE_DOWN;
+ break;
+ case TPAD_ARROW_LEFT:
+ *result = KEY_NSPIRE_LEFT;
+ break;
+ case TPAD_ARROW_RIGHT:
+ *result = KEY_NSPIRE_RIGHT;
+ break;
+ case TPAD_ARROW_CLICK:
+ *result = KEY_NSPIRE_CLICK;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+ } else
+ return 0;
+}
+
+State scan() {
+ State state;
+ t_key result;
+
+ // Grab this opportunity to refresh the display if needed
+ Simulator::Main::refresh();
+
+ // Catch the physical keyboard events
+ if (get_key_pressed(&result) || getTPArrow(&result)) {
+ for (int i = 0; i < sNumberOfKeyPairs; i++) {
+ if (isKey(result, sKeyPairs[i].ndlessKey())) {
+ state.setKey(sKeyPairs[i].key());
+ }
+ }
+ } else {
+ if (on_key_pressed()) {
+ state.setKey(Key::OnOff);
+ }
+ }
+
+ return state;
+}
+
+}
+}
+
+namespace Ion {
+namespace Simulator {
+namespace Keyboard {
+
+bool scanHandlesKey(t_key key) {
+ for (int i = 0; i < sNumberOfKeyPairs; i++) {
+ if (isKey(key, sKeyPairs[i].ndlessKey())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+}
+}
+}
diff --git a/ion/src/simulator/nspire/keyboard.h b/ion/src/simulator/nspire/keyboard.h
new file mode 100644
index 00000000000..69de59efd81
--- /dev/null
+++ b/ion/src/simulator/nspire/keyboard.h
@@ -0,0 +1,17 @@
+#ifndef ION_SIMULATOR_KEYBOARD_H
+#define ION_SIMULATOR_KEYBOARD_H
+
+#include
+#include
+
+namespace Ion {
+namespace Simulator {
+namespace Keyboard {
+
+bool scanHandlesKey(t_key key);
+
+}
+}
+}
+
+#endif
diff --git a/ion/src/simulator/nspire/main.cpp b/ion/src/simulator/nspire/main.cpp
new file mode 100644
index 00000000000..834dd2935be
--- /dev/null
+++ b/ion/src/simulator/nspire/main.cpp
@@ -0,0 +1,90 @@
+#include "main.h"
+#include "display.h"
+#include "platform.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+//#include "driver/common.h"
+
+int main(int argc, char * argv[]) {
+ Ion::Simulator::Main::init();
+
+ std::vector arguments(argv, argv + argc);
+
+ const char * language = "en";
+ if (language != nullptr) {
+ arguments.push_back("--language");
+ arguments.push_back(language);
+ }
+
+ ion_main(arguments.size(), &arguments[0]);
+ Ion::Simulator::Main::quit();
+
+ return 0;
+}
+
+namespace Ion {
+namespace Simulator {
+namespace Main {
+
+static bool sNeedsRefresh = false;
+
+void init() {
+ //Ion::Simulator::CommonDriver::init();
+ Ion::Simulator::Display::init();
+ relayout();
+}
+
+void relayout() {
+ int windowWidth = 320;
+ int windowHeight = 240;
+
+ // Keep original aspect ration in screen_only mode.
+ /*
+ float scale = (float)(Ion::Display::Width) / (float)(Ion::Display::Height);
+ if ((float)(windowHeight) * scale > float(windowWidth)) {
+ sScreenRect.w = windowWidth;
+ sScreenRect.h = (int)((float)(windowWidth) / scale);
+ } else {
+ sScreenRect.w = (int)((float)(windowHeight) * scale);
+ sScreenRect.h = windowHeight;
+ }
+
+ sScreenRect.x = (windowWidth - sScreenRect.w) / 2;
+ sScreenRect.y = (windowHeight - sScreenRect.h) / 2;
+ */
+
+ setNeedsRefresh();
+}
+
+void setNeedsRefresh() {
+ sNeedsRefresh = true;
+}
+
+void refresh() {
+ if (!sNeedsRefresh) {
+ return;
+ }
+
+ Display::draw();
+
+ sNeedsRefresh = false;
+}
+
+void quit() {
+ //Ion::Simulator::CommonDriver::deinit();
+ Ion::Simulator::Display::quit();
+}
+
+}
+}
+}
+
diff --git a/ion/src/simulator/nspire/main.h b/ion/src/simulator/nspire/main.h
new file mode 100644
index 00000000000..544f008f432
--- /dev/null
+++ b/ion/src/simulator/nspire/main.h
@@ -0,0 +1,19 @@
+#ifndef ION_SIMULATOR_MAIN_H
+#define ION_SIMULATOR_MAIN_H
+
+namespace Ion {
+namespace Simulator {
+namespace Main {
+
+void init();
+void quit();
+
+void setNeedsRefresh();
+void refresh();
+void relayout();
+
+}
+}
+}
+
+#endif
diff --git a/ion/src/simulator/nspire/platform.h b/ion/src/simulator/nspire/platform.h
new file mode 100644
index 00000000000..4977945c186
--- /dev/null
+++ b/ion/src/simulator/nspire/platform.h
@@ -0,0 +1,23 @@
+#ifndef ION_SIMULATOR_PLATFORM_H
+#define ION_SIMULATOR_PLATFORM_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Those functions should be implemented per-platform.
+ * They are defined as C function for easier interop. */
+
+const char * IonSimulatorGetLanguageCode();
+
+void IonSimulatorKeyboardKeyDown(int keyNumber);
+void IonSimulatorKeyboardKeyUp(int keyNumber);
+void IonSimulatorEventsPushEvent(int eventNumber);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/ion/src/simulator/nspire/telemetry_init.cpp b/ion/src/simulator/nspire/telemetry_init.cpp
new file mode 100644
index 00000000000..6df9feebeb3
--- /dev/null
+++ b/ion/src/simulator/nspire/telemetry_init.cpp
@@ -0,0 +1,15 @@
+#include "platform.h"
+
+namespace Ion {
+namespace Simulator {
+namespace Telemetry {
+
+void init() {
+}
+
+void shutdown() {
+}
+
+}
+}
+}
diff --git a/ion/src/simulator/nspire/timing.cpp b/ion/src/simulator/nspire/timing.cpp
new file mode 100644
index 00000000000..81ac5919dfb
--- /dev/null
+++ b/ion/src/simulator/nspire/timing.cpp
@@ -0,0 +1,21 @@
+#include
+#include "main.h"
+#include
+#include
+
+static auto start = std::chrono::steady_clock::now();
+
+namespace Ion {
+namespace Timing {
+
+uint64_t millis() {
+ auto elapsed = std::chrono::steady_clock::now() - start;
+ return std::chrono::duration_cast(elapsed).count();
+}
+
+void msleep(uint32_t ms) {
+ ::msleep(ms);
+}
+
+}
+}
diff --git a/poincare/include/poincare/integer.h b/poincare/include/poincare/integer.h
index 1d27e50c508..dded14e35d4 100644
--- a/poincare/include/poincare/integer.h
+++ b/poincare/include/poincare/integer.h
@@ -13,7 +13,7 @@ class LayoutNode;
class Integer;
struct IntegerDivision;
-#ifdef _3DS
+#if (defined _3DS) || (defined _NSPIRE)
typedef unsigned short half_native_uint_t;
typedef int native_int_t;
typedef long long int double_native_int_t;