Merge branch 'dev' of gitea.planet-casio.com:Lephenixnoir/PythonExtra into fileio

This commit is contained in:
Sylvain PILLOT 2024-02-25 18:07:40 +01:00
commit f4ce0019ff
20 changed files with 790 additions and 220 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: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

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).
@ -150,11 +155,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 +184,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 +239,169 @@ 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)`.
TODO: Example for `drect()`, `drect_border()`, `dline()`.
_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)
![](../../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).
@ -127,7 +132,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 +157,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 +234,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`](../../sh/ports/examples/ex_draw1.py))._
```py
from gint import *
@ -251,6 +256,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 +272,137 @@ 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)`.
TODO: Exemple pour `drect()`, `drect_border()`, `dline()`.
_Exemple ([`ex_circle.py`](../../sh/ports/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)
![](../../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,7 +6,7 @@ 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

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

View File

@ -19,6 +19,7 @@ SRC_C = \
ports/sh/mphalport.c \
ports/sh/objgintimage.c \
ports/sh/pyexec.c \
ports/sh/stredit.c \
ports/sh/widget_shell.c \
shared/runtime/gchelper_generic.c \
shared/runtime/stdout_helpers.c \

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

@ -262,12 +262,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);
}
@ -366,6 +360,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 +404,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 +417,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);
@ -489,12 +494,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 +538,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

@ -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));
}

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 */