在 Flipper Zero 上复刻拓麻歌子:Tama P1 模拟器从 ROM 到源码的完整指南
在 Flipper Zero 上复刻拓麻歌子Tama P1 模拟器从 ROM 到源码的完整指南【免费下载链接】FlipperPlayground (and dump) of stuff I make or modify for the Flipper Zero项目地址: https://gitcode.com/GitHub_Trending/fl/Flipper在一台口袋设备里复刻一台 32,768 Hz 的 4 位掌机这就是 Tama P1 模拟器干的事它把拓麻歌子初代的 ROM 从 SD 卡读进来模拟出那块 32×16 的液晶屏和蜂鸣器用 Flipper Zero 的三枚侧键完成喂食、清洁与游戏还自带存档。本文先拆原理再走部署最后落到排查手段。它是什么、由哪些件组成一句话定位这是一个编译成.fap的外部插件内核则是一个与平台无关的 TamaLIB 模拟库。整个目录 Applications/Official/source-OLDER/xMasterX/tama_p1 按谁负责什么分得很清楚文件 / 目录职责tama_p1.c应用主入口ROM 读取、事件循环、画面绘制、存档读写tama.h常量与结构体ROM/存档路径、缩放因子、TamaApp状态结构hal.c hal_types.hTamaLIB 的硬件抽象层在 STM32WB 上的落地实现tamalib/模拟内核cpu.c、hw.c、tamalib.c及头文件icons/ compiled/8 个 LCD 状态图标源图与编译产物assets_icons.happlication.fam构建元数据对照 application.fam 能确认几个影响部署的参数appid为TAMA_P1类型是EXTERNAL独立编成插件、从 SD 卡加载入口函数tama_p1_app依赖gui和storage两个系统服务应用主栈只有 1 KBCPU 步进跑在独立线程里发布到Games_Extra分类图标用tamaIcon.png。先把原理讲透模拟内核如何运转4 位 CPU 与内存模型把半字节内存打包进字节缓冲TamaLIB 是作者从零写成的 Tama P1 模拟库见 tamalib/README.md真机基于 Epson E0C6S46 4 位 MCU主频 32,768 Hz屏是 32×16 黑白点阵加 8 枚状态图标网上流传的 ROM 据称来自芯片裸片照片的光学识读。被模拟的 CPU 地址空间只有 4096 个 4 位单元cpu.h 里的MEMORY_SIZE 4096划分如下区域地址规模RAM0x000~0x27F640 个 4 位单元显示缓冲 10xE00~0xE4F80 个显示缓冲 20xE80~0xECF80 个I/O 寄存器0xF00~0xF7F128 个注意中间有大片地址根本不存在。为了不在 STM32WB 上白占 4 KB代码默认打开LOW_FOOTPRINT宏只把实际存在的 928 个半字节挤进一个 464 字节的缓冲区两个半字节塞进同一个u8_t读写全靠SET_RAM_MEMORY/GET_RAM_MEMORY这类宏做位运算。打个比方与其租一整层写字楼不如把四个工位拼成一张桌子。HAL 层如何接住宿主机TIM2 当模拟时钟源TamaLIB 本身不认识任何硬件它通过 hal.h 里的hal_t结构体把时钟、屏幕、声音、日志、halt 回调全部外包给宿主。本项目在 hal.c 里逐一填坑时钟直接用 STM32 硬件定时器 TIM2。初始化时Prescaler 999计数频率约为 64 kHzget_timestamp()就是读一下LL_TIM_GetCounter(TIM2)。sleep_until()是节奏关键——发现模拟时间落后目标时间戳时先放掉互斥锁、furi_delay_tick(1)让出 CPU再抢回锁继续跑否则模拟会越追越慢。屏幕set_lcd_matrix(x, y, val)把单个像素写进framebuffer[16]一行 32 像素恰好装进一个uint32_tset_lcd_icon(icon, val)写icons位图update_screen()留空因为真正的渲染交给 GUI 主循环。声音set_frequency把内核上报的频率除以 10.0 换算成实际值play_frequency走furi_hal_speaker_acquire/start/stop驱动内置蜂鸣器。halt模拟 CPU 停机时置位halted标志界面随即打出 Halted。还有一个容易被忽略的细节hal_types.h 里timestamp_t被注释为必须是无符号类型——按微秒计时时32 位无符号数大约 1 小时 11 分就自然回绕一次用有符号类型会让时间比较逻辑出错。32×16 画面怎么铺满屏幕两个线程怎么不打架主循环用 30 FPS 的周期定时器furi_kernel_get_tick_frequency() / 30不断投递EventTypeTick触发view_port_update()。绘制回调做的事很直接先抢互斥锁超时 25 tick抢不到就放弃这一帧然后把 32×16 像素矩阵按TAMA_SCREEN_SCALE_FACTOR 2放大、在画布中居中用canvas_draw_box逐点画出一个 64×32 的像素块再在矩阵上下两侧各排 4 枚 14×14 的状态图标按icons位图逐位决定显隐横向间距按矩阵宽度自动均分tama_p1.c 绘制分支。线程模型是一个名为 TamaLIB 的工作线程循环调用tamalib_step()推进 CPUGUI 线程负责输入与刷新两者共用一把递归互斥锁g_state_mutex保护帧缓冲事件则经过一个深度 8 的FuriMessageQueue传递EventTypeInput/EventTypeTick两类消息。之所以不用现成的tamalib_mainloop()而改用手动步进正是为了让 Flipper 的 GUI 事件循环掌握节奏。save.bin 存档结构存档落在/ext/tama_p1/save.bintama.h 的TAMA_SAVE_PATH由 tama_p1_save_state 写、tama_p1_load_state读二进制布局是顺序拼接的定长字段字段字节数说明Magic4固定TLST校验文件合法性Version1当前为 2不匹配直接放弃恢复PC2程序计数器高字节只取低 5 位掩码0x1FX / Y22通用寄存器高字节掩码0xFA / B / NP111累加器、B 寄存器各 4 位、页选择5 位SP / Flags11栈指针、标志寄存器tick_counter4时钟周期计数clk_timer_timestamp4时钟定时器时间戳prog_timer_timestamp4可编程定时器时间戳prog_timer_enabled / data / rld111定时器使能、数据、重载值call_depth4调用栈深度interrupts3×1616 个中断槽各存 factor/mask/triggeredRAM640每个半字节占 1 字节I/O1280xF00~0xF7F的全部 I/O 半字节加载时若 Magic 或版本不对只打FATAL错误日志、跳过恢复恢复完 RAM 与 I/O 后调tamalib_refresh_hw()让显示和蜂鸣器状态重新同步——所以关掉再开宠物接着上次活。从零到能玩部署全流程第一步备料ROM 准备与字节序重排 在 microSD 卡根目录建tama_p1文件夹放入名为rom.bin的 ROMREADME 提到网传资源里有个叫a的文件改名即可。路径常量TAMA_ROM_PATH即/ext/tama_p1/rom.bin。ROM 读入内存后并不是直接可用tama_p1_init 里紧接着做了一次字节序重排// Reorder endianess of ROM for(size_t i 0; i fi.size; i 2) { uint8_t b ctx-rom[i]; ctx-rom[i] ctx-rom[i 1]; ctx-rom[i 1] b 0xF; }原因是 P1 的程序存储按半字节组织每对相邻字节把后一个挪到前面、前一个只留低 4 位才还原出内核期望的顺序。完成后以tamalib_init((u12_t*)ctx-rom, NULL, 64000)初始化参数 64000 决定了时间戳的刻度。文件不存在时屏幕会显示 No ROM运行中 CPU 停机则显示 Halted。第二步编译fbt 一键编译部署 图标资源把整个tama_p1文件夹放到固件源码树的applications/plugins/tama_p1位置然后在仓库里执行./fbt launch_app APPSRCapplications/plugins/tama_p1launch_app目标会一次完成编译、串口部署并立即运行。原文档里APPSRC用的是 Windows 反斜杠写法在 Linux/macOS 的 Shell 中要换成斜杠如上面所示。应用还引用了 8 枚状态图标与启动图标需要先跑资源脚本把 PNG 编成 C 头文件scripts/assets.py icons applications/tama_p1/icons applications/tama_p1/compiled产物是compiled/assets_icons.h声明了I_icon_0~I_icon_7。 若编译时因未使用参数告警被当成错误按 README 的提示在site_cons/cc.scons的CCFLAGS里追加-Wno-unused-parameter即可压掉 TamaLIB 带来的该类告警。第三步上机运行后观察屏幕出现放大的宠物画面即成功出现 No ROM 检查 SD 卡路径与文件名出现 Halted 说明 ROM 内容或字节序有问题。怎么操作、怎么存档基础三键对应关系对照 tama_p1.c 输入处理Flipper 按键实际动作左键拓麻歌子 A 键OKB 键右键C 键长按返回保存并退出上键短按静音——同时按下 A 与 C下键短按/ 返回短按立即写一次存档最后两行是文档没细说、但代码里确实存在的行为InputKeyUp会同时向内核上报BTN_LEFT与BTN_RIGHT的按下/释放InputKeyDown短按与返回短按都会触发tama_p1_save_state()。至于能不能存档README 里自相矛盾正文写着目前不支持保存退出即重置下方 Implemented 清单却又列着保存/加载模拟器状态/ext/tama_p1/save.bin。对照源码可以下结论——前一句是过时信息保存/加载函数完整实现且已接入启动与退出路径存档功能实际可用。卡住了怎么查串口日志是最快的定位手段。参考 README 的调试一节用 FlipperScripts 里的serial_logger.py监听python serial_logger.py也可以编译部署和挂日志一起执行两条命令用分号串起来./fbt launch_app APPSRCapplications/plugins/tama_p1; python serial_logger.py日志分两个 TagTamaP1覆盖 ROM 加载、存档读写与输入事件TamaLIB覆盖内核层错误与状态信息。每条输入事件会打印sequence/key/type三要素排查按键映射或状态机行为时直接看这三个数即可。当前边界与可挖的方向README 的 To-do 清单标出了现有版本的边界对照源码大致是这些多存档槽Slots现在只有一个save.bin想扩展的话给TAMA_SAVE_PATH加参数化文件名即可游戏内重置目前只能退出重进来内核侧已有cpu_reset()接口cpu.h缺的是一个入口键测试模式拓麻歌子固件自带的自检入口尚未接通音量调节蜂鸣器响度写死在furi_hal_speaker_start的 0.5 参数里加速Fast-forwardfast_forward_done在初始化时就被置为true旁边挂着TODO: implement fast forwarding属于占位未完成。结语这套外部插件外壳 跨平台模拟内核的分工值得借鉴TamaLIB 管 4 位机指令与硬件状态机hal.c用不到两百行代码接上定时器、蜂鸣器与屏幕换个平台只需重写 HAL。想继续看同类玩法可以逛 Applications/Official/source-OLDER/xMasterX 下的其他游戏插件或深入 tamalib/ 的核心源码。【免费下载链接】FlipperPlayground (and dump) of stuff I make or modify for the Flipper Zero项目地址: https://gitcode.com/GitHub_Trending/fl/Flipper创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Swift Algorithm Club 完全指南:用 Swift 学习算法与数据结构的开源宝库

