Compare commits
No commits in common. "dev" and "master" have entirely different histories.
|
@ -85,19 +85,12 @@ if(NOT FXLINK_DISABLE_SDL2)
|
|||
target_link_libraries(fxlink PkgConfig::sdl2)
|
||||
endif()
|
||||
|
||||
# fxsdk-gdb-bridge
|
||||
add_executable(fxsdk-gdb-bridge fxsdk/gdb-bridge.c)
|
||||
target_link_libraries(fxsdk-gdb-bridge libfxlink)
|
||||
target_include_directories(fxsdk-gdb-bridge PRIVATE
|
||||
"${SRC}/fxlink/include")
|
||||
|
||||
# Install rules
|
||||
|
||||
# fxsdk
|
||||
install(PROGRAMS "${BIN}/fxsdk.sh" TYPE BIN RENAME fxsdk)
|
||||
install(DIRECTORY fxsdk/assets DESTINATION share/fxsdk)
|
||||
install(DIRECTORY fxsdk/cmake/ DESTINATION lib/cmake/fxsdk)
|
||||
install(TARGETS fxsdk-gdb-bridge)
|
||||
# fxgxa, fxg1a
|
||||
install(TARGETS fxgxa)
|
||||
install(FILES "${BIN}/fxg1a" TYPE BIN)
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# 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.
|
||||
|
@ -78,7 +76,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-9860G-series calculators (.g1a):
|
||||
# Build the add-in for fx-98600G-series calculators (.g1a):
|
||||
% fxsdk build-fx
|
||||
# Build the add-in for fx-CG-series calculators (.g3a):
|
||||
% fxsdk build-cg
|
||||
|
|
184
README_zh.md
184
README_zh.md
|
@ -1,184 +0,0 @@
|
|||
# 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 因为有 WSL(Linux 子系统),所以也有官方支持。然而 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.7(3.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
93
VSCode_zh.md
|
@ -1,93 +0,0 @@
|
|||
# 如何在 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。
|
||||
|
||||
## 结语
|
||||
|
||||
好啦!您已经学会了如何用这个牛逼轰轰的内核来开发计算器程序了,期待您的作品!
|
|
@ -23,7 +23,6 @@ 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)
|
||||
|
||||
|
@ -32,13 +31,8 @@ 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)
|
||||
|
||||
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)
|
||||
--fx Casio fx-9860G family (black-and-white calculators)
|
||||
--cg Casio fx-CG 50 family (16-bit color calculators)
|
||||
""".strip()
|
||||
|
||||
# Simple error-warnings system
|
||||
|
@ -66,7 +60,6 @@ def main():
|
|||
target = { 'toolchain': None, 'arch': None, 'section': None }
|
||||
use_custom = False
|
||||
converter_paths = []
|
||||
py = { 'enabled': False, 'compact': False }
|
||||
|
||||
# Parse command-line arguments
|
||||
|
||||
|
@ -76,7 +69,7 @@ def main():
|
|||
|
||||
try:
|
||||
longs = ["help", "output=", "toolchain=", "arch=", "section=", "fx",
|
||||
"cg", "converters=", "py", "py-compact"] + types.split()
|
||||
"cg", "converters="] + types.split()
|
||||
opts, args = getopt.gnu_getopt(sys.argv[1:], "hsbifo:", longs)
|
||||
except getopt.GetoptError as error:
|
||||
return err(error)
|
||||
|
@ -101,10 +94,6 @@ 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:]
|
||||
|
@ -155,7 +144,6 @@ 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__":
|
||||
|
|
200
fxconv/fxconv.py
200
fxconv/fxconv.py
|
@ -15,7 +15,7 @@ __all__ = [
|
|||
# Color names
|
||||
"FX_BLACK", "FX_DARK", "FX_LIGHT", "FX_WHITE", "FX_ALPHA",
|
||||
# Conversion mechanisms
|
||||
"ObjectData", "u8", "u16", "u32", "i8", "i16", "i32", "ref", "sym",
|
||||
"ObjectData", "u8", "u16", "u32", "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,40 +167,21 @@ FX_CHARSETS = {
|
|||
# Conversion mechanisms
|
||||
#
|
||||
|
||||
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 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 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):
|
||||
def ref(base, offset=None, padding=None, prefix_underscore=True):
|
||||
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, align or 4)
|
||||
return Ref("bytes", base)
|
||||
|
||||
elif isinstance(base, str):
|
||||
if padding is not None:
|
||||
|
@ -210,24 +191,19 @@ def ref(base, offset=None, padding=None, prefix_underscore=True, align=None):
|
|||
if offset is not None:
|
||||
offset = int(offset)
|
||||
base = f"{base} + {offset}"
|
||||
return Ref("name", base, align or 4)
|
||||
return Ref("name", base)
|
||||
|
||||
elif isinstance(base, ObjectData):
|
||||
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)
|
||||
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)
|
||||
|
||||
else:
|
||||
raise FxconvError(f"invalid type {type(base)} for ref()")
|
||||
|
||||
def ptr(*args, **kwargs):
|
||||
return ref(*args, **kwargs)
|
||||
def ptr(base):
|
||||
return ref(base)
|
||||
|
||||
def chars(text, length, require_final_nul=True):
|
||||
btext = bytes(text, 'utf-8')
|
||||
|
@ -245,7 +221,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", "align"])
|
||||
Ref = collections.namedtuple("Ref", ["kind", "target"])
|
||||
|
||||
Sym = collections.namedtuple("Sym", ["name"])
|
||||
|
||||
|
@ -274,7 +250,7 @@ class ObjectData:
|
|||
elif isinstance(other, Sym):
|
||||
self.inner.append(other)
|
||||
elif isinstance(other, ObjectData):
|
||||
self.inner += other.inner
|
||||
self.inner.append(other)
|
||||
return self
|
||||
|
||||
@staticmethod
|
||||
|
@ -297,23 +273,34 @@ class ObjectData:
|
|||
return padding
|
||||
|
||||
def link(self, symbol):
|
||||
inner = self.inner
|
||||
inner = []
|
||||
outer = []
|
||||
elements = []
|
||||
size = sum(self.element_size(el) for el in inner)
|
||||
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)
|
||||
|
||||
# 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":
|
||||
size += self.align(size, el.align, outer)
|
||||
elements.append(Ref("name", f"{symbol} + {size}", None))
|
||||
elements.append(Ref("name", f"{symbol} + {size}"))
|
||||
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}", None))
|
||||
elements.append(Ref("name", f"{symbol} + {size}"))
|
||||
code, code_size = el.target.link(f"{symbol} + {size}")
|
||||
outer.append((code, code_size))
|
||||
size += code_size
|
||||
|
@ -513,16 +500,8 @@ def convert_bopti_fx(input, params):
|
|||
data[n] = layer[4 * longword + i]
|
||||
n += 1
|
||||
|
||||
if "py" in params and 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
|
||||
o = ObjectData()
|
||||
o += header
|
||||
o += ptr(data)
|
||||
return o
|
||||
return header + data
|
||||
|
||||
def _image_project(img, f):
|
||||
# New width and height
|
||||
|
@ -602,17 +581,6 @@ def convert_image_cg(input, params):
|
|||
|
||||
data, stride, palette, color_count = image_encode(img, format)
|
||||
|
||||
if "py" in params and 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
|
||||
|
@ -807,19 +775,6 @@ def convert_topti(input, params):
|
|||
# Object file generation
|
||||
#---
|
||||
|
||||
if "py" in params and 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()
|
||||
|
@ -1184,14 +1139,8 @@ 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 "py" in params and 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
|
||||
else:
|
||||
elf(o, output, "_" + params["name"], **target)
|
||||
elf(o, output, "_" + params["name"], **target)
|
||||
|
||||
def elf(data, output, symbol, toolchain=None, arch=None, section=None,
|
||||
assembly=None):
|
||||
|
@ -1205,9 +1154,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:
|
||||
|
||||
|
@ -1237,14 +1186,6 @@ 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:
|
||||
|
@ -1265,9 +1206,16 @@ def elf(data, output, symbol, toolchain=None, arch=None, section=None,
|
|||
raise FxconvError(f"non-trivial architecture for {toolchain} must be "+
|
||||
"specified")
|
||||
|
||||
if assembly:
|
||||
sec = ".section " + section.split(",",1)[0]
|
||||
assembly = sec + "\n" + assembly
|
||||
# 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 data is None and assembly is None:
|
||||
raise FxconvError("elf() but no data and no assembly")
|
||||
|
@ -1327,52 +1275,6 @@ 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
|
||||
#
|
||||
|
|
|
@ -28,8 +28,7 @@ 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,
|
||||
char *outfolder);
|
||||
int main_send(struct fxlink_filter *filter, delay_t *delay, char **files);
|
||||
|
||||
/* Main function for -i */
|
||||
int main_interactive(struct fxlink_filter *filter, delay_t *delay,
|
||||
|
|
|
@ -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>... [--folder=OUTFOLDER] [General options]\n"
|
||||
" %1$s -s <FILES>... [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,7 +47,6 @@ 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"
|
||||
|
@ -68,7 +67,6 @@ 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;
|
||||
|
@ -79,7 +77,7 @@ int main(int argc, char **argv)
|
|||
// Command-line argument parsing
|
||||
//---
|
||||
|
||||
enum { LIBUSB_LOG=1, LOG_TO_FILE=2, OUT_FOLDER=3 };
|
||||
enum { LIBUSB_LOG=1, LOG_TO_FILE=2 };
|
||||
const struct option longs[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "list", no_argument, NULL, 'l' },
|
||||
|
@ -92,7 +90,6 @@ 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' },
|
||||
|
@ -168,9 +165,6 @@ int main(int argc, char **argv)
|
|||
case 'f':
|
||||
filter = fxlink_filter_parse(optarg);
|
||||
break;
|
||||
case OUT_FOLDER:
|
||||
outfolder = strdup(optarg);
|
||||
break;
|
||||
case '?':
|
||||
error = 1;
|
||||
}
|
||||
|
@ -225,7 +219,7 @@ int main(int argc, char **argv)
|
|||
}
|
||||
else if(mode == 's') {
|
||||
#ifndef FXLINK_DISABLE_UDISKS2
|
||||
rc = main_send(filter, &delay, argv + optind, outfolder);
|
||||
rc = main_send(filter, &delay, argv + optind);
|
||||
#else
|
||||
rc = elog("this fxlink was built without UDisks2; -s is disabled");
|
||||
#endif
|
||||
|
|
|
@ -44,13 +44,11 @@ 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");
|
||||
}
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
int main_send(struct fxlink_filter *filter, delay_t *delay, char **files,
|
||||
char *outfolder)
|
||||
int main_send(struct fxlink_filter *filter, delay_t *delay, char **files)
|
||||
{
|
||||
fxlink_filter_clean_udisks2(filter);
|
||||
GError *error = NULL;
|
||||
|
@ -54,14 +53,10 @@ int main_send(struct fxlink_filter *filter, delay_t *delay, char **files,
|
|||
printf("Mounted %s to %s.\n", dev, folder);
|
||||
}
|
||||
else {
|
||||
folder = mount_points[0];
|
||||
folder = strdup(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++;
|
||||
|
@ -74,7 +69,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] = outpath;
|
||||
argv[file_count+1] = folder;
|
||||
argv[file_count+2] = NULL;
|
||||
|
||||
/* Print command */
|
||||
|
@ -102,9 +97,6 @@ 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);
|
||||
|
@ -121,6 +113,7 @@ 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);
|
||||
|
|
|
@ -46,31 +46,18 @@ 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 scale)
|
||||
int height)
|
||||
{
|
||||
/* Little endian setup for RGB */
|
||||
SDL_Surface *s = SDL_CreateRGBSurface(0, width*scale, height*scale, 24,
|
||||
SDL_Surface *s = SDL_CreateRGBSurface(0, width, height, 24,
|
||||
0x000000ff, 0x0000ff00, 0x0000ff00, 0);
|
||||
if(!s) {
|
||||
elog("cannot create surface for image\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < height; i++)
|
||||
memcpy(s->pixels + i * s->pitch, RGB888_rows[i], width * 3);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -81,14 +68,11 @@ void fxlink_sdl2_display_raw(struct fxlink_message_image_raw const *raw)
|
|||
return;
|
||||
|
||||
int current_w, current_h;
|
||||
int scale = 1;
|
||||
|
||||
SDL_GetWindowSize(window, ¤t_w, ¤t_h);
|
||||
if(current_w != raw->width * scale || current_h != raw->height * scale)
|
||||
SDL_SetWindowSize(window, raw->width * scale, raw->height * scale);
|
||||
if(current_w != raw->width || current_h != raw->height)
|
||||
SDL_SetWindowSize(window, raw->width, raw->height);
|
||||
|
||||
SDL_Surface *src =
|
||||
surface_for_image(raw->data, raw->width, raw->height, scale);
|
||||
SDL_Surface *src = surface_for_image(raw->data, raw->width, raw->height);
|
||||
if(!src)
|
||||
return;
|
||||
|
||||
|
|
|
@ -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;
|
||||
while((msg = fxlink_device_finish_bulk_IN(fdev))) {
|
||||
struct fxlink_message *msg = fxlink_device_finish_bulk_IN(fdev);
|
||||
if(msg) {
|
||||
fxlink_interactive_handle_message(msg);
|
||||
fxlink_message_free(msg, true);
|
||||
fxlink_device_start_bulk_IN(fdev);
|
||||
|
|
|
@ -29,7 +29,7 @@ set(ASSETS_cg
|
|||
fxconv_declare_assets(${ASSETS} ${ASSETS_fx} ${ASSETS_cg} WITH_METADATA)
|
||||
|
||||
add_executable(myaddin ${SOURCES} ${ASSETS} ${ASSETS_${FXSDK_PLATFORM}})
|
||||
target_compile_options(myaddin PRIVATE -Wall -Wextra -Os -g)
|
||||
target_compile_options(myaddin PRIVATE -Wall -Wextra -Os)
|
||||
target_link_libraries(myaddin Gint::Gint)
|
||||
|
||||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
|
||||
|
@ -38,7 +38,4 @@ 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()
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
/build-fx
|
||||
/build-cg
|
||||
/build-cg-push
|
||||
/build-fxg3a
|
||||
/*.g1a
|
||||
/*.g3a
|
||||
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
"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
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
# 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)
|
|
@ -21,10 +21,6 @@ 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)
|
||||
|
|
|
@ -41,21 +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,
|
||||
call fxlink directly instead of this command.
|
||||
|
||||
${R}fxsdk${n} ${R}gdb${n} [${g}<OPTIONS>${n}...] [${R}--${n} ${g}<GDB OPTIONS>${n}...]
|
||||
Starts the GDB bridge and GDB itself. In general, you'll want at least one
|
||||
GDB option--the path to the executable file to debug.
|
||||
With --bridge-only, don't start GDB and instead enable bridge logs.
|
||||
|
||||
${R}fxsdk${n} ${R}path${n} (${R}sysroot${n}|${R}include${n}|${R}lib${n})
|
||||
Prints commonly-used paths in the SuperH sysroot:
|
||||
${R}sysroot${n} The root folder of the SuperH toolchain and libraries
|
||||
|
@ -110,7 +100,7 @@ fxsdk_new_project() {
|
|||
|
||||
# Copy initial files to project folder
|
||||
assets="$PREFIX/share/fxsdk/assets"
|
||||
mkdir -p "$1"/{,src,assets-fx,assets-cg,.vscode}
|
||||
mkdir -p "$1"/{,src,assets-fx,assets-cg}
|
||||
|
||||
case "$generator" in
|
||||
"Makefile")
|
||||
|
@ -135,11 +125,9 @@ 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() {
|
||||
|
@ -150,7 +138,7 @@ fxsdk_load_config() {
|
|||
|
||||
|
||||
fxsdk_build() {
|
||||
[[ ! -e build-fx && ! -e build-cg && ! -e build-fxg3a ]]
|
||||
[[ ! -e build-fx && ! -e build-cg ]]
|
||||
none_exists=$?
|
||||
|
||||
if [[ -e build-fx || $none_exists == 0 ]]; then
|
||||
|
@ -162,11 +150,6 @@ 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() {
|
||||
|
@ -178,9 +161,6 @@ 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"
|
||||
|
@ -242,20 +222,16 @@ fxsdk_build_in() {
|
|||
}
|
||||
|
||||
fxsdk_send() {
|
||||
if [[ -e "build-fx" && ! -e "build-cg" && ! -e "build-fxg3a" ]]; then
|
||||
if [[ -e "build-fx" && ! -e "build-cg" ]]; then
|
||||
fxsdk_send_fx
|
||||
fi
|
||||
|
||||
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
|
||||
if [[ -e "build-cg" && ! -e "build-fx" ]]; 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() {
|
||||
|
@ -291,10 +267,6 @@ fxsdk_send_cg-push() {
|
|||
fxlink -pw ${bin_files}
|
||||
}
|
||||
|
||||
fxsdk_send_fxg3a() {
|
||||
fxsdk_send_cg "$@"
|
||||
}
|
||||
|
||||
fxsdk_path() {
|
||||
case "$1" in
|
||||
"sysroot")
|
||||
|
@ -328,8 +300,6 @@ 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")
|
||||
|
@ -340,8 +310,6 @@ case "$1" in
|
|||
fxsdk_send_cg;;
|
||||
|
||||
# Utilities
|
||||
"gdb")
|
||||
fxsdk-gdb-bridge "${@:2}";;
|
||||
"path")
|
||||
fxsdk_path "${@:2}";;
|
||||
|
||||
|
|
|
@ -1,368 +0,0 @@
|
|||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
#include <fxlink/devices.h>
|
||||
#include <fxlink/filter.h>
|
||||
#include <fxlink/logging.h>
|
||||
|
||||
/* Establish a connection to the calculator. If nostart is set, assume the
|
||||
calculator is ready immediately; otherwise, wait for a message of type
|
||||
gdb:start. The latter can be useful is the calculator is using USB prior to
|
||||
starting a debugging session. */
|
||||
static struct fxlink_device *setup_calc(libusb_context *context, bool nostart)
|
||||
{
|
||||
struct fxlink_filter filter = { 0 };
|
||||
filter.intf_fxlink = true;
|
||||
fxlink_filter_clean_libusb(&filter);
|
||||
|
||||
hlog("calculators");
|
||||
log_("waiting for calculator to connect...\n");
|
||||
delay_t delay = delay_infinite();
|
||||
struct fxlink_device *fdev =
|
||||
fxlink_device_find_wait(context, &filter, &delay);
|
||||
|
||||
if(!fdev) {
|
||||
elog("unable to open calculator\n");
|
||||
return NULL;
|
||||
}
|
||||
if(!fxlink_device_claim_fxlink(fdev)) {
|
||||
elog("unable to claim fxlink interface\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fxlink_device_start_bulk_IN(fdev);
|
||||
|
||||
if(nostart)
|
||||
return fdev;
|
||||
|
||||
hlog("gdb");
|
||||
log_("waiting for gdb init message...\n");
|
||||
while(true) {
|
||||
libusb_handle_events(context);
|
||||
|
||||
struct fxlink_message *msg = fxlink_device_finish_bulk_IN(fdev);
|
||||
if(!msg)
|
||||
continue;
|
||||
hlog("gdb");
|
||||
if(fxlink_message_is_apptype(msg, "gdb", "start")) {
|
||||
log_("got start message\n");
|
||||
break;
|
||||
}
|
||||
wlog("dropped a message of type %.16s:%.16s\n",
|
||||
msg->application, msg->type);
|
||||
|
||||
fxlink_device_start_bulk_IN(fdev);
|
||||
}
|
||||
|
||||
return fdev;
|
||||
}
|
||||
|
||||
static int setup_socket(char const *listen_path)
|
||||
{
|
||||
int listen_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if(listen_socket < 0) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unlink(listen_path);
|
||||
|
||||
struct sockaddr_un listen_address = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = {0},
|
||||
};
|
||||
strncpy(listen_address.sun_path, listen_path,
|
||||
sizeof(listen_address.sun_path) - 1);
|
||||
if(bind(listen_socket, (struct sockaddr *)&listen_address, sizeof(listen_address)) < 0) {
|
||||
close(listen_socket);
|
||||
perror("bind");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(listen(listen_socket, 1024) < 0) {
|
||||
close(listen_socket);
|
||||
perror("listen");
|
||||
return -1;
|
||||
}
|
||||
|
||||
hlog("socket");
|
||||
log_("waiting for client on \"%s\"...\n", listen_address.sun_path);
|
||||
|
||||
return listen_socket;
|
||||
}
|
||||
|
||||
static int volatile gdb_terminated_flag = 0;
|
||||
static int volatile interrupted_flag = 0;
|
||||
|
||||
void SIGINT_SIGCHLD_handler(int signal)
|
||||
{
|
||||
if(signal == SIGCHLD)
|
||||
gdb_terminated_flag = 1;
|
||||
else
|
||||
interrupted_flag = 1;
|
||||
}
|
||||
|
||||
static pid_t fork_gdb(char **user_argv, char const *socket_path)
|
||||
{
|
||||
int n = 0;
|
||||
while(user_argv[n])
|
||||
n++;
|
||||
|
||||
char target_command[256];
|
||||
sprintf(target_command, "target remote %s", socket_path);
|
||||
|
||||
char **argv = malloc((n + 7) * sizeof *argv);
|
||||
argv[0] = "sh-elf-gdb";
|
||||
argv[1] = "-q";
|
||||
argv[2] = "-ex";
|
||||
argv[3] = "set architecture sh4al-dsp";
|
||||
argv[4] = "-ex";
|
||||
argv[5] = target_command;
|
||||
memcpy(argv+6, user_argv, (n+1) * sizeof *argv);
|
||||
|
||||
struct sigaction action = {
|
||||
.sa_handler = SIGINT_SIGCHLD_handler,
|
||||
};
|
||||
sigaction(SIGCHLD, &action, NULL);
|
||||
|
||||
pid_t pid = fork();
|
||||
|
||||
/* Child - execvp() only returns if there is an error */
|
||||
if(pid == 0) {
|
||||
execvp("sh-elf-gdb", argv);
|
||||
perror("execvp");
|
||||
exit(1);
|
||||
}
|
||||
/* Parent */
|
||||
if(pid == -1)
|
||||
perror("fork");
|
||||
return pid;
|
||||
}
|
||||
|
||||
static int accept_gdb(int listen_socket)
|
||||
{
|
||||
int client_socket = accept(listen_socket, NULL, NULL);
|
||||
if(client_socket < 0) {
|
||||
close(listen_socket);
|
||||
perror("accept");
|
||||
return -1;
|
||||
}
|
||||
close(listen_socket);
|
||||
return client_socket;
|
||||
}
|
||||
|
||||
struct options {
|
||||
bool bridge_only;
|
||||
bool log_packets;
|
||||
};
|
||||
|
||||
/* Parse options, returns positional arguments to forward to gdb. */
|
||||
static char **parse_argv(int argc, char **argv, struct options *opts)
|
||||
{
|
||||
int bridge_only = 0;
|
||||
int log_packets = 0;
|
||||
struct option longs[] = {
|
||||
{ "bridge-only", no_argument, &bridge_only, 1 },
|
||||
{ "log-packets", no_argument, &log_packets, 1 },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
getopt_long(argc, argv, "", longs, NULL);
|
||||
|
||||
opts->bridge_only = (bridge_only != 0);
|
||||
opts->log_packets = (log_packets != 0);
|
||||
return argv + optind + (argv[optind] && !strcmp(argv[optind], "--"));
|
||||
}
|
||||
|
||||
static void noop_log_handler(int display_fmt, char const *str)
|
||||
{
|
||||
(void)display_fmt;
|
||||
(void)str;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
libusb_context *context = NULL;
|
||||
struct fxlink_device *fdev = NULL;
|
||||
struct fxlink_pollfds fxlink_polled_fds = { 0 };
|
||||
struct fxlink_device_list device_list = { 0 };
|
||||
char socket_path[256] = { 0 };
|
||||
char log_path[256] = { 0 };
|
||||
FILE *log_fp = NULL;
|
||||
pid_t gdb_pid = -1;
|
||||
int ret = 1;
|
||||
|
||||
struct options opts;
|
||||
char **gdb_argv = parse_argv(argc, argv, &opts);
|
||||
|
||||
int err = libusb_init(&context);
|
||||
if(err != 0) {
|
||||
elog_libusb(err, "libusb_init");
|
||||
goto end;
|
||||
}
|
||||
bool nostart = true;
|
||||
fdev = setup_calc(context, nostart);
|
||||
if(!fdev)
|
||||
goto end;
|
||||
|
||||
sprintf(socket_path, "/tmp/fxsdk-gdb-bridge-%03d-%03d.socket",
|
||||
fdev->busNumber, fdev->deviceAddress);
|
||||
sprintf(log_path, "/tmp/fxsdk-gdb-bridge-%03d-%03d.txt",
|
||||
fdev->busNumber, fdev->deviceAddress);
|
||||
|
||||
int listen_socket = setup_socket(socket_path);
|
||||
if(listen_socket < 0)
|
||||
goto end;
|
||||
|
||||
if(opts.log_packets) {
|
||||
log_fp = fopen(log_path, "a");
|
||||
if(!log_fp)
|
||||
perror("cannot open packet log file");
|
||||
else {
|
||||
setvbuf(log_fp, NULL, _IOLBF, 0);
|
||||
hlog("gdb");
|
||||
log_("writing packets to %s\n", log_path);
|
||||
}
|
||||
}
|
||||
|
||||
if(!opts.bridge_only) {
|
||||
gdb_pid = fork_gdb(gdb_argv, socket_path);
|
||||
if(gdb_pid == -1)
|
||||
goto end;
|
||||
/* Cut fxlink logs to not mix in with gdb's output */
|
||||
fxlink_log_set_handler(noop_log_handler);
|
||||
}
|
||||
|
||||
int client_socket = accept_gdb(listen_socket);
|
||||
if(client_socket < 0)
|
||||
goto end;
|
||||
|
||||
struct sigaction action = {
|
||||
.sa_handler = opts.bridge_only ? SIGINT_SIGCHLD_handler : SIG_IGN,
|
||||
};
|
||||
sigaction(SIGINT, &action, NULL);
|
||||
|
||||
/* Track libusb fds as an indirect way to watch the calculator itself */
|
||||
struct pollfd client_socket_pollfd = {
|
||||
.fd = client_socket, .events = POLLIN
|
||||
};
|
||||
fxlink_pollfds_track(&fxlink_polled_fds, context);
|
||||
|
||||
/* Track devices to find out when our device is removed */
|
||||
fxlink_device_list_track(&device_list, context);
|
||||
|
||||
while(!interrupted_flag && !gdb_terminated_flag) {
|
||||
int err = fxlink_multipoll(-1,
|
||||
fxlink_polled_fds.fds, fxlink_polled_fds.count,
|
||||
&client_socket_pollfd, 1,
|
||||
NULL);
|
||||
if (err < 0) {
|
||||
perror("poll");
|
||||
goto end;
|
||||
}
|
||||
|
||||
struct timeval zero = {0};
|
||||
libusb_handle_events_timeout(context, &zero);
|
||||
|
||||
/* Check if our device is still in the list */
|
||||
fxlink_device_list_refresh(&device_list);
|
||||
bool still_there = false;
|
||||
for(int i = 0; i < device_list.count; i++)
|
||||
still_there = still_there || device_list.devices[i].dp == fdev->dp;
|
||||
if(!still_there) {
|
||||
hlog("gdb");
|
||||
log_("device disconnected\n");
|
||||
send(client_socket, "$W00#b7", 7, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
struct fxlink_message *msg;
|
||||
while((msg = fxlink_device_finish_bulk_IN(fdev)) != NULL) {
|
||||
if(fxlink_message_is_apptype(msg, "fxlink", "text")) {
|
||||
hlog("stub");
|
||||
log_("%.*s", msg->size, (char *)msg->data);
|
||||
fxlink_message_free(msg, true);
|
||||
fxlink_device_start_bulk_IN(fdev);
|
||||
continue;
|
||||
}
|
||||
if(!fxlink_message_is_apptype(msg, "gdb", "remote")) {
|
||||
hlog("gdb");
|
||||
wlog("dropped a message of type %.16s:%.16s\n",
|
||||
msg->application, msg->type);
|
||||
fxlink_message_free(msg, true);
|
||||
fxlink_device_start_bulk_IN(fdev);
|
||||
continue;
|
||||
}
|
||||
if(opts.bridge_only || log_fp) {
|
||||
fprintf(log_fp ? log_fp : stdout,
|
||||
"CAL> %.*s\n", msg->size, (char *)msg->data);
|
||||
}
|
||||
ssize_t send_ret = send(client_socket, msg->data, msg->size, 0);
|
||||
if(send_ret != msg->size) {
|
||||
perror("send");
|
||||
goto end;
|
||||
}
|
||||
fxlink_message_free(msg, true);
|
||||
|
||||
fxlink_device_start_bulk_IN(fdev);
|
||||
}
|
||||
|
||||
/* Don't start an OUT transfer if there's one in progress */
|
||||
if(fdev->comm->ftransfer_OUT)
|
||||
continue;
|
||||
|
||||
int bytes_socket;
|
||||
if(ioctl(client_socket, FIONREAD, &bytes_socket) < 0) {
|
||||
perror("ioctl");
|
||||
goto end;
|
||||
}
|
||||
if(bytes_socket > 0) {
|
||||
char buf[1024];
|
||||
ssize_t recv_ret = recv(client_socket, buf, sizeof(buf), 0);
|
||||
if(recv_ret < 0) {
|
||||
perror("recv");
|
||||
goto end;
|
||||
}
|
||||
if(opts.bridge_only || log_fp) {
|
||||
fprintf(log_fp ? log_fp : stdout,
|
||||
"GDB> %.*s\n", (int)recv_ret, buf);
|
||||
}
|
||||
if(!fxlink_device_start_bulk_OUT(fdev, "gdb", "remote", buf, recv_ret, false)) {
|
||||
elog("unable to start bulk OUT transfer\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
end:
|
||||
if(gdb_pid != -1)
|
||||
waitpid(gdb_pid, NULL, 0);
|
||||
if(log_fp)
|
||||
fclose(log_fp);
|
||||
if(socket_path[0])
|
||||
unlink(socket_path);
|
||||
if(device_list.ctx)
|
||||
fxlink_device_list_stop(&device_list);
|
||||
if(fxlink_polled_fds.ctx)
|
||||
fxlink_pollfds_stop(&fxlink_polled_fds);
|
||||
if(fdev) {
|
||||
fxlink_device_interrupt_transfers(fdev);
|
||||
fxlink_device_cleanup(fdev);
|
||||
free(fdev);
|
||||
}
|
||||
if(context)
|
||||
libusb_exit(context);
|
||||
return ret;
|
||||
}
|
|
@ -11,7 +11,6 @@
|
|||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
|
||||
char const *fmt_to_ANSI(int format)
|
||||
{
|
||||
|
@ -147,26 +146,3 @@ 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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
#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];
|
||||
|
@ -403,18 +401,17 @@ static void bulk_IN_callback(struct libusb_transfer *transfer)
|
|||
|
||||
switch(transfer->status) {
|
||||
case LIBUSB_TRANSFER_COMPLETED:
|
||||
// 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. */
|
||||
/* Start or continue an fxlink transfer. When finished, don't resubmit,
|
||||
instead have the user pick up the message before continuing. */
|
||||
resubmit = true;
|
||||
if(comm->ftransfer_IN)
|
||||
if(comm->ftransfer_IN) {
|
||||
fxlink_transfer_receive(comm->ftransfer_IN, data, data_size);
|
||||
else
|
||||
if(fxlink_transfer_complete(comm->ftransfer_IN))
|
||||
resubmit = false;
|
||||
}
|
||||
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 */
|
||||
|
@ -486,16 +483,18 @@ bool fxlink_device_start_bulk_IN(struct fxlink_device *fdev)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void fxlink_device_queue_finished_IN(struct fxlink_device *fdev)
|
||||
struct fxlink_message *fxlink_device_finish_bulk_IN(struct fxlink_device *fdev)
|
||||
{
|
||||
struct fxlink_comm *comm = fdev->comm;
|
||||
|
||||
if(!comm || !comm->ftransfer_IN)
|
||||
return;
|
||||
return NULL;
|
||||
if(!fxlink_transfer_complete(comm->ftransfer_IN))
|
||||
return NULL;
|
||||
|
||||
struct fxlink_message *msg = fxlink_transfer_finish_IN(comm->ftransfer_IN);
|
||||
if(!msg)
|
||||
return;
|
||||
return NULL;
|
||||
|
||||
int version_major = (msg->version >> 8) & 0xff;
|
||||
int version_minor = msg->version & 0xff;
|
||||
|
@ -506,28 +505,6 @@ static void fxlink_device_queue_finished_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;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <poll.h>
|
||||
|
||||
static inline int min(int x, int y)
|
||||
|
@ -91,9 +90,6 @@ 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
|
||||
//---
|
||||
|
|
|
@ -167,10 +167,6 @@ 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 */
|
||||
|
@ -188,11 +184,8 @@ struct fxlink_comm {
|
|||
struct fxlink_transfer *ftransfer_IN;
|
||||
/* Cancellation flag */
|
||||
bool cancelled_IN;
|
||||
/* Completed input message queue */
|
||||
struct {
|
||||
struct fxlink_message *messages[FXLINK_DEVICE_IN_QUEUE_SIZE];
|
||||
int size;
|
||||
} queue_IN;
|
||||
/* Completed transfer objects (to be checked by user every frame) */
|
||||
struct fxlink_message *message_IN;
|
||||
|
||||
/* Current OUT transfer */
|
||||
struct libusb_transfer *tr_bulk_OUT;
|
||||
|
@ -220,12 +213,10 @@ 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);
|
||||
|
||||
/* 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. */
|
||||
/* 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. */
|
||||
struct fxlink_message *fxlink_device_finish_bulk_IN(
|
||||
struct fxlink_device *fdev);
|
||||
|
||||
|
|
Loading…
Reference in New Issue