cnCalc计算器论坛

 找回密码
 注册
搜索
查看: 535|回复: 13

[fx-9860/9750] 使用新标准的C++为fx计算器写程序(gint)

[复制链接]
发表于 2022-12-29 13:16:01 | 显示全部楼层 |阅读模式
上一次玩计算器还是在高中的时候,转眼间就已经快大学毕业了。
大学期间没怎么碰过,最近打算回坑重写一个以前的解谜游戏(在几度换电脑之后备份就失踪了,反而手稿还保存完好。。。),但又比较想用C++写,而论坛里的魔改版9860sdk也就支持到C++98。
以前刚入坑的时候也在论坛里获取了大量的资源和教程,也感谢以前的各位大佬,而我几乎没发过帖子,所以打算开个坑搬运一下外网的教程,正好自己也在学习。


如果你符合以下几点,可以考虑继续看下去。

1、很想用C++写程序,尤其是C++11及以后(毕竟C的话C99基本也是完全体了,老sdk足够用了,新版三代又支持python)
2、稍微会一点点Linux,或者愿意稍微学习一点点知识(一点点就够了,我也不会很多哈哈)


最初发现这个是源自Mike大佬的一篇帖子(PS.老版的stlport库也是他移植的,以前一直拿这个写程序,非常感谢)
GCC 交叉编译工具链制作 Add-in 教程
但当时的问题是没办法用C++标准库
但我这两年去翻外网原帖,发现一切都被完善了,这位外网大神甚至依照sh4自己写了一个内核叫gint。
所以下面的内容都将依照这位 "Lephenixnoir" 的帖子来写。




主要的几个原文链接:
Casio Forum - fxSDK, an alternative SDK to write add-ins by Lephenixnoir · Planet Casio (planet-casio.com)
Lephenixnoir/GiteaPC: Utility program to automatically install, build and update repositories from this Gitea forge. - GiteaPC - Forge de Planète Casio (planet-casio.com)
P7, for ironing transfers (planet-casio.com)
Forum Casio - Tutoriels d'utilisation de gint par Lephenixnoir · Planète Casio (planet-casio.com)
PS.有能力阅读的英文的其实可以直接看原帖,最好用一个带自动翻译的浏览器翻译成英文或中文即可(只是翻译成中文看着怪怪的)
但可能对于纯小白而言,虽然安装其实足够简单,但可能也有些障碍,毕竟我刚来论坛的时候连C是啥都不太知道,所以我还是会写的稍微详细一点。
本帖子适用于大部分fx计算器,包括9750/9860全系列(II 和 III)和CG系列


附图
微信图片_20221229130147.jpg 我的伊拉克战损版9750II

使用gint的优势:(摘自原网,带一点机翻)


所有图像和字体都自动从PNG转换,无需复制代码(通过fxconv)
详细的键盘控制,具有自定义的GetKey() 和 SDL 风格的事件系统
比卡西欧更广泛的 C 标准库(参见fxlibc),以及大多数C++库
很多方便的快捷方式,比如说显示变量的值:dprint(1,1,“x=%d”,x)
手动优化的超快速绘图、图像和文本功能,尤其是在 Graph 90+E 上
非常精确的计时器(60 ns / 30 μs,视情况而定,而不是操作系统的25 ms),对于游戏至关重要
通过USB实时(通过fxlink)捕获加载项的屏幕截图和视频


劣势:
必须得用Linux虚拟机来编译,而且我也没找到(大概没有)类似9860sdk的图形界面,也就是说没办法像原来那样在电脑上就能调试,设置断点之类的(毕竟GCC可以用gdb调试,g1a在电脑上也运行不了啊)
程序会相对比较大,因为附带了一个内核,如下的自动生成的示例程序都29K起步。。。
  1. #include <gint/display.h>
复制代码

屏幕截图 2022-12-29 131439.png



评分

参与人数 1金钱 +2 收起 理由
zyf722 + 2 优质内容

查看全部评分

 楼主| 发表于 2022-12-29 13:18:07 | 显示全部楼层
不是这代码则么就剩一行了啊。。算了无所谓
 楼主| 发表于 2022-12-29 13:42:47 | 显示全部楼层
1、安装Linux会用Linux的就直接跳过吧,班门弄斧了哈哈(比较老的可能不行,我以前用过Ubuntu16.04不能用)
首先你需要一个有别于Windows的操作系统来完成这件事情,那么最简单的方式就是做一个虚拟机,类似于一个Windows上的应用程序。
虚拟机程序和系统的选择都很多啊,linux系的系统有很多比如说Debian,Ubuntu等等,我用的是如下
VMware Workstation Player | VMware
Download Ubuntu Desktop | Download | Ubuntu

你也可以用WSL,微软专门为Windows设计的一个虚拟机,但我没有了解过
Mac OS也可以直接装,但步骤好像有所不同,见Lephenixnoir/GiteaPC: Utility program to automatically install, build and update repositories from this Gitea forge. - GiteaPC - Forge de Planète Casio (planet-casio.com)的 “Mac OS note”