Swift Algorithm Club 完全指南:用 Swift 学习算法与数据结构的开源宝库

示例工程教程 【免费下载链接】swift-algorithm-club Algorithms and data structures in Swift, with explanations! 项目地址: https://gitcode.com/gh_mirrors/sw/swift-algorithm-club 点击查看 免费下载 Swift Algorithm Club 是一个用 Swift 语言实现主流算法…

2026/9/20 12:57:54 阅读更多 →
dbx 中的 gosasl 库实战:Go 实现的多机制 SASL 认证(GSSAPI / DIGEST-MD5 / PLAIN / ANONYMOUS)

dbx 中的 gosasl 库实战:Go 实现的多机制 SASL 认证(GSSAPI / DIGEST-MD5 / PLAIN / ANONYMOUS)

数据库开发者工具桌面应用CLIMCP 服务AI 应用 【免费下载链接】dbx 15MB,轻量级跨平台数据库客户端、数据库管理工具。支持 MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server 等。15MB, lightweight, cross-platform database client. S…

2026/9/20 12:56:53 阅读更多 →
CANN PyPTO 编程指南:Tensor 创建与操作实战

CANN PyPTO 编程指南:Tensor 创建与操作实战

人工智能编译器模型编译高性能计算深度学习CANN 【免费下载链接】pypto PyPTO(发音: pai p-t-o):Parallel Tensor/Tile Operation编程范式。 项目地址: https://gitcode.com/cann/pypto 点击查看 免费下载 导读 本文基于 PyPTO&…

