Compare commits

...

26 Commits

Author SHA1 Message Date
Lephenixnoir 8c5f68f16e
fxcg50/modion: rename file ion.c -> modion.c 2024-03-18 22:41:16 +01:00
Lephenixnoir fc9026798d
fxcg50/modion: clean up module implementation 2024-03-18 22:40:39 +01:00
Lephenixnoir 3775f28536
fxcg50/modkandinsky: use QSTRs to identify common colors 2024-03-18 22:34:48 +01:00
Lephenixnoir 5d51f205b9
fxcg50: officialize reserving 300 kB from _uram for the uPy GC 2024-03-18 22:10:41 +01:00
Lephenixnoir 3e2f7f0aa0
sh: slight cleanup of scheduled dupdate mechanism
Mostly renaming and integrating pe_dupdate() it with existing modules.
2024-03-18 22:08:37 +01:00
Lephenixnoir 71ddfff567
sh: switch add-in icon based on auto-detected dev builds
A dev build is detected if:
- There are dirty tracked files: "git status -uno --porcelain" nonempty
- We are not on main: "git describe" != "git describe main"
2024-03-18 22:07:59 +01:00
Lephenixnoir 4bec7be97f
numworks: extend timer tracking into autofree resource header
I imagine that we're going to use this more later, be it for serial/USB,
in-memory files, and maybe more.
2024-03-09 16:10:44 +01:00
Lephenixnoir fff06099a1
numworks: don't set global dwindow in kandinsky
This interferes with the shell and gives fairly bad results with print()
and input(). This way of doing things lets the shell print correctly.
The shell will override the borders, however. It's not immediately
obvious how to fix that properly with the auto-update system in main.c.
I'll try to move the auto screen update back to modkandinsky.c.
2024-03-05 07:28:57 +00:00
Lephenixnoir e7349aa227
numworks: replace custom time module with extension of official one 2024-03-04 22:40:32 +00:00
Lephenixnoir dfb73611f9
Merge remote-tracking branch 'slyvtt/numworks' into dev
This merges PR #14 with SlyVTT's Numworks support.
2024-03-03 13:17:10 +00:00
Lephenixnoir b804cb7583
docs: second pass on casiplot module 2024-03-03 13:10:20 +00:00
Lephenixnoir 38c0ec1d15
docs: add casioplot module (first pass) 2024-03-03 07:28:47 +00:00
Lephenixnoir eaf5f43da4
modgint: add dpoly() function (no doc yet) 2024-03-01 22:47:35 +00:00
Lephenixnoir c14f770483
docs: add example for drect(), dline() and the like 2024-02-26 22:38:10 +01:00
Lephenixnoir 430fe06adc
docs: add documentation for gint.getkey_opt() 2024-02-26 09:04:53 +01:00
Lephenixnoir 3523a83bda
docs: first version of gint image documentation (fr+en) 2024-02-25 17:13:23 +01:00
Lephenixnoir ae3a135a81
docs: start of image documentation in gint (French only so far) 2024-02-25 16:31:19 +01:00
Lephenixnoir 30fcf592d1
docs: more modgint doc including first images 2024-02-25 14:45:30 +01:00
Lephenixnoir f9f94c999a
ports/fxcg50: provide basic memory status view on SHIFT+VARS 2024-02-25 12:46:01 +01:00
Lephenixnoir 7abbcc1dfc
ports/fx9860g3: reduce console scrollback to 50 lines / 4kB 2024-02-25 12:45:43 +01:00
Lephenixnoir a1848c0920
ports/sh: reduce console memory usage
Includes 2 previous commits.

So far simply by freezing old console lines and removing edition
metadata. Plus a little bit of compaction.

Benchmark: _uram memory free after startup and help() on G-III.
- Before this change: 2880
- After adding stredit_t in every line: 1880
- After keeping stredit_t only for last line: 4596

There's more to be done (double buffer), but later. I'm also gonna cut
down on the history size, probably.
2024-02-25 11:11:48 +01:00
Lephenixnoir 3f02fd86ea
ports/sh: WIP console memory usage: use stredit_t only on last line 2024-02-25 09:11:05 +01:00
Lephenixnoir 95eb40e25d
ports/sh: WIP console memory usage: introduce stredit_t
Currently takes more memory because I kept an stredit_t in every single
line but once I remove it from all but the last one we'll see some
progress.
2024-02-25 08:49:49 +01:00
Lephenixnoir 93e021f144
ports/sh: fix long-standing console line counting bug
The computation of how many screen lines at given console lines takes
used the globally-configured font instead of the widget's font. This
cause certain lines to have their size over-estimated on fx-9860G III,
leading to weird scrolling and too few lines being displayed in some
situations.

The bug was present on the fx-CG 50 but did not occur because the font
used for rendering the console is the default font.
2024-02-24 15:43:13 +01:00
Lephenixnoir c84caa533d
ports/fx9860g3: memory info view on SHIFT+VARS 2024-02-24 15:02:33 +01:00
Lephenixnoir bcdb3b74ca
ports/fx9860g3: smaller banner 2024-02-24 14:33:02 +01:00
46 changed files with 1280 additions and 486 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,53 @@
# `casioplot`: CASIO's official I/O module
`casioplot` is a drawing library provided by CASIO as support for the official `turtle` and `matplotlib` modules. It was [announced in February 2020](https://www.planet-casio.com/Fr/forums/topic16154-1-modules-graphiques-python-en-avril-matplotlib-et-turtle.html) and [released in April](https://www.planet-casio.com/Fr/forums/topic16243-1-rendu-graphique-en-python-partie-1-decouverte-de-matplotlib-et-turtle.html). The module is notably available on G-III and fx-CG models with the same API. As of March 2024, `casioplot` is the only custom module in CASIO's official port of MicroPython. PythonExtra also provides the module for source compatibility.
```py
import casioplot
# or
from casioplot import *
```
**Contents**
- [Rendering functions](#rendering-functions)
- [Considerations for rendering](#considerations-for-rendering)
- [Differences with the official `casioplot` module](#differences-with-the-official-casioplot-module)
## Rendering functions
```py
show_screen() -> None
clear_screen() -> None
set_pixel(x: int, y: int, ?color: (int, int, int)) -> None
get_pixel(x: int, y: int) -> (int, int, int)
draw_string(x: int, y: int, text: str, ?color: (int, int, int), ?size: str) -> None
```
The rendering area is of size 128x64 on G-III and 384x216 on fx-CG. While the G-III supports only two colors (black and white), the fx-CG supports 65536 colors (16-bit RGB565). Still, the color format is _always_ (r,g,b) triplets with 0 ≤ r,g,b ≤ 255. This helps compatibility between models and with PC, at a performance cost. Calculators process these colors by _approximating_ them to the closest color they can represent.
Like the gint module, `casioplot` functions draw to an internal buffer called the VRAM, and the module only pushes that to the screen when explicitly asked to or at the end of the program.
`show_screen()` pushes the VRAM to screen after something has been drawn.
`clear_screen()` fills the VRAM in white.
`set_pixel()` changes the color of the pixel at (x,y) into the provided `color` encoded as an (r,g,b) triplet (black if not provided). Building triplets is a bit slow so don't write something like `set_pixel(x, y, (0,0,0))` in a loop, store the triplet in a variable and use that instead.
`get_pixel()` returns the color of the pixel at (x,y) in VRAM as an (r,g,b) triplet. Because VRAM stores colors in the same format as the display, which typically doesn't support all 16 million colors, `get_pixel()` immediately after `set_pixel()`usually returns an _approximation_ of the original color. Because it allocates a triplet, this function is also excruciatingly slow to call in a loop, to the point of creating GC-driven lag spikes that in some situations make it completely unusable.
`draw_string()` draws text. (x,y) is the location of the top-left corner of the rendered string. The text color is optional; if specified it should be an (r,g,b) triplet, otherwise it is black. The font size can be either of the strings `"small"`, `"medium"` or `"large"` and defaults to medium. On the G-III, the font sizes `"small"` and `"medium"` are identical, but on the fx-CG all three fonts are different. Any `'\n'` in the string is replaced with spaces when drawing.
TODO: Example
## Considerations for rendering
_Text mode._ `casioplot` automatically switches PythonExtra into graphics mode when imported. Programs that want to render graphics frames after a `print()` should `show_screen()` _before_ rendering the first frame since `print()` switches to text mode.
_Performance._ Writing to VRAM is the main reason why rendering in `casioplot` is sometimes much faster than in Basic. Basic pushes the rendered data to the display after every call, which is slow. Unfortunately, `casioplot` ends up losing for complex drawing tasks for which Basic has complex functions (like DrawStat) while `casioplot` just has single pixels. In this case, functions from the [gint module](modgint-en.md) can be of help.
## Differences with the official `casioplot` module
- PythonExtra's implementation doesn't automatically show the screen when the program finishes executing.
- The fonts for `draw_string()` are not identical on the G-III port.
- The fonts for `draw_string()` currently only support ASCII.
- The drawable space on fx-CG is at the top left section of the screen.

View File

@ -0,0 +1,53 @@
# `casioplot` : Module officiel d'I/O de CASIO
`casioplot` est une bibliothèque de dessin fournie par CASIO pour supporter les modules officiels `turtle` et `matplotlib`. Le module a été [annoncé en Février 2020](https://www.planet-casio.com/Fr/forums/topic16154-1-modules-graphiques-python-en-avril-matplotlib-et-turtle.html) et [publié en Avril](https://www.planet-casio.com/Fr/forums/topic16243-1-rendu-graphique-en-python-partie-1-decouverte-de-matplotlib-et-turtle.html). Le module est disponible sur Graph 35+E II et Graph 90+E avec la même API. C'est le seul module personnalisé dans le port MicroPython de CASIO (à date de Mars 2024). PythonExtra fournit le module par compatibilité pour les scripts officiels.
```py
import casioplot
# or
from casioplot import *
```
**Sommaire**
- [Fonctions de dessin](#fonctions-de-dessin)
- [Considérations spéciales pour le rendu](#considérations-spéciales-pour-le-rendu)
- [Différences avec le module `casioplot` officiel](#différences-avec-le-module-casioplot-officiel)
## Fonctions de dessin
```py
show_screen() -> None
clear_screen() -> None
set_pixel(x: int, y: int, ?color: (int, int, int)) -> None
get_pixel(x: int, y: int) -> (int, int, int)
draw_string(x: int, y: int, text: str, ?color: (int, int, int), ?size: str) -> None
```
La zone de dessin est de 128x64 pixels sur Graph 35+E II et 384x216 pixels sur la Graph 90+E. La Graph 35+E II ne supporte que deux couleurs (noir et blanc) tandis que la Graph 90+E peut afficher 65536 couleurs (RGB565 16 bits). Cependant, le format de couleurs est _quand même_ des triplets (r,g,b) avec 0 ≤ r,g,b ≤ 255. Ce choix maintient la compabilité entre les modèles et avec le PC, en échange d'un coût en performance. Les calculatrices approximent ces couleurs en les remplaçant par la couleur la plus proche qu'elles peuvent afficher.
Comme le module gint, `casioplot` dessine systématiquement dans un buffer interne qu'on appelle la VRAM, et n'affiche le résultat à l'écran que quand on lui demande explicitement, ou à la fin de l'exécution.
`show_screen()` affiche à l'écran les contenus de la VRAM après un dessin.
`clear_screen()` remplit la VRAM en blanc.
`set_pixel()` remplace la couleur du pixel à la position (x,y) par `color`, qui doit être un triplet (r,g,b) si fournie, et sera noir si absente. Construire des triplets est un peu lent donc il vaut mieux éviter d'écrire des appels comme `set_pixel(x, y, (0,0,0))` dans des boucles ; il est plus performant de stocker le triplet dans une variable et d'utiliser la variable.
`get_pixel()` renvoie la couleur du pixel à la position (x,y) de la VRAM sous la forme d'un triplet (r,g,b). Comme la VRAM stocke les couleurs au même format que l'écran, qui ne supporte généralement pas 16 millions de couleurs, appeler `get_pixel()` juste après `set_pixel()` renvoie une _approximation_ de la couleur originale. Comme cette fonction alloue un triplet, elle est atrocement lente à appeler en boucle, au point où elle peut créer du lag simplement par sa pression sur le GC, ce qui la rend parfois inutilisable.
`draw_string()` affiche du texte. (x,y) est la position du coin haut-gauche du rectangle dans lequel le texte est dessiné. La couleur est optionnelle ; si elle est spécifiée il faut que ce soit un triplet (r,g,b), sinon c'est noir. La taille peut être l'une des trois chaînes `"small"`, `"medium"` ou `"large"` ; la valeur par défaut est `"medium"`. Sur la Graph 35+E II, les polices `"small"` et `"medium"` sont identiques, tandis que sur la Graph 90+E les trois polices sont différentes. Tout `'\n'` dans la chaîne est remplacé par un espace avant d'afficher.
TODO: Exemple
## Considérations spéciales pour le rendu
_Mode texte._ `casioplot` passe automatiquement PythonExtra en mode graphique à l'import. Les programmes qui veulent faire du dessin graphique après un `print()` doivent appeler `show_screen()` _avant_ de commencer à dessiner le premier frame parce que `print()` repasse en mode texte.
_Performance._ Le fait que les fonctions de dessin écrivent silencieusement dans la VRAM est la principale raison pour laquelle `casioplot` est parfois beaucoup plus rapide que le dessin en Basic. PRGM réaffiche la VRAM à l'écran après chaque appel d'une fonction de dessin, ce qui est lent. Malheureusement, `casioplot` redevient plus lent que le Basic pour les dessins complexes pour lesquels il y a des fonctions spécialisées en Basic (comme `DrawStat`), puisque `casioplot` ne peut dessiner qu'un pixel à la fois. Dans ces situations, il vaut mieux utiliser les fonctions de dessin du [module gint](modgint-fr.md).
## Différences avec le module `casioplot` officiel
- L'implémentation PythonExtra du module n'affiche pas automatiquement l'écran à la fin du programme.
- Les polices pour `draw_string()` diffèrent des polices officielles sur Graph 35+E II.
- Les polices pour `draw_string()` ne supportent actuellement que les caractères ASCII.
- Les zone de dessin sur Graph 90+E est en haut à gauche de l'écran.

View File

@ -12,6 +12,11 @@ import gint
from gint import *
```
**Contents**
- [Keyboard input](#keyboard-input)
- [Drawing and rendering](#drawing-and-rendering)
- [Differences with gint's C API](#differences-with-gints-c-api)
## Keyboard input
Reference headers: [`<gint/keyboard.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/keyboard.h) and [`<gint/keycodes.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/keycodes.h).
@ -52,6 +57,7 @@ The functions `getkey()`, `getkey_opt()`, `pollevent()` and `waitevent()` all re
```py
getkey() -> key_event
getkey_opt(opts: GETKEY_*, timeout_ms: int | None) -> key_event
```
The function `getkey()` pauses the program until a key is pressed or repeated, and returns the associated event (which is always of type `KEYEV_DOWN` or `KEYEV_HOLD`). By default, only arrow keys are repeated, once after 400 ms, then every 40 ms.
@ -74,7 +80,25 @@ elif ev.key == KEY_DOWN and pos < N-1:
pos += 1 # Move one place down
```
TODO: Mention `getkey_opt()`
The function `getkey_opt()` generalizes `getkey()` and provides more options to customize its behavior. The `opts` parameter accepts a combination (sum) of options from the table below, and the parameter `timeout_ms`, when not `None`, specifies how long `getkey_opt()` should wait before returning a `KEYEV_NONE` event if nothing happens. The delay is specified in milliseconds but the actual resolution is about 7-8 ms.
| Option | `getkey()` | Description |
|------------------------|------------|--------------------------------------------------|
| `GETKEY_MOD_SHIFT` | Yes | SHIFT is a modifier key |
| `GETKEY_MOD_ALPHA` | Yes | ALPHA is a modifier key |
| `GETKEY_BACKLIGHT` | Yes | SHIFT+OPTN toggles display backlight |
| `GETKEY_MENU` | Yes | MENU brings up the calculator's main menu |
| `GETKEY_REP_ARROWS` | Yes | Repeats arrow keys when held |
| `GETKEY_REP_ALL` | No | Repeats all keys when held |
| `GETKEY_REP_PROFILE` | Yes* | Enable custom repeat delays |
| `GETKEY_FEATURES` | Yes* | Enable custom global shortcuts |
| `GETKEY_MENU_DUPDATE` | Yes | Update display after return to menu (fx-CG) |
| `GETKEY_MENU_EVENT` | No | Send a `KEYEV_OSMENU` event after return to menu |
| `GETKEY_POWEROFF` | Yes | SHIFT+AC/ON turns the calculator off |
`getkey()` is equivalent to `getkey_opt(opts, None)` where `opts` is the sum of all options that have "Yes" in the `getkey()` column above.
The two options `GETKEY_REP_PROFILE` and `GETKEY_FEATURES` are only useful when configured through other functions that are not yet available in PythonExtra.
### Reading keyboard events in real time
@ -150,11 +174,11 @@ keypressed(key: int) -> bool
keyreleased(key: int) -> bool
```
`keydown()` only tells whether keys are pressed at a given time; it cannot be used to check when keys change from the released state to the pressed state or the other way around. To do this, one must either read individual events (which can be annoying) or use the functions described below.
`keydown()` only tells whether keys are pressed at a given time; it cannot be used to check when keys change from the released state to the pressed state or the other way around. To do this, one must either read individual events (which can be annoying), remember which keys were pressed at the previous frame, or use the functions described below.
`keypressed(k)` and `keyreleased(k)` indicate whether key `k` was pressed/released since the last call to `cleareventflips()`. Be careful, here "pressed/released" should be interpreted as "indicated pressed/released by events read" not as a real-time state change.
`keypressed(k)` and `keyreleased(k)` indicate whether key `k` was pressed/released since the last call to `cleareventflips()`. As previously, here "pressed/released" means "as indicated by keyboard events read" not as a real-time state change.
_Example._ A game loop could test both the immediate state of some keys and state changes forother keys by using immediate functions after `cleareventflips()` followed by `clearevents()`.
_Example._ A game loop could test both the immediate state of some keys and state changes for other keys by using immediate functions after `cleareventflips()` followed by `clearevents()`.
```py
# Render game...
@ -179,7 +203,7 @@ keycode_function(key: int) -> int
keycode_digit(key: int) -> int
```
`keycode_function(k)` returns the F-key number if `k` (i.e. 1 for `KEY_F1`, 2 for `KEY_F2`, etc.) and -1 for other keys.
`keycode_function(k)` returns the F-key number of `k` (i.e. 1 for `KEY_F1`, 2 for `KEY_F2`, etc.) and -1 for other keys.
`keycode_digit(k)` returns the digit associated with `k` (i.e. 0 for `KEY_0`, 1 for `KEY_1`, etc.) and -1 for other keys.
@ -234,17 +258,171 @@ In PythonExtra, `dupdate()` also indicates a "switch to graphics mode". Due to c
`dclear()` fills the screen with a uniform color.
`dpixel()` changes a pixel's color. Coordinates are universally (x,y) where `x` is in the range 0 to DWIDTH-1 inclusive (0 being left) and `y` is in the range 0 to DHEIGHT-1 inclusive (0 being top).
`dpixel()` changes a pixel's color. Coordinates, as in every other drawing function, are (x,y) where `x` is in the range 0 to DWIDTH-1 inclusive (0 being left) and `y` is in the range 0 to DHEIGHT-1 inclusive (0 being top).
`dgetpixel()` returns the color of a pixel. Note that `dgetpixel()` reads from VRAM, not from the display.
_Example ([`ex_draw1.py`](../../ports/sh/examples/ex_draw1.py))._
```py
from gint import *
dclear(C_WHITE)
for y in range(10):
for x in range(10):
if (x^y) & 1:
dpixel(x, y, C_BLACK)
dupdate()
```
![](images/modgint-draw1-cg.png) ![](images/modgint-draw1-fx.png)
### Geometric shape rendering functions
TODO
```py
drect(x1: int, y1: int, x2: int, y2: int, color: int) -> None
drect_border(x1: int, y1: int, x2: int, y2: int, fill_color: int,
border_width: int, border_color: int) -> None
dline(x1: int, y1: int, x2: int, y2: int, color: int) -> None
dhline(y: int, color: int) -> None
dvline(x: int, color: int) -> None
dcircle(x: int, y: int, radius: int, fill_color: int,
border_color: int) -> None
dellipse(x1: int, y1: int, x2: int, y2: int, fill_color: int,
border_color: int) -> None
```
`drect()` draws a flat rectangle spanning from (x1, y1) to (x2, y2) (both inclusive). The order of points does not matter, i.e. x1 ≥ x2 and y1 ≥ y2 are both allowed.
`drect_border()` is similar but also draws a border. The border is located _inside_ the rectangle.
`dline()` draws a straight line from (x1, y1) to (x2, y2). The shortcut functions `dhline()` and `dvline()` draw a full horizontal and vertical line across the screen respectively.
`dcircle()` draws a circle defined by its center and radius using the Bresenham algorithm. The colors for the circle's interior and its edge can be specified separately, including as `C_NONE` (transparent). By construction, `dcircle()` can only draw circles of odd diameter; for even diameters, use `dellipse()`.
`dellipse()` draws an ellipse defined by its bounding box. Both (x1, y1) and (x2, y2) are included in the bounding box. To render an ellipse from its center coordinates (x,y) and semi-major/minor axes a/b, use `dellipse(x-a, y-b, x+a, y+b, fill_color, border_color)`.
_Exemple ([`ex_draw2.py`](../../ports/sh/examples/ex_draw2.py))._
![](images/modgint-draw2-cg.png) ![](images/modgint-draw2-fx.png)
_Example ([`ex_circle.py`](../../ports/sh/examples/ex_circle.py))._
![](images/modgint-circle-cg.png) ![](images/modgint-circle-fx.png)
### Image rendering functions
TODO
```py
dimage(x: int, y: int, img: image) -> None
dsubimage(x: int, y: int, img: image, left: int, top: int, width: int, height: int) -> None
```
**On black-and-white models**
```py
image:
.profile -> IMAGE_MONO | ...
.width -> int
.height -> int
.data -> buffer-like
# Constructor
image(profile: IMAGE_*, width: int, height: int, data: buffer-like) -> image
```
Images on black-and-white models have either 2 or 4 colors (when the gray engine is enabled). Each image has a `.profile` field indicating the image format (the name "profile" comes from old gint versions), two fields `.width` and `.height` specifying its size, and a `.data` field providing direct access to pixel data.
The table below lists the four available formats.
| Format | Colors | Layers | Name in fxconv |
|--------------------|---------------------------------|--------|----------------|
| `IMAGE_MONO` | Black/white (2) | 1 | `mono` |
| `IMAGE_MONO_ALPHA` | Black/white, transparent (3) | 2 | `mono_alpha` |
| `IMAGE_GRAY` | Shades of gray (4) | 2 | `gray` |
| `IMAGE_GRAY_ALPHA` | Shades of gray, transparent (5) | 3 | `gray_alpha` |
The raw storage format for data is a bit complicated. The image is stored in row-major order; each line is represented left-to-right by a series of words each covering 32 pixels. Each word contains one bit per pixel (as a 4-byte integer) for each layer.
The easiest way to obtain an image is to generate the associated code with the fxconv tool from the [fxSDK](https://gitea.planet-casio.com/Lephenixnoir/fxsdk). The options `--bopti-image-fx --fx` specify a conversion for black-and-white models and the metadata `profile:mono` selects the encoding. The table above lists the value of `profile:` to use for each desired format. For instance for [`fx_image_7seg.py`](../../ports/sh/examples/fx_image_7seg.png) :
![](../../ports/sh/examples/fx_image_7seg.png)
```bash
% fxconv --bopti-image fx_image_7seg.png -o 7seg.py --fx profile:mono name:seg --py
```
```py
import gint
seg = gint.image(0, 79, 12, b'|\x00||\x00|||||\x00\x00\xba\x02::\x82\xb8\xb8:\xba\xba\x00\x00\xc6\x06\x06\x06\xc6\xc0\xc0\x06\xc6\xc6\x00\x00\xc6\x06\x06\x06\xc6\xc0\xc0\x06\xc6\xc6\x00\x00\x82\x02\x02\x02\x82\x80\x80\x02\x82\x82\x00\x00\x00\x00|||||\x00||\x00\x00\x82\x02\xb8:::\xba\x02\xba:\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xba\x02\xb8:\x02:\xba\x02\xba:\x00\x00|\x00||\x00||\x00||\x00\x00')
```
The option `--py-compact` generates much shorter code (less `\x`), however the resulting file can generally not be read or modified by a text editor. The only easy option to use that file is to send it to the calculator and import it as-is. (It can also by manipulated by a program if needed.)
`dimage()` renders an image, positioned so that the top-left corner of the image is located at (x, y).
`dsubimage()` renders a sub-rectangle of an image. The rectangle starts at position (left, top) within the image and is of size `width` by `height`. It is positioned so that the (left, top) pixel is drawn at (x, y) on the screen.
_Example ([`fx_image.py`](../../ports/sh/examples/fx_image.py))._
![](images/modgint-image-fx.png)
**On color models**
```py
image:
.format -> IMAGE_RGB565 | ...
.flags -> int
.color_count -> int
.width -> int
.height -> int
.stride -> int
.data -> buffer-like
.palette -> buffer-like
# Constructor
image(format: IMAGE_*, color_count: int, width: int, height: int, stride: int, data: buffer-like, palette: buffer-like) -> image
# Format-specific constructors
image_rgb565(width: int, height: int, data: buffer-like) -> image
image_rgb565a(width: int, height: int, data: buffer-like) -> image
image_p8_rgb565(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
image_p8_rgb565a(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
image_p4_rgb565(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
image_p4_rgb565a(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
```
Images on color models are available in multiple formats as indicated by the `.format` field; the possible values are listed below. Formats differ in the number of colors, the presence of a transparent color, and the presence of a palette. Of course, the less colors the smaller the memory footprint of the image, so in general it is very beneficial to use the smallest format in which an image fits.
| Format | Colors | Palette | Name in fxconv |
|--------------------|---------------------|-------------|----------------|
| `IMAGE_RGB565` | 65536 | No | `rgb565` |
| `IMAGE_RGB565A` | 65535 + transparent | No | `rgb565a` |
| `IMAGE_P8_RGB565` | 256 | Yes (1-256) | `p8_rgb565` |
| `IMAGE_P8_RGB565A` | 255 + transparent | Yes (1-256) | `p8_rgb565a` |
| `IMAGE_P4_RGB565` | 16 | Yes (16) | `p4_rgb565` |
| `IMAGE_P4_RGB565A` | 15 + transparent | Yes (16) | `p4_rgb565a` |
The `.color_count` field indicates the number of colors in the palette, when there is one. For P8 formats this number varies between 1 and 256, and for P4 formats it is always equal to 16. The `.width` and `.height` fields indicate the image's size. Finally, the `.data` and `.palette` fields provide direct access to the raw data for the image's pixels and color palette.
(The `.flags` field has details of memory management that shouldn't be important to Python scripts. The `.stride` fields indicates the distance in bytes between rows of pixels in `.data` and should similarly be of rare use.)
The functions `dimage()` and `dsubimage()` work the same way as for black-and-white models; please see above.
As for black-and-white models, images can be converted into a Python format with fxconv. For instance with [`cg_image_puzzle.png`](../../ports/sh/examples/cg_image_puzzle.png) (full encoding elided):
![](../../ports/sh/examples/cg_image_puzzle.png)
```bash
% fxconv --bopti-image cg_image_puzzle.png -o puzzle.py --cg profile:p4_rgb565 name:puzzle --py
```
```py
import gint
puzzle = gint.image(6, 16, 64, 32, 32,
b'\xbb\xbb\xbb\xbb\xbb ... \xdd\xdd\xdd\xdd\xdd\xdd\xdd',
b'\xff\xff\xcfW\x86\xd8\xbe|\xceP\xe5\x8a\x963f9u\x9c}\xa8\x9dxD\xfa\x83\xceLNZ\xcci\xa7')
```
The option `--py-compact` is recommended to reduce code size; please see details in the black-and-white section above.
_Exemple ([`cg_image.py`](../../ports/sh/examples/cg_image.py))._
![](images/modgint-image-cg.png)
## Differences with gint's C API

View File

@ -12,6 +12,11 @@ import gint
from gint import *
```
**Sommaire**
- [Saisie au clavier](#saisie-au-clavier)
- [Dessin à l'écran](#dessin-à-lécran)
- [Différences avec l'API C de gint](#différences-avec-lapi-c-de-gint)
## Saisie au clavier
Les en-têtes de référence sont [`<gint/keyboard.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/keyboard.h) et [`<gint/keycodes.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/keycodes.h).
@ -54,6 +59,7 @@ Les fonctions `getkey()`, `getekey_opt()`, `pollevent()` et `waitevent()` renvoi
```py
getkey() -> key_event
getkey_opt(opts: GETKEY_*, timeout_ms: int | None) -> key_event
```
La fonction `getkey()` met le programme en pause jusqu'à ce qu'une touche soit pressée ou répétée, et renvoie l'événement associé (qui est forcément de type `KEYEV_DOWN` ou `KEYEV_HOLD`). Par défaut, les seules touches qui sont répétées sont les touches directionnelles, une première fois après 400 ms, et ensuite toutes les 40 ms.
@ -76,7 +82,25 @@ elif ev.key == KEY_DOWN and pos < N-1:
pos += 1 # Descendre d'une position
```
TODO: Parler de `getkey_opt()`
La fonction `getkey_opt()` est une généralisation de `getkey()` qui donne accès à plus d'options pour personnaliser le comportement de la fonction. Le paramètre `opts` accepte une combinaison d'options dans le tableau ci-dessous, et le paramètre `timeout_ms`, quand il n'est pas `None`, spécifie au bout de combien de temps la fonction doit s'arrêter et renvoyer un événement de type `KEYEV_NONE` s'il n'y a aucune activité sur le clavier. Le délai est exprimé en millisecondes mais la précision réelle est de l'ordre de 7-8 ms.
| Option | `getkey()` | Description |
|------------------------|------------|------------------------------------------------------------|
| `GETKEY_MOD_SHIFT` | Oui | La touche SHIFT est un modifieur |
| `GETKEY_MOD_ALPHA` | Oui | La touche ALPHA est un modifieur |
| `GETKEY_BACKLIGHT` | Oui | SHIFT+OPTN allume/éteint le rétroéclairage |
| `GETKEY_MENU` | Oui | MENU retourne au menu principal de la calculatrice |
| `GETKEY_REP_ARROWS` | Oui | Répète les touches directionnelles si maintenues |
| `GETKEY_REP_ALL` | Non | Répète toutes les touches si maintenues |
| `GETKEY_REP_PROFILE` | Oui* | Personnalisation des durées de répétition |
| `GETKEY_FEATURES` | Oui* | Personnalisation des raccourcis claviers globaux |
| `GETKEY_MENU_DUPDATE` | Oui | Rafraîchit l'écran après un retour au menu (Prizm/G90) |
| `GETKEY_MENU_EVENT` | Non | Envoie un événement `KEYEV_OSMENU` après un retour au menu |
| `GETKEY_POWEROFF` | Oui | SHIFT+AC/ON éteint la calculatrice |
`getkey()` est équivalent à `getkey_opt(opts, None)``opts` est la somme de toutes les options marquées "Oui" dans la colonne `getkey()` ci-dessus.
Les deux options `GETKEY_REP_PROFILE` et `GETKEY_FEATURES` ne sont utiles qu'au travers de fonctions de configuration qui ne sont pour l'instant pas disponibles dans PythonExtra.
### Lecture des événements en temps réel
@ -127,7 +151,7 @@ keydown_all(*keys: [int]) -> bool
keydown_any(*keys: [int]) -> bool
```
Une fois les événements lus, on peut tester l'état individuellement si les touches sont pressées ou pas à l'aide de la fonction `keydown()`. `keydown(k)` renvoie `True` si la touche `k` est pressée, `False` sinon. Cette fonction ne marche **que si les événements ont été lus**, ce qu'on fait souvent soit avec `pollevent()` soit avec `clearevents()`.
Une fois les événements lus, on peut tester individuellement si les touches sont pressées ou pas à l'aide de la fonction `keydown()`. `keydown(k)` renvoie `True` si la touche `k` est pressée, `False` sinon. Cette fonction ne marche **que si les événements ont été lus**, ce qu'on fait souvent soit avec `pollevent()` soit avec `clearevents()`.
_Exemple._ Une boucle de jeu pourrait tester si les touches gauche/droite sont pressées à chaque frame pour déplacer le joueur.
@ -152,7 +176,7 @@ keypressed(key: int) -> bool
keyreleased(key: int) -> bool
```
`keydown()` indique uniquement l'état instantané des touches. Elle ne permet pas de déterminer à quel moment une touche passe de l'état relâché à l'état pressé ou l'inverse. Pour ça, il faut soit utiliser les événements (ce qui est un peu lourd), soit utiliser les fonctions ci-dessous.
`keydown()` indique uniquement l'état instantané des touches. Elle ne permet pas de déterminer à quel moment une touche passe de l'état relâché à l'état pressé ou l'inverse. Pour ça, il faut soit utiliser les événements (ce qui est un peu lourd), soit se souvenir de quelles touches étaient pressées à "l'instant" précédent, soit utiliser les fonctions ci-dessous.
Les fonctions `keypressed(k)` et `keyreleased(k)` indiquent si la touche a été pressée/relâchée depuis le dernier appel à `cleareventflips()`. Attention la notion de "pressée/relâchée" ici n'est pas le temps réel mais la lecture des événements.
@ -229,17 +253,17 @@ dgetpixel(x: int, y: int) -> int
Les entiers `DWIDTH` et `DHEIGHT` indiquent la taille de l'écran. C'est 128x64 sur les Graph mono (type Graph 35+E II), 396x224 sur la Prizm et Graph 90+E (le plein écran est disponible).
Toutes les fonctions de dessin opèrent sur une image interne appellée "VRAM" ; l'effet du dessin n'est donc pas visible immédiatement à l'écran. Pour que le dessin se voie il faut appeler la fonction `dupdate()` qui transfère les contenus de la VRAM à l'écran réel. Généralement, on fait ça après avoir affiché tout ce dont on a besoin et surtout pas après chaque appel de fonction de dessin.
Toutes les fonctions de dessin opèrent sur une image interne appellée "VRAM" ; l'effet du dessin n'est donc pas visible immédiatement à l'écran. Pour que le dessin se voie il faut appeler la fonction `dupdate()` qui transfère les contenus de la VRAM à l'écran réel. Généralement, on fait ça après avoir affiché tout ce dont on a besoin et pas après chaque appel de fonction de dessin.
Dans PythonExtra, la fonction `dupdate()` indique aussi implicitement qu'on « passe en mode graphique ». À cause de certaines optimisations tout appel à `print()` est considéré comme un « passage en mode texte » et pendant qu'on est en mode texte le shell peut redessiner à tout moment. Si on veut dessiner après avoir utilisé le mode texte, il faut appeler `dupdate()` pour forcer un passage en mode graphique avant de commencer à dessiner. Sinon le dessin du shell pourrait interférer avec le dessin du programme.
La fonction `dclear()` remplit l'écran d'une couleur unie.
La fonction `dpixel()` modifie la couleur d'un pixel. Les coordonnées sont universellement (x,y) où `x` varie entre 0 et DWIDTH-1 inclus (0 étant à gauche), et `y` varie entre 0 et DHEIGHT-1 inclus (0 étant en haut).
La fonction `dpixel()` modifie la couleur d'un pixel. Les coordonnées, comme pour toutes les fonctions de dessin, sont (x,y) où `x` varie entre 0 et DWIDTH-1 inclus (0 étant à gauche), et `y` varie entre 0 et DHEIGHT-1 inclus (0 étant en haut).
La fonction `dgetpixel()` renvoie la couleur d'un pixel. Attention, `dgetpixel()` lit dans la VRAM, pas sur l'écran.
_Exemple ([`ex_draw1.py`](../../sh/examples/ex_draw1.py))._
_Exemple ([`ex_draw1.py`](../../ports/sh/examples/ex_draw1.py))._
```py
from gint import *
@ -251,6 +275,8 @@ for y in range(10):
dupdate()
```
![](images/modgint-draw1-cg.png) ![](images/modgint-draw1-fx.png)
### Fonctions de dessin de formes géométriques
```py
@ -265,11 +291,139 @@ dcircle(x: int, y: int, radius: int, fill_color: int,
dellipse(x1: int, y1: int, x2: int, y2: int, fill_color: int,
border_color: int) -> None
```
TODO
`drect()` dessine un rectangle plein allant de (x1, y1) à (x2, y2) (tous les deux inclus). L'ordre des points ne compte pas, i.e. x1 ≥ x2 ou y1 ≥ y2 est autorisé.
`drect_border()` est similaire mais dessine une bordure de largeur `border_width` et de couleur `border_color`. La bordure est dessinée _à l'intérieur_ du rectangle.
`dline()` dessine une ligne droite entre les points (x1, y1) et (x2, y2). Les raccourcis `dhline()` et `dvline()` dessinent respectivement une ligne horizontale et verticale à travers tout l'écran.
`dcircle()` dessine un cercle défini par son centre et rayon avec l'algorithme de Bresenham. La couleur de l'intérieur et du bord peuvent être spécifiées séparément, y compris avec `C_NONE` (transparente). Par construction, `dcircle()` ne peut construire que des cercles de diamètre impair ; pour les diamètres plus fin, utilisez `dellipse()`.
`dellipse()` dessine une ellipse définie par son rectangle englobant. Les points (x1, y1) et (x2, y2) sont tous les deux inclus dans le rectangle. Pour dessiner une ellipse à partir de son centre (x, y) et de ses demi-grand/petit axes a/b, utilisez `dellipse(x-a, y-b, x+a, y+b, fill_color, border_color)`.
_Exemple ([`ex_draw2.py`](../../ports/sh/examples/ex_draw2.py))._
![](images/modgint-draw2-cg.png) ![](images/modgint-draw2-fx.png)
_Exemple ([`ex_circle.py`](../../ports/sh/examples/ex_circle.py))._
![](images/modgint-circle-cg.png) ![](images/modgint-circle-fx.png)
### Fonctions de dessin d'images
TODO
```py
dimage(x: int, y: int, img: image) -> None
dsubimage(x: int, y: int, img: image, left: int, top: int, width: int, height: int) -> None
```
**Sur Graph mono**
```py
image:
.profile -> IMAGE_MONO | ...
.width -> int
.height -> int
.data -> buffer-like
# Constructeur
image(profile: IMAGE_*, width: int, height: int, data: buffer-like) -> image
```
Les images sur Graph mono sont en noir-et-blanc ou 4 couleurs (quand le moteur de gris est utilisé). Chaque image a un champ `.profile` indiquant le format d'image (le nom "profile" est hérité d'anciennes versions de gint), deux champs `.width` et `.height` indiquant sa taille, et un champ `.data` donnant accès aux données brutes.
Les quatre formats disponibles sont les suivants.
| Format | Couleurs | Calques | Nom dans fxconv |
|--------------------|----------------------------------|---------|-----------------|
| `IMAGE_MONO` | Noir/blanc (2) | 1 | `mono` |
| `IMAGE_MONO_ALPHA` | Noir/blanc, transparent (3) | 2 | `mono_alpha` |
| `IMAGE_GRAY` | Niveaux de gris (4) | 2 | `gray` |
| `IMAGE_GRAY_ALPHA` | Niveaux de gris, transparent (5) | 3 | `gray_alpha` |
Le format des données brutes est un peu compliqué. L'image est stockée ligne par ligne de haut en bas ; chaque ligne est représentée de gauche à droite par une série de mots représentant chacun 32 pixels. Chaque mot contient un bit par pixel (sous la forme d'un entier de 4 octets) pour chaque calque.
Le plus simple pour obtenir une image est de générer le code associé avec l'outil fxconv du [fxSDK](https://gitea.planet-casio.com/Lephenixnoir/fxsdk). Les options `--bopti-image-fx --fx` spécifient qu'on convertit une image pour Graph mono et la métadonnée `profile:mono` choisit le format de l'image produite. Le tableau ci-dessus liste la valeur de `profile:` à spécifier pour chaque format. Par exemple pour l'image [`fx_image_7seg.py`](../../ports/sh/examples/fx_image_7seg.png) :
![](../../ports/sh/examples/fx_image_7seg.png)
```bash
% fxconv --bopti-image fx_image_7seg.png -o 7seg.py --fx profile:mono name:seg --py
```
```py
import gint
seg = gint.image(0, 79, 12, b'|\x00||\x00|||||\x00\x00\xba\x02::\x82\xb8\xb8:\xba\xba\x00\x00\xc6\x06\x06\x06\xc6\xc0\xc0\x06\xc6\xc6\x00\x00\xc6\x06\x06\x06\xc6\xc0\xc0\x06\xc6\xc6\x00\x00\x82\x02\x02\x02\x82\x80\x80\x02\x82\x82\x00\x00\x00\x00|||||\x00||\x00\x00\x82\x02\xb8:::\xba\x02\xba:\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xba\x02\xb8:\x02:\xba\x02\xba:\x00\x00|\x00||\x00||\x00||\x00\x00')
```
L'option `--py-compact` permet de générer du code beaucoup plus compact (avec moins de `\x`), par contre le fichier obtenu ne peut pas être lu par un éditeur de texte. La seule option simple pour l'utiliser est de l'envoyer sur la calculatrice et l'importer tel quel (on peut aussi le manipuler avec un programme).
La fonction `dimage()` dessine une image à l'écran en positionnant le coin haut gauche de l'image à la position (x, y).
La fonction `dsubimage()` permet de dessiner un sous-rectangle d'une image. Le sous-rectangle commence à la position (left, top) de l'image et s'étend sur une largeur `width` et une hauteur `height`. Le sous-rectangle est dessiné de façon à ce que le pixel (left, top) arrive à la position (x, y) de l'écran.
_Exemple ([`fx_image.py`](../../ports/sh/examples/fx_image.py))._
![](images/modgint-image-fx.png)
**Sur Graph couleur**
```py
image:
.format -> IMAGE_RGB565 | ...
.flags -> int
.color_count -> int
.width -> int
.height -> int
.stride -> int
.data -> buffer-like
.palette -> buffer-like
# Constructeur
image(format: IMAGE_*, color_count: int, width: int, height: int, stride: int, data: buffer-like, palette: buffer-like) -> image
# Constructeurs spécialisés par format
image_rgb565(width: int, height: int, data: buffer-like) -> image
image_rgb565a(width: int, height: int, data: buffer-like) -> image
image_p8_rgb565(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
image_p8_rgb565a(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
image_p4_rgb565(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
image_p4_rgb565a(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
```
Les images sur Graph couleur sont déclinées en différents formats indiqués par le champ `.format`, dont les valeurs possibles sont listées ci-dessous. Les différences sont dans le nombre de couleurs, la présence ou pas de transparence, et la présence ou pas d'une palette de couleurs. Bien sûr, moins il y a de couleurs moins l'image prend de place en mémoire, donc de façon générale il est très bénéfique d'utiliser le plus petit format possible pour chaque image.
| Format | Couleurs | Palette | Nom dans fxconv |
|--------------------|---------------------|-------------|-----------------|
| `IMAGE_RGB565` | 65536 | Non | `rgb565` |
| `IMAGE_RGB565A` | 65535 + transparent | Non | `rgb565a` |
| `IMAGE_P8_RGB565` | 256 | Oui (1-256) | `p8_rgb565` |
| `IMAGE_P8_RGB565A` | 255 + transparent | Oui (1-256) | `p8_rgb565a` |
| `IMAGE_P4_RGB565` | 16 | Oui (16) | `p4_rgb565` |
| `IMAGE_P4_RGB565A` | 15 + transparent | Oui (16) | `p4_rgb565a` |
Le champ `.color_count` indique le nombre de couleurs dans la palette quand il y en a une. Pour les formats P8 ce nombre varie entre 1 et 256, pour les formats P4 il est toujours égal à 16. Les deux champs `.width` et `.height` indiquent la taille de l'image. Enfin, les champs `.data` et `.palette` permettent d'accéder aux données brutes des pixels ainsi que de la palette.
(Le champ `.flags` est un détail de gestion de mémoire qui ne devrait pas importer aux scripts Python. Le champ `.stride` indique combien d'octets séparent chaque ligne de pixels dans `.data` et sera de même rarement utilisé.)
Les fonctions `dimage()` et `dsubimage()` ont le même fonctionnement que pour les Graph mono ; voir ci-dessus.
Comme pour Graph mono les images peuvent être converties avec fxconv. Par exemple pour l'image [`cg_image_puzzle.png`](../../ports/sh/examples/cg_image_puzzle.png) (code abrégé) :
![](../../ports/sh/examples/cg_image_puzzle.png)
```bash
% fxconv --bopti-image cg_image_puzzle.png -o puzzle.py --cg profile:p4_rgb565 name:puzzle --py
```
```py
import gint
puzzle = gint.image(6, 16, 64, 32, 32,
b'\xbb\xbb\xbb\xbb\xbb ... \xdd\xdd\xdd\xdd\xdd\xdd\xdd',
b'\xff\xff\xcfW\x86\xd8\xbe|\xceP\xe5\x8a\x963f9u\x9c}\xa8\x9dxD\xfa\x83\xceLNZ\xcci\xa7')
```
L'option `--py-compact` est recommandée pour réduire la taille du code ; voir les détails dans la section Graph mono.
_Exemple ([`cg_image.py`](../../ports/sh/examples/cg_image.py))._
![](images/modgint-image-cg.png)
## Différences avec l'API C de gint

View File

@ -6,14 +6,17 @@ SH_LDFLAGS := -T fx9860g.ld -ljustui-fx -lm -lgint-fx -lc -lgint-fx -lgcc
SH_ASSETS := \
img_fkeys_main.png img_modifier_states.png \
font_5x7.png font_4x4.png font_4x6.png
font_5x7.png font_4x4.png font_4x6.png font_3x5.png
SH_METADATA := fxconv-metadata.txt
SH_CONVFLAGS := --fx
all: PythonEx.g1a
PythonEx.g1a: $(BUILD)/firmware.bin icon.png
fxgxa --g1a -n PythonExtra -i icon.png $< -o $@
ICON_RELEASE := icon.png
ICON_DEV := icon-dev.png
PythonEx.g1a: $(BUILD)/firmware.bin icon.png icon-dev.png
fxgxa --g1a -n PythonExtra -i $(ICON_$(BUILDTYPE)) $< -o $@
send: all
fxlink -sw PythonEx.g1a

BIN
ports/fx9860g3/font_3x5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -10,14 +10,12 @@ font_*.png:
grid.border: 0
font_5x7.png:
type: font
charset: print
grid.size: 5x7
grid.padding: 1
grid.border: 0
font_4x4.png:
type: font
charset: print
grid.size: 4x4
grid.padding: 1
@ -25,9 +23,15 @@ font_4x4.png:
proportional: true
font_4x6.png:
type: font
charset: print
grid.size: 5x6
grid.padding: 1
grid.border: 0
proportional: true
font_3x5.png:
charset: ascii
height: 5
grid.size: 5x6
grid.padding: 1
proportional: true

BIN
ports/fx9860g3/icon-dev.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,2 +1 @@
/icon.xcf
/*.g3a
/*.g3a

View File

@ -7,11 +7,14 @@ SH_LDFLAGS := -T fxcg50.ld -ljustui-cg -lm -lgint-cg -lc -lgint-cg -lgcc
SH_ASSETS := img_modifier_states.png font_9.png font_13.png font_19.png PoliceNW
SH_METADATA := fxconv-metadata.txt
SH_CONVFLAGS := --cg
all: PythonExtra.g3a
PythonExtra.g3a: $(BUILD)/firmware.bin icon-uns.png icon-sel.png
fxgxa --g3a -n PyExtra_NW --icon-uns=icon-uns.png --icon-sel=icon-sel.png $< -o $@
ICONS_RELEASE := --icon-uns=icon-uns.png --icon-sel=icon-sel.png
ICONS_DEV := --icon-uns=icon-uns-dev.png --icon-sel=icon-sel-dev.png
PythonExtra.g3a: $(BUILD)/firmware.bin
fxgxa --g3a -n PythonExtra $(ICONS_$(BUILDTYPE)) $< -o $@
send: all
fxlink -sw PythonExtra.g3a

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

BIN
ports/fxcg50/icon.xcf Normal file

Binary file not shown.

View File

@ -19,6 +19,8 @@ SRC_C = \
ports/sh/mphalport.c \
ports/sh/objgintimage.c \
ports/sh/pyexec.c \
ports/sh/resources.c \
ports/sh/stredit.c \
ports/sh/widget_shell.c \
shared/runtime/gchelper_generic.c \
shared/runtime/stdout_helpers.c \
@ -37,15 +39,12 @@ ifeq ($(TARGETCASIO),"FXCG50")
$(info ********* Add Numworks modules ********* )
SRC_C += \
ports/sh/numworks/modkandinsky.c \
ports/sh/numworks/ion.c \
ports/sh/numworks/time.c \
ports/sh/numworks/modion.c \
SRC_QSTR += \
ports/sh/numworks/modkandinsky.c \
ports/sh/numworks/ion.c \
ports/sh/numworks/time.c
ports/sh/numworks/modion.c \
endif
ifeq ($(TARGETCASIO),"FX9860G")
@ -53,6 +52,15 @@ ifeq ($(TARGETCASIO),"FX9860G")
endif
ifeq ($(shell [[ x"$$(git describe)" == x"$$(git describe main)" ]] \
&& [[ -z "$$(git status -uno --porcelain)" ]] \
&& echo y),y)
$(info * RELEASE BUILD)
BUILDTYPE := RELEASE
else
$(info * DEVELOPMENT BUILD)
BUILDTYPE := DEV
endif
ASSETS_O := $(SH_ASSETS:%=$(BUILD)/sh_assets/%.o)

View File

@ -17,7 +17,7 @@
#include "keymap.h"
/* Compute the shell's horizontal layout as:
1. Text width (region where the text is renderer)
1. Text width (region where the text is rendered)
2. Spacing left of the scrollbar
3. Width of the scrollbar */
static void view_params(int w,
@ -40,110 +40,25 @@ static void view_params(int w,
#endif
}
//=== Dynamic console lines ===//
//=== Static console lines ===//
bool console_line_init(console_line_t *line, int prealloc_size)
void console_fline_update_render_lines(console_fline_t *FL, int width)
{
char *data = malloc(prealloc_size + 1);
if(!data)
return false;
data[0] = 0;
line->data = data;
line->size = 0;
line->alloc_size = prealloc_size + 1;
line->render_lines = 0;
line->prefix = 0;
return true;
}
void console_line_deinit(console_line_t *line)
{
free(line->data);
/* Manual memset to allow for PRAM0 storage */
line->data = NULL;
line->size = 0;
line->alloc_size = 0;
line->render_lines = 0;
line->prefix = 0;
}
bool console_line_alloc(console_line_t *line, int n)
{
if(line->alloc_size >= n + 1)
return true;
/* Always increase the size by at least 16 so we can insert many times in a
row without worrying about excessive allocations. */
int newsize = max(line->alloc_size + 16, n + 1);
char *newdata = realloc(line->data, newsize);
if(!newdata)
return false;
line->data = newdata;
line->alloc_size = newsize;
return true;
}
int console_line_capacity(console_line_t *line)
{
return PE_CONSOLE_LINE_MAX_LENGTH - line->size;
}
void console_line_set_prefix(console_line_t *line, int prefix_size)
{
line->prefix = min(max(0, prefix_size), (int)line->size);
}
bool console_line_insert(console_line_t *line, int p, char const *str, int n)
{
if(p < 0 || p > line->size || n > console_line_capacity(line))
return false;
if(!console_line_alloc(line, line->size + n))
return false;
/* Move the end of the string (plus the NUL) n bytes forward */
memmove(line->data + p + n, line->data + p, line->size - p + 1);
memcpy(line->data + p, str, n);
line->size += n;
return true;
}
int console_line_delete(console_line_t *line, int p, int n)
{
if(p < line->prefix) {
int unremovable = line->prefix - p;
p += unremovable;
n -= unremovable;
}
n = min(n, line->size - p);
if(n < 0)
return 0;
/* Move the end of the string (plus the NUL) n bytes backwards */
memmove(line->data + p, line->data + p + n, line->size - n - p + 1);
line->size -= n;
return n;
}
void console_line_update_render_lines(console_line_t *line, int width)
{
char const *p = line->data;
line->render_lines = 0;
char const *p = FL->data;
FL->render_lines = 0;
do {
line->render_lines++;
FL->render_lines++;
p = drsize(p, NULL, width, NULL);
}
while(*p);
}
int console_line_render(int x, int y, console_line_t *line, int w, int dy,
int console_fline_render(int x, int y, console_fline_t *FL, int w, int dy,
int show_from, int show_until, int cursor)
{
char const *p = line->data;
char const *endline = p + strlen(p);
char const *p = FL->data;
char const *endline = p + FL->size;
int line_offset = 0;
int line_number = 0;
@ -184,6 +99,9 @@ bool linebuf_init(linebuf_t *buf, int capacity, int backlog_size)
buf->lines = kmalloc(capacity * sizeof *buf->lines, PE_CONSOLE_LINE_ALLOC);
if(!buf->lines)
return false;
memset(buf->lines, 0, capacity * sizeof *buf->lines);
stredit_init(&buf->edit, 0, 0, 0);
buf->capacity = capacity;
buf->start = 0;
@ -198,6 +116,9 @@ bool linebuf_init(linebuf_t *buf, int capacity, int backlog_size)
void linebuf_deinit(linebuf_t *buf)
{
stredit_reset(&buf->edit);
for(int i = 0; i < buf->capacity; i++)
free(buf->lines[i]);
kfree((void *)buf->lines);
memset(buf, 0, sizeof *buf);
}
@ -209,7 +130,7 @@ void linebuf_deinit(linebuf_t *buf)
lines independent of rotation, we use their absolute line number.
Such numbers refer to lines stored in the buffer if:
(index) 0 <= linebuf_index_to_nth(buf, index) < size
(index) 0 <= index < size
(nth) 0 <= nth < size
(abs) 0 <= abs - buf->absolute < size */
@ -219,13 +140,16 @@ GINLINE static int linebuf_nth_to_index(linebuf_t const *buf, int nth)
}
/* Get the nth line. */
GINLINE static console_line_t *linebuf_get_nth_line(linebuf_t const *buf,
GINLINE static console_fline_t *linebuf_get_nth_line(linebuf_t const *buf,
int nth)
{
if(nth < 0 || nth >= buf->size)
return NULL;
return &buf->lines[linebuf_nth_to_index(buf, nth)];
if(nth == buf->size - 1)
return (console_fline_t *)buf->edit.raw;
else
return buf->lines[linebuf_nth_to_index(buf, nth)];
}
/* Move `index` by `diff`; assumes |diff| <= buf->capacity. */
@ -244,31 +168,40 @@ int linebuf_end(linebuf_t const *buf)
return buf->absolute + buf->size;
}
console_line_t *linebuf_get_line(linebuf_t const *buf, int abs)
console_fline_t *linebuf_get_line(linebuf_t const *buf, int abs)
{
return linebuf_get_nth_line(buf, abs - buf->absolute);
}
console_line_t *linebuf_get_last_line(linebuf_t const *buf)
stredit_t *linebuf_get_last_line(linebuf_t *buf)
{
return linebuf_get_nth_line(buf, buf->size - 1);
return (buf->size > 0) ? &buf->edit : NULL;
}
console_line_t *linebuf_newline(linebuf_t *buf)
stredit_t *linebuf_newline(linebuf_t *buf)
{
/* Make space if the buffer is full */
linebuf_recycle_oldest_lines(buf, buf->size - buf->capacity + 1);
/* Freeze the current last line's size */
/* Freeze the current last line and reset the editor */
if(buf->size > 0) {
console_line_t *L = linebuf_get_nth_line(buf, buf->size - 1);
buf->total_size_except_last += L->size;
buf->total_size_except_last += buf->edit.size;
int size = buf->edit.size;
console_fline_t *frozen = (void *)stredit_freeze_and_reset(&buf->edit);
frozen->size = size;
int last_nth = linebuf_nth_to_index(buf, buf->size - 1);
assert(buf->lines[last_nth] == NULL);
buf->lines[last_nth] = frozen;
}
buf->size++;
console_line_t *L = linebuf_get_nth_line(buf, buf->size - 1);
console_line_init(L, 16);
return L;
int last_nth = linebuf_nth_to_index(buf, buf->size - 1);
buf->lines[last_nth] = NULL;
stredit_init(&buf->edit, 16, CONSOLE_FLINE_SIZE,
PE_CONSOLE_LINE_MAX_LENGTH);
return &buf->edit;
}
void linebuf_recycle_oldest_lines(linebuf_t *buf, int count)
@ -278,11 +211,11 @@ void linebuf_recycle_oldest_lines(linebuf_t *buf, int count)
return;
for(int nth = 0; nth < count; nth++) {
console_line_t *L = linebuf_get_nth_line(buf, nth);
buf->total_rendered -= L->render_lines;
console_fline_t *FL = linebuf_get_nth_line(buf, nth);
buf->total_rendered -= FL->render_lines;
if(nth != buf->size - 1)
buf->total_size_except_last -= L->size;
console_line_deinit(L);
buf->total_size_except_last -= FL->size;
free(FL);
}
buf->start = linebuf_index_add(buf, buf->start, count);
@ -317,10 +250,10 @@ void linebuf_update_render(linebuf_t *buf, int width, bool lazy)
view_params(width, &text_w, NULL, NULL);
for(int abs = start; abs < end; abs++) {
console_line_t *L = linebuf_get_nth_line(buf, abs - buf->absolute);
buf->total_rendered -= L->render_lines;
console_line_update_render_lines(L, text_w);
buf->total_rendered += L->render_lines;
console_fline_t *FL = linebuf_get_nth_line(buf, abs - buf->absolute);
buf->total_rendered -= FL->render_lines;
console_fline_update_render_lines(FL, text_w);
buf->total_rendered += FL->render_lines;
}
buf->absolute_rendered = max(buf->absolute_rendered, end - 2);
@ -383,7 +316,10 @@ void console_compute_view(console_t *cons, font_t const *font,
cons->render_font = font;
cons->render_width = width;
cons->render_lines = lines;
font_t const *old_font = dfont(font);
linebuf_update_render(&cons->lines, width, lazy);
dfont(old_font);
}
console_scrollpos_t console_clamp_scrollpos(console_t const *cons,
@ -396,7 +332,7 @@ console_scrollpos_t console_clamp_scrollpos(console_t const *cons,
return max(0, min(pos, cons->lines.total_rendered - cons->render_lines));
}
void console_render(int x, int y0, console_t const *cons, int dy,
void console_render(int x, int y0, console_t *cons, int dy,
console_scrollpos_t pos)
{
int total_lines = cons->lines.total_rendered;
@ -409,6 +345,12 @@ void console_render(int x, int y0, console_t const *cons, int dy,
font_t const *old_font = dfont(cons->render_font);
/* Normally only frozen lines have a valid size field. Update the size of
the last line so we don't have to make an exception for it. */
stredit_t *ed = linebuf_get_last_line(&cons->lines);
console_fline_t *ed_FL = (console_fline_t *)ed->raw;
ed_FL->size = ed->size;
/* Show only visible lines. We want to avoid counting all the lines in the
console, and instead start from the end. */
int line_y = visible_lines + pos;
@ -422,13 +364,13 @@ void console_render(int x, int y0, console_t const *cons, int dy,
/* If there isn't enough content to fill the view, start at the top. */
line_y = min(line_y, pos);
while(i < L_end && line_y < visible_lines) {
console_line_t *line = linebuf_get_line(&cons->lines, i);
while(i < L_end && line_y < visible_lines) {
console_fline_t *FL = linebuf_get_line(&cons->lines, i);
bool show_cursor = (i == L_end - 1);
y = console_line_render(x, y, line, text_w, dy, -line_y,
y = console_fline_render(x, y, FL, text_w, dy, -line_y,
visible_lines - line_y, show_cursor ? cons->cursor : -1);
line_y += line->render_lines;
line_y += FL->render_lines;
i++;
}
@ -453,16 +395,16 @@ void console_render(int x, int y0, console_t const *cons, int dy,
//=== Edition functions ===//
GINLINE static console_line_t *last_line(console_t *cons)
GINLINE static stredit_t *last_line(console_t *cons)
{
return linebuf_get_last_line(&cons->lines);
}
bool console_set_cursor(console_t *cons, int pos)
{
console_line_t *L = last_line(cons);
stredit_t *ed = last_line(cons);
if(pos < L->prefix || pos > L->size)
if(pos < ed->prefix || pos > ed->size)
return false;
cons->cursor = pos;
@ -476,8 +418,8 @@ bool console_move_cursor(console_t *cons, int cursor_movement)
char *console_get_line(console_t *cons, bool copy)
{
console_line_t *L = last_line(cons);
char *str = L->data + L->prefix;
stredit_t *ed = last_line(cons);
char *str = stredit_data(ed) + ed->prefix;
return copy ? strdup(str) : str;
}
@ -488,11 +430,11 @@ bool console_write_raw(console_t *cons, char const *str, int n)
/* Split string into chunks smaller than each line's storage capacity. */
while(n > 0) {
console_line_t *L = last_line(cons);
int capacity = console_line_capacity(L);
stredit_t *ed = last_line(cons);
int capacity = stredit_capacity(ed);
int round_size = min(n, capacity);
if(!console_line_insert(L, cons->cursor, str, round_size))
if(!stredit_insert(ed, cons->cursor, str, round_size))
return false;
cons->cursor += round_size;
cons->render_needed = true;
@ -530,27 +472,26 @@ bool console_write(console_t *cons, char const *buf, int n)
}
else if(buf[offset] == 8) {
offset++;
console_line_t *L = last_line(cons);
stredit_t *ed = last_line(cons);
if(cons->cursor > 0) {
console_line_delete(L, cons->cursor-1, 1);
stredit_delete(ed, cons->cursor-1, 1);
cons->cursor--;
}
}
else if(buf[offset] == '\e') {
console_line_t *L = last_line(cons);
stredit_t *ed = last_line(cons);
offset++;
/* TODO: Handle more complex escape sequences */
if(offset + 2 <= n && buf[offset] == '[' && buf[offset+1] == 'K') {
console_line_delete(L, cons->cursor,
L->size - cons->cursor);
stredit_delete(ed, cons->cursor, ed->size - cons->cursor);
offset += 2;
}
if(offset + 2 <= n && buf[offset] == 1 && buf[offset+1] == 'D') {
cons->cursor -= (cons->cursor > 0);
}
if(offset + 2 <= n && buf[offset] == 1 && buf[offset+1] == 'C') {
cons->cursor += (cons->cursor < L->size);
cons->cursor += (cons->cursor < ed->size);
}
}
@ -562,21 +503,21 @@ bool console_write(console_t *cons, char const *buf, int n)
void console_lock_prefix(console_t *cons)
{
console_line_set_prefix(last_line(cons), cons->cursor);
stredit_set_prefix(last_line(cons), cons->cursor);
}
void console_delete_at_cursor(console_t *cons, int n)
{
int real_n = console_line_delete(last_line(cons), cons->cursor - n, n);
int real_n = stredit_delete(last_line(cons), cons->cursor - n, n);
cons->cursor -= real_n;
cons->render_needed = true;
}
void console_clear_current_line(console_t *cons)
{
console_line_t *L = last_line(cons);
console_line_delete(L, L->prefix, L->size - L->prefix);
cons->cursor = L->prefix;
stredit_t *ed = last_line(cons);
stredit_delete(ed, ed->prefix, ed->size - ed->prefix);
cons->cursor = ed->prefix;
cons->render_needed = true;
}

View File

@ -26,65 +26,44 @@
#include <gint/display.h>
#include <gint/defs/attributes.h>
#include <stdbool.h>
#include "stredit.h"
/* Maximum line length, to ensure the console can threshold its memory usage
while cleaning only entire lines. Lines longer than this get split. */
#define PE_CONSOLE_LINE_MAX_LENGTH 1024
/* Allocation arena for arrays of lines. */
// TODO: Split circular buffers
#ifdef FX9860G
#define PE_CONSOLE_LINE_ALLOC "pram0"
#define PE_CONSOLE_LINE_ALLOC NULL
#else
#define PE_CONSOLE_LINE_ALLOC NULL
#endif
//=== Dynamic console lines ===//
//=== Static console lines ===//
typedef volatile struct
/* A line whose contents have been frozen. The only variable left is how many
render lines it takes, which is kept at the beginning of the buffer along
with its size. */
typedef struct
{
/* Line contents, NUL-terminated. The buffer might be larger. */
char *data;
/* Size of contents (not counting the NUL). */
int32_t size :16;
/* Allocated size (always ≥ size+1). */
int32_t alloc_size :16;
/* Number or render lines used (updated on-demand). */
int32_t render_lines :16;
/* Number of initial characters that can't be edited. */
int32_t prefix :16;
/* Number of non-NUL bytes in data[] (read-only) */
uint16_t size;
/* Number of render lines occupied by the line (read-write) */
uint8_t render_lines;
/* Raw NUL-terminated data (read-only) */
char data[];
} console_line_t;
} console_fline_t;
/* Create a new console line with at least init_chars characters of content
available. Returns false on error. Previous contents are not freed! */
bool console_line_init(console_line_t *line, int init_chars);
/* Clean up a line and free its contents. */
void console_line_deinit(console_line_t *line);
/* Realloc the line to ensure n characters plus a NUL can be written. */
bool console_line_alloc(console_line_t *line, int n);
/* Determine how many characters can be written before the line has to be
broken up. */
int console_line_capacity(console_line_t *line);
/* Set the prefix_size first characters of the line to not be editable. The
line must already have that many characters printed. */
void console_line_set_prefix(console_line_t *line, int prefix_size);
/* Insert n characters at position p. */
bool console_line_insert(console_line_t *line, int p, char const *str, int n);
/* Remove n characters at position p. Returns the number of characters
actually removed after bounds checking. */
int console_line_delete(console_line_t *line, int p, int n);
/* sizeof(console_fline_t) without alignment */
#define CONSOLE_FLINE_SIZE 3
/* Update the number of render lines for the chosen width. */
void console_line_update_render_lines(console_line_t *line, int width);
void console_fline_update_render_lines(console_fline_t *FL, int width);
/* Render a vertical slice of the wrapped line. */
int console_line_render(int x, int y, console_line_t *line, int w, int dy,
int console_fline_render(int x, int y, console_fline_t *FL, int w, int dy,
int show_from, int show_until, int cursor);
//=== Rotating line storage ===//
@ -93,8 +72,13 @@ int console_line_render(int x, int y, console_line_t *line, int w, int dy,
typedef struct
{
/* A rotating array of `capacity` lines starting at position `start` and
holding `size` lines. The array is pre-allocated. */
console_line_t *lines;
holding `size` lines. The array is pre-allocated. The last line, if
there is one, is stored as NULL and its address is edit->raw. */
console_fline_t **lines;
/* Editor for the last line. The pointer to the last line is stored there
instead of in the `lines` array because it gets reallocated regularly
during edition. */
stredit_t edit;
/* Invariants:
- capacity > 0
@ -125,8 +109,8 @@ typedef struct
int total_size_except_last;
/* Last absolute line that has been both laid out for rendering and frozen
for edition (ie. followed by another line). Lazy rendering can always
start at `absolute_rendered+1`. */
for edition (ie. followed by another line). Lazy layout would start at
`absolute_rendered+1`. */
int absolute_rendered;
} linebuf_t;
@ -146,13 +130,13 @@ int linebuf_start(linebuf_t const *buf);
int linebuf_end(linebuf_t const *buf);
/* Get a pointer to the line with the given absolute number. */
console_line_t *linebuf_get_line(linebuf_t const *buf, int absolute_number);
console_fline_t *linebuf_get_line(linebuf_t const *buf, int absolute_number);
/* Get a pointer to the last line in the buffer (usually the editable line). */
console_line_t *linebuf_get_last_line(linebuf_t const *buf);
/* Get a pointer to the last line in the buffer (the editable line). */
stredit_t *linebuf_get_last_line(linebuf_t *buf);
/* Add a new line to the buffer (recycling an old one if needed). */
console_line_t *linebuf_new_line(linebuf_t *buf);
stredit_t *linebuf_new_line(linebuf_t *buf);
/* Recycle the `n` oldest lines from the buffer. */
void linebuf_recycle_oldest_lines(linebuf_t *buf, int n);
@ -222,7 +206,7 @@ console_scrollpos_t console_clamp_scrollpos(console_t const *cons,
/* Render the console at (x,y). The render `width`, the number of `lines` and
the text `font` are all as specified by the latest console_compute_view().
`dy` indicates line height. */
void console_render(int x, int y, console_t const *cons, int dy,
void console_render(int x, int y, console_t *cons, int dy,
console_scrollpos_t pos);
//=== Edition functions ===//

View File

@ -23,6 +23,105 @@ void pe_debug_panic(char const *msg)
exit(1);
}
struct pe_debug_meminfo pe_debug_startup_meminfo[PE_DEBUG_STARTUP_N];
void pe_debug_get_meminfo(struct pe_debug_meminfo *info)
{
kmalloc_gint_stats_t *s;
#ifdef FX9860G
s = kmalloc_get_gint_stats(kmalloc_get_arena("_uram"));
info->_uram_used = s->used_memory;
info->_uram_free = s->free_memory;
s = kmalloc_get_gint_stats(kmalloc_get_arena("pram0"));
info->pram0_used = s->used_memory;
info->pram0_free = s->free_memory;
#endif
#ifdef FXCG50
s = kmalloc_get_gint_stats(kmalloc_get_arena("_uram"));
info->_uram_used = s->used_memory;
info->_uram_free = s->free_memory;
s = kmalloc_get_gint_stats(kmalloc_get_arena("_ostk"));
info->_ostk_used = s->used_memory;
info->_ostk_free = s->free_memory;
#endif
}
void pe_debug_browse_meminfo(void)
{
dclear(C_WHITE);
struct pe_debug_meminfo infonow;
pe_debug_get_meminfo(&infonow);
#ifdef FX9860G
static char const * const names[] = {
"main", "cons.", "upy", "prom.", "ui",
"now", /* extra element compared to original enum */
};
dprint(1, 0, C_BLACK, "Memory info");
extern font_t font_3x5, font_4x6;
font_t const *old_font = dfont(&font_4x6);
dtext(33, 9, C_BLACK, "_uram");
dtext(75, 9, C_BLACK, "pram0");
dline(2, 16, DWIDTH-3, 16, C_BLACK);
for(int i = 0; i < PE_DEBUG_STARTUP_N + 1; i++) {
int y = 7 * i + 18;
struct pe_debug_meminfo *info = &pe_debug_startup_meminfo[i];
if(i >= PE_DEBUG_STARTUP_N) {
dline(2, y, DWIDTH-3, y, C_BLACK);
y += 2;
info = &infonow;
}
dtext(2, y, C_BLACK, names[i]);
dfont(&font_3x5);
dprint(33, y+1, C_BLACK,
"%d,%d", info->_uram_used, info->_uram_free);
dprint(75, y+1, C_BLACK,
"%d,%d", info->pram0_used, info->pram0_free);
dfont(&font_4x6);
}
dfont(old_font);
#endif
#ifdef FXCG50
static char const * const names[] = {
"main", "console", "upy", "prompt", "ui",
"now", /* extra element compared to original enum */
};
dtext(1, 1, C_BLACK, "Memory info");
dtext(83, 24, C_BLACK, "_uram");
dtext(170, 24, C_BLACK, "_ostk");
dline(10, 36, DWIDTH-11, 36, C_BLACK);
for(int i = 0; i < PE_DEBUG_STARTUP_N + 1; i++) {
int y = 15 * i + 39;
struct pe_debug_meminfo *info = &pe_debug_startup_meminfo[i];
if(i >= PE_DEBUG_STARTUP_N) {
dline(10, y, DWIDTH-11, y, C_BLACK);
y += 4;
info = &infonow;
}
dtext(10, y, C_BLACK, names[i]);
dprint(83, y+1, C_BLACK,
"%d,%d", info->_uram_used, info->_uram_free);
dprint(170, y+1, C_BLACK,
"%d,%d", info->_ostk_used, info->_ostk_free);
}
#endif
dupdate();
while((getkey().key) != KEY_EXIT);
}
#if PE_DEBUG
#if 0 // Timeout fxlink not supported yet

View File

@ -23,6 +23,51 @@ void pe_debug_init(void);
void pe_debug_panic(char const *msg)
__attribute__((noreturn));
/*** Memory watch utilities (enabled even when PE_DEBUG=0) ***/
#ifdef FX9860G
struct pe_debug_meminfo {
uint16_t _uram_used, _uram_free;
uint32_t pram0_used, pram0_free;
};
#endif
#ifdef FXCG50
struct pe_debug_meminfo {
uint16_t _uram_used, _uram_free;
uint32_t _ostk_used, _ostk_free;
};
#endif
enum {
/* Just after entering main() */
PE_DEBUG_STARTUP_MAIN,
/* After allocating console */
PE_DEBUG_STARTUP_CONSOLE,
/* After initializing MicroPython */
PE_DEBUG_STARTUP_UPY,
/* After printing the first prompt */
PE_DEBUG_STARTUP_PROMPT,
/* After allocating and initializing the GUI */
PE_DEBUG_STARTUP_UI,
PE_DEBUG_STARTUP_N,
};
/* Fetch data about current memory statistics. */
void pe_debug_get_meminfo(struct pe_debug_meminfo *info);
/* Buffer for storing meminfo during startup. */
extern struct pe_debug_meminfo pe_debug_startup_meminfo[PE_DEBUG_STARTUP_N];
#define pe_debug_get_startup_meminfo(NAME) \
pe_debug_get_meminfo(&pe_debug_startup_meminfo[PE_DEBUG_STARTUP_ ## NAME])
/* Browse memory info through a GUI. */
void pe_debug_browse_meminfo(void);
/*** Debugging functions enabled only when PE_DEBUG=1 ***/
/* Print to the debug stream. This function is also called DEBUG_printf in
MicroPython code. */
int pe_debug_printf(char const *fmt, ...);

View File

@ -0,0 +1,46 @@
from gint import *
import math
if DWIDTH==128:
C_RGB = lambda r, g, b: 0
_ = lambda x, y: x
else:
_ = lambda x, y: y
dclear(C_WHITE)
c1 = _(C_BLACK, C_RGB(24, 24, 24))
c2 = _(C_BLACK, C_BLACK)
c3 = _(C_NONE, C_RGB(24, 24, 24))
x1 = _(5,20)
x2 = _(40,90)
x3 = _(120,360)
y1 = _(2,20)
y2 = _(19,60)
y3 = _(30,135)
y4 = _(45,170)
y5 = _(50,190)
xp = _(90,200)
yp = _(25,100)
rp = _(20,50)
drect(x1, y1, x2, y2, c1)
drect_border(x1, y3, x2, y4, c3, 2, c2)
dvline(x3, c1)
dvline(x3+2, c1)
dvline(x3+4, c1)
dhline(y5, c2)
dhline(y5+2, c2)
dhline(y5+4, c2)
p = [2*math.pi*i/7 for i in range(7)]
p = [(int(xp+rp*math.cos(a)), int(yp+rp*math.sin(a))) for a in p]
for i in range(7):
dline(*p[i], *p[(i+2)%7], C_BLACK)
dupdate()
getkey()

View File

@ -0,0 +1,16 @@
from gint import *
dclear(C_WHITE)
listvert = [0,0,127,0,127,63,0,63]
fill = C_RED
border = C_BLUE
dpoly(listvert, fill, border)
listvert2 = [100, 50, 200, 50, 225, 75, 200, 100, 100, 100, 75, 75]
listvert3 = [100, 100, 200, 100, 225, 125, 200, 150, 100, 150, 75, 125]
dpoly(listvert2, C_NONE, C_BLACK)
dpoly(listvert3, C_GREEN, C_NONE)
dupdate()
getkey()

View File

@ -0,0 +1,4 @@
from kandinsky import *
# Check whether global dwindow from kandisky interferes with shell
input("Happy? (y/n): ")

View File

@ -18,7 +18,6 @@
#include <gint/keyboard.h>
#include <gint/kmalloc.h>
#include <gint/fs.h>
#include <gint/timer.h>
#include <justui/jscene.h>
#include <justui/jlabel.h>
@ -36,6 +35,7 @@
#include "console.h"
#include "widget_shell.h"
#include "debug.h"
#include "resources.h"
//=== Application globals ===//
@ -55,17 +55,12 @@ struct pe_globals {
// TODO: Put pe_globals in a header for use by the loop hook in mpconfigport.h
widget_shell *pe_shell;
/* Whether a delayed dupdate has been scheduled asynchronously. */
bool pe_dupdate_scheduled;
struct pe_globals PE = { 0 };
// TODO : make this more clean by putting these globals into pe_globals and
// making this accessible to modules
bool is_dwindowed = false;
bool is_timered = false;
unsigned int timer_altered[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
bool is_refreshed_required = false;
//=== Hook for redirecting stdout/stderr to the shell ===//
static ssize_t stdouterr_write(void *data, void const *buf, size_t size)
@ -158,6 +153,7 @@ void pe_after_python_exec(int input_kind, int exec_flags, void *ret_val,
(void)ret_val;
(void)ret;
clearevents();
pe_resources_autofree();
}
//=== Rendering ===//
@ -179,13 +175,17 @@ void pe_enter_graphics_mode(void)
PE.shell->widget.update = 0;
}
void pe_refresh_graphics(void)
void pe_schedule_dupdate(void)
{
pe_enter_graphics_mode();
pe_dupdate_scheduled = true;
}
void pe_dupdate(void)
{
/* refresh graphical output on request by setting
is_refresh_graphics to true */
dupdate();
pe_debug_run_videocapture();
is_refreshed_required = false;
pe_dupdate_scheduled = false;
}
void pe_draw(void)
@ -206,37 +206,11 @@ void pe_draw(void)
dsubimage(377, 207, &img_modifier_states, 16*icon, 0, 15, 14,
DIMAGE_NONE);
#endif
dupdate();
pe_debug_run_videocapture();
pe_dupdate();
}
//=== Application control functions ===//
void pe_restore_window_and_timer(void)
{
if (is_dwindowed)
{
struct dwindow win;
win.left = 0;
win.top = 0;
win.right = DWIDTH;
win.bottom = DHEIGHT;
dwindow_set(win);
is_dwindowed = false; // we mark as not windowed
}
if (is_timered)
{
for (int u = 0; u < 9; u++)
if (timer_altered[u] == 1)
timer_stop(u);
is_timered = false;
}
}
static void pe_reset_micropython(void)
{
gc_sweep_all();
@ -262,12 +236,6 @@ static void pe_print_prompt(int which)
else
prompt = mp_repl_get_ps1();
char str[32];
kmalloc_gint_stats_t *s;
s = kmalloc_get_gint_stats(kmalloc_get_arena("_uram"));
sprintf(str, "%lu", s->free_memory);
console_write(PE.console, str, -1);
console_write(PE.console, prompt, -1);
console_lock_prefix(PE.console);
}
@ -339,8 +307,6 @@ static char *pe_handle_event(jevent e, bool shell_bound)
}
free(module);
pe_restore_window_and_timer();
pe_print_prompt(1);
}
}
@ -366,6 +332,10 @@ static char *pe_handle_event(jevent e, bool shell_bound)
jscene_show_and_focus(PE.scene, PE.shell);
jwidget_set_visible(PE.title, PE.show_title_in_shell);
}
if(!shell_bound && key == KEY_VARS && e.key.shift) {
pe_debug_browse_meminfo();
PE.scene->widget.update = true;
}
return NULL;
}
@ -406,7 +376,7 @@ int main(int argc, char **argv)
pe_debug_init();
pe_debug_printf("---\n");
pe_debug_kmalloc("main");
pe_debug_get_startup_meminfo(MAIN);
//=== Init sequence ===//
@ -419,9 +389,16 @@ int main(int argc, char **argv)
tr.enabled |= KEYDEV_TR_DELAYED_ALPHA;
keydev_set_transform(keydev_std(), tr);
PE.console = console_create(8192, 200);
#ifdef FX9860G
int console_scrollback = 50;
int console_max_bytes = 4096;
#else
int console_scrollback = 200;
int console_max_bytes = 8192;
#endif
PE.console = console_create(console_max_bytes, console_scrollback);
pe_debug_kmalloc("console");
pe_debug_get_startup_meminfo(CONSOLE);
/* Set up standard streams */
close(STDOUT_FILENO);
@ -460,23 +437,21 @@ int main(int argc, char **argv)
gc_add(py_ram_start, py_ram_end); */
#endif
#else
/* Get everything from the OS stack (~ 350 ko) */
/* Get everything from the OS stack (~ 350 kB) */
size_t gc_area_size;
void *gc_area = kmalloc_max(&gc_area_size, "_ostk");
gc_init(gc_area, gc_area + gc_area_size);
/* Also get most of _uram; we can try and fit in the remaining ~150 kB */
void *uram_area = kmalloc(300000, "_uram");
if(uram_area)
gc_add(uram_area, uram_area + 300000);
/* Other options:
- All of _uram (leaving the OS heap for the shell/GUI/etc)
- The OS' extra VRAM
- Memory past the 2 MB boundary on tested OSes */
// gc_add(start, end)...
/* TODO : test to check if we can definitely maintain this addition of RAM */
void *uram_area = kmalloc(300000, "_uram");
if(uram_area)
gc_add(uram_area, uram_area+300000);
#endif
mp_init();
@ -489,12 +464,12 @@ int main(int argc, char **argv)
MP_OBJ_NEW_QSTR(qstr_from_str("."));
#endif
pe_debug_kmalloc("upy");
pe_debug_get_startup_meminfo(UPY);
pyexec_event_repl_init();
pe_print_prompt(1);
pe_debug_kmalloc("prompt");
pe_debug_get_startup_meminfo(PROMPT);
//=== GUI setup ===//
@ -533,7 +508,7 @@ int main(int argc, char **argv)
jwidget_set_padding(stack, 0, 6, 0, 6);
#endif
pe_debug_kmalloc("ui");
pe_debug_get_startup_meminfo(UI);
/* Initial state */
jfileselect_browse(PE.fileselect, "/");

View File

@ -12,6 +12,9 @@
#include <stdlib.h>
#include <string.h>
extern void pe_enter_graphics_mode(void);
extern void pe_dupdate(void);
#ifdef FX9860G
extern font_t font_4x4;
extern font_t font_4x6;
@ -70,10 +73,8 @@ static mp_obj_t init(void)
static mp_obj_t show_screen(void)
{
void pe_enter_graphics_mode(void);
pe_enter_graphics_mode();
dupdate();
pe_debug_run_videocapture();
pe_dupdate();
return mp_const_none;
}

View File

@ -17,8 +17,10 @@
#include <gint/keyboard.h>
#include <gint/timer.h>
#include <gint/drivers/keydev.h>
#include <stdlib.h>
void pe_enter_graphics_mode(void);
extern void pe_enter_graphics_mode(void);
extern void pe_dupdate(void);
#define FUN_0(NAME) \
MP_DEFINE_CONST_FUN_OBJ_0(modgint_ ## NAME ## _obj, modgint_ ## NAME)
@ -229,8 +231,7 @@ STATIC mp_obj_t modgint_dclear(mp_obj_t arg1)
STATIC mp_obj_t modgint_dupdate(void)
{
pe_enter_graphics_mode();
dupdate();
pe_debug_run_videocapture();
pe_dupdate();
return mp_const_none;
}
@ -326,6 +327,34 @@ STATIC mp_obj_t modgint_dellipse(size_t n_args, const mp_obj_t *args)
return mp_const_none;
}
STATIC mp_obj_t modgint_dpoly(mp_obj_t arg1, mp_obj_t arg2, mp_obj_t arg3)
{
mp_uint_t nbitems;
mp_obj_t *items;
mp_obj_list_get(arg1, &nbitems, &items);
int len = nbitems / 2;
int *x = malloc(len * sizeof *x);
int *y = malloc(len * sizeof *y);
if(!x || !y)
goto dpoly_end;
for(int i = 0; i < len; i++) {
x[i] = mp_obj_get_int(items[2*i]);
y[i] = mp_obj_get_int(items[2*i+1]);
}
mp_int_t fill = mp_obj_get_int(arg2);
mp_int_t border = mp_obj_get_int(arg3);
dpoly(x, y, len, fill, border);
dpoly_end:
free(x);
free(y);
return mp_const_none;
}
// TODO: modgint: Font management?
STATIC mp_obj_t modgint_dtext_opt(size_t n, mp_obj_t const *args)
@ -470,6 +499,7 @@ FUN_2(dhline);
FUN_2(dvline);
FUN_BETWEEN(dcircle, 5, 5);
FUN_BETWEEN(dellipse, 6, 6);
FUN_3(dpoly);
FUN_BETWEEN(dtext_opt, 8, 8);
FUN_BETWEEN(dtext, 4, 4);
#ifdef FXCG50
@ -640,6 +670,7 @@ STATIC const mp_rom_map_elem_t modgint_module_globals_table[] = {
OBJ(dvline),
OBJ(dcircle),
OBJ(dellipse),
OBJ(dpoly),
OBJ(dtext_opt),
OBJ(dtext),

24
ports/sh/modtime.c Normal file
View File

@ -0,0 +1,24 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
// pe.modtime: Custom extensions to the `time` module
#include <time.h>
#include "py/runtime.h"
STATIC mp_obj_t time_monotonic(void) {
// TODO: Use libprof instead
uint64_t ms = ((uint64_t)clock() * 1000000000) / CLOCKS_PER_SEC;
return mp_obj_new_float((double)ms);
}
MP_DEFINE_CONST_FUN_OBJ_0(mp_time_monotonic_obj, time_monotonic);
STATIC mp_obj_t mp_time_time_get(void) {
mp_float_t seconds = (mp_float_t)rtc_ticks() / 128;
return mp_obj_new_float(seconds);
}
#define MICROPY_PY_TIME_EXTRA_GLOBALS \
{ MP_ROM_QSTR(MP_QSTR_monotonic), MP_ROM_PTR(&mp_time_monotonic_obj) },

View File

@ -88,6 +88,8 @@ extern const struct _mp_print_t mp_debug_print;
#define MICROPY_PY_RANDOM_EXTRA_FUNCS (1)
#define MICROPY_PY_SYS (1)
#define MICROPY_PY_TIME (1)
#define MICROPY_PY_TIME_TIME_TIME_NS (1)
#define MICROPY_PY_TIME_INCLUDEFILE "ports/sh/modtime.c"
// TODO: Enable the os module:
// #define MICROPY_PY_UOS (1)
// TODO: Enable other modules
@ -107,11 +109,11 @@ void pe_after_python_exec(
/* Command executed regularly during execution */
extern void pe_draw(void);
extern widget_shell *pe_shell;
extern void pe_refresh_graphics(void);
extern bool is_refreshed_required;
extern void pe_dupdate(void);
extern bool pe_dupdate_scheduled;
#define MICROPY_VM_HOOK_LOOP \
{ if(pe_shell->widget.update) pe_draw(); \
if(is_refreshed_required) pe_refresh_graphics(); }
if(pe_dupdate_scheduled) pe_dupdate(); }
/* extra built in names to add to the global namespace
#define MICROPY_PORT_BUILTINS \

View File

@ -3,13 +3,8 @@
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
// pe.ion: `gint` module
//
// This module aims to wrap commonly-used gint functions (not all APIs are
// considered relevant for high-level Python development).
//---
// pe.io: Compatibility module for NumWorks Ion library
#include "console.h"
#include "py/objtuple.h"
#include "py/runtime.h"
#include <gint/keyboard.h>
@ -90,20 +85,6 @@ int KeyTranslationMap[ 53 ] = { 0x85, 0x86, 0x75, 0x76, 0x91, // gint LEFT, UP,
/* END OF KEY TRANSLATION */
#define FUN_0(NAME) MP_DEFINE_CONST_FUN_OBJ_0(ion_##NAME##_obj, ion_##NAME)
#define FUN_1(NAME) MP_DEFINE_CONST_FUN_OBJ_1(ion_##NAME##_obj, ion_##NAME)
#define FUN_2(NAME) MP_DEFINE_CONST_FUN_OBJ_2(ion_##NAME##_obj, ion_##NAME)
#define FUN_3(NAME) MP_DEFINE_CONST_FUN_OBJ_3(ion_##NAME##_obj, ion_##NAME)
#define FUN_VAR(NAME, MIN) \
MP_DEFINE_CONST_FUN_OBJ_VAR(ion_##NAME##_obj, MIN, ion_##NAME)
#define FUN_BETWEEN(NAME, MIN, MAX) \
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ion_##NAME##_obj, MIN, MAX, ion_##NAME)
STATIC mp_obj_t ion___init__(void) { return mp_const_none; }
/* <gint/keyboard.h> */
STATIC mp_obj_t ion_keydown(mp_obj_t arg1) {
mp_int_t key = mp_obj_get_int(arg1);
@ -121,22 +102,16 @@ STATIC mp_obj_t ion_keydown(mp_obj_t arg1) {
return mp_obj_new_bool(down);
}
FUN_1(keydown);
FUN_0(__init__);
MP_DEFINE_CONST_FUN_OBJ_1(ion_keydown_obj, ion_keydown);
/* Module definition */
// Helper: define object "ion_F_obj" as object "F" in the module
#define OBJ(F) \
{ MP_ROM_QSTR(MP_QSTR_##F), MP_ROM_PTR(&ion_##F##_obj) }
// Helper: define small integer constant "I" as "I" in the module
#define INT(I) \
{ MP_ROM_QSTR(MP_QSTR_##I), MP_OBJ_NEW_SMALL_INT(I) }
STATIC const mp_rom_map_elem_t ion_module_globals_table[] = {
{MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_ion)},
OBJ(__init__),
/*Numworks keycodes */
/* BE CAREFUL THERE ARE MISSING SLOTS */
@ -192,7 +167,7 @@ STATIC const mp_rom_map_elem_t ion_module_globals_table[] = {
INT(KEY_ANS),
INT(KEY_EXE), // value 52
OBJ(keydown),
{ MP_ROM_QSTR(MP_QSTR_keydown), MP_ROM_PTR(&ion_keydown_obj) }
};
STATIC MP_DEFINE_CONST_DICT(ion_module_globals, ion_module_globals_table);

View File

@ -10,16 +10,16 @@
#include <gint/display.h>
#include <gint/drivers/r61524.h>
#include <gint/timer.h>
#include "../resources.h"
#include <stdlib.h>
#include <string.h>
extern void pe_schedule_dupdate(void);
extern font_t numworks;
extern bool is_dwindowed;
extern bool is_timered;
extern unsigned int timer_altered[9];
extern bool is_refreshed_required;
static bool is_dwindowed;
#define NW_MAX_X 320
#define NW_MAX_Y 222
@ -58,10 +58,33 @@ extern bool is_refreshed_required;
// There are possibly some others to be listed correctly
static int callback(void) {
is_refreshed_required = true;
pe_schedule_dupdate();
return TIMER_CONTINUE;
}
static void set_window(void) {
if(is_dwindowed) {
struct dwindow nw;
nw.left = DELTAXNW;
nw.top = DELTAYNW;
nw.right = 320 + DELTAXNW;
nw.bottom = 222 + DELTAYNW;
dwindow_set(nw);
}
}
static void reset_window(void)
{
if(is_dwindowed) {
struct dwindow nw;
nw.left = 0;
nw.top = 0;
nw.right = DWIDTH;
nw.bottom = DHEIGHT;
dwindow_set(nw);
}
}
static mp_obj_t Kandinsky_make_color(color_t color) {
int r, g, b;
r = (color >> 8) & 0xf8;
@ -82,20 +105,14 @@ static mp_obj_t Kandinsky_init(void) {
dclear(NW_WHITE);
struct dwindow nw;
nw.left = DELTAXNW;
nw.top = DELTAYNW;
nw.right = 320 + DELTAXNW;
nw.bottom = 222 + DELTAYNW;
dwindow_set(nw);
is_dwindowed = true; // we mark as windowed
/* Start in windowed 320x222 windowed mode */
is_dwindowed = true;
int t = timer_configure(TIMER_TMU, (1000000/TARGET_FPS), GINT_CALL(callback));
if (t >= 0) {
/* Allocate an auto-freed timer until the end of the program's execution */
int t = pe_timer_configure(TIMER_ANY, (1000000/TARGET_FPS),
GINT_CALL(callback), false);
if (t >= 0)
timer_start(t);
is_timered = true; // there is a timer altered from this module
timer_altered[t] = 1; // we put the corresponding timer at 1 to identify it
}
return mp_const_none;
}
@ -108,34 +125,35 @@ static mp_obj_t Kandinsky_color(size_t n, mp_obj_t const *args) {
return mp_obj_new_int(color);
}
int Internal_Get_Color_From_String(const char *str) {
// TODO: Use qstrs here
int Internal_Get_Color_From_String(int qstr) {
if (strcmp(str, "red") == 0 || strcmp(str, "r") == 0)
if (qstr == MP_QSTR_red || qstr == MP_QSTR_r)
return NW_RED;
else if (strcmp(str, "green") == 0 || strcmp(str, "g") == 0)
else if (qstr == MP_QSTR_green || qstr == MP_QSTR_g)
return NW_GREEN;
else if (strcmp(str, "blue") == 0 || strcmp(str, "b") == 0)
else if (qstr == MP_QSTR_blue || qstr == MP_QSTR_b)
return NW_BLUE;
else if (strcmp(str, "black") == 0 || strcmp(str, "k") == 0)
else if (qstr == MP_QSTR_black || qstr == MP_QSTR_k)
return NW_BLACK;
else if (strcmp(str, "white") == 0 || strcmp(str, "w") == 0)
else if (qstr == MP_QSTR_white || qstr == MP_QSTR_w)
return NW_WHITE;
else if (strcmp(str, "yellow") == 0 || strcmp(str, "y") == 0)
else if (qstr == MP_QSTR_yellow || qstr == MP_QSTR_y)
return NW_YELLOW;
else if (strcmp(str, "pink") == 0)
else if (qstr == MP_QSTR_pink)
return NW_PINK;
else if (strcmp(str, "magenta") == 0)
else if (qstr == MP_QSTR_magenta)
return NW_MAGENTA;
else if (strcmp(str, "grey") == 0 || strcmp(str, "gray") == 0)
else if (qstr == MP_QSTR_grey || qstr == MP_QSTR_gray)
return NW_GRAY;
else if (strcmp(str, "purple") == 0)
else if (qstr == MP_QSTR_purple)
return NW_PURPLE;
else if (strcmp(str, "orange") == 0)
else if (qstr == MP_QSTR_orange)
return NW_ORANGE;
else if (strcmp(str, "cyan") == 0)
else if (qstr == MP_QSTR_cyan)
return NW_CYAN;
else if (strcmp(str, "brown") == 0)
else if (qstr == MP_QSTR_brown)
return NW_BROWN;
else
return C_NONE;
@ -144,11 +162,8 @@ int Internal_Get_Color_From_String(const char *str) {
int Internal_Treat_Color(mp_obj_t color) {
const mp_obj_type_t *type = mp_obj_get_type(color);
if (type == &mp_type_str) {
size_t text_len;
char const *text = mp_obj_str_get_data(color, &text_len);
return Internal_Get_Color_From_String(text);
}
if (type == &mp_type_str)
return Internal_Get_Color_From_String(mp_obj_str_get_qstr(color));
else if (type == &mp_type_int)
return mp_obj_get_int(color);
@ -161,7 +176,9 @@ int Internal_Treat_Color(mp_obj_t color) {
int g = mp_obj_get_int(items[1]);
int b = mp_obj_get_int(items[2]);
return NW_RGB(r, g, b);
} else
}
else
return NW_BLACK;
}
@ -173,7 +190,9 @@ static mp_obj_t Kandinsky_fill_rect(size_t n, mp_obj_t const *args) {
int color = Internal_Treat_Color(args[4]);
set_window();
drect(x, y, x + w - 1, y + h - 1, color);
reset_window();
return mp_const_none;
}
@ -188,7 +207,10 @@ static mp_obj_t Kandinsky_set_pixel(size_t n, mp_obj_t const *args) {
else
color = NW_BLACK;
/* TODO: Terribly inefficient */
set_window();
dpixel(x, y, color);
reset_window();
return mp_const_none;
}
@ -221,6 +243,7 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) {
font_t const *old_font = dfont(&numworks);
set_window();
int u = 0;
int v = 0;
for (int l = 0; l < (int)text_len; l++) {
@ -230,7 +253,7 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) {
} else {
/* The following test is for support of unicode characters that are encoded on 1 char or more */
/* we need to pass multiple chars to dtext to take care of unicode encoding */
if(((unsigned char) text[l]) >= 0x00 && ((unsigned char) text[l]) <= 0x7F){
if(((unsigned char) text[l]) <= 0x7F){
drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback);
dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, &text[l], 1);
u += 10;
@ -256,6 +279,7 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) {
}
}
reset_window();
dfont(old_font);
return mp_const_none;
@ -263,27 +287,13 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) {
static mp_obj_t Kandinsky_CGEXT_Enable_Wide_Screen( void ) {
struct dwindow nw;
nw.left = 0;
nw.top = 0;
nw.right = DWIDTH;
nw.bottom = DHEIGHT;
dwindow_set(nw);
is_dwindowed = false; // we mark as not windowed
return mp_const_none;
}
static mp_obj_t Kandinsky_CGEXT_Disable_Wide_Screen( void ) {
struct dwindow nw;
nw.left = DELTAXNW;
nw.top = DELTAYNW;
nw.right = 320 + DELTAXNW;
nw.bottom = 222 + DELTAYNW;
dwindow_set(nw);
is_dwindowed = true; // we mark as windowed
is_dwindowed = true; // we mark as windowed
return mp_const_none;
}
@ -297,9 +307,7 @@ static mp_obj_t Kandinsky_CGEXT_Set_Margin_Color( mp_obj_t color ) {
color_t colorside = NW_BLACK;
colorside = Internal_Treat_Color(color);
Kandinsky_CGEXT_Enable_Wide_Screen();
dclear(colorside);
Kandinsky_CGEXT_Disable_Wide_Screen();
return mp_obj_new_bool( is_dwindowed );
}

View File

@ -1,111 +0,0 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
// pe.time: `gint` module
//
// This module aims to wrap commonly-used gint functtimes (not all APIs are
// considered relevant for high-level Python development).
//---
#include "console.h"
#include "py/objtuple.h"
#include "py/runtime.h"
#include <gint/clock.h>
#include <gint/timer.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define FUN_0(NAME) MP_DEFINE_CONST_FUN_OBJ_0(time_##NAME##_obj, time_##NAME)
#define FUN_1(NAME) MP_DEFINE_CONST_FUN_OBJ_1(time_##NAME##_obj, time_##NAME)
#define FUN_2(NAME) MP_DEFINE_CONST_FUN_OBJ_2(time_##NAME##_obj, time_##NAME)
#define FUN_3(NAME) MP_DEFINE_CONST_FUN_OBJ_3(time_##NAME##_obj, time_##NAME)
#define FUN_VAR(NAME, MIN) \
MP_DEFINE_CONST_FUN_OBJ_VAR(time_##NAME##_obj, MIN, time_##NAME)
#define FUN_BETWEEN(NAME, MIN, MAX) \
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(time_##NAME##_obj, MIN, MAX, time_##NAME)
extern bool is_timered;
extern unsigned int timer_altered[9];
static uint64_t tickmono = 0;
static int monotonic_callback(void) {
tickmono++;
return TIMER_CONTINUE;
}
STATIC mp_obj_t time___init__(void)
{
tickmono = 0;
int t = timer_configure(TIMER_TMU, 1000, GINT_CALL(monotonic_callback));
if (t >= 0)
{
timer_start(t);
is_timered = true; // there is a timer altered from this module
timer_altered[t] = 1; // we put the corresponding timer at 1 to identify it
}
return mp_const_none;
}
/* <gint/keyboard.h> */
STATIC mp_obj_t time_sleep(mp_obj_t arg1)
{
mp_float_t duration = mp_obj_get_float(arg1);
uint64_t length = (uint64_t)(duration * 1000000.0f); // duration is in seconds and length in µs
sleep_us(length);
return mp_const_none;
}
STATIC mp_obj_t time_monotonic(void)
{
float value = (float) ((uint64_t) (tickmono * 1000 +0.5 )) / 1000000.0f;
return mp_obj_new_float( value );
}
FUN_1(sleep);
FUN_0(monotonic);
FUN_0(__init__);
/* Module definittime */
// Helper: define object "time_F_obj" as object "F" in the module
#define OBJ(F) \
{ MP_ROM_QSTR(MP_QSTR_##F), MP_ROM_PTR(&time_##F##_obj) }
// Helper: define small integer constant "I" as "I" in the module
#define INT(I) \
{ MP_ROM_QSTR(MP_QSTR_##I), MP_OBJ_NEW_SMALL_INT(I) }
STATIC const mp_rom_map_elem_t time_module_globals_table[] = {
{MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_time)},
OBJ(__init__),
OBJ(sleep),
OBJ(monotonic),
};
STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table);
const mp_obj_module_t time_module = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t *)&time_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_time, time_module);

View File

@ -194,11 +194,20 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
void pyexec_event_repl_init(void) {
MP_STATE_VM(repl_line) = vstr_new(32);
#ifdef FX9860G
mp_hal_stdout_tx_str("MicroPython " MICROPY_GIT_TAG "\n");
#else
mp_hal_stdout_tx_str(MICROPY_BANNER_NAME_AND_VERSION);
mp_hal_stdout_tx_str("; " MICROPY_BANNER_MACHINE);
mp_hal_stdout_tx_str("\r\n");
mp_hal_stdout_tx_str("\n");
#endif
#if MICROPY_PY_BUILTINS_HELP
mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n");
#ifdef FX9860G
mp_hal_stdout_tx_str("Type \"help()\" for info.\n");
#else
mp_hal_stdout_tx_str("Type \"help()\" for more information.\n");
#endif
#endif
vstr_reset(MP_STATE_VM(repl_line));
}

69
ports/sh/resources.c Normal file
View File

@ -0,0 +1,69 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
#include "resources.h"
#include "debug.h"
/*** Timers ***/
/* There should be at most 32 timers, which is fine. */
static uint32_t timer_used = 0;
#if PE_DEBUG
static uint32_t timer_warn = 0;
#endif
int pe_timer_configure(int timer, uint64_t delay, gint_call_t call, bool waf)
{
int t = timer_configure(timer, delay, call);
if(t < 0)
return t;
if(t >= 32) {
timer_stop(t);
return -1;
}
timer_used |= (1 << t);
#if PE_DEBUG
if(waf)
timer_warn |= (1 << t);
#endif
return t;
}
void pe_timer_stop(int t)
{
if(t < 0 || t >= 32)
return;
#if PE_DEBUG
if(!(timer_used & (1 << t)))
pe_debug_printf("autofree: bad timer %d\n", t);
timer_warn &= ~(1 << t);
#endif
timer_used &= ~(1 << t);
}
static void pe_timer_autofree(void)
{
for(int t = 0; t < 32; t++) {
if(!(timer_used & (1 << t)))
continue;
#if PE_DEBUG
if(timer_warn & (1 << t))
pe_debug_printf("autofree: timer %d\n", t);
#endif
timer_stop(t);
}
}
/*** Autofree function ***/
void pe_resources_autofree(void)
{
pe_timer_autofree();
}

29
ports/sh/resources.h Normal file
View File

@ -0,0 +1,29 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
// pe.resources: Utility for allocating auto-freed resources in Python code
//
// This header provides wrapper around gint or other functions that allocate
// resources. Its purpose is to track these resources and allow PythonExtra to
// free them automatically when the execution of Python program finishes. This
// avoids any situation where important resources are exhausted over the course
// of multiple executions.
//
// This header only provides wrappers around allocation and deallocation
// functions; other usual functions can be used directly. Each allocation
// function has an extra boolean parameter indicating whether to raise a
// warning if debug mode is enabled and the resource is auto-freed. It should
// be set to true when there is a mechanism to free the resource, false when
// there isn't and auto-free is the expected outcome.
//---
#include <gint/timer.h>
/* Autofree all resources. */
void pe_resources_autofree(void);
/* gint's timer_configure() and timer_stop() */
int pe_timer_configure(int, uint64_t, gint_call_t, bool warn_autofree);
void pe_timer_stop(int);

112
ports/sh/stredit.c Normal file
View File

@ -0,0 +1,112 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
#include "stredit.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <gint/defs/util.h>
bool stredit_init(stredit_t *ed, int prealloc_size, int reserved_bytes,
int max_size)
{
if(!max_size) {
memset(ed, 0, sizeof *ed);
return true;
}
char *raw = malloc(reserved_bytes + prealloc_size + 1);
if(!raw)
return false;
memset(raw, 0, reserved_bytes);
raw[reserved_bytes] = 0;
ed->raw = raw;
ed->reserved = reserved_bytes;
ed->max_size = max_size;
ed->size = 0;
ed->alloc_size = reserved_bytes + prealloc_size + 1;
ed->prefix = 0;
return true;
}
char *stredit_freeze_and_reset(stredit_t *ed)
{
/* Downsize the allocation if it's larger than needed. */
int size_needed = ed->reserved + ed->size + 1;
if(ed->alloc_size >= size_needed + 4)
ed->raw = realloc(ed->raw, size_needed);
char *raw = ed->raw;
ed->raw = NULL;
stredit_reset(ed);
return raw;
}
void stredit_reset(stredit_t *ed)
{
free(ed->raw);
memset(ed, 0, sizeof *ed);
}
bool stredit_alloc(stredit_t *ed, int n)
{
if(ed->alloc_size >= ed->reserved + n + 1)
return true;
/* Always increase the size by at least 16 so we can insert many times in a
row without worrying about excessive allocations. */
int newsize = max(ed->alloc_size + 16, ed->reserved + n + 1);
char *newraw = realloc(ed->raw, newsize);
if(!newraw)
return false;
ed->raw = newraw;
ed->alloc_size = newsize;
return true;
}
void stredit_set_prefix(stredit_t *ed, int prefix_size)
{
ed->prefix = min(max(0, prefix_size), (int)ed->size);
}
bool stredit_insert(stredit_t *ed, int p, char const *insert_str, int n)
{
if(p < 0 || p > ed->size || ed->size + n > ed->max_size)
return false;
if(!stredit_alloc(ed, ed->size + n))
return false;
char *str = ed->raw + ed->reserved;
/* Move the end of the string (plus the NUL) n bytes forward */
memmove(str + p + n, str + p, ed->size - p + 1);
memcpy(str + p, insert_str, n);
ed->size += n;
return true;
}
int stredit_delete(stredit_t *ed, int p, int n)
{
if(p < ed->prefix) {
int unremovable = ed->prefix - p;
p += unremovable;
n -= unremovable;
}
n = min(n, ed->size - p);
if(n < 0)
return 0;
char *str = ed->raw + ed->reserved;
/* Move the end of the string (plus the NUL) n bytes backwards */
memmove(str + p, str + p + n, ed->size - n - p + 1);
ed->size -= n;
return n;
}

80
ports/sh/stredit.h Normal file
View File

@ -0,0 +1,80 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
// pe.stredit: String edition utilities
//
// This header implements string edition utilities used in the console to
// handle user input and printing on the last line. To minimize memory
// friction, edited strings are stored in a format compatible with the
// storage format of frozen console lines, i.e. with a few reserved bytes at
// the beginning for metadata.
//---
#ifndef __PYTHONEXTRA_STREDIT_H
#define __PYTHONEXTRA_STREDIT_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
typedef struct
{
/* The raw buffer has reserved bytes at the beginning. To avoid confusion,
we call this initial pointer "raw" and raw+reserved "str". */
char *raw;
/* Maximum length (text only, not counting the NUL). */
uint16_t max_size;
/* Number of reserved bytes. */
uint16_t reserved;
/* Size of contents (not counting the NUL). */
uint16_t size;
/* Allocated size (always ≥ size+1). */
uint16_t alloc_size;
/* Number of initial characters that can't be edited. */
uint16_t prefix;
} stredit_t;
/* Create a new editable string with at least init_chars characters of content
available. Returns false on error. Previous contents are not freed! If
max_size is 0, clears the stredit without allocating a new string. */
bool stredit_init(stredit_t *ed, int init_chars, int reserved_bytes,
int max_size);
/* Get the data pointer out of an stredit. */
static inline char *stredit_data(stredit_t *ed)
{
return ed->raw + ed->reserved;
}
/* Reset an editable string. This frees and destroys the string. */
void stredit_reset(stredit_t *ed);
/* Finish editing; return the raw pointer (with its ownership) and reset the
editor structure. free() the raw pointer after use. */
char *stredit_freeze_and_reset(stredit_t *ed);
/* Number of bytes that can be added before the size limit is reached. */
static inline int stredit_capacity(stredit_t *ed)
{
return ed->max_size - ed->size;
}
/* Realloc the string to ensure n characters plus a NUL can be written. */
bool stredit_alloc(stredit_t *ed, int n);
/* Set the prefix_size first characters of the strings to not be editable. The
string must already have that many characters printed. This is used in the
console to prevent erasing the prompt. */
void stredit_set_prefix(stredit_t *ed, int prefix_size);
/* Insert n characters at position p. */
bool stredit_insert(stredit_t *ed, int p, char const *str, int n);
/* Remove n characters at position p. Returns the number of characters
actually removed after bounds checking. */
int stredit_delete(stredit_t *ed, int p, int n);
#endif /* __PYTHONEXTRA_STREDIT_H */