下面大概说说如果你用VMware和Ubuntu该咋做。
首先肯定是下载安装VMware,然后下载Ubuntu镜像文件(我用的是20.04,最新的是22.04可以下最新的,找LTS版就可以),这会是一个.iso文件
然后打开你的VMware,创建虚拟机,镜像文件就选择你刚才的.iso即可。
几点注意事项:
屏幕截图 2022-12-29 133020.png
1.硬盘大小最好不低于20GB,内存至少3G朝上(2G编译GCC会失败,亲测),如果你的电脑只有8G内存那可以在所有都装完之后调成2G.
2.安装过程会有一个用户名和密码的阶段,用户名随便取,密码建议取一个简单短小的,因为你会无数次用到这个密码
屏幕截图 2022-12-29 133300.png
3.配置好之后,点击虚拟机设置,找到如图的共享文件夹,新建一个共享文件夹,这个文件夹可以在虚拟机和你的电脑之间传输数据,更方便的是,有这个文件夹之后你就可以直接在两侧进行任意地复制黏贴,文件和文本都可以
然后就播放虚拟机吧,会有一段漫长的时间安装系统,之后应该就能进入主界面了
然后找到设置(settings)
屏幕截图 2022-12-29 133700.png

地区与语言,选择Chinese即可
屏幕截图 2022-12-29 133721.png
ok基本就完事了
Linux的大部分操作都是在命令行下完成的,找到一个文件夹然后右键-在终端打开就可以打开命令行,之后的几乎所有操作都需要这个
大部分直接安装的过程不需要具体哪一个文件夹,但比如说之后在进行项目的编译时那就必须得再你自己选好的文件夹里进行操作
理论上可以使用cd命令到达所有文件夹,但不会的话也可以用图形界面操作
 楼主| 发表于 2022-12-29 14:07:05 | 显示全部楼层
2.安装fxsdk

进入正菜,这篇大部分就是翻译帖子了,gitea上的英文教程很详细
Lephenixnoir/GiteaPC: Utility program to automatically install, build and update repositories from this Gitea forge. - GiteaPC - Forge de Planète Casio (planet-casio.com)
最方便的方式就是用这位大佬做的工具GiteaPC
以下均默认Ubuntu,如果是其他的就自己看吧

首先安装GiteaPC
首先是依赖项,按照第一行运行
  1. # For users of Debian, Ubuntu, WSL on Microsft Windows, and other Debian-based:
  2. % sudo apt install curl git python3 build-essential pkg-config

  3. # For users of Arch Linux, Manjaro, and other Arch-based:
  4. % sudo pacman -S curl git python3 gcc make pkgconf
复制代码
sudo会要求密码,就是你安装时的设置的那个密码,相当于管理员命令
然后安装
  1. % curl "https://gitea.planet-casio.com/Lephenixnoir/GiteaPC/raw/branch/master/install.sh" -o /tmp/giteapc-install.sh && bash /tmp/giteapc-install.sh
复制代码
但y1s1,git经常挂掉,我也不知道怎么解决,我的方法是科学上网,但还得装linux版的梯子,应该是有不需要科学上网的方法的可以百度搜搜,反正如果提醒你失败很可能是这个原因。
然后会有如下消息
  1. <giteapc> In order to use programs installed by GiteaPC, you will need to add their
  2. <giteapc> install folder to your PATH. This can be done automatically when you log
  3. <giteapc> in by adding the following command to your startup file:
  4. <giteapc>
  5. <giteapc>   export PATH="$PATH:/home/el/.local/bin"
  6. <giteapc>
  7. <giteapc> -> Press Enter to add this command to /home/el/.profile, or
  8. <giteapc> -> Type another file name to add this command to, or
  9. <giteapc> -> Type "-" to skip setting the PATH entirely.
复制代码
回车即可
然后GiteaPC就装完了
接下来就是用它来装我们的fxsdk
首先还是依赖项,按照第二行来
  1. # For users of WSL on Microsoft Windows:
  2. % sudo apt install cmake python3-pil libusb-1.0-0-dev libsdl2-dev

  3. # For users of Debian, Ubuntu, and other Debian-based:
  4. % sudo apt install cmake python3-pil libusb-1.0-0-dev libsdl2-dev libudisks2-dev libglib2.0-dev

  5. # For users of Arch Linux, Manjaro, and other Arch-based:
  6. % sudo pacman -S cmake python-pillow libusb sdl2 udisks2
复制代码

然后编译gcc和binutils
  1. % giteapc install Lephenixnoir/fxsdk Lephenixnoir/sh-elf-binutils Lephenixnoir/sh-elf-gcc
复制代码
然后还要再来一遍,因为第一遍只安装了C的库,C++的库需要C的库来编译
  1. % giteapc install Lephenixnoir/OpenLibm Vhex-Kernel-Core/fxlibc Lephenixnoir/sh-elf-gcc
复制代码
最后是gint
  1. % giteapc install Lephenixnoir/gint Lephenixnoir/libprof
复制代码
大功告成。