2026/9/20 12:56:53 阅读更多 →

最新新闻

React Native 加密数据库实战:用 RxDB Encryption 插件保护移动端本地数据

React Native 加密数据库实战:用 RxDB Encryption 插件保护移动端本地数据

React Native 加密数据库实战:用 RxDB Encryption 插件保护移动端本地数据 【免费下载链接】rxdb The local-first database that runs on every JS runtime and replicates with your existing backend - no vendor, no lock-in - https://rxdb.info/ 项目地址: …

2026/9/20 13:43:28 阅读更多 →
AlphaFold结果解读:pLDDT、PAE、pTM与ipTM指标全解析

AlphaFold结果解读:pLDDT、PAE、pTM与ipTM指标全解析

拿到AlphaFold预测结果之后,你的第一反应是什么?说实话,我以前也干过这种事:先打开pLDDT,别的指标一概不看,颜色鲜亮就长出一口气,看到大段红橙就开始怀疑人生。直到有一次,一个蛋白…

2026/9/20 13:43:28 阅读更多 →
@eggjs/cluster 集群管理器全解析:Egg 多进程架构演进、启动模式与配置实战

@eggjs/cluster 集群管理器全解析:Egg 多进程架构演进、启动模式与配置实战

后端Web框架 【免费下载链接】egg 🥚🥚🥚🥚 Born to build better enterprise frameworks and apps with Node.js & Koa. https://307.run/eggcode 项目地址: https://gitcode.com/gh_mirrors/eg/egg 点击查看 免费…

