Compare commits

...

40 Commits
master ... dev

Author SHA1 Message Date
Lephenixnoir 0134f37f7d
Merge remote-tracking branch 'czm/master' into dev (#12) 2024-03-27 19:17:17 +01:00
陈湛明 0e2379ea51 Improve the wording in README_zh.md 2024-03-26 21:40:11 +00:00
陈湛明 aeb8dda6c8 Add support for VSCode 2024-03-26 11:51:51 +00:00
陈湛明 cf0aea6c4c Remove redundant explanation about macro 2024-03-26 11:47:42 +00:00
陈湛明 daf69d1898 Add the date of translation for README_zh.md 2024-03-26 09:55:37 +00:00
陈湛明 bc8157c385 Fix several issues in VSCode_zh.md 2024-03-26 09:39:49 +00:00
陈湛明 df05f68c65 Change language suffix to "zh" 2024-03-26 08:45:57 +00:00
陈湛明 7e519f9f22 Merge branch 'master' of https://git.planet-casio.com/Chen-Zhanming/fxsdk 2024-03-25 21:10:34 +00:00
陈湛明 ea871becf5 Fix a typo in README 2024-03-25 21:08:55 +00:00
陈湛明 8e2b1d58b2 Translate README into Simplified Chinese
Add a tutorial on how to use fxSDK with VSCode.
2024-03-25 21:08:32 +00:00
陈湛明 eec1455bfd Change the link back 2024-03-25 15:12:46 +00:00
陈湛明 274f6aa8c2 Use Forgeio style link 2024-03-25 11:35:49 +00:00
陈湛明 6632c37e45 Use relative path 2024-03-25 11:23:46 +00:00
陈湛明 06d5b6f852 Add a tutorial on using fxSDK in VSCode 2024-03-25 11:20:47 +00:00
陈湛明 82fb8d2716 Add more translation to README_zhCN.md 2024-03-25 11:18:11 +00:00
陈湛明 3013343e19 Partially translated README to Simplified Chinese 2024-03-24 18:46:11 +00:00
Lephenixnoir 85d7fcf9e9
libfxlink: fix race condition leading to lost messages
Basically if the calculator sends two messages in a row, it is possible
for a single libusb_handle_events() to get both. And the comm structure
wasn't designed for that, because it could buffer only one message at a
time, which the user needed to read after event handling.

The comm structure now has a 16-message buffer, which should be more
than enough for any single event handling loop. On the user level this
has implications in that fxlink_device_finish_bulk_IN() must be called
*in a loop* after each event handling cycle.

Reported in Lephenixnoir/gint#27
2024-03-24 19:25:35 +01:00
Lephenixnoir 2d293eeb35
libfxlink: remove confusingly unused IN message 2024-03-24 18:17:18 +01:00
Lephenixnoir 8d8fca3ea2
fxsdk: add build-fxg3a -s (send) option
Note that this only makes sense if there is only one .g3a in the current
folder. If both the cg and the fxg3a targets are used, this will send
both g3a files, which is a waste of time.
2024-03-24 11:12:11 +01:00
Lephenixnoir 8092621967
fxsdk: better defaults for build-fxg3a projects 2024-03-24 10:45:19 +01:00
Lephenixnoir 519b4201fc
fxsdk: cleanup for building fx code as g3a (#11)
- Feature name: "fx g3a" or "fx as g3a"
- No need for a special send function
- Remove leftover bld file
2024-03-21 08:51:49 +01:00
Sylvain PILLOT 883ca77167
corrected typo for send option 2024-03-21 08:30:00 +01:00
Sylvain PILLOT 8574824b31
removed build-fxascg-push target due to possible RAM allocation issues 2024-03-21 08:30:00 +01:00
Sylvain PILLOT 45b207b150
'fxsdk build-fx-as-cg' working and added send command line option 2024-03-21 08:30:00 +01:00
Sylvain PILLOT a3aa537e84
Start working on a build target FX9860G_AS_CG for fxSDK & gint 2024-03-21 08:30:00 +01:00
Lephenixnoir 4c307af02b
fxconv: preliminary support for fonts in PythonExtra 2024-03-21 08:29:06 +01:00
Lephenixnoir 2acc439ed4
fxsdk: add TARGET_FXCG50_FASTLOAD macro for add-in push builds 2024-02-03 19:45:15 +01:00
Lephenixnoir 8030d6bdc6
fxconv: PythonExtra support for bopti-cg 2024-02-03 16:00:05 +01:00
Lephenixnoir 4a84bfdcd4
fxconv: fix stupid compact mode bug 2024-02-03 16:00:05 +01:00
Lephenixnoir 6e62fb7d6d
fxconv: PythonExtra support for bopti-fx 2024-02-01 17:48:45 +01:00
Lephenixnoir 975f29a471
fxconv: (fix indent) 2024-01-31 23:28:49 +01:00
Lephenixnoir da79a6a0e8
fxlink: add --folder option to fxlink -s to select output folder 2024-01-30 22:19:19 +01:00
Lephenixnoir ecf04cb634
fxconv: make bopti-fx image data a pointer 2024-01-21 21:06:01 +01:00
Lephenixnoir 45fd52444f
fxlink: add (unused) scale parameter to SDL2 video capture 2024-01-21 21:05:36 +01:00
Lephenixnoir 9f4d17ca4f
fxlink: don't free glib pointer apparently not malloc'ed 2023-12-03 16:08:58 +01:00
Lephenixnoir 8f50f7694a
fxconv: fix c-c/c-v in range error messages 2023-10-17 21:56:38 +02:00
Lephenixnoir 88235041a3
fxconv: add i8/i16/i32, with range checks 2023-10-17 20:18:09 +02:00
Lephenixnoir be8c1f0d94
fxconv: alignment parameter in fxconv.ptr() (default 4) 2023-08-20 10:41:53 +02:00
Lephenixnoir 11e3b614c2
fxconv: add an elf_multi() function to produce multiple variables 2023-08-15 21:59:17 +02:00
Lephenixnoir cf3ab5d5e0
fxconv: copy elements in += ObjectData instead of referencing subobject
This way, after o1 += o2, when o1 is linked all the outer data of o2 is
linked alongside the outer data of o1, and the inner data remains
contiguous. This is important for arrays, where we don't want the outer
data of o2 to appear before the next inner field of o1.

With this commit, an update to o2 after o1 += o2 no longer updates o1.
This wasn't a feature in the first place.
2023-08-08 20:26:37 +02:00
21 changed files with 707 additions and 97 deletions

View File

@ -1,5 +1,7 @@
# fxSDK
🌍 **English** | [简体中文](README_zh.md)
*Topic on Planète Casio : [fxSDK, un SDK alternatif pour écrire des add-ins](https://www.planet-casio.com/Fr/forums/topic13164-last-fxsdk-un-sdk-alternatif-pour-ecrire-des-add-ins.html)*
The fxSDK is a development kit for CASIO graphing calculators in the fx-9860G and fx-CG series. It provides command-line tools, build systems and a cross-compilation setup for add-ins and libraries. It's designed to be used with the [gint unikernel](/Lephenixnoir/gint) which provides a powerful base runtime to build add-ins from.
@ -76,7 +78,7 @@ Type 'fxsdk build-fx' or 'fxsdk build-cg' to compile the program.
From that folder, you can build the add-in with the `fxsdk build-*` commands:
```bash
# Build the add-in for fx-98600G-series calculators (.g1a):
# Build the add-in for fx-9860G-series calculators (.g1a):
% fxsdk build-fx
# Build the add-in for fx-CG-series calculators (.g3a):
% fxsdk build-cg

184
README_zh.md Normal file
View File

@ -0,0 +1,184 @@
# fxSDK
🌏 [English](README.md) | **简体中文**
*Translated by 陈湛明 (Chén Zhànmíng) on 2024-03-26*
*下文中的“程序”指的不是 BASIC 程序,而是带有图标且可以在主菜单打开的 add-in。*
*Planète Casio 上的一个帖子:[fxSDK 是另外一套开发计算器程序的工具链(法语帖子)](https://www.planet-casio.com/Fr/forums/topic13164-last-fxsdk-un-sdk-alternatif-pour-ecrire-des-add-ins.html)*
fxSDK 是一套开发卡西欧图形计算器里的程序的工具,里面提供了命令行工具、构建系统以及一个跨平台编译器,用来开发程序以及库。这套工具需要和 [gint 内核](/Lephenixnoir/gint) 一起使用,因为这个内核提供的运行时非常之厉害。
这个仓库只提供了 SDK 的命令行工具。如果想要构建程序,需要一些其他的组件,比如[跨平台编译器](/Lephenixnoir/sh-elf-gcc)还有 [gint 内核](/Lephenixnoir/gint)。更多信息请移步至[如何安装]()。
fxSDK 以 [MIT 协议](LICENSE)发布。
## 支持的机型及其兼容性
**计算器**
fxSDK或者说 gint支持卡西欧 fx-9860G 系列几乎所有的型号,包括:
* ![](https://www.planet-casio.com/images/icones/calc/g85.png)部分支持基于SH3 的 fx-9860G、fx-9860G SD、以及类似运行 1.xx 版本操作系统的型号,还有 fx-9860G SDK 里的模拟器。
* ![](https://www.planet-casio.com/images/icones/calc/g95.png) 基于 SH3 的 fx-9750G、fx-9860G II、fx-9860G II SD以及类似运行 2.xx 版本操作系统的型号。
* ![](https://www.planet-casio.com/images/icones/calc/g75+.png) 所有基于 SH4 且运行操作系统 2.xx 的型号,包括 fx-9750G II 与 SH4 的 fx-9860G II。
* ![](https://www.planet-casio.com/images/icones/calc/g35+e2.png) fx-9750G III 与 fx-9860G III。
也支持卡西欧 fx-CG 系列的计算器:
* ![](https://www.planet-casio.com/images/icones/calc/cg20.png) 经典款 fx-CG 10/20。
* ![](https://www.planet-casio.com/images/icones/calc/g90+e.png) fx-CG 50。
**操作系统**
* Linux 有官方支持。只要装好了依赖,任何的发行版都可以运行。
* macOS 由于缺少测试,需要进行一些小小的调整才可以运行。
* Windows 因为有 WSLLinux 子系统),所以也有官方支持。然而 Windows 本身并不受支持,但如果您想的话可以尝试贡献。
**编程语言**
* C 语言:支持最新版 GCC目前是 C11 或者 C2X。标准库 [fxlibc](https://gitea.planet-casio.com/Vhex-Kernel-Core/fxlibc/) 是我们自行构建的,大致遵循 C99。使用其他的标准库也是可以的。
* C++:支持最新版 GCC目前是 C++20 或者 C++23。标准库 [libstdc++](https://gcc.gnu.org/onlinedocs/libstdc++/) 也是最新的。
* 汇编语言:卡西欧计算器的处理器为 SuperH 架构,其中数 SH4AL-DSP 最多。binutils 里面提供的汇编器可供使用。
* 还想要其他的语言?只要能编译到 C 就行,比如 [fxtran for Fortran](https://www.planet-casio.com/Fr/forums/topic17064-1-fxtran-codez-en-fortran-pour-votre-casio.html)。其他的 GCC 支持的前端语言也可以,比如 [the D compiler builds](https://www.planet-casio.com/Fr/forums/topic17037-last-omegafail-dlang-et-gint.html)。对于那些基于 LLVM 的语言(比如 Rust不在考虑范围内因为 LLVM 没有 SuperH 后端。
**构建系统**
fxSDK 主要使用 CMake 来构建程序和库。有个老的 Makefile 模板仍然可以用,而且命令行工具仍有官方支持,但是在更新的时候就得手动修改您自己的构建系统了。
## 如何安装
这里说明的安装方法不仅仅适用这个仓库,还包括 fxSDK 里的所有东西。
**方法一:使用 GiteaPC新手推荐虽然现在不叫这个了**
[GiteaPC](https://gitea.planet-casio.com/Lephenixnoir/GiteaPC) 是一个类似包管理器一样的东西,专门用来下载、构建及安装来自 Planète Casio 仓库的东西。整个过程是自动的,所以推荐在安装 fxSDK 的时候使用。
**方法二:在 Arch Linux 发行版里使用 AUR**
[Dark Storm](https://www.planet-casio.com/Fr/compte/voir_profil.php?membre=Dark%20Storm) 为 Arch Linux 维护了一个叫 [MiddleArch](https://www.planet-casio.com/Fr/forums/topic16790-1-middlearch-un-depot-communautaire.html) 的仓库,包含了最新版本的 fxSDK。
**方法三:手动构建(如果您是专家)**
您可以照着 README 文件里的说明来手动构建各个工具。请先看看 GiteaPC 的教程好知道需要以什么顺序安装哪些东西。在此提醒要装的东西真的很多SDK、跨平台编译器、libm、libc、libstdc++、内核、用户库等等),安装和更新都会很花时间。
## 命令行工具
当你用 fxSDK 开发程序的时候,你主要会使用命令行工具和 fxSDK 的构建系统。先来看看命令行工具吧。当你不传任何的参数直接调用工具的时候会显示帮助。例如 `fxsdk``fxgxa`
*曾经在这里有一个叫 `fxos` 的工具,现在被移到[它自己的仓库](/Lephenixnoir/fxos)里了。*
使用 `fxsdk` 来**管理项目**
你可以用 `fxsdk new` 来创建一个新项目并指定名字。本例为“MyAddin”
```sh
fxsdk new MyAddin
```
然后进入这个文件夹:
```bash
cd MyAddin
```
进入新创建的文件夹之后,就可以用 `fxsdk build-*` 来构建程序:
```bash
# 构建 fx-9860G 系列计算器的程序(后缀 .g1a
fxsdk build-fx
# 构建 fx-CG 系列计算器的程序(后缀 .g3a
fxsdk build-cg
```
然后你就可以把这个程序用你喜欢的方式发到计算器上了。但是这里有些更简单的方法:
* `fxsdk send-fx` 会使用 [p7](/cake/p7utils) 来发送 g1a 程序,就像 FA-124 和 xfer9860 那样。除了 fx-975G0G III 和 fx-9860G III所有 fx-9860G 系列的型号都可以用。
* `fxsdk send-cg` 会使用 UDisks2 来发送 g3a 程序,其实就相当与您在文件资源管理器里面操作一样。下面具体有写 fxlink 做了什么。
`fxsdk path` 会告诉你 SDK 里面的一些重要文件(主要是跨平台编译器)的位置。
使用 `fxgxa` 来**生成 G1A 和 G3A 文件**
`fxgxa` 是一个多功能的 g1a 与 g3a 文件编辑器,可以创建、编辑与导出程序的头部。实际上,在运行 `fxsdk build-*` 这个命令的时候,您也间接调用了 `fxgxa`。所以这个只有在您想要查看已经编译好的程序的内容的时候才会用得到。
这可以用来导出 PNG 格式的图标、执行校验和、修复损坏的头部以及导出程序的其他信息。主要是这些命令:
* `fxgxa --g1a|--g3a`:生成 g1a 或 g3a 文件
* `fxgxa -e`:编辑 g1a 或 g3a 文件
* `fxgxa -d`:提取元数据、校验和,以及图标
* `fxgxa -r`:修复破损文件的控制字节与校验和
* `fxgxa -x`:以 PNG 格式提取图标
`fxgxa` 有另一个名字 `fxg1a`。这是为了兼容 2.7 版本以前的 fxSDK因为在那时还不能生成 g3a 程序。
使用 `fxconv` 来**转换素材格式**
`fxconv` 是一个素材转换器。可以把照片、字体和其他常见的素材变成可以直接在程序中使用的数据结构,包括 gint 图像、gint 字体、[libimg](/Lephenixnoir/libimg) 图像,以及二进制数据。
其他项目可以扩展它的功能来生成自定义的素材比如地图、对话框、GUI 文本。对 `fxconv` 的扩展需要在对应的项目里面用 Python 来实现。
`fxconv` 稍微和构建系统集成在一起。通常您只需要在 `CMakeLists.txt` 里面生命有哪些素材,把参数填进 `fxconv-metadata.txt`,然后构建系统自己会把剩下的事情做好。
> 未完待续:应该更详细地解释一下怎么使用 fxconv。
使用 `fxlink` 来**进行 USB 通信**
`fxlink` 是一个 USB 通信工具,可以用来往 gint 的 USB 驱动或者计算器里面发送文件。但是这个工具目前还不成熟,不过已经有两个很有用的功能了。
注意:`fxlink` 在 Windows 的 WSL 里面不能用,参见[这个 bug](https://github.com/Microsoft/WSL/issues/2195)。
第一个功能是使用 libusb 与程序进行交流,可以让程序把文字、照片和截图实时发到电脑上。
第二个功能是使用 UDisks2 来往 fx-CG 和 G-III 系列计算器上发文件(像一个 USB 磁盘一样)。`fxlink` 可以把计算器挂在到电脑上,然后传送文件,最后再移除,全程不需要 root 权限。
## 代码提示
[如何在 Visual Studio Code 中获得代码提示](VSCode_zh.md)
## 构建系统
**使用 CMake**
自从 fxSDK 2.3,官方的构建系统就是 CMake 了。当创建一个新的项目的时候,会生成一个 `CMakeLists.txt`。这个文件和正常的 CMake 有点偏差,在[这篇帖子(法语帖子)](https://www.planet-casio.com/Fr/forums/topic16647-1-tutoriel-compiler-des-add-ins-avec-cmake-fxsdk.html)里解释了。下面也一并解释了它们有什么区别。
因为我们需要使用跨平台编译器,所以 `cmake` 没办法弄明白我们要干什么,所以需要传一些额外的参数。`fxsdk build-*` 这个命令会帮你传好正确的参数的。
fxSDK 提供了[许多模块](fxsdk/cmake)
* [`FX9860G.cmake`](fxsdk/cmake/FX9860G.cmake) 和 [`FXCG50.cmake`](fxsdk/cmake/FXCG50.cmake) 会在运行 `fxsdk build-fx``fxsdk build-cg` 的时候加载。不直接调用 `cmake` 是有原因的,就是我们会定义一些形似 `FXSDK_*` 的变量,它们指定了有关编译目标的一些信息,且都存在上面的两个文件里面。
* [`Fxconv.cmake`](fxsdk/cmake/Fxconv.cmake) 提供了一些使用 fxconv 的一些函数。`fxconv_declare_assets(... WITH_METADATA)` 会把一些文件标记为需要转换的素材。然后 `fxconv_declare_converters(...)` 定义了这个项目需要用哪些自定义的 Python 模块来转换自定义素材。
* [`GenerateG1A.cmake`](fxsdk/cmake/GenerateG1A.cmake) 和 [`GenerateG3A.cmake`](fxsdk/cmake/GenerateG3A.cmake) 帮您调用 `fxgxa` 来生成 g1a/g3a 程序。默认的 `CMakeLists.txt` 说明了它们如何使用.
* [`FindSimpleLibrary.cmake`](fxsdk/cmake/FindSimpleLibrary.cmake) 和 [`GitVersionNumber.cmake`](fxsdk/cmake/GitVersionNumber.cmake) 是一些小工具与库。看看 [Lephenixnoir/Template-gint-library](https://gitea.planet-casio.com/Lephenixnoir/Template-gint-library),里面讲了如何用 fxSDK 开发一个库。
**只使用 Makefile**
原来的 Makefile 仍然可以用。可以用 `fxsdk new``--makefile` 选项来创建一个基于 Makefile 的项目。但是由于很少人用,所以可能会过时,而且在更新的时候需要您自己来维护。只有在您对 make 很熟悉的情况下才建议使用。
## 手动构建
下面是手动安装 fxSDK 的步骤。一开始,您应该先安装本仓库。如果您之前已经安装过 fxSDK 或者跨平台编译器之类的,为了避免冲突,您应该先把它们删掉之后再来安装。
这个仓库的依赖:
* CMake
* libpng ≥ 1.6
* Python ≥ 3.73.6 有可能也可以)
* Python 3 的 Pillow 库
* libusb 1.0
* UDisks2 库(在构建工具链的时候如果没用就不用)
当您在运行 `configure.sh` 的时候,您需要确保您有权限写入安装路径,个人建议使用 `$HOME/.local`
需要注意,跨平台编译器**必须**被安装在 `fxsdk path sysroot` 这个命令说的路径下面。
* 通过 `-DCMAKE_INSTALL_PREFIX=...` 来改变安装文件夹;
* 当您在使用 WSL 子系统的时候,或者您没有 UDisks2 的时候,通过 `-DFXLINK_DISABLE_UDISKS2=1` 来禁用 `fxlink` 对于 UDisks2 的支持。
```bash
% cmake -B build [OPTIONS...]
% make -C build
% make -C build install
```
然后您就可以去安装跨平台编译器了。如果您不清楚安装的顺序,看看 [GiteaPC README](https://gitea.planet-casio.com/Lephenixnoir/GiteaPC) 或者 各个仓库里的 `giteapc.make`,里面写明了依赖项。

93
VSCode_zh.md Normal file
View File

@ -0,0 +1,93 @@
# 如何在 Visual Studio Code 中获得代码提示
虽然目前您没有办法在电脑上运行程序,更不用说 debug 了,但是您仍然可以享受全方位的代码提示!这已经比卡西欧在 2007 年推出的那个 IDE 好多了。
## 下载
虽然这个软件很火,但有些人可能真的没听说过,先去 [Visual Studio Code 官网](https://code.visualstudio.com) 下载吧!
## 安装插件
首先安装来自微软的 [C/C++ 插件](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools)。或者直接在“扩展”中搜索 `C/C++` 也可以。
## 选择计算器型号
我们知道,彩屏计算器可以显示彩色(废话),但是单色屏的计算器只能显示黑白,所以您在写程序的时候,能用的颜色取决于您选择的计算器型号。所以在开始之前,得选择一下:
1. 打开命令面板 (CTRL/CMD + Shift + P)
2. 输入 `C/C++: Select a Configuration...`,您不需要打得跟这个完全一样,因为 VSCode 会自动补全。如果是我的话,会输入 `c sel a conf`
3. 按下回车然后选择您想要的计算器型号fx-9860G 或者 fx-CG50。
打开 `src/main.c` 文件,输入以下代码,看看代码提示是否有正常工作。
```c
#include <gint/display.h>
#include <gint/keyboard.h>
int main(void)
{
// 用白色来清空屏幕
dclear(C_WHITE);
// 用黑色在左上角写上 Hello world!
dtext(1, 1, C_BLACK, "Hello world!");
// 将缓冲区的内容更新到屏幕上
dupdate();
// 在检测到下一次按键之前卡在这里。
// 因为在卡西欧计算器上,程序结束之后将直接返回菜单,
// 所以只有卡在这里不让它结束,才能看到程序运行结果。
getkey();
return 1;
}
```
## 对不同的型号分别考虑
如果您希望在不同的型号中做一些不同的事,需要使用以下两个宏来判断。
* `FX9860G` 黑白屏
* `FXCG50` 彩屏
譬如我想要在不同的型号上显示不同的文字:
```c
#include <gint/display.h>
#include <gint/keyboard.h>
int main(void)
{
dclear(C_WHITE);
dtext(1, 1, C_BLACK,
// 如果是黑白屏
#ifdef FX9860G
"9860G: Hello world!"
#endif
// 如果是彩屏
#ifdef FXCG50
"CG50: Hello world!"
#endif
);
dupdate();
getkey();
return 1;
}
```
这样,在黑白屏上就会显示 `9860G: Hello world!`,而在彩屏上就会显示 `CG50: Hello world!`。您也许还发现了,这两个字符串有一个的颜色比其他的淡一点,这代表了这个字符串不会在您刚才选择的计算器型号上显示。刚刚讲到了您可以在两种型号之间切换,所以在切换的时候您也应该可以看到两个字符串的颜色深浅发生了变化。
同时,在彩屏和黑白屏上能使用的颜色也是不一样的。感兴趣的话,去看看 `gint/display.h` 里面的定义吧!
## 救命!我看不到代码提示!
先检查一下在 `~/.local/bin` 这个文件夹下有没有 `sh-elf-gcc` 这个跨平台编译器。如果没有,您需要重新安装 fxSDK。
## 结语
好啦!您已经学会了如何用这个牛逼轰轰的内核来开发计算器程序了,期待您的作品!

View File

@ -23,6 +23,7 @@ When TYPE is specified (one-shot conversion), it should be one of:
--libimg-image Convert to the libimg image format
--custom Use a custom converter; you might want to specify an explicit
type by adding "custom-type:your_custom_type" (see below)
Custom converters can be specified by:
--converters Semicolon-separated list of custom converters (converters.py
in the current directory is detected as one per legacy)
@ -31,8 +32,13 @@ syntax (names can contain dots). For example:
fxconv -f myfont.png -o myfont.o charset:ascii grid.padding:1 height:7
Some formats differ between platforms so you should specify it when possible:
--fx Casio fx-9860G family (black-and-white calculators)
--cg Casio fx-CG 50 family (16-bit color calculators)
--fx CASIO fx-9860G family (black-and-white calculators)
--cg CASIO fx-CG 50 family (16-bit color calculators)
Finally, there is some support (non-final) for PythonExtra, in which case the
output file is as Python file instead of an object file.
--py Convert for PythonExtra (some types supported)
--py-compact Use compact bytes notation (shorter, but non-printable)
""".strip()
# Simple error-warnings system
@ -60,6 +66,7 @@ def main():
target = { 'toolchain': None, 'arch': None, 'section': None }
use_custom = False
converter_paths = []
py = { 'enabled': False, 'compact': False }
# Parse command-line arguments
@ -69,7 +76,7 @@ def main():
try:
longs = ["help", "output=", "toolchain=", "arch=", "section=", "fx",
"cg", "converters="] + types.split()
"cg", "converters=", "py", "py-compact"] + types.split()
opts, args = getopt.gnu_getopt(sys.argv[1:], "hsbifo:", longs)
except getopt.GetoptError as error:
return err(error)
@ -94,6 +101,10 @@ def main():
mode = "custom"
elif name == "--converters":
converter_paths = [path for path in value.split(";") if path]
elif name == "--py":
py['enabled'] = True
elif name == "--py-compact":
py['compact'] = True
# Other names are modes
else:
mode = name[1] if len(name)==2 else name[2:]
@ -144,6 +155,7 @@ def main():
spec.loader.exec_module(module)
converters.append(module.convert)
params["py"] = py
fxconv.convert(input, params, target, output, model, converters)
if __name__ == "__main__":

View File

@ -15,7 +15,7 @@ __all__ = [
# Color names
"FX_BLACK", "FX_DARK", "FX_LIGHT", "FX_WHITE", "FX_ALPHA",
# Conversion mechanisms
"ObjectData", "u8", "u16", "u32", "ref", "sym",
"ObjectData", "u8", "u16", "u32", "i8", "i16", "i32", "ref", "sym",
# Functions
"quantize", "convert", "elf",
# Reusable classes
@ -69,7 +69,7 @@ FX_PROFILES = [
FxProfile(0x0, "mono", { FX_BLACK, FX_WHITE }, [
lambda c: (c == FX_BLACK),
]),
# Black-and-white with transparency, equivalent of two bitmaps in ML
# Black-and-white with transparency, equivalent of two bitmaps in ML
FxProfile(0x1, "mono_alpha", { FX_BLACK, FX_WHITE, FX_ALPHA }, [
lambda c: (c != FX_ALPHA),
lambda c: (c == FX_BLACK),
@ -159,7 +159,7 @@ FX_CHARSETS = {
"ascii": [ (0x00, 128) ],
# Custom Unicode block intervals
"unicode": [],
# Single block 0x00-0xff (does not imply single-byte encoding)
# Single block 0x00-0xff (does not imply single-byte encoding)
"256chars": [ (0x00, 256) ],
}
@ -167,21 +167,40 @@ FX_CHARSETS = {
# Conversion mechanisms
#
def u8(x):
return bytes([ x & 255 ])
def u16(x):
return bytes([ (x >> 8) & 255, x & 255 ])
def u32(x):
return bytes([ (x >> 24) & 255, (x >> 16) & 255, (x >> 8) & 255, x & 255 ])
def u8(x, check=False):
if check and not (0 <= x < 2**8):
raise FxconvError(f"integer {x} out of range for u8")
return bytes([ x & 255 ])
def u16(x, check=False):
if check and not (0 <= x < 2**16):
raise FxconvError(f"integer {x} out of range for u16")
return bytes([ (x >> 8) & 255, x & 255 ])
def u32(x, check=False):
if check and not (0 <= x < 2**32):
raise FxconvError(f"integer {x} out of range for u32")
return bytes([ (x >> 24) & 255, (x >> 16) & 255, (x >> 8) & 255, x & 255 ])
def ref(base, offset=None, padding=None, prefix_underscore=True):
def i8(x, check=True):
if check and not (-2**7 <= x < 2**7):
raise FxconvError(f"integer {x} out of range for i8")
return bytes([ x & 255 ])
def i16(x, check=True):
if check and not (-2**15 <= x < 2**15):
raise FxconvError(f"integer {x} out of range for i16")
return bytes([ (x >> 8) & 255, x & 255 ])
def i32(x, check=True):
if check and not (-2**31 <= x < 2**31):
raise FxconvError(f"integer {x} out of range for i32")
return bytes([ (x >> 24) & 255, (x >> 16) & 255, (x >> 8) & 255, x & 255 ])
def ref(base, offset=None, padding=None, prefix_underscore=True, align=None):
if isinstance(base, bytes) or isinstance(base, bytearray):
base = bytes(base)
if offset is not None:
raise FxconvError(f"reference to bytes does not allow offset")
if padding and len(base) % padding != 0:
base += bytes(padding - len(base) % padding)
return Ref("bytes", base)
return Ref("bytes", base, align or 4)
elif isinstance(base, str):
if padding is not None:
@ -191,19 +210,24 @@ def ref(base, offset=None, padding=None, prefix_underscore=True):
if offset is not None:
offset = int(offset)
base = f"{base} + {offset}"
return Ref("name", base)
return Ref("name", base, align or 4)
elif isinstance(base, ObjectData):
if offset is not None or padding is not None:
raise FxconvError("reference to structure does not allow offset " +
"or padding")
return Ref("struct", base)
if offset is not None:
raise FxconvError("reference to structure does not allow offset")
if padding is not None:
raise FxconvError("reference to structure does not allow padding")
# Allow over-aligning the structure (but not more than 4)
align = max(base.alignment, align or 4)
if align > 4:
raise FxconvError(f"align {align} > 4 will not be honored (yet)")
return Ref("struct", base, align)
else:
raise FxconvError(f"invalid type {type(base)} for ref()")
def ptr(base):
return ref(base)
def ptr(*args, **kwargs):
return ref(*args, **kwargs)
def chars(text, length, require_final_nul=True):
btext = bytes(text, 'utf-8')
@ -221,7 +245,7 @@ def sym(name):
# "bytes" -> target is a bytes(), we point to that data
# "name" -> target is an str like "_sym+2", we point to that
# "struct" -> target is an ObjectData
Ref = collections.namedtuple("Ref", ["kind", "target"])
Ref = collections.namedtuple("Ref", ["kind", "target", "align"])
Sym = collections.namedtuple("Sym", ["name"])
@ -250,7 +274,7 @@ class ObjectData:
elif isinstance(other, Sym):
self.inner.append(other)
elif isinstance(other, ObjectData):
self.inner.append(other)
self.inner += other.inner
return self
@staticmethod
@ -273,34 +297,23 @@ class ObjectData:
return padding
def link(self, symbol):
inner = []
inner = self.inner
outer = []
elements = []
size = 0
# First unfold all structures within [inner] as we accumulate the total
# size of the inner data
for el in self.inner:
if isinstance(el, ObjectData):
size += self.align(size, el.alignment, inner)
code, code_size = el.link(f"{symbol} + {size}")
inner.append((code, code_size))
size += code_size
else:
inner.append(el)
size += self.element_size(el)
size = sum(self.element_size(el) for el in inner)
# Then replace complex references with unfolded data appended at the
# end of the structure
for el in inner:
if isinstance(el, Ref) and el.kind == "bytes":
elements.append(Ref("name", f"{symbol} + {size}"))
size += self.align(size, el.align, outer)
elements.append(Ref("name", f"{symbol} + {size}", None))
outer.append(el.target)
size += self.element_size(el.target)
elif isinstance(el, Ref) and el.kind == "struct":
size += self.align(size, el.target.alignment, outer)
elements.append(Ref("name", f"{symbol} + {size}"))
elements.append(Ref("name", f"{symbol} + {size}", None))
code, code_size = el.target.link(f"{symbol} + {size}")
outer.append((code, code_size))
size += code_size
@ -500,8 +513,16 @@ def convert_bopti_fx(input, params):
data[n] = layer[4 * longword + i]
n += 1
if params["py"]["enabled"]:
w, h = img.size
return ["import gint\n",
f"{params['name']} = gint.image({p.id}, {w}, {h}, ", data, ")\n"]
# Generate the object file
return header + data
o = ObjectData()
o += header
o += ptr(data)
return o
def _image_project(img, f):
# New width and height
@ -581,6 +602,17 @@ def convert_image_cg(input, params):
data, stride, palette, color_count = image_encode(img, format)
if params["py"]["enabled"]:
w, h = img.size
return [
"import gint\n",
f"{params['name']} = gint.image({format.id}, {color_count}, ",
f"{img.width}, {img.height}, {stride}, ",
data,
", ",
"None" if palette is None else palette,
")\n"]
o = ObjectData()
o += u8(format.id)
o += u8(3) # DATA_RO, PALETTE_RO
@ -775,6 +807,19 @@ def convert_topti(input, params):
# Object file generation
#---
if params["py"]["enabled"]:
l = [ "import gint\n",
f"{params['name']} = gint.font({title or None}, {flags}, ",
f"{line_height}, {grid.h}, {len(blocks)}, {glyph_count}, ",
f"{char_spacing}, ",
data_blocks,
data_glyphs ]
if proportional:
l += [data_index, data_width]
else:
l += [f"{grid.w}, {(grid.w * grid.h + 31) >> 5}"]
return l + [")\n"]
# Base data: always put the raw data and blocks first since they are
# 4-aligned, to preserve alignment on the rest of the references.
o = ObjectData()
@ -1139,8 +1184,14 @@ def convert(input, params, target, output=None, model=None, custom=None):
else:
raise FxconvError(f'unknown resource type \'{t}\'')
# PythonExtra conversion: output a file
if params["py"]["enabled"]:
if isinstance(o, ObjectData):
raise FxconvError(f'conversion doe not support Python output')
pyout(o, output, params)
# Standard conversions: save to ELF in the natural way
elf(o, output, "_" + params["name"], **target)
else:
elf(o, output, "_" + params["name"], **target)
def elf(data, output, symbol, toolchain=None, arch=None, section=None,
assembly=None):
@ -1154,9 +1205,9 @@ def elf(data, output, symbol, toolchain=None, arch=None, section=None,
The symbol name must have a leading underscore if it is to be declared and
used from a C program.
The toolchain can be any target triplet for which the compiler is
available. The architecture is deduced from some typical triplets;
otherwise it can be set, usually as "sh3" or "sh4-nofpu". This affects the
The toolchain can be any target triplet for which the compiler is
available. The architecture is deduced from some typical triplets;
otherwise it can be set, usually as "sh3" or "sh4-nofpu". This affects the
--binary-architecture flag of objcopy. If arch is set to "fx" or "cg", this
function tries to be smart and:
@ -1186,6 +1237,14 @@ def elf(data, output, symbol, toolchain=None, arch=None, section=None,
Produces an output file and returns nothing.
"""
# Unfold ObjectData into data and assembly
if isinstance(data, ObjectData):
assembly = f".global {symbol}\n" + \
f"{symbol}:\n" + \
data.link(symbol)[0] + \
(assembly or "")
data = None
# Toolchain parameters
if toolchain is None:
@ -1206,16 +1265,9 @@ def elf(data, output, symbol, toolchain=None, arch=None, section=None,
raise FxconvError(f"non-trivial architecture for {toolchain} must be "+
"specified")
# Unfold ObjectData into data and assembly
if isinstance(data, ObjectData):
asm = ".section " + section.split(",",1)[0] + "\n"
asm += f".global {symbol}\n"
asm += f"{symbol}:\n"
asm += data.link(symbol)[0]
asm += (assembly or "")
data = None
assembly = asm
if assembly:
sec = ".section " + section.split(",",1)[0]
assembly = sec + "\n" + assembly
if data is None and assembly is None:
raise FxconvError("elf() but no data and no assembly")
@ -1275,6 +1327,52 @@ def elf(data, output, symbol, toolchain=None, arch=None, section=None,
if assembly:
fp_asm.close()
def elf_multi(vars, output, assembly=None, **kwargs):
"""
Like elf(), but instead of one symbol/data pair, allows defining multiple
variables. vars should be a list [(symbol, ObjectData), ...]. Keyword
arguments are passed to elf().
"""
asm = ""
for symbol, objdata in vars:
asm += f".global {symbol}\n"
asm += f"{symbol}:\n"
asm += objdata.link(symbol)[0]
asm += assembly or ""
return elf(None, output, None, assembly=asm, **kwargs)
def pyout(bits, output, params):
# Compact into byte strings to avoid building tuples in the heap;
# MicroPython allows basically anything in literal strings (including
# NUL!), we just have to escape \, \n, \r, and ".
def byteify(c):
if c == ord('"'):
return b'\\"'
if c == ord('\n'):
return b'\\n'
if c == ord('\r'):
return b'\\r'
if c == ord('\\'):
return b'\\\\'
return bytes([c])
with open(output, "wb") as fp:
for section in bits:
if isinstance(section, bytearray):
section = bytes(section)
if isinstance(section, bytes):
if params["py"]["compact"]:
fp.write(b'b"')
for byte in section:
fp.write(byteify(byte))
fp.write(b'"')
else:
fp.write(repr(section).encode("utf-8"))
else:
fp.write(section.encode("utf-8"))
#
# Meta API
#

View File

@ -28,7 +28,8 @@ int main_list(struct fxlink_filter *filter, delay_t *delay,
int main_blocks(struct fxlink_filter *filter, delay_t *delay);
/* Main function for -s */
int main_send(struct fxlink_filter *filter, delay_t *delay, char **files);
int main_send(struct fxlink_filter *filter, delay_t *delay, char **files,
char *outfolder);
/* Main function for -i */
int main_interactive(struct fxlink_filter *filter, delay_t *delay,

View File

@ -20,7 +20,7 @@ static const char *help_string =
"usage: %1$s (-l|-b|-t) [General options]\n"
" %1$s -i [-r] [--fxlink-log[=<FILE>]] [General options]\n"
" %1$s -p <FILE> [General options]\n"
" %1$s -s <FILES>... [General options]\n"
" %1$s -s <FILES>... [--folder=OUTFOLDER] [General options]\n"
"\n"
"fxlink interacts with CASIO calculators of the fx and fx-CG families over\n"
"the USB port, using libusb. It can also transfer files for Mass Storage\n"
@ -47,6 +47,7 @@ static const char *help_string =
" --fxlink-log[=FILE] -i: Append fxlink text messages to FILE. Without\n"
" argument, a unique name is generated.\n"
" -r, --repeat -i: Reconnect if the calc disconnects (implies -w)\n"
" --folder=FOLDER -s: Select destination folder for files\n"
"\n"
"Device filters:\n"
" A device filter narrows down what devices we list or connect to by\n"
@ -67,6 +68,7 @@ int main(int argc, char **argv)
delay_t delay = delay_seconds(0);
struct fxlink_filter *filter = NULL;
bool repeat = false;
char *outfolder = NULL;
options.log_file = NULL;
options.verbose = false;
@ -77,7 +79,7 @@ int main(int argc, char **argv)
// Command-line argument parsing
//---
enum { LIBUSB_LOG=1, LOG_TO_FILE=2 };
enum { LIBUSB_LOG=1, LOG_TO_FILE=2, OUT_FOLDER=3 };
const struct option longs[] = {
{ "help", no_argument, NULL, 'h' },
{ "list", no_argument, NULL, 'l' },
@ -90,6 +92,7 @@ int main(int argc, char **argv)
{ "fxlink-log", optional_argument, NULL, LOG_TO_FILE },
{ "repeat", no_argument, NULL, 'r' },
{ "verbose", no_argument, NULL, 'v' },
{ "folder", required_argument, NULL, OUT_FOLDER },
/* Deprecated options ignored for compatibility: */
{ "quiet", no_argument, NULL, 'q' },
{ "unmount", no_argument, NULL, 'u' },
@ -165,6 +168,9 @@ int main(int argc, char **argv)
case 'f':
filter = fxlink_filter_parse(optarg);
break;
case OUT_FOLDER:
outfolder = strdup(optarg);
break;
case '?':
error = 1;
}
@ -219,7 +225,7 @@ int main(int argc, char **argv)
}
else if(mode == 's') {
#ifndef FXLINK_DISABLE_UDISKS2
rc = main_send(filter, &delay, argv + optind);
rc = main_send(filter, &delay, argv + optind, outfolder);
#else
rc = elog("this fxlink was built without UDisks2; -s is disabled");
#endif

View File

@ -44,11 +44,13 @@ static void handle_new_message(struct fxlink_device *fdev,
if(options.verbose)
printf("------------------\n");
fwrite(str, 1, msg->size, stdout);
#if 0
if(str[msg->size - 1] != '\n') {
if(!options.verbose)
printf("\e[30;47m%%\e[0m");
printf("\n");
}
#endif
if(options.verbose) {
printf("------------------\n");
}

View File

@ -16,7 +16,8 @@
#include <sys/types.h>
#include <sys/wait.h>
int main_send(struct fxlink_filter *filter, delay_t *delay, char **files)
int main_send(struct fxlink_filter *filter, delay_t *delay, char **files,
char *outfolder)
{
fxlink_filter_clean_udisks2(filter);
GError *error = NULL;
@ -53,10 +54,14 @@ int main_send(struct fxlink_filter *filter, delay_t *delay, char **files)
printf("Mounted %s to %s.\n", dev, folder);
}
else {
folder = strdup(mount_points[0]);
folder = mount_points[0];
printf("Already mounted at %s.\n", folder);
}
gchar *outpath = folder;
if(outfolder)
asprintf(&outpath, "%s/%s/", folder, outfolder);
/* Copy files with external cp(1) */
int file_count = 0;
while(files[file_count]) file_count++;
@ -69,7 +74,7 @@ int main_send(struct fxlink_filter *filter, delay_t *delay, char **files)
argv[0] = "cp";
for(int i = 0; files[i]; i++)
argv[i+1] = files[i];
argv[file_count+1] = folder;
argv[file_count+1] = outpath;
argv[file_count+2] = NULL;
/* Print command */
@ -97,6 +102,9 @@ int main_send(struct fxlink_filter *filter, delay_t *delay, char **files)
}
}
if(outfolder)
free(outpath);
/* Unmount the filesystem and eject the device */
GVariant *args = g_variant_new("a{sv}", NULL);
udisks_filesystem_call_unmount_sync(fs, args, NULL, &error);
@ -113,7 +121,6 @@ int main_send(struct fxlink_filter *filter, delay_t *delay, char **files)
printf("Ejected %s.\n", dev);
end:
free(folder);
if(argv) free(argv);
if(fs) g_object_unref(fs);
if(drive) g_object_unref(drive);

View File

@ -46,18 +46,31 @@ static void quit(void)
/* Generate an RGB888 surface from image data. */
static SDL_Surface *surface_for_image(uint8_t **RGB888_rows, int width,
int height)
int height, int scale)
{
/* Little endian setup for RGB */
SDL_Surface *s = SDL_CreateRGBSurface(0, width, height, 24,
SDL_Surface *s = SDL_CreateRGBSurface(0, width*scale, height*scale, 24,
0x000000ff, 0x0000ff00, 0x0000ff00, 0);
if(!s) {
elog("cannot create surface for image\n");
return NULL;
}
for(int i = 0; i < height; i++)
memcpy(s->pixels + i * s->pitch, RGB888_rows[i], width * 3);
for(int y = 0; y < height; y++) {
for(int dy = 0; dy < scale; dy++) {
uint8_t *src = RGB888_rows[y];
uint8_t *dst = s->pixels + (y*scale+dy) * s->pitch;
for(int x = 0; x < width; x++) {
for(int dx = 0; dx < scale; dx++) {
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst += 3;
}
src += 3;
}
}
}
return s;
}
@ -68,11 +81,14 @@ void fxlink_sdl2_display_raw(struct fxlink_message_image_raw const *raw)
return;
int current_w, current_h;
SDL_GetWindowSize(window, &current_w, &current_h);
if(current_w != raw->width || current_h != raw->height)
SDL_SetWindowSize(window, raw->width, raw->height);
int scale = 1;
SDL_Surface *src = surface_for_image(raw->data, raw->width, raw->height);
SDL_GetWindowSize(window, &current_w, &current_h);
if(current_w != raw->width * scale || current_h != raw->height * scale)
SDL_SetWindowSize(window, raw->width * scale, raw->height * scale);
SDL_Surface *src =
surface_for_image(raw->data, raw->width, raw->height, scale);
if(!src)
return;

View File

@ -549,8 +549,8 @@ int main_tui_interactive(libusb_context *ctx)
/* Check for devices with finished transfers */
for(int i = 0; i < TUI.devices.count; i++) {
struct fxlink_device *fdev = &TUI.devices.devices[i];
struct fxlink_message *msg = fxlink_device_finish_bulk_IN(fdev);
if(msg) {
struct fxlink_message *msg;
while((msg = fxlink_device_finish_bulk_IN(fdev))) {
fxlink_interactive_handle_message(msg);
fxlink_message_free(msg, true);
fxlink_device_start_bulk_IN(fdev);

View File

@ -38,4 +38,7 @@ if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50)
generate_g3a(TARGET myaddin OUTPUT "MyAddin.g3a"
NAME "MyAddin" ICONS assets-cg/icon-uns.png assets-cg/icon-sel.png)
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G_G3A)
generate_g3a(TARGET myaddin OUTPUT "MyAddin-fx.g3a"
NAME "MyAddin-fx" ICONS assets-cg/icon-uns.png assets-cg/icon-sel.png)
endif()

View File

@ -2,6 +2,7 @@
/build-fx
/build-cg
/build-cg-push
/build-fxg3a
/*.g1a
/*.g3a

View File

@ -0,0 +1,37 @@
{
"configurations": [
{
"name": "fx-9860G",
"compilerPath": "~/.local/bin/sh-elf-gcc",
"compilerArgs": [ "-D FX9860G" ],
"cStandard": "c17",
"cppStandard": "gnu++17",
"includePath": [],
"intelliSenseMode": "${default}",
"mergeConfigurations": false,
"browse": {
"path": [],
"limitSymbolsToIncludedHeaders": true
}
},
{
"name": "fx-CG50",
"compilerPath": "~/.local/bin/sh-elf-gcc",
"compilerArgs": [ "-D FXCG50" ],
"cStandard": "c17",
"cppStandard": "gnu++17",
"includePath": [],
"intelliSenseMode": "${default}",
"mergeConfigurations": false,
"browse": {
"path": [],
"limitSymbolsToIncludedHeaders": true
}
}
],
"version": 4
}

View File

@ -0,0 +1,59 @@
# fxSDK toolchain file for Casio graphing calculators
# Special file selecting an fx-CG target while running fx API code.
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR sh)
# TODO: Use gint's flexible configuration for fx9860g-g3a
# Base plateform is fx to pick the assets for the fx9860G
set(FXSDK_PLATFORM fx)
# the long name is change to be able to separate in CMakeLists.txt the different targets
set(FXSDK_PLATFORM_LONG fx9860G_G3A)
set(FXSDK_TOOLCHAIN sh-elf-)
set(CMAKE_C_COMPILER sh-elf-gcc)
set(CMAKE_CXX_COMPILER sh-elf-g++)
set(CMAKE_C_FLAGS_INIT "")
set(CMAKE_CXX_FLAGS_INIT "")
# set -DFXCG50 and -DTARGET_FXCG50 to get the CG drivers
add_compile_options(-m4-nofpu -mb -ffreestanding -nostdlib -Wa,--dsp -DFXCG50)
add_link_options(-nostdlib -Wl,--no-warn-rwx-segments)
link_libraries(-lgcc)
add_compile_definitions(TARGET_FXCG50)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(FXSDK_CMAKE_MODULE_PATH "${FXSDK_CMAKE_MODULE_PATH}")
# Add the fxSDK prefix path to the search
set(FXSDK_PREFIX "$ENV{FXSDK_PREFIX}")
foreach(DIR IN LISTS FXSDK_PREFIX)
include_directories("${DIR}/include")
link_directories("${DIR}/lib")
endforeach()
# Determine compiler install path
execute_process(
COMMAND ${CMAKE_C_COMPILER} --print-file-name=.
OUTPUT_VARIABLE FXSDK_COMPILER_INSTALL
OUTPUT_STRIP_TRAILING_WHITESPACE)
# Provide fxSDK sysroot and standard install folders
execute_process(
COMMAND fxsdk path sysroot
OUTPUT_VARIABLE FXSDK_SYSROOT
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND fxsdk path include
OUTPUT_VARIABLE FXSDK_INCLUDE
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND fxsdk path lib
OUTPUT_VARIABLE FXSDK_LIB
OUTPUT_STRIP_TRAILING_WHITESPACE)

View File

@ -21,6 +21,10 @@ add_link_options(-nostdlib -Wl,--no-warn-rwx-segments)
link_libraries(-lgcc)
add_compile_definitions(TARGET_FXCG50)
if(CMAKE_BUILD_TYPE STREQUAL FastLoad)
add_compile_definitions(TARGET_FXCG50_FASTLOAD)
endif()
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

View File

@ -41,6 +41,11 @@ ${R}fxsdk${n} ${R}build-cg-push${n} [${R}-c${n}] [${R}-s${n}] [${R}--${n}] \
are identical to other build commands. Typical workflows will always set -s
(which requires libusb support in fxlink).
${R}fxsdk${n} ${R}build-fxg3a${n} [${R}-c${n}] [${R}-s${n}] [${R}--${n}] \
[${g}<ARGS>${n}...]
Builds the current project for fx-CG 50 (.g3a file) from a code source
initially targeting the fx-9860G(II).
${R}fxsdk${n} (${R}send${n}|${R}send-fx${n}|${R}send-cg${n})
Sends the target file to the calculator. Uses p7 (which must be installed
externally) for the fx-9860G, and fxlink for the fx-CG. For the G-III series,
@ -100,7 +105,7 @@ fxsdk_new_project() {
# Copy initial files to project folder
assets="$PREFIX/share/fxsdk/assets"
mkdir -p "$1"/{,src,assets-fx,assets-cg}
mkdir -p "$1"/{,src,assets-fx,assets-cg,.vscode}
case "$generator" in
"Makefile")
@ -125,9 +130,11 @@ fxsdk_new_project() {
cp "$assets"/icon-fx.png "$1"/assets-fx/icon.png
cp "$assets"/icon-cg-uns.png "$1"/assets-cg/icon-uns.png
cp "$assets"/icon-cg-sel.png "$1"/assets-cg/icon-sel.png
cp "$assets"/vscode/c_cpp_properties.json "$1"/.vscode/c_cpp_properties.json
echo "Created a new project $NAME (build system: $generator)."
echo "Type 'fxsdk build-fx' or 'fxsdk build-cg' to compile the program."
echo "Other options are available, see fxsdk --help."
}
fxsdk_load_config() {
@ -138,7 +145,7 @@ fxsdk_load_config() {
fxsdk_build() {
[[ ! -e build-fx && ! -e build-cg ]]
[[ ! -e build-fx && ! -e build-cg && ! -e build-fxg3a ]]
none_exists=$?
if [[ -e build-fx || $none_exists == 0 ]]; then
@ -150,6 +157,11 @@ fxsdk_build() {
echo "$TAG Making into build-cg"
fxsdk_build_cg "$@"
fi
if [[ -e build-fxg3a || $none_exists == 0 ]]; then
echo "$TAG Making into build-fxg3a"
fxsdk_build_fxg3a "$@"
fi
}
fxsdk_build_fx() {
@ -161,6 +173,9 @@ fxsdk_build_cg() {
fxsdk_build_cg_push() {
fxsdk_build_in "cg-push" "FXCG50" "$@"
}
fxsdk_build_fxg3a() {
fxsdk_build_in "fxg3a" "FX9860G_G3A" "$@"
}
fxsdk_build_in() {
platform="$1"
@ -222,16 +237,20 @@ fxsdk_build_in() {
}
fxsdk_send() {
if [[ -e "build-fx" && ! -e "build-cg" ]]; then
if [[ -e "build-fx" && ! -e "build-cg" && ! -e "build-fxg3a" ]]; then
fxsdk_send_fx
fi
if [[ -e "build-cg" && ! -e "build-fx" ]]; then
if [[ -e "build-cg" && ! -e "build-fx" && ! -e "build-fxg3a" ]]; then
fxsdk_send_cg
fi
if [[ -e "build-fxg3a" && ! -e "build-fx" && ! -e "build-cg" ]]; then
fxsdk_send_cg
fi
echo "either no or several platforms are targeted, use 'fxsdk send-fx' or"
echo "fxsdk 'send-cg' to specify which calculator to send to."
echo "'fxsdk send-cg' to specify which calculator to send to."
}
fxsdk_send_fx() {
@ -267,6 +286,10 @@ fxsdk_send_cg-push() {
fxlink -pw ${bin_files}
}
fxsdk_send_fxg3a() {
fxsdk_send_cg "$@"
}
fxsdk_path() {
case "$1" in
"sysroot")
@ -300,6 +323,8 @@ case "$1" in
fxsdk_build_cg "${@:2}";;
"build-cg-push"|"bcgp")
fxsdk_build_cg_push "${@:2}";;
"build-fxg3a"|"bf3"|"bfx3")
fxsdk_build_fxg3a "${@:2}";;
# Install
"send"|"s")

View File

@ -11,6 +11,7 @@
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
char const *fmt_to_ANSI(int format)
{
@ -146,3 +147,26 @@ bool delay_cycle(delay_t *delay)
if(*delay > 0) (*delay)--;
return false;
}
void fxlink_hexdump(char const *data, int size, FILE *fp)
{
for(int read = 0; read < size; read += 16) {
for(int i = 0; i < 16; i++) {
if(read + i < size) {
int byte = data[read + i] & 0xff;
fprintf(fp, " %02x", byte);
}
else {
fprintf(fp, " ");
}
}
fprintf(fp, " | ");
for(int i = 0; i < 16 && read + i < size; i++) {
int byte = data[read + i] & 0xff;
fprintf(fp, "%c", isprint(byte) ? byte : '.');
}
fprintf(fp, "\n");
}
}

View File

@ -10,6 +10,8 @@
#include <string.h>
#include <assert.h>
static void fxlink_device_queue_finished_IN(struct fxlink_device *fdev);
char const *fxlink_device_id(struct fxlink_device const *fdev)
{
static char str[32];
@ -401,17 +403,18 @@ static void bulk_IN_callback(struct libusb_transfer *transfer)
switch(transfer->status) {
case LIBUSB_TRANSFER_COMPLETED:
/* Start or continue an fxlink transfer. When finished, don't resubmit,
instead have the user pick up the message before continuing. */
// log_("bulk_IN_callback: got %d bytes (comm->ftransfer_IN=%p)\n",
// data_size, comm->ftransfer_IN);
// fxlink_hexdump(data, data_size, stderr);
/* Start or continue an fxlink transfer. */
resubmit = true;
if(comm->ftransfer_IN) {
if(comm->ftransfer_IN)
fxlink_transfer_receive(comm->ftransfer_IN, data, data_size);
if(fxlink_transfer_complete(comm->ftransfer_IN))
resubmit = false;
}
else {
else
comm->ftransfer_IN = fxlink_transfer_make_IN(data, data_size);
}
if(fxlink_transfer_complete(comm->ftransfer_IN))
fxlink_device_queue_finished_IN(fdev);
break;
/* Error: drop data and don't resubmit */
@ -483,18 +486,16 @@ bool fxlink_device_start_bulk_IN(struct fxlink_device *fdev)
return true;
}
struct fxlink_message *fxlink_device_finish_bulk_IN(struct fxlink_device *fdev)
static void fxlink_device_queue_finished_IN(struct fxlink_device *fdev)
{
struct fxlink_comm *comm = fdev->comm;
if(!comm || !comm->ftransfer_IN)
return NULL;
if(!fxlink_transfer_complete(comm->ftransfer_IN))
return NULL;
return;
struct fxlink_message *msg = fxlink_transfer_finish_IN(comm->ftransfer_IN);
if(!msg)
return NULL;
return;
int version_major = (msg->version >> 8) & 0xff;
int version_minor = msg->version & 0xff;
@ -505,6 +506,28 @@ struct fxlink_message *fxlink_device_finish_bulk_IN(struct fxlink_device *fdev)
msg->application, msg->type, fxlink_size_string(msg->size));
comm->ftransfer_IN = NULL;
if(comm->queue_IN.size >= FXLINK_DEVICE_IN_QUEUE_SIZE) {
elog("device queue full (how?!), dropping above message");
fxlink_message_free(msg, true);
return;
}
comm->queue_IN.messages[comm->queue_IN.size++] = msg;
}
struct fxlink_message *fxlink_device_finish_bulk_IN(struct fxlink_device *fdev)
{
struct fxlink_comm *comm = fdev->comm;
if(!comm || comm->queue_IN.size <= 0)
return NULL;
struct fxlink_message *msg = comm->queue_IN.messages[0];
for(int i = 1; i < comm->queue_IN.size; i++)
comm->queue_IN.messages[i-1] = comm->queue_IN.messages[i];
comm->queue_IN.size--;
comm->queue_IN.messages[comm->queue_IN.size] = 0;
return msg;
}

View File

@ -11,6 +11,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdio.h>
#include <poll.h>
static inline int min(int x, int y)
@ -90,6 +91,9 @@ int fxlink_multipoll(int timeout, struct pollfd *fds1, int count1, ...);
pointer to a statically-allocated string. */
char const *fxlink_size_string(int bytes);
/* Hexdump a data buffer to FILE. */
void fxlink_hexdump(char const *data, int size, FILE *fp);
//---
// Delay
//---

View File

@ -167,6 +167,10 @@ enum {
/* Return a string representation of the calc determined system. */
char const *fxlink_device_system_string(struct fxlink_device const *fdev);
/* Size of the queue where received messages are stored between USB handling
and user code reading them. It almost never has more then one element. */
#define FXLINK_DEVICE_IN_QUEUE_SIZE 16
/* Device state tracked for communication targets. */
struct fxlink_comm {
/* Whether the fxlink interface could be claimed */
@ -184,8 +188,11 @@ struct fxlink_comm {
struct fxlink_transfer *ftransfer_IN;
/* Cancellation flag */
bool cancelled_IN;
/* Completed transfer objects (to be checked by user every frame) */
struct fxlink_message *message_IN;
/* Completed input message queue */
struct {
struct fxlink_message *messages[FXLINK_DEVICE_IN_QUEUE_SIZE];
int size;
} queue_IN;
/* Current OUT transfer */
struct libusb_transfer *tr_bulk_OUT;
@ -213,10 +220,12 @@ bool fxlink_device_claim_fxlink(struct fxlink_device *fdev);
device structure is always ready to receive data from the calculator. */
bool fxlink_device_start_bulk_IN(struct fxlink_device *fdev);
/* Finish an IN transfer and obtain the completed message. This function should
be checked every frame as it will return a non-NULL pointer as soon as the
message is completed and the device will only start a new bulk IN transfer
until the message is moved out by this function. */
/* Get a pointer to a completed IN message. This function should be checked *in
a loop* at every frame as completed messages queue up in a small buffer and
the device device will only start a new bulk IN transfer until the message
is moved out by this function. In practice, the buffer will only ever hold
more than one message if two messages complete within a single libusb event
handling call. But that happens. */
struct fxlink_message *fxlink_device_finish_bulk_IN(
struct fxlink_device *fdev);