不对,并没有哈哈。
如果你是GIII或者CG用户,那么到这里就结束了,如果或是老板的GII,那么还得安装p7,这好像是那个论坛别人写的为linux做的类似FA-124的东西,也就是说如果你不装的话那你还得把.G1A拷出来在用FA-124。。。那也太睿智了。
p7的网站:P7, for ironing transfers (planet-casio.com)
但是!
这个网站的github库是失效的,也就是你直接去用它上面的最简单的那几行字是行不通的,得用下面手动版的。。
首先安装libp7
  1. cd /tmp
  2. curl -o libp7-3.0.tar.gz https://p7.planet-casio.com/pub/libp7-3.0.tar.gz
  3. tar xzf libp7-3.0.tar.gz && cd libp7-3.0
  4. ./configure --udev && make && sudo make install
复制代码
然后是p7utils
  1. cd /tmp
  2. curl -o p7utils-3.0.tar.gz https://p7.planet-casio.com/pub/p7utils-3.0.tar.gz
  3. tar xzf p7utils-3.0.tar.gz && cd p7utils-3.0
  4. ./configure && make && sudo make install
复制代码
这下就ok了
PS.我第一遍没看着装的,结果第一个漏了 --udev,就导致我的东西会用不了报错
屏幕截图 2022-12-29 140345.png
我试图重新configure然后make,还是不行。。
如果你也碰到类似情况,直接加一个sudo即可
  1. sudo p7 send test.g1a
复制代码
但是fxsdk自带一个非常舒服的send命令就用不了。。就很烦,所以不要犯错哈哈。
发表于 2022-12-29 14:23:59 | 显示全部楼层
确实很方便,用 cmake 之类的工具,配合 CLion 之类的 IDE 爽得起飞
甚至可以用 Github Actions 搞持续集成
发表于 2022-12-29 14:25:24 | 显示全部楼层
设备权限问题应该可以通过配置 udev 规则解决
 楼主| 发表于 2022-12-29 14:25:35 | 显示全部楼层
本帖最后由 zmlalalala 于 2023-1-29 16:13 编辑

3.基本使用方法

讲一点基本操作
对于linux而言,这样就算安装完了,但可能会感觉很懵,毕竟没有任何地方能看得出来
其实很简单就可以上手,比如说我们创建一个写程序的文件夹,然后打开终端
屏幕截图 2022-12-29 141355.png
输入fxsdk,就能有所有的教程
屏幕截图 2022-12-29 141501.png
那么最基本的,创建一个新项目
  1. # fxsdk new test
  2. # cd test
复制代码
然后我们就能看到创建的test文件夹里,点进去

屏幕截图 2022-12-29 141728.png
src里是我们的源代码
assets里面是图片之类的,比如说图标assets-fx和assets-cg分别代表fx计算器和cg计算器的分别的内容
然后回到控制台,确保我们现在在test文件夹里,然后build即可
屏幕截图 2022-12-29 142004.png
同样,如果你是cg计算器,改成build-cg即可
然后插入你的计算器,会跳出一个弹窗,把它接入你的虚拟机
如果你一开始没有这么做,也可以在左上角”可移动设备“里找到它,并接入虚拟机
屏幕截图 2022-12-29 142355.png
然后send 即可
  1. fxsdk send-fx
复制代码
有一些参数调试需要编辑一些文档,比如说CMakeLists.txt和assets文件夹里的fxconv-matadata.txt,后者暂时用不到,后面讲图形的时候会再来说这个事情。
CMakeLists.txt如下图

屏幕截图 2023-01-19 152833.png

其中有几点要注意:
1、SOURCE那里,如果你有多个文件需要编译,必须把所有.c,.cpp都添加进去
2、assets同理,之后再说
3、最下面OUTPUT后面的文件名就是输出的文件名,可以随意更改
4、下面一行里的icon.png即为程序图标,既可以在这里改名字也可以就用这几个名字去替换assets里的文件,并且这个文件只能是png,不能是bmp,assets里的无所谓

基本这些就够了,如果想了解更多关于cmake的用法,可以参考这篇帖子:
Forum Casio - [Tutoriel] Compiler des add-ins avec CMake (fxSDK) par Lephenixnoir · Planète Casio (planet-casio.com)


 楼主| 发表于 2022-12-29 14:28:48 | 显示全部楼层
Myth 发表于 2022-12-29 14:25
设备权限问题应该可以通过配置 udev 规则解决

确实是的,但我重新configure make完了还是不行,我估计可能得先卸载之类的操作?
我其实不是太懂linux也就不想再去弄了
 楼主| 发表于 2022-12-29 14:40:30 | 显示全部楼层
本帖最后由 zmlalalala 于 2022-12-29 14:43 编辑

4.进一步使用gint

图形界面相关参考3里的那个帖子,但其实还有相当多的功能没有写出来,具体查看可以找这里(.local是个隐藏文件夹)
屏幕截图 2022-12-29 143159.png
这里有所有需要用到的头文件,然后每一个头文件几乎都有很详细的注释说明,所以参照这个基本就没问题,至于这里的其他的一些头文件具体的使用方式的详细说明,可能得过一段再来补充了吧,毕竟我也是刚考完研回来,刚装完了整个编译器也才初步开始看,之后可能我看一点会稍微发一点教程,先留一个大坑哈哈哈哈。


不过有一件事情非常想吐槽,我以前高中的时候写过一个需要大量读写文件的一个程序,当时老是出bug,就是文件会莫名发生奇妙的变化,我排查了一个星期才发现好像不是我的问题。。。
然后直到我今天看到了这一份文件的一点说明。。。文件:gint/bfile.h
屏幕截图 2022-12-29 144219.png
原来是这样啊。。。我服了哈哈哈哈(仅限于9860GII及以前的计算器,之后的III和CG系列都是新文件系统)

 楼主| 发表于 2022-12-29 14:58:39 | 显示全部楼层
帖子里可能会有错误,我不是计算机专业的,计算机的知识了解的也不多,如有任何问题欢迎大家指正

y1s1我感觉受众应该很少,毕竟愿意写C++或者熟悉linux的人应该也有能力看外网,而且也有能力自己看头文件里的注释,而如果只是想写点c或者python的话其实完全犯不上搞这个,所以这篇东西也基本就算记录一下我安装和学习的过程吧。
不过如果追求更多功能和性能的话可能也会用,可以显示四种灰度,显示图片也方便得多,而且还有一套比较完整的系统函数(之前用的syscall我感觉好复杂,都是些奇奇怪怪的固定地址哈哈)
 楼主| 发表于 2022-12-31 17:58:14 | 显示全部楼层
本帖最后由 zmlalalala 于 2022-12-31 18:05 编辑

开坑开坑

4.1文件系统
本文基本参考 gint/bfile.h

首先,如果你是GIII或者CG计算器,那么可以直接跳过这部分。按照文档说明,新版文件系统(文档中称之为FUGUE)应该是可以直接用标准的c的接口的(例如fopen等),所以会方便很多

  1. // Wherever Fugue is used, gint supports the Unix file API (open, read, write,
  2. // etc) and the C99 standard file API (fopen, fread, fwrite, etc), so there is
  3. // no need to call into BFile directly (you should still do a world switch
  4. // before using these functions).
复制代码



但是按照最后一行字,可能还是会需要调用switch函数(见下),我手头没新计算器没法测试哈哈,如果有人有测试结果欢迎补充


那么如果你是GII及以前,文档中称之为CASIOWIN系统,那么首先有几个注意事项,图片见9楼附图,大致来说几点:

1、文件创建之后就是定长,不能修改长度
2、文件初始值全为1,而write的操作就是把一部分1换成0,也就是说,你只能更改一次文档,不要试图修改同一位置两次
3、每次写入的长度必须为偶数,否则会有奇妙的事情发生
4、不能有二级目录,否则也会有奇妙的事情发生

按照我以前写程序的经验,如果你要经常对一个文件添加或读写的话,最好的方法其实是每次都先把文件内容全部读出来,然后在程序里操作,完事了之后再一起写回去(删除然后创建写入),这样会比较稳妥。。
然后的话,基本操作与9860sdk一致,建议参考“fx9860g_sdk_manuals”和diameter的精华帖(不会真的有人没看过吧)
fx-9860G SDK开发从入门到实践 - 第2页 - 卡西欧(CASIO)图形编程计算器 - cnCalc计算器论坛 - Powered by Discuz!
大致列一下.h里的对应的函数(error code太长了没列,自己去看文档吧)PS.文档里好多注释啊,这里为了省地方全删了

  1. int BFile_Remove(uint16_t const *path);

  2. #define BFile_File 1
  3. #define BFile_Folder 5

  4. int BFile_Create(uint16_t const *path, int type, int *size);

  5. #define BFile_ReadOnly 0x01
  6. #define BFile_WriteOnly 0x02
  7. #define BFile_ReadWrite (BFile_ReadOnly | BFile_WriteOnly)
  8. #define BFile_Share 0x80
  9. #define BFile_ReadShare (BFile_ReadOnly | BFile_Share)
  10. #define BFile_ReadWriteShare (BFile_ReadWrite | BFile_Share)

  11. int BFile_Open(uint16_t const *path, int mode);
  12. int BFile_Close(int fd);
  13. int BFile_Size(int fd);
  14. int BFile_Write(int fd, void const *data, int even_size);
  15. int BFile_Read(int handle, void *data, int size, int whence);
  16. int BFile_Seek(int fd, int offset);
  17. int BFile_GetPos(int fd);

  18. struct BFile_FileInfo
  19. {
  20. uint16_t index;
  21. uint16_t type;
  22. uint32_t file_size;
  23. /* Data size (file size minus the header) */
  24. uint32_t data_size;
  25. /* Is 0 if the file is complete */
  26. uint32_t property;
  27. /* Address of first fragment (do not use directly) */
  28. void *address;
  29. };

  30. #define BFile_Type_Directory 0x0000
  31. #define BFile_Type_File 0x0001
  32. #define BFile_Type_Addin 0x0002
  33. #define BFile_Type_Eact 0x0003
  34. #define BFile_Type_Language 0x0004
  35. #define BFile_Type_Bitmap 0x0005
  36. #define BFile_Type_MainMem 0x0006
  37. #define BFile_Type_Temp 0x0007
  38. #define BFile_Type_Dot 0x0008
  39. #define BFile_Type_DotDot 0x0009
  40. #define BFile_Type_Volume 0x000a
  41. #define BFile_Type_Archived 0x0041

  42. int BFile_FindFirst(uint16_t const *pattern, int *shandle, uint16_t *foundfile, struct BFile_FileInfo *fileinfo);
  43. int BFile_FindNext(int shandle, uint16_t *foundfile, struct BFile_FileInfo *fileinfo);
  44. int BFile_FindClose(int shandle);
复制代码


几点说明:
1、这里folder和file是作为参数的,在create里调用即可,并没有单独的两个函数
2、可以很方便地使用前缀u来生成FONTCHARACTOR(这个作者好像很讨厌这个东西,所以文中都是uint16_t),例如u"\\\\fls0\\bin.data"(这好像是gcc的功能),对的,所以我们并不需要char_to_font函数了
3、BFile,f是大写的。。。
4、Create函数里的size是一个指针。。。但是read和write都是int。。。

ok,然后就是switch的问题。整个文件功能好像是属于系统的功能,并不与gint兼容,所以调用前得先跳出去然后再调用
首先我们需要include <gint/gint.h>
在这里有一个函数
  1. int gint_world_switch(gint_call_t function);
复制代码
也就是说,我们要用这个来调用所有的BFile函数(别担心,几乎只有文件要用到这个。。。)
那么什么是gint_call_t呢?gint/defs/call.h有定义
  1. typedef struct {
  2. void *function;
  3.         gint_call_arg_t args[4];
  4. } gint_call_t;
复制代码
gint_call_arg_t是一个union,包含了所有可能调用的数据类型,这有一点原来那个syscall的味道
那我们用的时候怎么来创建这个呢?作者写了一个宏
  1. #define GINT_CALL(func, ...) \
  2.         ((gint_call_t){ .function = (void *)func, .args = { \
  3.                 __VA_OPT__(GINT_CALL_ARGS1(__VA_ARGS__)) \
  4.         }})
  5. #define GINT_CALL_ARGS1(a1, ...) \
  6.         GINT_CALL_ARG(a1), __VA_OPT__(GINT_CALL_ARGS2(__VA_ARGS__))
  7. #define GINT_CALL_ARGS2(a2, ...) \
  8.         GINT_CALL_ARG(a2), __VA_OPT__(GINT_CALL_ARGS3(__VA_ARGS__))
  9. #define GINT_CALL_ARGS3(a3, ...) \
  10.         GINT_CALL_ARG(a3), __VA_OPT__(GINT_CALL_ARGS4(__VA_ARGS__))
  11. #define GINT_CALL_ARGS4(a4, ...) \
  12.         ({ __VA_OPT__(_Static_assert(0, \
  13.                 "GINT_CALL: too many arguments (maximum 4)");) \
  14.         GINT_CALL_ARG(a4); })
复制代码
看着很复杂,其实用起来还好,下面是一个简单的例子

屏幕截图 2022-12-31 175123.png

就相当于用任何file函数都需要一个包装
可能也没什么简化的办法,除非你愿意为所有常用函数都写一个单独的版本。。。(毕竟大家参数都不一样)
文件系统就这样了。







 楼主| 发表于 2023-1-19 16:30:14 | 显示全部楼层
本帖最后由 zmlalalala 于 2023-1-29 18:20 编辑

4.2 图像
还是稍微写一点吧,把这个补全了。
同上,详细内容参考该帖子:
Casio Forum - Tutorials on using gint by Lephenixnoir · Planet Casio (planet-casio.com)

ok我们开始

首先我们用到的头文件就是gint/display.h,当然还有后缀fx和cg版本的里面定义了具体的颜色
里面的几乎所有函数:
  1. void dupdate(void);

  2. void dupdate_set_hook(gint_call_t function);
  3. gint_call_t dupdate_get_hook(void);


  4. void dclear(color_t color);
  5. void drect(int x1, int y1, int x2, int y2, int color);
  6. void drect_border(int x1, int y1, int x2, int y2,
  7.         int fill_color, int border_width, int border_color);
  8. void dpixel(int x, int y, int color);
  9. void dline(int x1, int y1, int x2, int y2, int color);
  10. void dhline(int y, int color);
  11. void dvline(int x, int color);

  12. font_t const *dfont(font_t const *font);
  13. font_t const *dfont_default(void);
  14. void dsize(char const *str, font_t const *font, int *w, int *h);
  15. void dnsize(char const *str, int size, font_t const *font, int *w, int *h);
  16. char const *drsize(char const *str, font_t const *font, int width, int *w);

  17. enum {
  18.         /* Horizontal settings: default in dtext() is DTEXT_LEFT */
  19.         DTEXT_LEFT   = 0,
  20.         DTEXT_CENTER = 1,
  21.         DTEXT_RIGHT  = 2,
  22.         /* Vertical settings: default in dtext() is DTEXT_TOP */
  23.         DTEXT_TOP    = 0,
  24.         DTEXT_MIDDLE = 1,
  25.         DTEXT_BOTTOM = 2,
  26. };

  27. void dtext_opt(int x, int y, int fg, int bg, int halign, int valign,
  28.         char const *str, int size);
  29. #define dtext_opt8(x,y,fg,bg,h,v,str,sz,...) dtext_opt(x,y,fg,bg,h,v,str,sz)
  30. #define dtext_opt(...) dtext_opt8(__VA_ARGS__, -1)
  31. void dtext(int x, int y, int fg, char const *str);
  32. void dprint_opt(int x, int y, int fg, int bg, int halign, int valign,
  33.         char const *format, ...);
  34. void dprint(int x, int y, int fg, char const *format, ...);



  35. void dimage(int x, int y, bopti_image_t const *image);

  36. enum {
  37.         /* No option */
  38.         DIMAGE_NONE = 0x00,
  39.         /* Disable clipping, ie. adjustments to the specified subrectangle and
  40.            screen location such that any part that overflows from the image or
  41.            the screen is ignored. Slightly faster. */
  42.         DIMAGE_NOCLIP = 0x01,
  43. };
  44. void dsubimage(int x, int y, bopti_image_t const *image, int left, int top,
  45.         int width, int height, int flags);
复制代码




一个一个来
1、dupdate
这个和putdisp_DD功能几乎一致
这里的话整个都没有直接打印到DD的功能,也就是说所有显示函数在用完之后都得update一次
否则屏幕上是没有图像的

2、set_hook
这两个是说,你可以搞一个函数,在每次刷新屏幕缓冲区之后都使用一次,比如说可以用下面的函数
  1. // gint/usb-ff-bulk.h
  2. void usb_fxlink_screenshot(bool onscreen);
复制代码
来做到每刷新一次都截一次屏幕

3、dclear,dline,drect。。
dclear 对全屏幕做一次染色, 这里color和下面所有的都一样,定义在了-fx和-cg文件中,最基本的有比如说C_WHITE, C_BLACK, C_INVERT
所以可以用它来清空屏幕,或者反转屏幕(C_INVERT),做到reverse的功效
甚至在灰度引擎开启的情况下还可以用C_LIGHTEN或者C_DARKEN

drect 是包含四周的正方形区域内做染色,而drect_border还能指定一个边框宽度,以及边框颜色
dpixel和dline就不说了,一个点一个线
dvline和dhline看可以直接画全屏的竖直线或水平线

4、font
font前面一堆函数暂时不讨论它,大致意思是你可以用你自己的图片来指定一个字体。。等会讨论图片的时候在说它
这里只讨论下面几个函数

dtext, dprint:前两个是像素坐标!128 * 64如果是9860,不是21 * 8哈,
第三个是字体颜色
然后如果你只是一段文字,那就dtext就好了,如果是格式化输出那就用print,从第四位开始和printf用法一致。

dtext_opt, dprint_opt:这俩是更细致的参数版本,fg是文字颜色,bg是底色
然后halign和valign,这俩是说你的坐标是在哪里的,比如说dtext默认你的x,y是你的左上角,但这里可以设置比如说向右上对齐,右下对齐等等
dtext还有一个size是最大的输出文字量,这个参数可以不加因为是新版本引入的,他为了兼容性默认设置成了-1

有一个要提的是dtext(.., .., C_BLACK, " ")并不能用白色覆盖,同样,如果你强制设置底色是白色,也不管用
换而言之,如果你要在一个地方显示变小的数字,你得用drect把底下清空了再写。。
这个和9860sdk里的print就不太一样

5、dimage
image怎么添加呢?之后再说,总而言之,不需要用一个转换器把它搞成二进制代码然后再copy进去了
我们可以简单的这样写
  1. extern bopti_image_t my_image;
复制代码
就非常爽
dimage很简单,就是xy处打印图像
dsubimage可以截取图像上的一小块打印,自己指定left和top(包含在内)也就是子图像的左上角和高度宽度,
这也就是说,如果需要在好几个图标之间切换,可以简单地拼成一张图,对齐,然后就可以用dsubimage很简单的打印了
最后一个参数是个优化参数,不太重要。

ok那么image怎么添加呢?
简而言之,我们只要把我们的图片放进assets文件夹里就好了,我以我的9860为例,我就只需要把我的图片放入assets-fx文件夹里
然后编辑fxconv-matadata.txt
往里面添加一行字
  1. my.png:
  2. type: bopti-image
  3. name: my_image
复制代码
就可以了,到时候链接的时候就会有一个小程序处理你的文件并且生成可执行文件,然后前面的extern就能找到这个了
那如果很多图片怎么办,这里也可以用正则表达式,比如说
  1. *.png:
  2. type: bopti-image
  3. name_regex: (.*)\.png img_\1
复制代码
这样子所有的png文件都会被自动的转换成img_...(文件名)的格式,当然你也可以把img_删掉。
注意不要加空格

然后提一嘴之前的font,我也不是太懂其实。原作者教程里举了个例子
gint-tuto-01-font.png
他做了这样一张png,然后在metadata里面加了这样几行字
  1. font_mystere.png: type: font
  2. charset: print
  3. grid.size: 5x7
  4. grid.padding: 1
  5. proportional:true
复制代码

然后你就可以用dfont(&font)来设置自己的字体了。。
grid.padding表示每个字符周围的边框大小,这里每个字符都是有一格边框分隔的
proportional表示显示的时候按每个字符大小来,大概是可以缩进?比如说I比W窄,那么显示的时候也是如此

这大概就是为什么他会有一堆dsize函数来确认对应的字符串的长度,比如说dsize("abcd", font, &w, &h)大概就会告诉你abcd在font字符下是多长多宽

更新:有一个很有用的东西,原作者提供了一个一部分Unicode的字符集,参考如下网址:
Lephenixnoir/unicode-fonts: General purposes Unicode fonts (fixed 5x7, proportional 8x9). - unicode-fonts - Forge de Planète Casio (planet-casio.com)
这里有非常多的奇怪的字符,可以很大程度替代原有的FONTCHARACTER,9860和cg-50的都有
使用方法点进去就能看到,下载对应的文件夹然后在assets里添几行,程序里再添几行就行了。

最后在提一嘴gray
有一个文件叫gint/dgray.h
  1. enum {
  2.         /* Start or stop the engine */
  3.         DGRAY_ON,
  4.         DGRAY_OFF,
  5.         /* Start or stop the engine, but remember previous state */
  6.         DGRAY_PUSH_ON,
  7.         DGRAY_PUSH_OFF,
  8.         /* Restore previous state remembered by DGRAY_PUSH_* */
  9.         DGRAY_POP,
  10. };

  11. int dgray(int mode);
复制代码
这个函数会在dupdate之后生效,也就是先dgray(DGRAY_ON),然后dupdate就有灰度效果了
可以用来画灰度图像
比如说如果在画图软件里面生成一个128*64的png,调成彩色,然后只用黑笔画,这时候图片其实是有灰度的,正常计算器是显示不出来的,但如果你把dgray给打开,就能了

图像就差不多这么多,基本只写了函数的解释,如果你想看一段很流畅的教程,建议去看原帖翻译或者是diameter的9860sdk教程
下个楼在写点getkey和time的就差不多了。
 楼主| 发表于 6 天前 | 显示全部楼层
本帖最后由 zmlalalala 于 2023-1-29 18:27 编辑

4.3 一些其他的小工具


主要是以下几个
gint/keyboard.h
gint/clock.h
gint/timer.h
gint/rtc.h

先来个两个开胃菜:
clock:cpu时钟,这里最主要用的就是sleep, 它有两个版本
  1. void sleep_us(uint64_t delay_us);
  2. #define sleep_ms(delay_ms) sleep_us((delay_ms) * 1000ull)
复制代码
ms是毫秒, us是微秒

rtc:真实时钟,这个主要可以用来给srand采集种子,比如说
  1. srand((int)rtc_ticks());
复制代码
用里面的其他函数也可以


然后是比较复杂的key:
key的具体编号见“keycodes.h”
它这里会有好多偏底层的事件的函数,就不多说了
主要日常用的也就是getkey:
  1. typedef struct
  2. {
  3. uint time :16; /* Time of event, unique over short periods */

  4. uint :3; /* Reserved for future use */

  5. uint mod :1; /* Whether modifiers are used */
  6. uint shift :1; /* If mod=1, whether SHIFT was pressed */
  7. uint alpha :1; /* If mod=1, whether ALPHA was pressed */

  8. uint type :2; /* Type of key event */
  9. uint key :8; /* Hit key */

  10. } GPACKED(4) key_event_t;

  11. key_event_t getkey(void);
  12. key_event_t getkey_opt(int options, volatile int *timeout);
复制代码

key_event_t是它定义的一个结构,可以知道这个按键有没有shift alpha,时间,是不是有效按键(type,后面会说到)等等,最基本的就是key
所以一般来说可以这样写
  1. int key = getkey().key;
复制代码
getkey_opt提供了更多的选项
  1. enum {
  2.         /* Enable modifiers keys */
  3.         GETKEY_MOD_SHIFT   = 0x01,
  4.         GETKEY_MOD_ALPHA   = 0x02,
  5.         /* SHIFT + OPTN toggles backlight (requires GETKEY_MOD_SHIFT) */
  6.         GETKEY_BACKLIGHT   = 0x04,
  7.         /* MENU triggers a task switch and displays the main menu */
  8.         GETKEY_MENU        = 0x08,
  9.         /* Repeat arrow keys, or even all keys */
  10.         GETKEY_REP_ARROWS  = 0x10,
  11.         GETKEY_REP_ALL     = 0x20,
  12.         /* Enable custom repeat profiles; see getkey_set_repeat_profile() */
  13.         GETKEY_REP_PROFILE = 0x40,
  14.         /* Enable application shortcuts; see getkey_set_feature_function() */
  15.         GETKEY_FEATURES    = 0x80,

  16.         /* No modifiers */
  17.         GETKEY_NONE        = 0x00,
  18.         /* Default settings of getkey() */
  19.         GETKEY_DEFAULT     = 0xdf,
  20. };
复制代码
这是option里可以填的东西,从上往下比如说是不是允许组合shift/alpha,是不是允许背光和menu按钮生效,是不是允许方向键或者所有按键长按,是不是允许定制的延迟时间,是不是允许feature函数。
getkey默认除了所有按键长按以外都是开着的,你也可以用 | 符号自由组合
第二个选项涉及到延迟,之后讲到timer时候再说,类似于getkeywait的功能,不用的话填入NULL就可以了。
feature函数意思是类似capture之类的功能一样,是不是给某些键一个全局性质的功能,碰到了这个键就单独处理,具体功能自己看头文件吧。
然后如果想设置repeat time,也即长按按下去一个键之后过多少时间开始识别是长按,以及之后的时间间隔。
默认是400ms + 40ms,其实是有点快的,设置的话在gint/drivers/keydev.h里
  1. void keydev_set_standard_repeats(keydev_t *d, int first_us, int next_us);
复制代码
keydev_t那里带入keydev_std()即可
单位是微秒

最后是timer:
SH4计算器有3个TMU和6个ETMU,前者大约250ns的刷新率,后者是30微秒
  1. /* Timer selection; see timer_configure() */
  2. enum {
  3.         TIMER_ANY  = -1,
  4.         TIMER_TMU  = -2,
  5.         TIMER_ETMU = -3,
  6. };

  7. /* Return value for timer callbacks, indicating whether the timer should
  8.    continue running and fire again, or stop now */
  9. enum {
  10.         TIMER_CONTINUE = 0,
  11.         TIMER_STOP     = 1,
  12. };
复制代码
一般来说不在意的话直接用TIMER_ANY即可
流程是用timer_configure创建一个timer,然后timer_start,之后用完之后用stop把他停下来或者是callback停下来了(下面会说的)
注意!这个时候就不能再start了!这个timer已经被释放了,
如果是pause那么可以继续用start开始。
wait是等,知道timer结束。
除了configure里的timer以外,其他的参数均是configure的返回值,-1的话说明分配失败(一般不会)
configure里第一项表示哪种计时器,第二项表示时长,单位是微秒,第三项是一个函数,在计时器结束的时候会被调用,下面是一个例子
  1. static int callback_tick(volatile int *tick)
  2. {
  3.     *tick = 1;
  4.     return TIMER_CONTINUE;
  5. }

  6.     volatile int tick = 0;
  7.     int timer = timer_configure(TIMER_ANY, 800 * 1000, GINT_CALL(callback_tick, &tick));
  8.     timer_start(timer);
复制代码

这里接受一个函数和若干个变量的调用(你自己定),用GINT_CALL包装起来,然后返回一个int值
这里的含义就是,0.8秒的计时器,如果到时间了那么调用callback_tick函数,把tick改成1并且继续下一次计时循环,如果是TIMER_STOP那就结束计时器
注意,变量必须声明成volatile,否则优化上会出问题,这样子之后就可以写我们的getkey_opt了
  1. //接上文
  2. key_event_t key;
  3. key.key = 0;
  4. key = getkey_opt(GETKEY_DEFAULT, &tick);
  5. timer_stop(timer);
复制代码
getkey_opt函数接受一个整数指针作为参数,直到这个整数变为1之前都等待,如果变成1那么就直接返回,返回的key_event_t里的type就会是KEYEV_NONE,这两个函数是这样协同来实现getkeywait功能的

实际上也可以写一些更复杂的东西,比如说按照作者的教程,可以写一个比如说设置tick是25ms的引擎,每25ms刷新一下界面状态,检测输入按键,刷新屏幕,这样可以显示出流畅动画出来
但他用的函数是比较老版本的sleep,感觉timer_wait应该能替代,我还没去尝试,试完了再回来改。

那么教程就结束了,感谢观看。

 楼主| 发表于 6 天前 | 显示全部楼层
本帖最后由 zmlalalala 于 2023-1-29 18:26 编辑

最后传一个移植过去的中文显示吧

基于diameter的DL移植的
注意,如果要用这个库的话,必须在“CMakeLists.txt”里的target_compile_options这一行的括号里加一句-fexec-charset=gbk
比如说如下
  1. add_executable(myaddin ${SOURCES} ${ASSETS} ${ASSETS_${FXSDK_PLATFORM}})
  2. target_compile_options(myaddin PRIVATE -Wall -Wextra -Os -fexec-charset=gbk)
  3. target_link_libraries(myaddin Gint::Gint)
复制代码
因为linux用的utf-8编码,而HZK12.df用的是GBK,所以要加一个命令


HZK12.df

191.67 KB, 下载次数: 0

fxchlib.c

1.44 KB, 下载次数: 0

fxchlib.h

1.19 KB, 下载次数: 0

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|cnCalc计算器论坛

GMT+8, 2023-2-4 20:25 , Processed in 0.049175 second(s), 22 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表