2026/9/20 13:43:28 阅读更多 →
Voyager 的 Image Refinement:Gemini 生成图片水印的像素级无损去除方案

Voyager 的 Image Refinement:Gemini 生成图片水印的像素级无损去除方案

Voyager 的 Image Refinement:Gemini 生成图片水印的像素级无损去除方案 【免费下载链接】voyager Enhancement suite for Gemini, AI Studio, Claude & ChatGPT — plus a prompt manager for any websites, DeepSeek Harness included. / 面向 Gemini、AI Stu…

2026/9/20 13:43:28 阅读更多 →
POPGENE遗传多样性分析实战:从数据格式到结果解读

POPGENE遗传多样性分析实战:从数据格式到结果解读

简介:POPGENE是一款基于Windows的免费物种遗传分析软件,这份图文版中文使用教程正是为它而写的,适合从事群体遗传学、分子生态学研究的科研人员和研究生快速上手。内容依托软件实际界面展开,系统介绍了POPGENE的窗口构成、八大菜单…

2026/9/20 13:43:28 阅读更多 →
OpenToonz 快速上手:3 步免费做出第一部 2D 动画

OpenToonz 快速上手:3 步免费做出第一部 2D 动画

OpenToonz 快速上手:3 步免费做出第一部 2D 动画 【免费下载链接】opentoonz OpenToonz - An open-source full-featured 2D animation creation software 项目地址: https://gitcode.com/GitHub_Trending/op/opentoonz OpenToonz 是一款开源、免费的 2D 动画…

2026/9/20 13:42:28 阅读更多 →

日新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/19 23:01:36 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/19 17:50:38 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →