├── emulator ├── Gui │ ├── Terminal.cpp │ ├── Terminal.hpp │ ├── UiBase.cpp │ ├── Injector.hpp │ ├── UiBase.hpp │ ├── WatchWindow.hpp │ ├── MemBreakPoint.hpp │ ├── Ui.hpp │ ├── CodeViewer.hpp │ ├── imgui_impl_sdlrenderer2.h │ ├── imgui_impl_sdl2.h │ ├── MemBreakPoint.cpp │ ├── Injector.cpp │ ├── WatchWindow.cpp │ ├── Ui.cpp │ └── CodeViewer.cpp ├── Data │ ├── EventCode.hpp │ ├── ColourInfo.hpp │ ├── HardwareId.hpp │ ├── SpriteInfo.hpp │ ├── ModelInfo.hpp │ └── ModelInfo.cpp ├── Peripheral │ ├── Screen.hpp │ ├── ROMWindow.hpp │ ├── StandbyControl.hpp │ ├── BatteryBackedRAM.hpp │ ├── Peripheral.cpp │ ├── Timer.hpp │ ├── Peripheral.hpp │ ├── Miscellaneous.hpp │ ├── BCDCalc.hpp │ ├── Keyboard.hpp │ ├── StandbyControl.cpp │ ├── Miscellaneous.cpp │ ├── Timer.cpp │ ├── BatteryBackedRAM.cpp │ └── ROMWindow.cpp ├── Logger.hpp ├── Logger.cpp ├── Config.hpp ├── Chipset │ ├── InterruptSource.hpp │ ├── MMURegion.cpp │ ├── MMU.hpp │ ├── InterruptSource.cpp │ ├── CPULoadStore.cpp │ ├── Chipset.hpp │ ├── MMURegion.hpp │ ├── CPUPushPop.cpp │ ├── CPUControl.cpp │ ├── CPU.hpp │ ├── Chipset.cpp │ ├── MMU.cpp │ └── CPUArithmetic.cpp ├── README.md ├── utils.h ├── Config │ ├── Config.hpp │ └── Config.cpp ├── utils.cpp ├── Emulator.hpp └── casioemu.cpp ├── run.bat ├── .gitattributes ├── DEPENDENCIES ├── docs ├── wc1.png ├── wc2.png ├── wc3.png ├── image.png ├── rop_1.png ├── rop_2.png ├── rop_3.png ├── rop_4.png ├── sym_1.png ├── sym_2.png ├── sym_3.png ├── sym_4.png ├── intro_ui.md ├── intro_asm.md ├── intro_rop.md └── README_en.md ├── disas ├── lib.h ├── README.md ├── lib.cpp ├── help.txt ├── test.cpp ├── Makefile ├── example.cpp ├── main.cpp ├── temp └── nX-U8_is_split.txt ├── asm ├── test_easy.asm ├── test_copy_run.asm ├── test2.asm ├── analyze │ ├── s.txt │ ├── strange_funcs.txt │ └── printline.txt ├── test.asm ├── loader_cpy.asm ├── test_copy.asm ├── loader.asm ├── keys.py ├── sym.txt └── asm.py ├── config.ini ├── .gitignore ├── run.lua ├── xmake.lua ├── lang ├── cn.ini └── en.ini ├── README.md └── imgui.ini /emulator/Gui/Terminal.cpp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | xmake l ./run.lua %1 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | Makefile linguist-vendored 2 | -------------------------------------------------------------------------------- /DEPENDENCIES: -------------------------------------------------------------------------------- 1 | libsdl2-dev 2 | libsdl2-image-dev 3 | liblua5.3-0-dev 4 | 5 | -------------------------------------------------------------------------------- /docs/wc1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiufuyu123/CasioEmuNeo/HEAD/docs/wc1.png -------------------------------------------------------------------------------- /docs/wc2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiufuyu123/CasioEmuNeo/HEAD/docs/wc2.png -------------------------------------------------------------------------------- /docs/wc3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiufuyu123/CasioEmuNeo/HEAD/docs/wc3.png -------------------------------------------------------------------------------- /docs/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiufuyu123/CasioEmuNeo/HEAD/docs/image.png -------------------------------------------------------------------------------- /docs/rop_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiufuyu123/CasioEmuNeo/HEAD/docs/rop_1.png -------------------------------------------------------------------------------- /docs/rop_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiufuyu123/CasioEmuNeo/HEAD/docs/rop_2.png -------------------------------------------------------------------------------- /docs/rop_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiufuyu123/CasioEmuNeo/HEAD/docs/rop_3.png -------------------------------------------------------------------------------- /docs/rop_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiufuyu123/CasioEmuNeo/HEAD/docs/rop_4.png -------------------------------------------------------------------------------- /docs/sym_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiufuyu123/CasioEmuNeo/HEAD/docs/sym_1.png -------------------------------------------------------------------------------- /docs/sym_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiufuyu123/CasioEmuNeo/HEAD/docs/sym_2.png -------------------------------------------------------------------------------- /docs/sym_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiufuyu123/CasioEmuNeo/HEAD/docs/sym_3.png -------------------------------------------------------------------------------- /docs/sym_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiufuyu123/CasioEmuNeo/HEAD/docs/sym_4.png -------------------------------------------------------------------------------- /disas/lib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | std::string tohex(int, int); 4 | std::string tobin(int, int); 5 | -------------------------------------------------------------------------------- /asm/test_easy.asm: -------------------------------------------------------------------------------- 1 | 2 | hex 6c 6f 76 65 20 79 6f 75 ; `love you` 3 | space 9 4 | space 17 5 | line_print_col0 -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [settings] 2 | scale=1.000000 3 | font=unifont.otf 4 | model=E:/projects/CasioEmuX/models/fx991cnx 5 | [lang] 6 | lang=cn -------------------------------------------------------------------------------- /disas/README.md: -------------------------------------------------------------------------------- 1 | My disassembler. 2 | 3 | For Windows users: Make sure that the line ending in `nX-U8*.txt`, `example.cpp` and `help.txt` is correct. 4 | -------------------------------------------------------------------------------- /emulator/Gui/Terminal.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | class Terminal{ 5 | public: 6 | Terminal(); 7 | 8 | void show(); 9 | 10 | }; -------------------------------------------------------------------------------- /emulator/Gui/UiBase.cpp: -------------------------------------------------------------------------------- 1 | #include "UiBase.hpp" 2 | 3 | 4 | void UiBase::Show(){ 5 | 6 | } 7 | 8 | void UiBase::BeforeShow(){ 9 | 10 | } -------------------------------------------------------------------------------- /asm/test_copy_run.asm: -------------------------------------------------------------------------------- 1 | space 34 2 | pop qr0 ;;EA34 3 | hex 40 e7 e0 e9 30 30 30 30 4 | smart_strcpy 5 | pop er14 6 | hex 60 e7 7 | jpop qr80 -------------------------------------------------------------------------------- /emulator/Data/EventCode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace casioemu 4 | { 5 | enum EventCode 6 | { 7 | CE_FRAME_REQUEST, 8 | CE_EMU_STOPPED 9 | }; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .xmake/ 3 | .vscode/ 4 | .cache/ 5 | models/ 6 | asm/__pycache__ 7 | models/*/*.bin 8 | models/*/*.asm 9 | models/*/*.names 10 | ignore/ 11 | build/ 12 | imgui.ini 13 | font.ttc -------------------------------------------------------------------------------- /docs/intro_ui.md: -------------------------------------------------------------------------------- 1 | # ui介绍 2 | --> [返回目录](../README.md) 3 | 4 | ## 监视窗口 5 | ![](wc1.png) 6 | 7 | ## 内存窗口 8 | ![](wc2.png) 9 | 10 | ## ROP注入窗口 11 | ![](wc3.png) 12 | 13 | ## 调试窗口 14 | ![](image.png) -------------------------------------------------------------------------------- /emulator/Data/ColourInfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include 5 | 6 | namespace casioemu 7 | { 8 | struct ColourInfo 9 | { 10 | int r, g, b; 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /emulator/Data/HardwareId.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace casioemu 4 | { 5 | enum HardwareId 6 | { 7 | HW_ES_PLUS = 3, 8 | HW_CLASSWIZ = 4, 9 | HW_CLASSWIZ_II = 5 10 | }; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /asm/test2.asm: -------------------------------------------------------------------------------- 1 | hex 31 32 33 34 35 36 37 38 2 | space 26 3 | pop qr0 ;;EA34 4 | hex e0 ea 80 d1 30 30 30 30 5 | smart_strcpy 6 | pop ER14 7 | hex 2c ea 8 | jpop qr8 ;jump and pop qr8,pop qr0 -------------------------------------------------------------------------------- /emulator/Peripheral/Screen.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include "Peripheral.hpp" 5 | 6 | namespace casioemu 7 | { 8 | Peripheral *CreateScreen(Emulator& emulator); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /emulator/Data/SpriteInfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include 5 | 6 | namespace casioemu 7 | { 8 | struct SpriteInfo 9 | { 10 | SDL_Rect src, dest; 11 | }; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /emulator/Gui/Injector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Emulator.hpp" 3 | #include "UiBase.hpp" 4 | class Injector : public UiBase{ 5 | private: 6 | char* data_buf; 7 | public: 8 | UI_SINGLE_HEAD(Injector) 9 | Injector(); 10 | void Show(); 11 | }; -------------------------------------------------------------------------------- /emulator/Logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Config.hpp" 3 | 4 | #include 5 | 6 | namespace casioemu 7 | { 8 | namespace logger 9 | { 10 | // Note that the printed string should end with a new line character. 11 | void Info(const char *format, ...); 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /emulator/Gui/UiBase.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class UiBase{ 4 | 5 | public: 6 | 7 | UiBase(){}; 8 | 9 | virtual void BeforeShow(); 10 | 11 | virtual void Show(); 12 | 13 | }; 14 | 15 | #define UI_SINGLE_HEAD(T) static T* instance; 16 | #define UI_SINGLE_IMPL(T) T* T::instance = nullptr; -------------------------------------------------------------------------------- /run.lua: -------------------------------------------------------------------------------- 1 | function main(arg) 2 | 3 | local model_name = "./models/" 4 | if arg == nil then 5 | model_name = model_name .. "fx991cnx" 6 | else 7 | model_name = model_name .. arg 8 | end 9 | print("Load Model: ",model_name) 10 | os.exec("xmake run CasioEmuX "..model_name) 11 | end 12 | -------------------------------------------------------------------------------- /asm/analyze/s.txt: -------------------------------------------------------------------------------- 1 | 2 | pop qr8 13560 3 | 30 30 30 30 2e d9 48 D2 4 | 173c4 5 | pop xr8 6 | c0 d9 30 30 7 | pop QR0 8 | 30 30 18 00 30 30 01 30 9 | smart_strcpy 10 | [er8]+=er2,pop xr8 11 | 08 d9 30 30 12 | st er2,[er0],pop xr8 (8f28) 13 | 24 d9 30 30 14 | pop er2 15 | 31 30 16 | st er2,[er0],pop xr8 (8f28) 17 | 30 30 30 30 18 | pop ER12 19 | 30 30 20 | pop ea ... pop xr4 21 | 87 d9 c2 D8 22 | 23 | -------------------------------------------------------------------------------- /emulator/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.hpp" 2 | 3 | #include 4 | #include 5 | 6 | 7 | namespace casioemu 8 | { 9 | namespace logger 10 | { 11 | void Info(const char *format, ...) 12 | { 13 | // * TODO may introduce race condition 14 | 15 | va_list args; 16 | va_start(args, format); 17 | vprintf(format, args); 18 | va_end(args); 19 | } 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /emulator/Config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __GNUG__ 6 | # define FUNCTION_NAME __PRETTY_FUNCTION__ 7 | #else 8 | # define FUNCTION_NAME __func__ 9 | #endif 10 | #define PANIC(...) ( \ 11 | std::fprintf(stderr, "%s:%i: in %s: ", __FILE__, __LINE__, FUNCTION_NAME), \ 12 | std::fprintf(stderr, __VA_ARGS__), \ 13 | std::exit(1) \ 14 | ) 15 | 16 | #define MODEL_DEF_NAME "model.def" 17 | 18 | -------------------------------------------------------------------------------- /xmake.lua: -------------------------------------------------------------------------------- 1 | add_rules("mode.release", "mode.debug") 2 | add_requires("libsdl2","libsdl2_image 2.6.2","lua 5.3") 3 | add_requires("imgui v1.90.5-docking", {configs = {sdl2renderer = true}}) 4 | target("CasioEmuX") 5 | 6 | set_kind("binary") 7 | set_languages("c++17") 8 | add_files("emulator/*.cpp","emulator/*/*.cpp") 9 | add_packages("libsdl2","libsdl2_image","lua","python3.10") 10 | set_rundir("./") 11 | add_packages("imgui", {public = true}) 12 | 13 | 14 | -------------------------------------------------------------------------------- /emulator/Chipset/InterruptSource.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | namespace casioemu 5 | { 6 | class Emulator; 7 | 8 | class InterruptSource 9 | { 10 | Emulator *emulator; 11 | bool raise_success, setup_done; 12 | size_t interrupt_index; 13 | 14 | public: 15 | InterruptSource(); 16 | void Setup(size_t interrupt_index, Emulator &_emulator); 17 | bool Enabled(); 18 | bool TryRaise(); 19 | bool Success(); 20 | }; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /emulator/Data/ModelInfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include 5 | 6 | namespace casioemu 7 | { 8 | class Emulator; 9 | class SpriteInfo; 10 | class ColourInfo; 11 | 12 | struct ModelInfo 13 | { 14 | ModelInfo(Emulator &emulator, std::string key); 15 | Emulator &emulator; 16 | std::string key; 17 | 18 | operator std::string(); 19 | operator int(); 20 | operator SpriteInfo(); 21 | operator ColourInfo(); 22 | }; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /disas/lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | std::string tohex(int n, int len) { 4 | std::string retval = ""; 5 | for (int x = 0; x < len; x++) { 6 | retval = "0123456789ABCDEF"[n & 0xF] + retval; 7 | n >>= 4; 8 | } 9 | return retval; 10 | } 11 | 12 | std::string tobin(int n, int len) { 13 | std::string retval = ""; 14 | for (int x = 0; x < len; x++) { 15 | retval = "01"[n & 1] + retval; 16 | n >>= 1; 17 | } 18 | return retval; 19 | } 20 | -------------------------------------------------------------------------------- /disas/help.txt: -------------------------------------------------------------------------------- 1 | Disassembler source code creator 2 | 3 | Usage: 4 | 5 | Disassembler instruction_set_file_name.txt output_file_name.cpp 6 | 7 | * 8 | 9 | ??? disassembler 10 | 11 | Usage: 12 | 13 | a.exe file_to_disassemble start_position length output_file_name.txt 14 | 15 | start_position, length are: 16 | + In decimal if there is no prefix or suffix 17 | + In hexadecimal if there is 0x prefix 18 | + In octal if there is 0 prefix 19 | 20 | Warning: If the disassembler work on invalid code it may crash! 21 | -------------------------------------------------------------------------------- /emulator/Peripheral/ROMWindow.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include "Peripheral.hpp" 5 | #include "../Chipset/MMURegion.hpp" 6 | 7 | #include 8 | 9 | namespace casioemu 10 | { 11 | class ROMWindow : public Peripheral 12 | { 13 | std::unique_ptr regions; 14 | 15 | public: 16 | using Peripheral::Peripheral; 17 | 18 | void Initialise(); 19 | void Uninitialise(); 20 | void Tick(); 21 | void Frame(); 22 | void UIEvent(SDL_Event &event); 23 | }; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /emulator/Peripheral/StandbyControl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include "Peripheral.hpp" 5 | #include "../Chipset/MMURegion.hpp" 6 | 7 | namespace casioemu 8 | { 9 | class StandbyControl : public Peripheral 10 | { 11 | MMURegion region_stpacp, region_sbycon, region_F312; 12 | uint8_t stpacp_last, F312_last; 13 | bool stop_acceptor_enabled, shutdown_acceptor_enabled; 14 | 15 | public: 16 | using Peripheral::Peripheral; 17 | 18 | void Initialise(); 19 | void Reset(); 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /lang/cn.ini: -------------------------------------------------------------------------------- 1 | CasioEmuNeo模拟器 2 | 反汇编 3 | 正在加载符号,请稍等... 4 | 查找地址: 5 | 逐步 6 | 跟踪 7 | 跟踪 8 | 资源窗口 9 | 设置栈显示区域 10 | 区域 11 | 栈窗口 12 | 注入器/设置 13 | 更改UI缩放 14 | scale 15 | 输入你的rop 16 | 设置rop输入区大小 17 | 设置an偏移 18 | 载入an 19 | 加载rop 20 | 从字符串中加载rop 21 | 模拟器已经进入Math I/O模式 22 | 'an'输入完毕%请确保模拟器处于Math I/O模式%然后回到模拟器,按下[->][=]完成 23 | ROP输入完毕%回到模拟器,按下[->][=]完成 24 | 内存编辑器 25 | 寄存器请在断点状态下查看 26 | 请选择断点模式: 27 | 查找是什么访问了这个地址 28 | 查找是什么写入了这个地址 29 | 删除此地址 30 | 未设置任何断点,请添加地址->右键地址->选择模式 31 | 正在监听地址:%04x 32 | 清除记录 33 | 地址: 34 | 添加地址 35 | 模式: 36 | 写 37 | 读 38 | 内存断点 -------------------------------------------------------------------------------- /disas/test.cpp: -------------------------------------------------------------------------------- 1 | // Independent from the project. 2 | 3 | #include 4 | 5 | std::string signedtohex(int n, int binlen) { 6 | binlen--; 7 | std::string retval = (n >> binlen) == 0 ? "" : "-"; 8 | binlen = (binlen + 3) / 4; // ceil of binlen/4 9 | // now binlen <- hexlen 10 | for (int x = 0; x < binlen; x++) { 11 | retval = "0123456789ABCDEF"[n & 0xF] + retval; 12 | n >>= 4; 13 | } 14 | return retval; 15 | } 16 | 17 | int main(int argc, char** argv) { 18 | std::cout << 0xf0 << " " << (0xf0 << 24 >> 24); 19 | } 20 | -------------------------------------------------------------------------------- /emulator/Gui/WatchWindow.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../Emulator.hpp" 4 | #include "UiBase.hpp" 5 | #include "hex.hpp" 6 | #include 7 | 8 | class WatchWindow:public UiBase{ 9 | private: 10 | uint8_t reg_rx[16][3]; 11 | char reg_lr[5],reg_sp[5],reg_ea[5],reg_pc[5],reg_psw[3]; 12 | int char_width; 13 | MemoryEditor mem_editor; 14 | public: 15 | 16 | UI_SINGLE_HEAD(WatchWindow) 17 | 18 | WatchWindow(); 19 | 20 | void Show(); 21 | 22 | void ShowRX(); 23 | 24 | void PrepareRX(); 25 | 26 | void UpdateRX(); 27 | }; -------------------------------------------------------------------------------- /docs/intro_asm.md: -------------------------------------------------------------------------------- 1 | # 汇编器使用 2 | --> [返回目录](../README.md) 3 | ## 运行环境 4 | - python3.8+ 5 | 6 | ## 使用 7 | **以下所有操作在asm文件夹内进行** 8 | 1. 将你需要的gadget/函数放在sym.txt 9 | 我们已经为你准备了一些常用的函数表 10 | ### 预装gadget表 11 | ![](./sym_1.png "") 12 | ![](./sym_2.png "") 13 | ### 预装函数表 14 | ![](./sym_3.png) 15 | 16 | 以上只是部分,详情查看sym.txt 17 | ### 添加函数/gadget 18 | 命名规则: 19 | ``` 20 | 01234 xxxxx 21 | ``` 22 | 注意,空格数要严格一致! 23 | 24 | ## 一些写好的例子已经放在asm目录下 25 | ``` 26 | python3 ./asm.py test.asm 27 | ``` 28 | 执行编译 29 | ![](./sym_4.png) 30 | 会输出按键序列和16进制序列 -------------------------------------------------------------------------------- /docs/intro_rop.md: -------------------------------------------------------------------------------- 1 | # ROP使用 2 | --> [返回目录](../README.md) 3 | 4 | ## ROP注入 5 | [rop输入教程](../README.md) 6 | 7 | ## ROP调试 8 | 9 | 1. 例如,你的程序是: 10 | ``` 11 | pop er0 12 | hex 30 d2 13 | read_key 14 | pop er0 15 | hex 32 d2 16 | ``` 17 | 你想要查看read_key之后的效果 18 | 那么,首先找到pop er0 对应的地址:**0x121a8** (sym.txt) 19 | 在执行rop 之前, 20 | ![](./rop_1.png) 21 | 在disas窗口定位这个地址 22 | 点击左侧 **[ o ]** 23 | ![](rop_2.png) 24 | 等到变成红色的 **[ x ]** 25 | 然后执行rop 26 | ![](rop_3.png) 27 | 断点触发 28 | - step 单步执行 29 | - next 继续 30 | - trace 每次pop pc或ret 触发断点(可以用于跟踪rop chain) 31 | 32 | 寄存器值窗口 33 | ![](rop_4.png) -------------------------------------------------------------------------------- /emulator/Peripheral/BatteryBackedRAM.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include "Peripheral.hpp" 5 | #include "../Chipset/MMURegion.hpp" 6 | 7 | namespace casioemu 8 | { 9 | class BatteryBackedRAM : public Peripheral 10 | { 11 | MMURegion region, region_2; 12 | 13 | size_t ram_size; 14 | bool ram_file_requested; 15 | uint8_t *ram_buffer; 16 | 17 | 18 | public: 19 | static char* rom_addr; 20 | 21 | using Peripheral::Peripheral; 22 | void Initialise(); 23 | void Uninitialise(); 24 | void SaveRAMImage(); 25 | void LoadRAMImage(); 26 | }; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /emulator/README.md: -------------------------------------------------------------------------------- 1 | # Emulator 2 | 3 | Assuming your current directory is this one, you have 4 | [meson](https://mesonbuild.com/) and [ninja](https://ninja-build.org/) installed 5 | and you have the proper ROM (name `rom.bin`) in `../../models/`: 6 | 7 | ``` 8 | $ meson build && cd build 9 | $ meson configure -Dwarning_level=3 -Dcpp_std=c++11 10 | $ ninja 11 | $ ./emulator ../../models/ 12 | ``` 13 | 14 | Unlike the previous, terrible excuse of a build system, this one is likely to 15 | work well on platforms other than unices with coreutils and other shady stuff 16 | installed. 17 | -------------------------------------------------------------------------------- /disas/Makefile: -------------------------------------------------------------------------------- 1 | # folder names 'bin' and 'obj' are hardcoded 2 | 3 | CXX ?= g++ 4 | RM ?= rm -f 5 | CCFLAGS ?= -D_GLIBCXX_DEBUG -O2 -std=c++14 -Wall -Wextra -Werror -pedantic 6 | 7 | _dummy := $(shell mkdir -p obj bin) 8 | 9 | bin/u8-disas: nX-U8_is.txt bin/main Makefile example.cpp 10 | @bin/main $< temp.cpp 11 | @$(CXX) $(CCFLAGS) temp.cpp -o $@ 12 | @$(RM) temp.cpp 13 | 14 | bin/u8-disas-split: nX-U8_is_split.txt bin/main Makefile example.cpp 15 | @bin/main $< temp.cpp 16 | @$(CXX) $(CCFLAGS) temp.cpp -o $@ 17 | @$(RM) temp.cpp 18 | 19 | bin/main: obj/main.o obj/lib.o Makefile 20 | @$(CXX) obj/main.o obj/lib.o -o $@ 21 | 22 | obj/%.o: %.cpp lib.h Makefile 23 | @$(CXX) -c $(CCFLAGS) $< -o $@ 24 | 25 | clean: 26 | @$(RM) bin/* 27 | @$(RM) obj/* 28 | -------------------------------------------------------------------------------- /emulator/Peripheral/Peripheral.cpp: -------------------------------------------------------------------------------- 1 | #include "Peripheral.hpp" 2 | 3 | namespace casioemu 4 | { 5 | Peripheral::Peripheral(Emulator &_emulator) : emulator(_emulator), require_frame(false) 6 | { 7 | } 8 | 9 | Peripheral::~Peripheral() 10 | { 11 | } 12 | 13 | void Peripheral::Initialise() 14 | { 15 | } 16 | 17 | void Peripheral::Uninitialise() 18 | { 19 | } 20 | 21 | void Peripheral::Tick() 22 | { 23 | } 24 | 25 | void Peripheral::TickAfterInterrupts() 26 | { 27 | } 28 | 29 | void Peripheral::Frame() 30 | { 31 | require_frame = false; 32 | } 33 | 34 | void Peripheral::UIEvent(SDL_Event &) 35 | { 36 | } 37 | 38 | void Peripheral::Reset() 39 | { 40 | } 41 | 42 | bool Peripheral::GetRequireFrame() 43 | { 44 | return require_frame; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /emulator/Gui/MemBreakPoint.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "UiBase.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct{ 8 | bool Write; 9 | uint16_t pc; 10 | }MemRecord_t; 11 | 12 | typedef struct { 13 | bool enableWrite = false; 14 | uint16_t addr; 15 | std::unordered_map records; 16 | }MemBPData_t; 17 | 18 | class MemBreakPoint : public UiBase 19 | { 20 | 21 | private: 22 | std::vector break_point_hash; 23 | 24 | int target_addr = -1; 25 | 26 | void DrawFindContent(); 27 | 28 | void DrawContent(); 29 | public: 30 | 31 | UI_SINGLE_HEAD(MemBreakPoint) 32 | 33 | MemBreakPoint(); 34 | 35 | void TryTrigBp(uint16_t addr_edit,bool write); 36 | 37 | void Show(); 38 | }; -------------------------------------------------------------------------------- /emulator/Peripheral/Timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include "Peripheral.hpp" 5 | #include "../Chipset/MMURegion.hpp" 6 | #include "../Chipset/InterruptSource.hpp" 7 | 8 | namespace casioemu 9 | { 10 | class Timer : public Peripheral 11 | { 12 | MMURegion region_counter, region_interval, region_F024, region_control; 13 | uint16_t data_counter, data_interval; 14 | uint8_t data_F024, data_control; 15 | 16 | InterruptSource interrupt_source; 17 | bool real_hardware; 18 | bool TimerSkipped; 19 | bool raise_required; 20 | uint64_t ext_to_int_counter, ext_to_int_next, ext_to_int_int_done; 21 | static const uint64_t ext_to_int_frequency = 10000; 22 | 23 | public: 24 | using Peripheral::Peripheral; 25 | 26 | void Initialise(); 27 | void Reset(); 28 | void Tick(); 29 | void TickAfterInterrupts(); 30 | void DivideTicks(); 31 | }; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /emulator/Peripheral/Peripheral.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include 5 | 6 | namespace casioemu 7 | { 8 | class Emulator; 9 | 10 | class Peripheral 11 | { 12 | protected: 13 | Emulator &emulator; 14 | 15 | /** 16 | * This should be true if the state of this peripheral changed 17 | * so that it requires a call to Frame(). 18 | * It should not directly call Frame() because otherwise it may 19 | * call it more than required (once per timer_interval) 20 | */ 21 | bool require_frame; 22 | 23 | public: 24 | Peripheral(Emulator &emulator); 25 | virtual void Initialise(); 26 | virtual void Uninitialise(); 27 | virtual void Tick(); 28 | virtual void TickAfterInterrupts(); 29 | virtual void Frame(); 30 | virtual void UIEvent(SDL_Event &event); 31 | virtual void Reset(); 32 | virtual bool GetRequireFrame(); 33 | virtual ~Peripheral(); 34 | }; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /lang/en.ini: -------------------------------------------------------------------------------- 1 | CasioEmuNeo 2 | Disassembler 3 | Please wait ... 4 | go to addr: 5 | step 6 | continue 7 | trace 8 | Watch Window 9 | set stack range 10 | range 11 | Stack Window 12 | Injector 13 | Change Scale 14 | scale 15 | Input your rop! 16 | Set rop size 17 | set 'an' offset bytes 18 | Enter 'an' 19 | Load ROP 20 | Load ROP from string 21 | Calculator is already set to Math I/O mode! 22 | 'an' is entered!%Please make sure you're in Math I/O mode% Back to emulator and press [->][=] to finish! 23 | ROP is entered!%Please press [->][=] to finish! 24 | Memory Viewer 25 | Please view registers while at a breakpoint 26 | Select breakpoint mode: 27 | Find what reads this address 28 | Find what writes to this address 29 | Delete this address 30 | No breakpoints set. Add an address, right-click it, and select a mode. 31 | Listening on address: %04x 32 | Clear records 33 | Address: 34 | Add address 35 | Mode: 36 | Write 37 | Read 38 | Memory Breakpoint -------------------------------------------------------------------------------- /emulator/Chipset/MMURegion.cpp: -------------------------------------------------------------------------------- 1 | #include "MMURegion.hpp" 2 | 3 | #include "../Emulator.hpp" 4 | #include "Chipset.hpp" 5 | #include "MMU.hpp" 6 | 7 | namespace casioemu 8 | { 9 | MMURegion::MMURegion() 10 | { 11 | setup_done = false; 12 | } 13 | 14 | MMURegion::~MMURegion() 15 | { 16 | if (setup_done) 17 | Kill(); 18 | } 19 | 20 | void MMURegion::Setup(size_t _base, size_t _size, std::string _description, void *_userdata, ReadFunction _read, WriteFunction _write, Emulator &_emulator) 21 | { 22 | if (setup_done) 23 | PANIC("Setup invoked twice\n"); 24 | 25 | emulator = &_emulator; 26 | base = _base; 27 | size = _size; 28 | description = _description; 29 | userdata = _userdata; 30 | read = _read; 31 | write = _write; 32 | 33 | emulator->chipset.mmu.RegisterRegion(this); 34 | setup_done = true; 35 | } 36 | 37 | void MMURegion::Kill() 38 | { 39 | emulator->chipset.mmu.UnregisterRegion(this); 40 | setup_done = false; 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /emulator/Peripheral/Miscellaneous.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include "Peripheral.hpp" 5 | #include "../Chipset/MMURegion.hpp" 6 | #include 7 | 8 | namespace casioemu 9 | { 10 | class Miscellaneous : public Peripheral 11 | { 12 | MMURegion region_dsr, region_F048, region_F220,region_F0D0,region_F0D1,region_F0D2; 13 | uint64_t data_F048; 14 | uint32_t data_F220; 15 | uint8_t data_F0D0,data_F0D1,data_F0D2; 16 | static constexpr uint16_t addr [] = { 17 | 0xF00A, 0xF018, 0xF033, 0xF034, 0xF041, // both HW_ES_PLUS, HW_CLASSWIZ and HW_CLASSWIZ_II 18 | 0xF035, 0xF036, 0xF039, 0xF012, 0xF03D, 0xF224, 0xF028, 0xF310, // HW_CLASSWIZ and HW_CLASSWIZ_II 19 | 0xF037 //HW_CLASSWIZ_II only 20 | }; 21 | static constexpr int N_BYTE = sizeof(addr) / sizeof(addr[0]); 22 | MMURegion region [N_BYTE]; 23 | uint8_t data [N_BYTE]; 24 | 25 | public: 26 | using Peripheral::Peripheral; 27 | 28 | void Initialise(); 29 | }; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /emulator/Chipset/MMU.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include "MMURegion.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace casioemu 10 | { 11 | class Emulator; 12 | 13 | class MMU 14 | { 15 | private: 16 | Emulator &emulator; 17 | 18 | struct MemoryByte 19 | { 20 | MMURegion *region; 21 | /** 22 | * Lua index to a function to execute when this byte is read from 23 | * or written to as data. If this is LUA_REFNIL, no function is executed. 24 | */ 25 | int on_read, on_write; 26 | }; 27 | MemoryByte **segment_dispatch; 28 | 29 | public: 30 | MMU(Emulator &emulator); 31 | ~MMU(); 32 | void SetupInternals(); 33 | void GenerateSegmentDispatch(size_t segment_index); 34 | uint16_t ReadCode(size_t offset); 35 | uint8_t ReadData(size_t offset); 36 | void WriteData(size_t offset, uint8_t data); 37 | 38 | void RegisterRegion(MMURegion *region); 39 | void UnregisterRegion(MMURegion *region); 40 | }; 41 | } 42 | 43 | -------------------------------------------------------------------------------- /asm/test.asm: -------------------------------------------------------------------------------- 1 | ; loop test 2 | ; 3 | 4 | ; Effect: 5 | ; 6 | ; 1. print `love you` 7 | ; 2. wait for key press 8 | ; 3. print `forever` 9 | ; 4. wait for key press 10 | ; 5. goto 1. and repeat forever 11 | 12 | ;mode 100+9+13an 13 | 14 | 15 | hex 6c 6f 76 65 20 79 6f 75 ; `love you` 16 | space 8 17 | ;space 16 18 | home: 19 | pop qr0 20 | hex 20 20 80 d1 30 30 30 30 21 | ;remember, we do not use label here to represent `love you` 22 | ;since, stacks will be filled and broke by other functions 23 | ;we use 0xd180 (input area rather than stack area) 24 | printline 25 | waitkey 26 | pop qr0 27 | ; 74 d5 28 | hex 20 20 29 | adr label2 30 | hex 30 30 30 30 31 | printline 32 | waitkey 33 | pop qr0 34 | hex 22 d5 80 d1 30 30 30 30 35 | smart_strcpy 36 | pop ER14 37 | adr home -16 38 | jpop qr80 ;jump and pop qr8,pop qr0 39 | label2: 40 | hex 46 6f 72 20 65 76 65 72 ; `Forever` 41 | ; here, since it locates at the end of stack 42 | ; we do not care -------------------------------------------------------------------------------- /emulator/utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by bczhc on 15/04/24. 3 | // 4 | 5 | #ifndef CASIOEMUX_UTILS_H 6 | #define CASIOEMUX_UTILS_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "Gui/hex.hpp" 14 | 15 | namespace casioemu { 16 | class Exception : public std::exception { 17 | private: 18 | std::string msg; 19 | public: 20 | explicit Exception(std::string _msg); 21 | 22 | [[nodiscard]] const char *what() const noexcept override; 23 | }; 24 | 25 | bool starts_with(const std::string &str, const std::string &prefix); 26 | 27 | std::vector ParseColoredSpansConfig(const std::string &path); 28 | 29 | class FileSystem { 30 | public: 31 | static bool exists(const std::string &path); 32 | 33 | static timespec mtime(const std::string &path); 34 | 35 | static uint64_t mtime_ms(const std::string &path); 36 | }; 37 | } 38 | 39 | #endif //CASIOEMUX_UTILS_H 40 | -------------------------------------------------------------------------------- /asm/loader_cpy.asm: -------------------------------------------------------------------------------- 1 | ;132 an 2 | 3 | ; 4 | ; 0xd180 处内存结构 5 | ; [xx] [xx] [yy ... yy] [jump t0 e9 e0...] 6 | ; ^^--目标地址 7 | ; ^^--写入数据 8 | 9 | ; 数据 - 0x6c --> 值 10 | ; 所以我们把 11 | ; sinh( cosh( tanh( ... tan-1( 映射到 12 | ; 01 02 03 ... 0f 13 | 14 | 15 | ;mode 162 16 | 17 | #org e9e0 18 | 19 | loop: 20 | ; 21 | ;v 22 | pop er12 ;8 23 | hex e4 30 ;10 24 | setlr 25 | ;^ 26 | ;setlr 设置er14 因为后面有些rop地址是RT返回不是pop pc返回 27 | pop qr0 28 | hex 30 30 29 | adr y 30 | l er0,[er2] 31 | l r0,[er0] ;rt 32 | and r0,15 33 | mov r2,r0,pop er0 34 | y: 35 | hex ea e0 36 | st r2,[er0] 37 | ;自增 38 | mov er2,1 39 | pop er8 40 | adr y 41 | r0=0,[er8]+=er2,pop xr8 42 | hex 30 30 30 30 ;for xr8 43 | 44 | pop qr0 ;;EA34 45 | hex 22 d5 e0 e9 30 30 30 30 46 | smart_strcpy 47 | pop ER14 48 | hex 1a d5 49 | jpop qr8 ;jump and pop qr8,pop qr0 50 | 51 | ;86 bytes 52 | 53 | pop qr0 54 | hex e0 e9 80 d1 30 30 30 30 55 | smart_strcpy 56 | -------------------------------------------------------------------------------- /asm/test_copy.asm: -------------------------------------------------------------------------------- 1 | ; loop test 2 | ; 3 | 4 | ; Effect: 5 | ; 6 | ; 1. print `love you` 7 | ; 2. wait for key press 8 | ; 3. print `forever` 9 | ; 4. wait for key press 10 | ; 5. goto 1. and repeat forever 11 | 12 | ;mode 100+9+13an 13 | 14 | #org e740 15 | hex 6c 6f 76 65 20 79 6f 75 ; `love you` 16 | space 8 17 | ;space 16 18 | home: 19 | pop qr0 20 | hex 20 20 e0 e9 30 30 30 30 21 | ;remember, we do not use label here to represent `love you` 22 | ;since, stacks will be filled and broke by other functions 23 | ;we use 0xd180 (input area rather than stack area) 24 | printline 25 | waitkey 26 | pop qr0 27 | ; 74 d5 28 | hex 20 20 29 | adr label2 30 | hex 30 30 30 30 31 | printline 32 | waitkey 33 | pop qr0 34 | hex 40 e7 e9 e0 30 30 30 30 35 | smart_strcpy 36 | pop ER14 37 | adr home -16 38 | jpop qr80 ;jump and pop qr8,pop qr0 39 | label2: 40 | hex 46 6f 72 20 65 76 65 72 ; `Forever` 41 | ; here, since it locates at the end of stack 42 | ; we do not care 43 | 44 | copy: 45 | pop qr0 ;;EA34 46 | hex e0 e9 80 d1 30 30 30 30 47 | smart_strcpy -------------------------------------------------------------------------------- /emulator/Peripheral/BCDCalc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include "Peripheral.hpp" 5 | #include "../Chipset/MMURegion.hpp" 6 | 7 | namespace casioemu 8 | { 9 | class BCDCalc : public Peripheral 10 | { 11 | MMURegion region_bcdcontrol, region_F402, region_F404, region_F405, region_F410, region_F414, region_F415, region_param1, region_param2, region_temp1, region_temp2; 12 | 13 | uint8_t data_F400, data_F402, data_F404, data_F405, data_F410, data_F414, data_F415; 14 | 15 | uint8_t data_param1[12]; 16 | uint8_t data_param2[12]; 17 | uint8_t data_temp1[12]; 18 | uint8_t data_temp2[12]; 19 | 20 | bool F400_write; 21 | bool F402_write; 22 | bool F404_write; 23 | bool F405_write; 24 | 25 | uint8_t data_operator, data_type_1, data_type_2, param1, param2, param3, param4, data_F404_copy, 26 | data_mode, data_repeat_flag, data_a, data_b, data_c, data_d, data_F402_copy; 27 | 28 | public: 29 | using Peripheral::Peripheral; 30 | 31 | void Initialise(); 32 | void Reset(); 33 | void Tick(); 34 | 35 | void GenerateParams(); 36 | void F405control(); 37 | void ShiftLeft(int param); 38 | void ShiftRight(int param); 39 | void DataOperate(); 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /emulator/Gui/Ui.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Emulator.hpp" 3 | #include "SDL2/SDL.h" 4 | #include "CodeViewer.hpp" 5 | #include "SDL_video.h" 6 | #include "hex.hpp" 7 | 8 | #include "UiBase.hpp" 9 | #include 10 | #include 11 | 12 | // #include "../Emulator.hpp" 13 | // #include "CodeViewer.hpp" 14 | // int test_gui(); 15 | // void gui_cleanup(); 16 | // void gui_loop(); 17 | // extern char *n_ram_buffer; 18 | // extern casioemu::Emulator *m_emu; 19 | // extern CodeViewer *code_viewer; 20 | 21 | class DebugUi{ 22 | 23 | private: 24 | std::mutex render_lock; 25 | SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI ); 26 | SDL_Window* window; 27 | SDL_Renderer* renderer; 28 | char* rom_addr; 29 | std::vector ui_components; 30 | bool need_paint = false; 31 | 32 | void DockerHelper(); 33 | 34 | public: 35 | UI_SINGLE_HEAD(DebugUi) 36 | int ram_start = 0,ram_length = 0; 37 | static MemoryEditor::OptionalMarkedSpans *MARKED_SPANS; 38 | 39 | static void UpdateMarkedSpans(const MemoryEditor::OptionalMarkedSpans &spans); 40 | 41 | DebugUi(); 42 | 43 | void PaintUi(); 44 | 45 | void PaintSDL(); 46 | }; -------------------------------------------------------------------------------- /asm/loader.asm: -------------------------------------------------------------------------------- 1 | 2 | ;mode 160an 3 | loop: 4 | ; 5 | ;v 6 | pop er0 7 | hex 30 d2 8 | read_key ;18 9 | ;^ 10 | ;读ki ko 到 d230的位置 11 | 12 | ; 13 | ;v 14 | pop er0 15 | hex 32 d2 16 | read_key ;18 17 | ;^ 18 | ;读ki ko 到d232的位置 19 | 20 | ; 21 | ;v 22 | pop er12 ;8 23 | hex e4 30 ;10 24 | setlr 25 | ;^ 26 | ;setlr 设置er14 因为后面有些rop地址是RT返回不是pop pc返回 27 | 28 | ; 29 | ;v 30 | pop xr0 31 | hex 30 30 31 d2 32 | l er0,[er2] 33 | ;^ 34 | ; [xx xx] [xx xx] <- 两次read key 内存排布 35 | ; xx [ ] xx <- 取中间这两个字节到er0中 36 | 37 | sll r0,4 38 | or r0,r1 39 | mov r2,r0,pop er0 40 | hex 30 30 41 | ;现在r2的值是我们目标写入的字节 42 | 43 | pop er0 44 | ;60 above 45 | y: 46 | ;写入地址 选取e9e0是因为这一块地址在重启后不会清除 47 | hex e0 e9 48 | ;计算一下偏移 49 | ; hex e0 e9 offset = 60 bytes 50 | ; 0xd180 + 60 = d1bc 51 | st r2,[er0] 52 | ;自增 53 | mov er2,1 54 | pop er8 55 | hex bc d1 56 | r0=0,[er8]+=er2,pop xr8 57 | hex 30 30 30 30 ;for xr8 58 | 59 | ; 程序主入口: 60 | start: 61 | pop qr0 62 | hex 22 d5 80 d1 30 30 30 30 63 | smart_strcpy 64 | pop ER14 65 | adr loop -8 66 | jpop qr8 ;jump and pop qr8,pop qr0 67 | -------------------------------------------------------------------------------- /emulator/Chipset/InterruptSource.cpp: -------------------------------------------------------------------------------- 1 | #include "InterruptSource.hpp" 2 | 3 | #include "../Emulator.hpp" 4 | #include "Chipset.hpp" 5 | 6 | namespace casioemu 7 | { 8 | InterruptSource::InterruptSource() 9 | { 10 | setup_done = false; 11 | } 12 | 13 | void InterruptSource::Setup(size_t _interrupt_index, Emulator &_emulator) 14 | { 15 | if (setup_done) 16 | PANIC("Setup invoked twice\n"); 17 | 18 | interrupt_index = _interrupt_index; 19 | emulator = &_emulator; 20 | 21 | setup_done = true; 22 | } 23 | 24 | bool InterruptSource::Enabled() 25 | { 26 | if (!setup_done) 27 | PANIC("Setup not invoked\n"); 28 | 29 | return emulator->chipset.InterruptEnabledBySFR(interrupt_index); 30 | } 31 | 32 | bool InterruptSource::TryRaise() 33 | { 34 | if (!setup_done) 35 | PANIC("Setup not invoked\n"); 36 | 37 | if (!emulator->chipset.InterruptEnabledBySFR(interrupt_index) || emulator->chipset.GetInterruptPendingSFR(interrupt_index)) 38 | { 39 | raise_success = false; 40 | } 41 | else 42 | { 43 | emulator->chipset.RaiseMaskable(interrupt_index); 44 | raise_success = true; 45 | } 46 | 47 | return raise_success; 48 | } 49 | 50 | bool InterruptSource::Success() 51 | { 52 | if (!setup_done) 53 | PANIC("Setup not invoked\n"); 54 | 55 | return raise_success && emulator->chipset.GetInterruptPendingSFR(interrupt_index); 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /asm/analyze/strange_funcs.txt: -------------------------------------------------------------------------------- 1 | 00D4C8: 2 | | 参数: er0(指针) | 3 | 4 | a = *er0 5 | if(a<0xf0){ 6 | //这步判断猜测用于检测是否是单/双字节字符 7 | r1 = 0 8 | er2 = er0 9 | er0 = er2 10 | ret 11 | }else{ 12 | **一坨** 13 | } 14 | 15 | 00AF62: 16 | | 命名: next_char(*addr) 17 | 18 | if(*addr >= 0xf0){ 19 | addr+=2 20 | } 21 | else{ 22 | addr+=1 23 | } 24 | return addr 25 | 26 | 0179C2: 27 | | 命名 next_char_14(*addr) 28 | | 参数 er14 29 | 30 | next_char_14(er14) 31 | ret er0 or er14 32 | 33 | 15eea: 34 | | check_type 35 | if(r1 == 0xfb){ 36 | 37 | }else if(r1 == 0xfd){ 38 | 39 | }else if(r1 == 0xfe){ 40 | 41 | }else if(r1 == 0xfa){ 42 | 43 | }else if(r0 == 0){ 44 | 45 | }else if(r0 < 0x20){ 46 | 47 | }else if(r0 < 0x23){ 48 | 49 | }else if(r0 == 0x23){ 50 | 51 | }else if(r0 == 0x24){ 52 | 53 | } 54 | ... 55 | if(r0 < 0x40){ 56 | return 9 57 | } 58 | 015ECE: 59 | | 参数 er14(指针)| 60 | 61 | er2 = call 00D4C8 62 | next_char_14() 63 | check_type() 64 | ret 65 | 66 | 0179C2: 67 | | 参数 er14(指针) 68 | | er14(指针,目标地址) 69 | 70 | if(015ece < 10){ 71 | 72 | } 73 | 74 | 0177DA 75 | | d180输入区 76 | 77 | 262b0 cif 78 | if(er0 <= er2){ 79 | er0 = er2 80 | } 81 | return 82 | 83 | 0262B0 27 F0 CMP ER0, ER2 84 | 0262B2 01 C3 BC LE, 262B6h 85 | 0262B4 1F FE RT 86 | 0262B6 25 F0 MOV ER0, ER2 87 | 0262B8 1F FE RT -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CasioEmuNeo 2 | 3 | [中文版本](./README.md) | [English](./docs/README_en.md) 4 | 5 | 卡西欧classwizard系列模拟器,汇编器,调试器,rop自动注入器 6 | 7 | ## 教程 8 | - [基础界面操作](./docs/intro_ui.md) 9 | - [汇编器使用](./docs/intro_asm.md) 10 | - [rop注入/调试](./docs/intro_rop.md) 11 | 12 | ## 不想从源码构建?下载windows预编译版本 13 | [release](https://github.com/qiufuyu123/CasioEmuNeo/releases) 14 | 15 | ## 构建 16 | 0. 安装 xmake & Mingw & 下载字体 17 | 1. `curl -fsSL https://xmake.io/shget.text | bash` 18 | 19 | 2. 安装并配置 Mingw64 *(windows下选则Posix版本!!!不是win32版本) 20 | 3. [字体](http://unifoundry.com/pub/unifont/unifont-15.1.05/font-builds/unifont-15.1.05.otf) 下载,重命名为unifont.otf,放到工程根目录 21 | 1. 构建模拟器 22 | ``` 23 | cd emulator 24 | xmake f -p mingw 25 | xmake 26 | xmake run CasioEmuX ../models/fx991cnx 27 | ``` 28 | 29 | 2. *可选* `反编译器` 30 | ``` 31 | cd disas 32 | make 33 | ``` 34 | 3. *可选* 构建机型 35 | 下载对应机型的 `rom`,命名为 `rom.bin` 放在models 目录下对应名称的目录内 36 | **注意:** 37 | **由于casio版权问题,源码不包含任何rom,rom文件请自行找资源下载** 38 | **如果想要fx991cnx的rom,请到release页面下载编译好的exe版本** 39 | 40 | 例子, fx991cnx: 41 | ``` 42 | cd disas 43 | ``` 44 | ``` 45 | ./bin/u8-disas ../models/fx991cnx/rom.bin 0 0x40000 ./_disas.txt 46 | ``` 47 | 将_disas.txt复制到 models/fxcnx991目录 48 | ``` 49 | cp ./_disas.txt ../models/fx991cnx/ 50 | ``` 51 | 修改 `model.lua` 52 | 设置`rom_path` 为`"rom.bin"` 53 | -------------------------------------------------------------------------------- /emulator/Peripheral/Keyboard.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include "Peripheral.hpp" 5 | #include "../Chipset/MMURegion.hpp" 6 | #include "../Chipset/InterruptSource.hpp" 7 | 8 | #include 9 | 10 | namespace casioemu 11 | { 12 | class Keyboard : public Peripheral 13 | { 14 | MMURegion region_ko_mask, region_ko, region_ki, region_input_filter; 15 | uint16_t keyboard_out, keyboard_out_mask; 16 | uint8_t keyboard_in, input_filter, keyboard_ghost[8]; 17 | 18 | bool real_hardware; 19 | MMURegion region_ready_emu, region_ko_emu, region_ki_emu, region_pd_emu; 20 | uint8_t keyboard_ready_emu, keyboard_out_emu, keyboard_in_emu, keyboard_pd_emu; 21 | 22 | uint8_t has_input; 23 | InterruptSource interrupt_source; 24 | 25 | SDL_Renderer *renderer; 26 | 27 | struct Button 28 | { 29 | enum ButtonType 30 | { 31 | BT_NONE, 32 | BT_BUTTON, 33 | BT_POWER 34 | } type; 35 | SDL_Rect rect; 36 | uint8_t ko_bit, ki_bit; 37 | bool pressed, stuck; 38 | } buttons[64]; 39 | 40 | // Maps from keycode to an index to (buttons). 41 | std::unordered_map keyboard_map; 42 | 43 | bool p0, p1, p146; 44 | 45 | public: 46 | using Peripheral::Peripheral; 47 | 48 | void Initialise(); 49 | void Reset(); 50 | void Tick(); 51 | void Frame(); 52 | void UIEvent(SDL_Event &event); 53 | void PressButton(Button& button, bool stick); 54 | void PressAt(int x, int y, bool stick); 55 | void ReleaseAll(); 56 | void RecalculateKI(); 57 | void RecalculateGhost(); 58 | }; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /emulator/Gui/CodeViewer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "UiBase.hpp" 10 | #include "../Emulator.hpp" 11 | typedef struct{ 12 | uint8_t segment; 13 | uint16_t offset; 14 | char srcbuf[40]; 15 | }CodeElem; 16 | 17 | enum EmuDebugFlag{ 18 | DEBUG_BREAKPOINT=1, 19 | DEBUG_STEP=2, 20 | DEBUG_RET_TRACE=4 21 | }; 22 | 23 | extern std::unordered_map DebugBreakPoints; 24 | 25 | class CodeViewer :public UiBase 26 | { 27 | private: 28 | casioemu::Emulator *emu; 29 | std::vector codes; 30 | size_t rows; 31 | std::string src_path; 32 | char adrbuf[9]{0}; 33 | int max_row = 0; 34 | int max_col = 0; 35 | int cur_col = 0; 36 | bool is_loaded = false; 37 | bool edit_active = false; 38 | bool need_roll = false; 39 | int cur_break_real_pc = -1; 40 | uint32_t selected_addr = -1; 41 | 42 | public: 43 | UI_SINGLE_HEAD(CodeViewer) 44 | uint8_t debug_flags = DEBUG_BREAKPOINT; 45 | bool isbreaked = false; 46 | CodeViewer(std::string path); 47 | ~CodeViewer(); 48 | bool TryTrigBP(uint8_t seg,uint16_t offset,bool bp_mode=true); 49 | CodeElem LookUp(uint8_t seg,uint16_t offset,int *idx=0); 50 | void DrawWindow(); 51 | void Show(); 52 | void DrawContent(); 53 | void DrawMonitor(); 54 | void JumpTo(uint8_t seg,uint16_t offset); 55 | }; -------------------------------------------------------------------------------- /asm/keys.py: -------------------------------------------------------------------------------- 1 | key_codes=[ 2 | # 3 | ['[nul]','[space]',*['@']*14], 4 | [*['@']*9,'[]',*['@']*6], 5 | ['i','e','兀',':','$','?',*['@']*6,',','x10','.','@'], 6 | ['0','1','2','3','4','5','6','7','8','9','[A]','[B]','[C]','[D]','[E]','[F]'], 7 | ['M','ANS','A','B','C','D','E','F','x','y','PreAns',*['@']*5], 8 | [*['@']*16], 9 | ['(',*['@']*5,'Conjg','Arg','Abs','Rnd(',*['@']*2,'sinh','cosh','tanh','sinh-1'], 10 | ['cosh-1','tanh-1','e^','10^','√','ln','∛','sin','cos','tan','sin-1','cos-1','tan-1','log(',*['@']*2], 11 | [*['@']*15,'Rep'], 12 | [*['@']*16], 13 | [*['@']*5,'=','+','-','*','∻','∻R','·','∠','|P','|C','@'], 14 | [*['@']*16], 15 | [*['@']*8,'∟','^(','x√x',*['@']*5], 16 | [')','@','>a+bi','>r∠0','-1','square','cube','%','!',*['@']*3,'∘','E','P','T'], 17 | ['G','M','k','m','u','n','p','f',*['@']*8], 18 | [*['@']*16] 19 | ] 20 | 21 | tmp_uglyc=[] 22 | def byte2keys(b): 23 | if len(b) != 2: 24 | return '',False 25 | y,x = 0,0 26 | if b[0] >= 'a': 27 | y = ord(b[0]) -ord('a')+10 28 | elif '0' <= b[0] <= '9': 29 | y = ord(b[0]) -ord('0') 30 | else: 31 | return '',False 32 | if b[1] >= 'a': 33 | x = ord(b[1]) -ord('a')+10 34 | elif '0' <= b[1] <= '9': 35 | x = ord(b[1]) -ord('0') 36 | else: 37 | return '',False 38 | try: 39 | s = key_codes[y][x] 40 | except: 41 | tmp_uglyc.append(str(b)) 42 | return '<'+b+'>',True 43 | if s == '@': 44 | tmp_uglyc.append(str(b)) 45 | return '<'+b+'>',True 46 | return key_codes[y][x],True -------------------------------------------------------------------------------- /docs/README_en.md: -------------------------------------------------------------------------------- 1 | # CasioEmuNeo 2 | 3 | [中文版本](../README.md) | [English](./README_en.md) 4 | 5 | An emulator and disassembler for the CASIO calculator series using the nX-U8/100 core. 6 | With debuggers. 7 | 8 | ## Guide 9 | 10 | ## Build 11 | 0. install xmake 12 | `curl -fsSL https://xmake.io/shget.text | bash` 13 | install and configure Mingw64 14 | 1. notice that this project has already included the full `fx991cn` debugging resources 15 | which means you don need to download any extra resources for `fx991cnx` 16 | To run debug on it, please: 17 | ``` 18 | cd emulator 19 | xmake f -p mingw 20 | xmake 21 | xmake run CasioEmuX ../models/fx991cnx 22 | ``` 23 | Other models please follow the instructions below: 24 | 25 | 2. build `disas` 26 | ``` 27 | cd disas 28 | make 29 | ``` 30 | 3. build model directory 31 | download `rom` as name `rom.bin` in your model's directory 32 | 33 | for example, fx991cnx: 34 | ``` 35 | cd disas 36 | ``` 37 | REMEMBER cd to `disas` IS IMPORTANT! 38 | ``` 39 | ./bin/u8-disas ../models/fx991cnx/rom.bin 0 0x40000 ./_disas.txt 40 | ``` 41 | check _disas.txt and make sure it successfully generated 42 | then, copy it to your model's directory 43 | ``` 44 | cp ./_disas.txt ../models/fx991cnx/ 45 | ``` 46 | then modify `model.lua` 47 | set `rom_path` to `"rom.bin"` 48 | 49 | 4. build emulator & debugger 50 | ``` 51 | cd emulator 52 | xmake 53 | ``` 54 | wait until everything is done 55 | then 56 | ``` 57 | xmake run CasioEmuX ../models/fx991cnx 58 | ``` -------------------------------------------------------------------------------- /emulator/Config/Config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | #include "ini.hpp" 8 | 9 | #include "imgui.h" 10 | 11 | enum TranslateMap{ 12 | UI_TITLE, 13 | UI_DISAS, 14 | UI_DISAS_WAITING, 15 | UI_DISAS_GOTO_ADDR, 16 | UI_DISAS_STEP, 17 | UI_DISAS_TRACE, 18 | UI_DISAS_CONTINUE, 19 | UI_REPORT_WINDOW, 20 | UI_REPORT_RANGE, 21 | UI_REPORT_RANGE_SLIDER, 22 | UI_STACK, 23 | UI_INJECTOR, 24 | UI_CHANGE_SCALE, 25 | UI_CHANGE_SCALE_SLIDER, 26 | UI_ROP_INPUT, 27 | UI_ROP_SETINPUTRANGE, 28 | UI_ROP_ANOFFSET, 29 | UI_ROP_ENTERAN, 30 | UI_ROP_LOAD, 31 | UI_ROP_LOADFROMSTR, 32 | UI_INFO1, 33 | UI_INFO2, 34 | UI_INFO3, 35 | UI_MEMEDIT, 36 | UI_REGS_BREAK_HINT, 37 | UI_BP_SELECT_MODE, 38 | UI_BP_FIND_READ, 39 | UI_BP_FIND_WRITE, 40 | UI_BP_DELETE, 41 | UI_BP_NOT_SET, 42 | UI_BP_LISTENING, 43 | UI_BP_CLEAR_RECORDS, 44 | UI_BP_ADDR, 45 | UI_BP_ADD_ADDR, 46 | UI_BP_MODE, 47 | UI_BP_WRITE, 48 | UI_BP_READ, 49 | UI_MEM_BP, 50 | }; 51 | 52 | class EmuConfig{ 53 | 54 | private: 55 | std::string path; 56 | mINI::INIStructure root; 57 | mINI::INIFile *file = nullptr; 58 | std::unordered_map translate; 59 | ImFontGlyphRangesBuilder fbuilder; 60 | bool format_succ = false;; 61 | 62 | void initTranslate(); 63 | 64 | void loadTranslate(std::string path); 65 | 66 | void update(); 67 | public: 68 | EmuConfig(const char* filepath); 69 | 70 | ImFontGlyphRangesBuilder& GetAtlas(); 71 | 72 | float GetScale(); 73 | 74 | void SetScale(float num); 75 | 76 | char* operator[](int idx); 77 | 78 | std::string GetFontPath(); 79 | 80 | std::string GetModulePath(); 81 | 82 | }; 83 | 84 | extern EmuConfig EmuGloConfig; -------------------------------------------------------------------------------- /emulator/Chipset/CPULoadStore.cpp: -------------------------------------------------------------------------------- 1 | #include "CPU.hpp" 2 | 3 | #include "../Emulator.hpp" 4 | #include "Chipset.hpp" 5 | #include "MMU.hpp" 6 | 7 | namespace casioemu 8 | { 9 | // * Load/Store Instructions 10 | void CPU::OP_LS_EA() 11 | { 12 | LoadStore(reg_ea, impl_hint >> 8); 13 | } 14 | 15 | void CPU::OP_LS_R() 16 | { 17 | LoadStore(impl_operands[1].value, impl_hint >> 8); 18 | } 19 | 20 | void CPU::OP_LS_I_R() 21 | { 22 | LoadStore(impl_operands[1].value + impl_long_imm, impl_hint >> 8); 23 | } 24 | 25 | void CPU::OP_LS_BP() 26 | { 27 | impl_operands[1].value |= (impl_operands[1].value & 0x20) ? 0xFFC0 : 0; 28 | impl_operands[1].value += reg_r[12] | (((uint16_t)reg_r[13]) << 8); 29 | LoadStore(impl_operands[1].value, impl_hint >> 8); 30 | } 31 | 32 | void CPU::OP_LS_FP() 33 | { 34 | impl_operands[1].value |= (impl_operands[1].value & 0x20) ? 0xFFC0 : 0; 35 | impl_operands[1].value += reg_r[14] | (((uint16_t)reg_r[15]) << 8); 36 | LoadStore(impl_operands[1].value, impl_hint >> 8); 37 | } 38 | 39 | void CPU::OP_LS_I() 40 | { 41 | LoadStore(impl_long_imm, impl_hint >> 8); 42 | } 43 | 44 | void CPU::LoadStore(uint16_t offset, size_t length) 45 | { 46 | if (length % 2 == 0) 47 | offset &= ~1; 48 | size_t reg_base = impl_operands[0].value; 49 | if (impl_hint & H_ST) 50 | { 51 | for (size_t ix = length - 1; ix != (size_t)-1; --ix) 52 | emulator.chipset.mmu.WriteData((((size_t)reg_dsr) << 16) | (uint16_t)(offset + ix), reg_r[reg_base + ix]); 53 | } 54 | else 55 | { 56 | for (size_t ix = 0; ix != length; ++ix) 57 | { 58 | impl_operands[0].value = emulator.chipset.mmu.ReadData((((size_t)reg_dsr) << 16) | (uint16_t)(offset + ix)); 59 | ZSCheck(); // * defined in CPUArithmetic.cpp 60 | reg_r[reg_base + ix] = impl_operands[0].value; 61 | } 62 | } 63 | 64 | if (impl_hint & H_IA) 65 | BumpEA(length); // * defined in CPUControl.cpp 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /emulator/Peripheral/StandbyControl.cpp: -------------------------------------------------------------------------------- 1 | #include "StandbyControl.hpp" 2 | 3 | #include "../Logger.hpp" 4 | #include "../Chipset/MMU.hpp" 5 | #include "../Emulator.hpp" 6 | #include "../Chipset/Chipset.hpp" 7 | 8 | namespace casioemu 9 | { 10 | void StandbyControl::Initialise() 11 | { 12 | region_stpacp.Setup(0xF008, 1, "StandbyControl/STPACP", this, MMURegion::IgnoreRead<0x00>, [](MMURegion *region, size_t, uint8_t data) { 13 | StandbyControl *self = (StandbyControl *)(region->userdata); 14 | if ((data & 0xF0) == 0xA0 && (self->stpacp_last & 0xF0) == 0x50) 15 | self->stop_acceptor_enabled = true; 16 | self->stpacp_last = data; 17 | }, emulator); 18 | 19 | region_sbycon.Setup(0xF009, 1, "StandbyControl/SBYCON", this, MMURegion::IgnoreRead<0x00>, [](MMURegion *region, size_t, uint8_t data) { 20 | StandbyControl *self = (StandbyControl *)(region->userdata); 21 | 22 | if (data & 0x01) 23 | { 24 | self->emulator.chipset.Halt(); 25 | return; 26 | } 27 | 28 | if (data & 0x02 && self->stop_acceptor_enabled) 29 | { 30 | self->stop_acceptor_enabled = false; 31 | self->emulator.chipset.Stop(); 32 | return; 33 | } 34 | }, emulator); 35 | 36 | if(emulator.hardware_id == HW_CLASSWIZ_II) { 37 | region_F312.Setup(0xF312, 1, "StandbyControl/F312", this, MMURegion::IgnoreRead<0x00>, [](MMURegion *region, size_t, uint8_t data) { 38 | StandbyControl *self = (StandbyControl *)(region->userdata); 39 | if (data == 0x3C && self->F312_last == 0x5A) 40 | self->shutdown_acceptor_enabled = true; 41 | self->F312_last = data; 42 | 43 | if(self->shutdown_acceptor_enabled && (data & 0xF0) == 0) { 44 | self->emulator.chipset.mmu.WriteData(0xF031, 0x03); 45 | self->emulator.chipset.Stop(); 46 | self->shutdown_acceptor_enabled = false; 47 | } 48 | }, emulator); 49 | } 50 | } 51 | 52 | void StandbyControl::Reset() 53 | { 54 | stpacp_last = 0; 55 | F312_last = 0; 56 | stop_acceptor_enabled = false; 57 | shutdown_acceptor_enabled = false; 58 | } 59 | } -------------------------------------------------------------------------------- /emulator/Gui/imgui_impl_sdlrenderer2.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer Backend for SDL_Renderer for SDL2 2 | // (Requires: SDL 2.0.17+) 3 | 4 | // Note how SDL_Renderer is an _optional_ component of SDL2. 5 | // For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. 6 | // If your application will want to render any non trivial amount of graphics other than UI, 7 | // please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and 8 | // it might be difficult to step out of those boundaries. 9 | 10 | // Implemented features: 11 | // [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! 12 | // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. 13 | 14 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 15 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 16 | // Learn about Dear ImGui: 17 | // - FAQ https://dearimgui.com/faq 18 | // - Getting Started https://dearimgui.com/getting-started 19 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). 20 | // - Introduction, links and more at the top of imgui.cpp 21 | 22 | #pragma once 23 | #ifndef IMGUI_DISABLE 24 | #include "imgui.h" // IMGUI_IMPL_API 25 | 26 | struct SDL_Renderer; 27 | 28 | IMGUI_IMPL_API bool ImGui_ImplSDLRenderer2_Init(SDL_Renderer* renderer); 29 | IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_Shutdown(); 30 | IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_NewFrame(); 31 | IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data); 32 | 33 | // Called by Init/NewFrame/Shutdown 34 | IMGUI_IMPL_API bool ImGui_ImplSDLRenderer2_CreateFontsTexture(); 35 | IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_DestroyFontsTexture(); 36 | IMGUI_IMPL_API bool ImGui_ImplSDLRenderer2_CreateDeviceObjects(); 37 | IMGUI_IMPL_API void ImGui_ImplSDLRenderer2_DestroyDeviceObjects(); 38 | 39 | #endif // #ifndef IMGUI_DISABLE 40 | -------------------------------------------------------------------------------- /emulator/Chipset/Chipset.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include "MMURegion.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace casioemu 12 | { 13 | class Emulator; 14 | class CPU; 15 | class MMU; 16 | class Peripheral; 17 | 18 | class Chipset 19 | { 20 | enum InterruptIndex 21 | { 22 | INT_CHECKFLAG, 23 | INT_RESET, 24 | INT_BREAK, 25 | INT_EMULATOR, 26 | INT_NONMASKABLE, 27 | INT_MASKABLE, 28 | INT_SOFTWARE = 64, 29 | INT_COUNT = 128 30 | }; 31 | 32 | enum RunMode 33 | { 34 | RM_STOP, 35 | RM_HALT, 36 | RM_RUN 37 | }; 38 | RunMode run_mode; 39 | 40 | std::forward_list peripherals; 41 | 42 | /** 43 | * A bunch of internally used methods for encapsulation purposes. 44 | */ 45 | size_t pending_interrupt_count; 46 | bool interrupts_active[INT_COUNT]; 47 | void AcceptInterrupt(); 48 | void RaiseSoftware(size_t index); 49 | 50 | void ConstructPeripherals(); 51 | void DestructPeripherals(); 52 | 53 | void ConstructInterruptSFR(); 54 | void DestructInterruptSFR(); 55 | MMURegion region_int_mask, region_int_pending; 56 | uint16_t data_int_mask, data_int_pending; 57 | static const size_t managed_interrupt_base = 4, managed_interrupt_amount = 13; 58 | static const uint16_t interrupt_bitfield_mask = (1 << managed_interrupt_amount) - 1; 59 | 60 | public: 61 | Chipset(Emulator &emulator); 62 | void Setup(); // must be called after emulator.hardware_id is initialized 63 | ~Chipset(); 64 | 65 | Emulator &emulator; 66 | CPU &cpu; 67 | MMU &mmu; 68 | std::vector rom_data; 69 | 70 | /** 71 | * This exists because the Emulator that owns this Chipset is not ready 72 | * to supply a ROM path upon construction. It has to call `LoadROM` later 73 | * in its constructor. 74 | */ 75 | void SetupInternals(); 76 | 77 | /** 78 | * See 1.3.7 in the nX-U8 manual. 79 | */ 80 | void Reset(); 81 | void Break(); 82 | void Halt(); 83 | void Stop(); 84 | void RaiseEmulator(); 85 | void RaiseNonmaskable(); 86 | void RaiseMaskable(size_t index); 87 | bool InterruptEnabledBySFR(size_t index); 88 | void SetInterruptPendingSFR(size_t index); 89 | bool GetInterruptPendingSFR(size_t index); 90 | 91 | bool GetRunningState(); 92 | 93 | void Tick(); 94 | bool GetRequireFrame(); 95 | void Frame(); 96 | void UIEvent(SDL_Event &event); 97 | 98 | friend class CPU; 99 | }; 100 | } 101 | 102 | -------------------------------------------------------------------------------- /emulator/Peripheral/Miscellaneous.cpp: -------------------------------------------------------------------------------- 1 | #include "Miscellaneous.hpp" 2 | 3 | #include "../Logger.hpp" 4 | #include "../Emulator.hpp" 5 | #include "../Chipset/Chipset.hpp" 6 | #include "../Chipset/CPU.hpp" 7 | 8 | #include 9 | #include 10 | 11 | namespace casioemu 12 | { 13 | constexpr uint16_t Miscellaneous::addr []; 14 | 15 | void Miscellaneous::Initialise() 16 | { 17 | region_dsr.Setup(0xF000, 1, "Miscellaneous/DSR", this, [](MMURegion *region, size_t) { 18 | return (uint8_t)((Miscellaneous *)region->userdata)->emulator.chipset.cpu.reg_dsr; 19 | }, [](MMURegion *region, size_t, uint8_t data) { 20 | ((Miscellaneous *)region->userdata)->emulator.chipset.cpu.reg_dsr = data; 21 | }, emulator); 22 | 23 | // * TODO: figure out what these are 24 | 25 | int n_byte; 26 | switch (emulator.hardware_id) 27 | { 28 | case HW_ES_PLUS: 29 | n_byte = 5; 30 | break; 31 | case HW_CLASSWIZ: 32 | n_byte = 13; 33 | break; 34 | case HW_CLASSWIZ_II: 35 | n_byte = 13; 36 | break; 37 | default: 38 | PANIC("Unknown hardware_id\n"); 39 | } 40 | for (int i = 0; i < n_byte; ++ i) 41 | { 42 | std::ostringstream stream; 43 | stream << "Miscellaneous/Unknown/" << std::hex << std::uppercase << addr[i] << "*1"; 44 | region[i].Setup(addr[i], 1, stream.str(), &data[i], MMURegion::DefaultRead, MMURegion::DefaultWrite, emulator); 45 | } 46 | region_F048.Setup(0xF048, 8, "Miscellaneous/Unknown/F048*8", &data_F048, MMURegion::DefaultRead, MMURegion::DefaultWrite, emulator); 47 | region_F220.Setup(0xF220, 4, "Miscellaneous/Unknown/F220*4", &data_F220, MMURegion::DefaultRead, MMURegion::DefaultWrite, emulator); 48 | if(emulator.hardware_id == HW_CLASSWIZ_II) { 49 | region_F0D0.Setup(0xF0D0, 1, "Miscellaneous/Battery/F0D0", &data_F0D0, MMURegion::DefaultRead, MMURegion::DefaultWrite, emulator); 50 | region_F0D2.Setup(0xF0D2, 1, "Miscellaneous/Battery/F0D2", &data_F0D2, MMURegion::DefaultRead, MMURegion::DefaultWrite, emulator); 51 | region_F0D1.Setup(0xF0D1, 1, "Miscellaneous/Battery/F0D1", this, [](MMURegion *region, size_t offset) { 52 | Miscellaneous* miscellaneous = (Miscellaneous*) region->userdata; 53 | return miscellaneous->data_F0D1; 54 | }, [](MMURegion *region, size_t offset, uint8_t data) { 55 | Miscellaneous* miscellaneous = (Miscellaneous*) region->userdata; 56 | if(miscellaneous->data_F0D0 == 3 && miscellaneous->data_F0D2 == 0 && data == 5) { 57 | miscellaneous->data_F0D1 = 6; 58 | return; 59 | } 60 | miscellaneous->data_F0D1 = data; 61 | }, emulator); 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /emulator/Gui/imgui_impl_sdl2.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Backend for SDL2 2 | // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) 3 | // (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) 4 | 5 | // Implemented features: 6 | // [X] Platform: Clipboard support. 7 | // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. 8 | // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] 9 | // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 10 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. 11 | // [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!. 12 | 13 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 14 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 15 | // Learn about Dear ImGui: 16 | // - FAQ https://dearimgui.com/faq 17 | // - Getting Started https://dearimgui.com/getting-started 18 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). 19 | // - Introduction, links and more at the top of imgui.cpp 20 | 21 | #pragma once 22 | #include "imgui.h" // IMGUI_IMPL_API 23 | #ifndef IMGUI_DISABLE 24 | 25 | struct SDL_Window; 26 | struct SDL_Renderer; 27 | typedef union SDL_Event SDL_Event; 28 | 29 | IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); 30 | IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window); 31 | IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window); 32 | IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window); 33 | IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer); 34 | IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOther(SDL_Window* window); 35 | IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown(); 36 | IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(); 37 | IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event); 38 | 39 | #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 40 | static inline void ImGui_ImplSDL2_NewFrame(SDL_Window*) { ImGui_ImplSDL2_NewFrame(); } // 1.84: removed unnecessary parameter 41 | #endif 42 | 43 | #endif // #ifndef IMGUI_DISABLE 44 | -------------------------------------------------------------------------------- /imgui.ini: -------------------------------------------------------------------------------- 1 | [Window][Debug##Default] 2 | Pos=60,9 3 | Size=400,400 4 | Collapsed=0 5 | 6 | [Window][Disassemble Window] 7 | Pos=22,90 8 | Size=739,458 9 | Collapsed=0 10 | 11 | [Window][Memory Editor] 12 | Pos=600,43 13 | Size=552,615 14 | Collapsed=0 15 | 16 | [Window][Watch Window] 17 | Pos=469,408 18 | Size=906,534 19 | Collapsed=0 20 | 21 | [Window][Injector & Settings] 22 | Pos=179,102 23 | Size=642,576 24 | Collapsed=0 25 | 26 | [Window][info] 27 | Pos=258,310 28 | Size=347,106 29 | Collapsed=0 30 | 31 | [Window][info2] 32 | Pos=212,292 33 | Size=514,127 34 | Collapsed=0 35 | 36 | [Window][Memory Viewer] 37 | Pos=60,60 38 | Size=552,611 39 | Collapsed=0 40 | 41 | [Window][Disassembler] 42 | Pos=531,248 43 | Size=600,320 44 | Collapsed=0 45 | 46 | [Window][Injector] 47 | Pos=725,359 48 | Size=986,576 49 | Collapsed=0 50 | 51 | [Window][反汇编] 52 | Pos=0,574 53 | Size=456,269 54 | Collapsed=0 55 | DockId=0x00000001,0 56 | 57 | [Window][资源窗口] 58 | Pos=962,574 59 | Size=501,269 60 | Collapsed=0 61 | DockId=0x00000008,0 62 | 63 | [Window][注入器/设置] 64 | Pos=943,0 65 | Size=520,572 66 | Collapsed=0 67 | DockId=0x00000006,0 68 | 69 | [Window][内存编辑器] 70 | Pos=0,0 71 | Size=941,572 72 | Collapsed=0 73 | DockId=0x00000005,0 74 | 75 | [Window][set stack range] 76 | Pos=60,60 77 | Size=32,82 78 | Collapsed=0 79 | 80 | [Window][Change Scale] 81 | Pos=60,60 82 | Size=996,490 83 | Collapsed=0 84 | 85 | [Window][Terminal] 86 | Pos=-478,51 87 | Size=871,405 88 | Collapsed=0 89 | 90 | [Window][DockSpace Demo] 91 | Size=1280,720 92 | Collapsed=0 93 | 94 | [Window][按住SHIFT,拖拽来启用docking] 95 | Pos=0,0 96 | Size=1463,843 97 | Collapsed=0 98 | 99 | [Window][内存断点] 100 | Pos=458,574 101 | Size=502,269 102 | Collapsed=0 103 | DockId=0x00000007,0 104 | 105 | [Table][0x0A7D8A2C,2] 106 | Column 0 Weight=1.0000 107 | Column 1 Weight=1.0000 108 | 109 | [Docking][Data] 110 | DockSpace ID=0x3CE91334 Window=0x7425FAFE Pos=0,0 Size=1463,843 Split=Y 111 | DockNode ID=0x00000003 Parent=0x3CE91334 SizeRef=962,389 Split=X Selected=0x5F2A4183 112 | DockNode ID=0x00000005 Parent=0x00000003 SizeRef=941,604 CentralNode=1 HiddenTabBar=1 Selected=0x5F2A4183 113 | DockNode ID=0x00000006 Parent=0x00000003 SizeRef=520,604 HiddenTabBar=1 Selected=0x76DAEA8E 114 | DockNode ID=0x00000004 Parent=0x3CE91334 SizeRef=962,269 Split=X Selected=0x7332E699 115 | DockNode ID=0x00000001 Parent=0x00000004 SizeRef=399,177 HiddenTabBar=1 Selected=0x7332E699 116 | DockNode ID=0x00000002 Parent=0x00000004 SizeRef=879,177 Split=X Selected=0x3854EA19 117 | DockNode ID=0x00000007 Parent=0x00000002 SizeRef=439,269 HiddenTabBar=1 Selected=0x3CD991FF 118 | DockNode ID=0x00000008 Parent=0x00000002 SizeRef=438,269 HiddenTabBar=1 Selected=0x3854EA19 119 | 120 | -------------------------------------------------------------------------------- /emulator/Chipset/MMURegion.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Config.hpp" 3 | 4 | #include "../Logger.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace casioemu 10 | { 11 | class Emulator; 12 | 13 | struct MMURegion 14 | { 15 | typedef uint8_t (*ReadFunction)(MMURegion *, size_t); 16 | typedef void (*WriteFunction)(MMURegion *, size_t, uint8_t); 17 | 18 | size_t base, size; 19 | std::string description; 20 | void *userdata; 21 | ReadFunction read; 22 | WriteFunction write; 23 | bool setup_done; 24 | Emulator *emulator; 25 | 26 | MMURegion(); 27 | // Note: it should not be possible to copy region because there can only be at most one region 28 | // registered for each memory byte 29 | MMURegion(const MMURegion &) = delete; 30 | // Note: registered regions are referenced by pointer so they should not change their address 31 | MMURegion(MMURegion &&) = delete; 32 | MMURegion &operator=(const MMURegion &) = delete; 33 | MMURegion &operator=(MMURegion &&) = delete; 34 | ~MMURegion(); 35 | void Setup(size_t base, size_t size, std::string description, void *userdata, ReadFunction read, WriteFunction write, Emulator &emulator); 36 | void Kill(); 37 | 38 | template 39 | static uint8_t IgnoreRead(MMURegion *, size_t) 40 | { 41 | return read_value; 42 | } 43 | 44 | static void IgnoreWrite(MMURegion *, size_t, uint8_t) 45 | { 46 | } 47 | 48 | template 49 | static uint8_t DefaultRead(MMURegion *region, size_t offset) 50 | { 51 | value_type *value = (value_type *)(region->userdata); 52 | return ((*value) & mask) >> ((offset - region->base) * 8); 53 | } 54 | 55 | template 56 | static void DefaultWrite(MMURegion *region, size_t offset, uint8_t data) 57 | { 58 | value_type *value = (value_type *)(region->userdata); 59 | *value &= ~(((value_type)0xFF) << ((offset - region->base) * 8)); 60 | *value |= ((value_type)data) << ((offset - region->base) * 8); 61 | *value &= mask; 62 | } 63 | 64 | template 65 | static uint8_t DefaultReadLog(MMURegion *region, size_t offset) 66 | { 67 | logger::Info("SFR read from %06X\n", offset); 68 | value_type *value = (value_type *)(region->userdata); 69 | return ((*value) & mask) >> ((offset - region->base) * 8); 70 | } 71 | 72 | template 73 | static void DefaultWriteLog(MMURegion *region, size_t offset, uint8_t data) 74 | { 75 | value_type *value = (value_type *)(region->userdata); 76 | *value &= ~(((value_type)0xFF) << ((offset - region->base) * 8)); 77 | *value |= ((value_type)data) << ((offset - region->base) * 8); 78 | *value &= mask; 79 | logger::Info("SFR write to %06X (%02X)\n", offset, data); 80 | } 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /emulator/Data/ModelInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "ModelInfo.hpp" 2 | 3 | #include "../Emulator.hpp" 4 | #include "SpriteInfo.hpp" 5 | #include "ColourInfo.hpp" 6 | 7 | 8 | 9 | namespace casioemu 10 | { 11 | ModelInfo Emulator::GetModelInfo(std::string key) 12 | { 13 | return ModelInfo(*this, key); 14 | } 15 | 16 | ModelInfo::ModelInfo(Emulator &_emulator, std::string _key) : emulator(_emulator) 17 | { 18 | key = _key; 19 | } 20 | 21 | ModelInfo::operator std::string() 22 | { 23 | lua_geti(emulator.lua_state, LUA_REGISTRYINDEX, emulator.lua_model_ref); 24 | if (lua_getfield(emulator.lua_state, -1, key.c_str()) != LUA_TSTRING) 25 | PANIC("key '%s' is not a string\n", key.c_str()); 26 | const char *value = lua_tostring(emulator.lua_state, -1); 27 | lua_pop(emulator.lua_state, 2); 28 | return std::string(value); 29 | } 30 | 31 | ModelInfo::operator int() 32 | { 33 | lua_geti(emulator.lua_state, LUA_REGISTRYINDEX, emulator.lua_model_ref); 34 | if (lua_getfield(emulator.lua_state, -1, key.c_str()) != LUA_TNUMBER) 35 | PANIC("key '%s' is not a number\n", key.c_str()); 36 | int value = lua_tointeger(emulator.lua_state, -1); 37 | lua_pop(emulator.lua_state, 2); 38 | return value; 39 | } 40 | 41 | ModelInfo::operator SpriteInfo() 42 | { 43 | lua_geti(emulator.lua_state, LUA_REGISTRYINDEX, emulator.lua_model_ref); 44 | if (lua_getfield(emulator.lua_state, -1, key.c_str()) != LUA_TTABLE) 45 | PANIC("key '%s' is not a table\n", key.c_str()); 46 | 47 | for (int ix = 0; ix != 6; ++ix) 48 | if (lua_geti(emulator.lua_state, -1 - ix, ix + 1) != LUA_TNUMBER) 49 | PANIC("key '%s'[%i] is not a number\n", key.c_str(), ix + 1); 50 | 51 | SpriteInfo sprite_info; 52 | sprite_info.src.x = lua_tointeger(emulator.lua_state, -6); 53 | sprite_info.src.y = lua_tointeger(emulator.lua_state, -5); 54 | sprite_info.src.w = lua_tointeger(emulator.lua_state, -4); 55 | sprite_info.src.h = lua_tointeger(emulator.lua_state, -3); 56 | sprite_info.dest.x = lua_tointeger(emulator.lua_state, -2); 57 | sprite_info.dest.y = lua_tointeger(emulator.lua_state, -1); 58 | sprite_info.dest.w = sprite_info.src.w; 59 | sprite_info.dest.h = sprite_info.src.h; 60 | 61 | lua_pop(emulator.lua_state, 7); 62 | return sprite_info; 63 | } 64 | 65 | ModelInfo::operator ColourInfo() 66 | { 67 | lua_geti(emulator.lua_state, LUA_REGISTRYINDEX, emulator.lua_model_ref); 68 | if (lua_getfield(emulator.lua_state, -1, key.c_str()) != LUA_TTABLE) 69 | PANIC("key '%s' is not a table\n", key.c_str()); 70 | 71 | for (int ix = 0; ix != 3; ++ix) 72 | if (lua_geti(emulator.lua_state, -1 - ix, ix + 1) != LUA_TNUMBER) 73 | PANIC("key '%s'[%i] is not a number\n", key.c_str(), ix + 1); 74 | 75 | ColourInfo colour_info; 76 | colour_info.r = lua_tointeger(emulator.lua_state, -3); 77 | colour_info.g = lua_tointeger(emulator.lua_state, -2); 78 | colour_info.b = lua_tointeger(emulator.lua_state, -1); 79 | 80 | lua_pop(emulator.lua_state, 4); 81 | return colour_info; 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /asm/sym.txt: -------------------------------------------------------------------------------- 1 | ; content here is used for assembler to work 2 | ; MODIFY CAREFULLY! 3 | 4 | 086f0 memcpy 5 | 6 | 203C8 smart_strcpy 7 | 203D6 smart_strcat 8 | @203B8 smart_strlen 9 | 10 | 220fe line_print_col0 11 | 12 | @221AC printline 13 | 14 | 22492 hex_byte 15 | 16 | @086F8 render_e3d4 17 | @08704 render_ddd4 18 | @08704 flush_screen 19 | 20 | @08FB8 memzero 21 | 22 | @1F99C reset_routine 23 | 24 | 29D5C get_string_constant 25 | @07EAC fill_screen 26 | $081A2 str_decompress_print 27 | 221c4 waitkey 28 | @0F746 read_key 29 | ;convert read_phy_key result (er0) to key code 30 | 252dc key_cvt 31 | 32 | 119B6 setlr 33 | 34 | @0916A delay 35 | 093FE _start 36 | 24E36 main 37 | 38 | @086E0 buf1_to_buf2 39 | 40 | @26226 byte_strlen 41 | 42 | @22040 diagnostic_mode 43 | @2209E diagnostic 44 | 45 | 46 | @2A048 line_print__call__ 47 | 48 | @082EC char_print_1byte 49 | @082F6 char_print 50 | 51 | 084D8 char_get_14 52 | 53 | 085B8 char_get_l14 54 | 0939A zero_KO 55 | 56 | 07F00 buffer_clear 57 | 58 | 084D8 char_get_14 59 | 09060 assign_var 60 | ; er0: char, er2: output adr 61 | 62 | ;dbd0 有意思 63 | ; 0f 0e d9 0c 0b 0a 09 08 07 06 64 | [GADGETS] 65 | 66 | 08f22 r0=0,[er8]+=er2,pop xr8 67 | 203D2 st r2,[er0] 68 | 130E2 pop QR0 69 | 121a8 pop er0 70 | 2208a pop xr0 71 | 26920 pop xr8 72 | 187D6 pop er4 73 | 0d06c pop er8 74 | 08f44 pop qr8 75 | 18FE6 pop er12 76 | 20d72 pop ER14 77 | 20d70 jpop er14 78 | 12d34 jpop qr8 79 | 2237a jpop qr80 80 | 222AC nop 81 | 09D22 ENTER 82 | 13CD0 LEAVE 83 | 090B0 store_sp_er0 84 | ;084F8 EA magic instr :) 85 | 13ADC l er0,[er2] 86 | 13916 st er2,[er0] 87 | 13aa8 L er8,[er0] 88 | 10e52 l r0,[er14] 89 | 1810e l r0,[er0] 90 | 15ee4 mov r2,r0,pop er0 91 | 0ec6c mov er0,1 92 | 09a1a mov er2,1 93 | 94 | 1fbe0 mov er0,er2 95 | 14b14 mov er2,er0 96 | 1e768 mov er0,er4,pop er4 97 | 187e2 add er0,er2 98 | ;14f0c 99 | 12312 or r0,r1 100 | 1b87a sll r0,4 101 | ; 102 | 262b0 cif 103 | 13abe add er4,er0 104 | 17B46 lea [er12] 105 | 203d2 st r2,[er0] 106 | 1c82c st r2,[ea+] 107 | 204e6 st r2,[ea+], pop xr4, pop qr8 108 | 1D806 and r0,15 109 | 20838 and r0,15 110 | ;19940 and r1,15 111 | 1a45e and r1,15 112 | 20840 and r1, 15, sll r0, 4, or r1, r0 113 | 187e2 add er0,er2 114 | ;13aa6 add er0 er2 115 | ;14f0c ~ 116 | ;187e2 ~ 117 | ;2abf8 ~ 118 | ;8eee ~ 119 | ;d112 c4 --> complex mode 120 | ;d11e --> math i/o 121 | ;15ee4 122 | ;f2de mov r2,r1 123 | 124 | ;16d7c好函数,两两byte or运算 125 | ; r10 byte个数 er12 目标地址 ea 起始地址 -------------------------------------------------------------------------------- /emulator/utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by bczhc on 15/04/24. 3 | // 4 | 5 | #include "utils.h" 6 | #include 7 | #include 8 | 9 | const char *casioemu::Exception::what() const noexcept { 10 | return msg.c_str(); 11 | } 12 | 13 | casioemu::Exception::Exception(std::string _msg) : msg(std::move(_msg)) {} 14 | 15 | std::vector casioemu::ParseColoredSpansConfig(const std::string &path) { 16 | auto result = std::vector(); 17 | 18 | std::ifstream file(path); 19 | if (!file.is_open()) { 20 | throw Exception("Failed to open the file."); 21 | } 22 | 23 | std::string line; 24 | while (std::getline(file, line)) { 25 | if (starts_with(line, "#")) { 26 | // ignore "comments" 27 | continue; 28 | } 29 | 30 | std::stringstream ss(line); 31 | std::vector parts; 32 | std::string part; 33 | while (std::getline(ss, part, ',')) { 34 | parts.push_back(part); 35 | } 36 | if (parts.size() < 3) continue; 37 | 38 | auto range_start = strtoul(parts[0].c_str(), nullptr, 16); 39 | size_t range_end; 40 | if (starts_with(parts[1], "0x")) { 41 | range_end = strtoul(parts[1].c_str(), nullptr, 16); 42 | } else { 43 | range_end = range_start + strtoul(parts[1].c_str(), nullptr, 10) - 1; 44 | } 45 | int r = 0, g = 0, b = 0, a = 0; 46 | 47 | if (parts[2].length() == 6) { 48 | sscanf(parts[2].c_str(), "%02x%02x%02x", &r, &g, &b); // NOLINT(*-err34-c) 49 | } else if (parts[2].length() == 8) { 50 | sscanf(parts[2].c_str(), "%02x%02x%02x%02x", &a, &r, &g, &b); // NOLINT(*-err34-c) 51 | } 52 | 53 | MemoryEditor::MarkedSpan span = { 54 | .start = range_start, 55 | .length = range_end - range_start + 1, 56 | .color = ImColor(r, g, b, a == 0 ? 50 : a), 57 | }; 58 | if (parts.size() == 4) { 59 | span.desc = parts[3]; 60 | } 61 | result.push_back(span); 62 | } 63 | file.close(); 64 | return result; 65 | } 66 | 67 | bool casioemu::starts_with(const std::string &str, const std::string &prefix) { 68 | if (str.length() < prefix.length()) { 69 | return false; 70 | } 71 | return str.substr(0, prefix.length()) == prefix; 72 | } 73 | 74 | bool casioemu::FileSystem::exists(const std::string &path) { 75 | return access(path.c_str(), F_OK) == 0; 76 | } 77 | 78 | timespec casioemu::FileSystem::mtime(const std::string &path) { 79 | struct stat stat_r{}; 80 | if (stat(path.c_str(), &stat_r) != 0) { 81 | perror("stat"); 82 | return {}; 83 | } 84 | #ifdef _WIN32 85 | return {stat_r.st_mtime,0}; 86 | #else 87 | return stat_r.st_mtim; 88 | #endif 89 | } 90 | 91 | uint64_t casioemu::FileSystem::mtime_ms(const std::string &path) { 92 | const timespec &ts = FileSystem::mtime(path); 93 | return (uint64_t) ts.tv_sec * 1000 + (uint64_t) ts.tv_nsec / 1000000; 94 | } 95 | -------------------------------------------------------------------------------- /asm/analyze/printline.txt: -------------------------------------------------------------------------------- 1 | printline(er2) 2 | { 3 | r1 = lineno 4 | r0 = 14 // 一行14个字符 5 | (*0xd137) = r0 6 | (*0821Ch)() 7 | } 8 | 0221AC CE F8 PUSH LR 9 | 0221AE 00 81 MOV R1, R0 10 | 0221B0 0E 00 MOV R0, #14 11 | 0221B2 11 90 37 D1 ST R0, 0D137h 12 | 0221B6 01 F0 1C 82 BL 00h:0821Ch 13 | 0221BA 8E F2 POP PC 14 | 15 | printlinecall() 16 | { 17 | r0 = 0 18 | er8 = er0 19 | er10 = er2 20 | r13 = 11 21 | r0 = *(0x0d137) // 一行字符数 22 | if(r0 != 14){ 23 | r13 = 6 24 | } 25 | 26 | r15 = 192 // 0xc0 27 | r15 -= r13 28 | r12 = 0 29 | r3 = 0 30 | if(r8 > r15){ 31 | 32 | } 33 | } 34 | 35 | 00821C 00 00 MOV R0, #0 36 | 00821E CE F8 PUSH LR 37 | 008220 7E F8 PUSH QR8 38 | 008222 05 F8 MOV ER8, ER0 39 | 008224 25 FA MOV ER10, ER2 40 | 008226 0B 0D MOV R13, #11 41 | 008228 10 90 37 D1 L R0, 0D137h 42 | 00822C 0E 70 CMP R0, #14 43 | 00822E 01 C9 BC EQ, 08232h 44 | 008230 06 0D MOV R13, #6 45 | 008232 C0 0F MOV R15, #192 46 | 008234 D8 8F SUB R15, R13 47 | 008236 00 0C MOV R12, #0 48 | 008238 00 03 MOV R3, #0 49 | 00823A F7 88 CMP R8, R15 50 | 00823C 28 C2 BC GT, 0828Eh 51 | 00823E A0 92 L R2, [ER10] 52 | 008240 26 C9 BC EQ, 0828Eh 53 | 008242 04 72 CMP R2, #4 54 | 008244 07 C9 BC EQ, 08254h 55 | 008246 F0 72 CMP R2, #240 56 | 008248 1F C0 BC GE, 08288h 57 | 00824A 01 72 CMP R2, #1 58 | 00824C 0E C8 BC NE, 0826Ah 59 | 00824E 01 F0 96 82 BL 00h:08296h 60 | 008252 17 CE BC AL, 08282h 61 | 008254 10 90 37 D1 L R0, 0D137h 62 | 008258 0E 70 CMP R0, #14 63 | 00825A 17 C8 BC NE, 0828Ah 64 | 00825C 81 EA ADD ER10, #1 65 | 00825E A0 92 L R2, [ER10] 66 | 008260 85 F0 MOV ER0, ER8 67 | 008262 01 F0 F6 82 BL 00h:082F6h 68 | 008266 09 18 ADD R8, #9 69 | 008268 0C CE BC AL, 08282h 70 | 00826A 85 F0 MOV ER0, ER8 71 | 00826C 5E F2 PUSH ER2 72 | 00826E 01 F0 F6 82 BL 00h:082F6h 73 | 008272 1E F2 POP ER2 74 | 008274 F1 73 CMP R3, #241 75 | 008276 04 C9 BC EQ, 08280h 76 | 008278 F2 73 CMP R3, #242 77 | 00827A 02 C9 BC EQ, 08280h 78 | 00827C D1 88 ADD R8, R13 79 | 00827E 01 CE BC AL, 08282h 80 | 008280 0C 18 ADD R8, #12 81 | 008282 00 03 MOV R3, #0 82 | 008284 01 1C ADD R12, #1 83 | 008286 01 CE BC AL, 0828Ah 84 | 008288 20 83 MOV R3, R2 85 | 00828A 81 EA ADD ER10, #1 86 | 00828C D6 CE BC AL, 0823Ah 87 | 00828E C0 80 MOV R0, R12 88 | 008290 A5 F2 MOV ER2, ER10 89 | 008292 3E F8 POP QR8 90 | 008294 8E F2 POP PC -------------------------------------------------------------------------------- /emulator/Peripheral/Timer.cpp: -------------------------------------------------------------------------------- 1 | #include "Timer.hpp" 2 | 3 | #include "../Logger.hpp" 4 | #include "../Chipset/MMU.hpp" 5 | #include "../Emulator.hpp" 6 | #include "../Chipset/Chipset.hpp" 7 | 8 | namespace casioemu 9 | { 10 | void Timer::Initialise() 11 | { 12 | interrupt_source.Setup(9, emulator); 13 | 14 | real_hardware = emulator.GetModelInfo("real_hardware"); 15 | TimerSkipped = false; 16 | 17 | region_interval.Setup(0xF020, 2, "Timer/Interval", &data_interval, MMURegion::DefaultRead, [](MMURegion *region, size_t offset, uint8_t data) { 18 | uint16_t *value = (uint16_t *)(region->userdata); 19 | *value &= ~(((uint16_t)0xFF) << ((offset - region->base) * 8)); 20 | *value |= ((uint16_t)data) << ((offset - region->base) * 8); 21 | if (!*value) 22 | *value = 1; 23 | }, emulator); 24 | 25 | region_counter.Setup(0xF022, 2, "Timer/Counter", &data_counter, MMURegion::DefaultRead, [](MMURegion *region, size_t, uint8_t) { 26 | *((uint16_t *)region->userdata) = 0; 27 | }, emulator); 28 | 29 | region_control.Setup(0xF025, 1, "Timer/Control", this, [](MMURegion *region, size_t) { 30 | Timer *timer = (Timer *)region->userdata; 31 | return (uint8_t)(timer->data_control & 0x01); 32 | }, [](MMURegion *region, size_t, uint8_t data) { 33 | Timer *timer = (Timer *)region->userdata; 34 | timer->data_control = data & 0x01; 35 | timer->raise_required = false; 36 | timer->TimerSkipped = false; 37 | }, emulator); 38 | 39 | region_F024.Setup(0xF024, 1, "Timer/Unknown/F024*1", &data_F024, MMURegion::DefaultRead, MMURegion::DefaultWrite, emulator); 40 | } 41 | 42 | void Timer::Reset() 43 | { 44 | ext_to_int_counter = 0; 45 | ext_to_int_next = 0; 46 | ext_to_int_int_done = 0; 47 | DivideTicks(); 48 | 49 | raise_required = false; 50 | TimerSkipped = false; 51 | data_control = 0; 52 | } 53 | 54 | void Timer::Tick() 55 | { 56 | if (ext_to_int_counter == ext_to_int_next) 57 | DivideTicks(); 58 | ++ext_to_int_counter; 59 | 60 | if (raise_required) 61 | interrupt_source.TryRaise(); 62 | } 63 | 64 | void Timer::TickAfterInterrupts() 65 | { 66 | if (raise_required && interrupt_source.Success()) { 67 | raise_required = false; 68 | TimerSkipped = false; 69 | } 70 | } 71 | 72 | void Timer::DivideTicks() 73 | { 74 | if(emulator.hardware_id == HW_CLASSWIZ_II && !real_hardware && !emulator.chipset.GetRunningState()) { 75 | uint8_t keyboard_ready_emu = emulator.chipset.mmu.ReadData(0x088E00); 76 | if(keyboard_ready_emu == 8) { 77 | TimerSkipped = true; 78 | if(emulator.chipset.mmu.ReadData(0x088E01) == 4 && emulator.chipset.mmu.ReadData(0x088E02) == 16) { 79 | emulator.chipset.mmu.WriteData(0x088E00, 1); 80 | } else { 81 | emulator.chipset.mmu.WriteData(0x088E00, 0); 82 | } 83 | } else if(keyboard_ready_emu == 4) { 84 | TimerSkipped = true; 85 | } 86 | } 87 | ++ext_to_int_int_done; 88 | if (ext_to_int_int_done == ext_to_int_frequency) 89 | { 90 | ext_to_int_int_done = 0; 91 | ext_to_int_counter = 0; 92 | } 93 | ext_to_int_next = emulator.GetCyclesPerSecond() * (ext_to_int_int_done + 1) / ext_to_int_frequency; 94 | 95 | if (data_control & 0x01) 96 | { 97 | if (data_counter == (TimerSkipped ? 1 : data_interval)) 98 | { 99 | data_counter = 0; 100 | if (interrupt_source.Enabled()) 101 | raise_required = true; 102 | } 103 | ++data_counter; 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /emulator/Chipset/CPUPushPop.cpp: -------------------------------------------------------------------------------- 1 | #include "CPU.hpp" 2 | 3 | #include "../Emulator.hpp" 4 | #include "Chipset.hpp" 5 | #include "MMU.hpp" 6 | 7 | #include "../Gui/Ui.hpp" 8 | 9 | namespace casioemu 10 | { 11 | // * PUSH/POP Instructions 12 | void CPU::OP_PUSH() 13 | { 14 | size_t push_size = impl_operands[1].register_size; 15 | if (push_size == 1) 16 | push_size = 2; 17 | reg_sp -= push_size; 18 | for (size_t ix = impl_operands[1].register_size - 1; ix != (size_t)-1; --ix) 19 | emulator.chipset.mmu.WriteData(reg_sp + ix, impl_operands[1].value >> (8 * ix)); 20 | } 21 | 22 | void CPU::OP_PUSHL() 23 | { 24 | if (impl_operands[1].value & 2) 25 | { 26 | if (memory_model == MM_LARGE) 27 | Push16(reg_ecsr[reg_psw & PSW_ELEVEL]); 28 | Push16(reg_elr[reg_psw & PSW_ELEVEL]); 29 | } 30 | if (impl_operands[1].value & 4) 31 | Push16(reg_epsw[reg_psw & PSW_ELEVEL]); 32 | if (impl_operands[1].value & 8) 33 | { 34 | if (memory_model == MM_LARGE) 35 | Push16(reg_lcsr); 36 | Push16(reg_lr); 37 | 38 | if (stack.empty()) 39 | {} 40 | else if (stack.back().lr_pushed) 41 | {} 42 | else 43 | { 44 | stack.back().lr_pushed = true; 45 | stack.back().lr_push_address = reg_sp; 46 | } 47 | } 48 | if (impl_operands[1].value & 1) 49 | Push16(reg_ea); 50 | } 51 | 52 | void CPU::OP_POP() 53 | { 54 | size_t pop_size = impl_operands[0].register_size; 55 | if (pop_size == 1) 56 | pop_size = 2; 57 | impl_operands[0].value = 0; 58 | for (size_t ix = 0; ix != impl_operands[0].register_size; ++ix) 59 | impl_operands[0].value |= ((uint64_t)emulator.chipset.mmu.ReadData(reg_sp + ix)) << (8 * ix); 60 | reg_sp += pop_size; 61 | } 62 | 63 | void CPU::OP_POPL() 64 | { 65 | if (impl_operands[0].value & 1) 66 | reg_ea = Pop16(); 67 | if (impl_operands[0].value & 8) 68 | { 69 | /** 70 | * Sometimes a function calls another function in one branch, and 71 | * does not call another function in another branch. In that case 72 | * the compiler may decide to do a `push lr` / `pop lr` in only the 73 | * branch that has to save `lr`. 74 | */ 75 | if (!stack.empty() && stack.back().lr_pushed && 76 | stack.back().lr_push_address == reg_sp) 77 | stack.back().lr_pushed = false; 78 | 79 | reg_lr = Pop16(); 80 | if (memory_model == MM_LARGE) 81 | reg_lcsr = Pop16() & 0x000F; 82 | } 83 | if (impl_operands[0].value & 4) 84 | reg_psw = Pop16(); 85 | if (impl_operands[0].value & 2) 86 | { 87 | int oldsp = reg_sp; 88 | reg_pc = Pop16(); 89 | if (memory_model == MM_LARGE) 90 | reg_csr = Pop16() & 0x000F; 91 | if(CodeViewer::instance){ 92 | if(CodeViewer::instance->debug_flags & DEBUG_RET_TRACE){ 93 | if(CodeViewer::instance->TryTrigBP(reg_csr, reg_pc,false)){ 94 | emulator.SetPaused(true); 95 | } 96 | } 97 | } 98 | if (!stack.empty() && stack.back().lr_pushed && 99 | stack.back().lr_push_address == oldsp) 100 | stack.pop_back(); 101 | 102 | } 103 | } 104 | 105 | void CPU::Push16(uint16_t data) 106 | { 107 | reg_sp -= 2; 108 | emulator.chipset.mmu.WriteData(reg_sp + 1, data >> 8); 109 | emulator.chipset.mmu.WriteData(reg_sp, data & 0xFF); 110 | } 111 | 112 | uint16_t CPU::Pop16() 113 | { 114 | uint16_t result = emulator.chipset.mmu.ReadData(reg_sp) | (((uint16_t)emulator.chipset.mmu.ReadData(reg_sp + 1)) << 8); 115 | reg_sp += 2; 116 | return result; 117 | } 118 | } 119 | 120 | -------------------------------------------------------------------------------- /emulator/Peripheral/BatteryBackedRAM.cpp: -------------------------------------------------------------------------------- 1 | #include "BatteryBackedRAM.hpp" 2 | 3 | #include "../Data/HardwareId.hpp" 4 | #include "../Chipset/MMU.hpp" 5 | #include "../Emulator.hpp" 6 | #include "../Chipset/Chipset.hpp" 7 | #include "../Logger.hpp" 8 | #include "../Gui/Ui.hpp" 9 | #include 10 | #include 11 | 12 | namespace casioemu 13 | { 14 | void BatteryBackedRAM::Initialise() 15 | { 16 | bool real_hardware = emulator.GetModelInfo("real_hardware"); 17 | switch (emulator.hardware_id) 18 | { 19 | case HW_ES_PLUS: 20 | ram_size = 0xE00; 21 | break; 22 | case HW_CLASSWIZ: 23 | ram_size = 0x2000; 24 | break; 25 | case HW_CLASSWIZ_II: 26 | ram_size = 0x6000; 27 | break; 28 | } 29 | if (!real_hardware) 30 | ram_size += 0x100; 31 | 32 | ram_buffer = new uint8_t[ram_size]; 33 | for (size_t ix = 0; ix != ram_size; ++ix) 34 | ram_buffer[ix] = 0; 35 | 36 | ram_file_requested = false; 37 | if (emulator.argv_map.find("ram") != emulator.argv_map.end()) 38 | { 39 | ram_file_requested = true; 40 | 41 | if (emulator.argv_map.find("clean_ram") == emulator.argv_map.end()) 42 | LoadRAMImage(); 43 | } 44 | 45 | region.Setup(emulator.hardware_id == HW_ES_PLUS ? 0x8000 : emulator.hardware_id == HW_CLASSWIZ ? 0xD000 : 0x9000, 46 | emulator.hardware_id == HW_ES_PLUS ? 0x0E00 : emulator.hardware_id == HW_CLASSWIZ ? 0x2000 : 0x6000 , 47 | "BatteryBackedRAM", ram_buffer, [](MMURegion *region, size_t offset) { 48 | return ((uint8_t *)region->userdata)[offset - region->base]; 49 | }, [](MMURegion *region, size_t offset, uint8_t data) { 50 | ((uint8_t *)region->userdata)[offset - region->base] = data; 51 | }, emulator); 52 | if (!real_hardware) 53 | region_2.Setup(emulator.hardware_id == HW_ES_PLUS ? 0x9800 : emulator.hardware_id == HW_CLASSWIZ ? 0x49800 : 0x89800, 0x0100, 54 | "BatteryBackedRAM/2", ram_buffer + ram_size - 0x100, [](MMURegion* region, size_t offset) { 55 | return ((uint8_t*)region->userdata)[offset - region->base]; 56 | }, [](MMURegion* region, size_t offset, uint8_t data) { 57 | ((uint8_t*)region->userdata)[offset - region->base] = data; 58 | }, emulator); 59 | BatteryBackedRAM::rom_addr = (char*)ram_buffer; 60 | logger::Info("inited hex editor!\n"); 61 | } 62 | 63 | void BatteryBackedRAM::Uninitialise() 64 | { 65 | if (ram_file_requested && emulator.argv_map.find("preserve_ram") == emulator.argv_map.end()) 66 | SaveRAMImage(); 67 | 68 | delete[] ram_buffer; 69 | } 70 | 71 | void BatteryBackedRAM::SaveRAMImage() 72 | { 73 | std::ofstream ram_handle(emulator.argv_map["ram"], std::ofstream::binary); 74 | if (ram_handle.fail()) 75 | { 76 | logger::Info("[BatteryBackedRAM] std::ofstream failed: %s\n", std::strerror(errno)); 77 | return; 78 | } 79 | ram_handle.write((char *)ram_buffer, ram_size); 80 | if (ram_handle.fail()) 81 | { 82 | logger::Info("[BatteryBackedRAM] std::ofstream failed: %s\n", std::strerror(errno)); 83 | return; 84 | } 85 | } 86 | 87 | void BatteryBackedRAM::LoadRAMImage() 88 | { 89 | std::ifstream ram_handle(emulator.argv_map["ram"], std::ifstream::binary); 90 | if (ram_handle.fail()) 91 | { 92 | logger::Info("[BatteryBackedRAM] std::ifstream failed: %s\n", std::strerror(errno)); 93 | return; 94 | } 95 | ram_handle.read((char *)ram_buffer, ram_size); 96 | if (ram_handle.fail()) 97 | { 98 | logger::Info("[BatteryBackedRAM] std::ifstream failed: %s\n", std::strerror(errno)); 99 | return; 100 | } 101 | } 102 | 103 | char* BatteryBackedRAM::rom_addr = 0; 104 | } 105 | -------------------------------------------------------------------------------- /emulator/Peripheral/ROMWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "ROMWindow.hpp" 2 | 3 | #include "../Data/HardwareId.hpp" 4 | #include "../Logger.hpp" 5 | #include "../Chipset/MMU.hpp" 6 | #include "../Emulator.hpp" 7 | #include "../Chipset/Chipset.hpp" 8 | 9 | #include 10 | 11 | namespace casioemu 12 | { 13 | static void SetupROMRegion(MMURegion ®ion, size_t region_base, size_t size, size_t rom_base, bool strict_memory, Emulator& emulator, std::string description = {}) 14 | { 15 | if (rom_base + size > emulator.chipset.rom_data.size()) 16 | PANIC("Invalid ROM region: base %zx, size %zx\n", rom_base, size); 17 | uint8_t *data = emulator.chipset.rom_data.data(); 18 | auto offset = (ssize_t) rom_base - (ssize_t) region_base; 19 | if (description.empty()) 20 | description = "ROM/Segment" + std::to_string(region_base >> 16); 21 | 22 | MMURegion::WriteFunction write_function = strict_memory ? [](MMURegion *region, size_t address, uint8_t data) { 23 | logger::Info("ROM::[region write lambda]: attempt to write %02hhX to %06zX\n", data, address); 24 | region->emulator->HandleMemoryError(); 25 | } : [](MMURegion *, size_t, uint8_t) { 26 | }; 27 | 28 | if (offset >= 0) 29 | region.Setup(region_base, size, description, data + offset, [](MMURegion *region, size_t address) { 30 | return ((uint8_t *)(region->userdata))[address]; 31 | }, write_function, emulator); 32 | else 33 | region.Setup(region_base, size, description, data + rom_base, [](MMURegion *region, size_t address) { 34 | return ((uint8_t *)(region->userdata))[address - region->base]; 35 | }, write_function, emulator); 36 | } 37 | 38 | void ROMWindow::Initialise() 39 | { 40 | bool strict_memory = emulator.argv_map.find("strict_memory") != emulator.argv_map.end(); 41 | 42 | switch (emulator.hardware_id) 43 | { // Initializer list cannot be used with move-only type: https://stackoverflow.com/q/8468774 44 | case HW_ES_PLUS: 45 | regions.reset(new MMURegion[3]); 46 | SetupROMRegion(regions[0], 0x00000, 0x08000, 0x00000, strict_memory, emulator); 47 | SetupROMRegion(regions[1], 0x10000, 0x10000, 0x10000, strict_memory, emulator); 48 | SetupROMRegion(regions[2], 0x80000, 0x10000, 0x00000, strict_memory, emulator); 49 | break; 50 | 51 | case HW_CLASSWIZ: 52 | regions.reset(new MMURegion[5]); 53 | SetupROMRegion(regions[0], 0x00000, 0x0D000, 0x00000, strict_memory, emulator); 54 | SetupROMRegion(regions[1], 0x10000, 0x10000, 0x10000, strict_memory, emulator); 55 | SetupROMRegion(regions[2], 0x20000, 0x10000, 0x20000, strict_memory, emulator); 56 | SetupROMRegion(regions[3], 0x30000, 0x10000, 0x30000, strict_memory, emulator); 57 | SetupROMRegion(regions[4], 0x50000, 0x10000, 0x00000, strict_memory, emulator); 58 | break; 59 | case HW_CLASSWIZ_II: 60 | regions.reset(new MMURegion[8]); 61 | SetupROMRegion(regions[0], 0x00000, 0x09000, 0x00000, strict_memory, emulator); 62 | SetupROMRegion(regions[1], 0x10000, 0x10000, 0x10000, strict_memory, emulator); 63 | SetupROMRegion(regions[2], 0x20000, 0x10000, 0x20000, strict_memory, emulator); 64 | SetupROMRegion(regions[3], 0x30000, 0x10000, 0x30000, strict_memory, emulator); 65 | SetupROMRegion(regions[4], 0x40000, 0x10000, 0x40000, strict_memory, emulator); 66 | SetupROMRegion(regions[5], 0x50000, 0x10000, 0x50000, strict_memory, emulator); 67 | SetupROMRegion(regions[6], 0x70000, 0x10000, 0x70000, strict_memory, emulator); 68 | SetupROMRegion(regions[7], 0x80000, 0x08E00, 0x00000, strict_memory, emulator); 69 | break; 70 | } 71 | } 72 | 73 | void ROMWindow::Uninitialise() 74 | { 75 | } 76 | 77 | void ROMWindow::Tick() 78 | { 79 | } 80 | 81 | void ROMWindow::Frame() 82 | { 83 | } 84 | 85 | void ROMWindow::UIEvent(SDL_Event &) 86 | { 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /disas/example.cpp: -------------------------------------------------------------------------------- 1 | // Independent from the project. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define ip (totallen - length - buf.size()) 11 | 12 | std::string tohex(int n, int len) { 13 | std::string retval = ""; 14 | for (int x = 0; x < len; x++) { 15 | retval = "0123456789ABCDEF"[n & 0xF] + retval; 16 | n >>= 4; 17 | } 18 | return retval; 19 | } 20 | // Those are important to the disassembler. 21 | 22 | // ------------------------------------------------------------------ 1 23 | // ------------------------------------------------------------------ 2 24 | 25 | int main(int argc, char** argv) { 26 | char* st = new char[0x400]; 27 | if (argc != 5) { // argv[0] = executable file name 28 | std::ifstream fi {"help.txt"}; 29 | do { 30 | fi.getline(st, 0x400); 31 | } while (std::strcmp(st, "*") != 0); 32 | while (true) { 33 | fi.getline(st, 0x400); 34 | if (fi.fail()) { 35 | fi.close(); 36 | return 0; 37 | } 38 | std::cout << st << "\n"; 39 | } 40 | } 41 | 42 | std::ifstream in {argv[1], std::ios_base::binary}; 43 | in.seekg(std::stoi(argv[2], nullptr, 0)); 44 | int length = std::stoi(argv[3], nullptr, 0), l1, i, totallen = length; 45 | std::ofstream out {argv[4]}; 46 | std::deque buf {}; 47 | char* readbuf = new char[0x10000]; 48 | 49 | l1 = std::min(length, 0x10000); 50 | in.read(readbuf, l1); 51 | length -= l1; 52 | for (i = 0; i < l1; i++) { 53 | buf.push_back(readbuf[i]); 54 | } 55 | 56 | while (buf.size() > 0) { // assume the block is valid. 57 | // That part read number in dequeue, pop_front it for some bytes and write to (ofstream) out. 58 | 59 | // ------------------------------------------------------------------ 3 60 | 61 | // #1 62 | 63 | // aaabbb00 f1 #{a}, #{b} 64 | if ((buf[0] & 0b00000011) == 0b00000000) { 65 | int a = buf[0] >> 5 & 0b111, b = buf[0] >> 2 & 0b111; // >> has higher precedence than & 66 | out << tohex(ip, 6) << " " << tohex(buf[0], 2) << ' ' << " " // IP and opcode 67 | << "f1 #" << a << ", #" << b // command 68 | << "\n"; 69 | buf.pop_front(); 70 | goto done; 71 | } 72 | 73 | // aaabbb01 f2 #{a}, #{b} 74 | if ((buf[0] & 0b00000011) == 0b00000001) { 75 | int a = buf[0] >> 5 & 0b111, b = buf[0] >> 2 & 0b111; 76 | out << tohex(ip, 6) << " " << tohex(buf[0], 2) << ' ' << " " // def convenience: exceed 7 spaces, plus one 77 | << "f2 #" << a << ", #" << b // command 78 | << "\n"; 79 | buf.pop_front(); 80 | goto done; 81 | } 82 | 83 | // #2 84 | 85 | // aaaaaa11 bbbbbbbb f3 #{a}, #{b} 86 | if ((buf[0] & 0b00000011) == 0b00000011 && (buf[1] & 0b00000000) == 0b00000000) { 87 | int a = buf[0] >> 2 & 0b111111, b = buf[1] >> 0 & 0b11111111; 88 | out << tohex(ip, 6) << " " << tohex(buf[0], 2) << ' ' << tohex(buf[1], 2) << ' ' << " " 89 | << "f3 #" << a << ", #" << b // command 90 | << "\n"; 91 | buf.pop_front(); buf.pop_front(); 92 | goto done; 93 | } 94 | 95 | // * 96 | 97 | out << tohex(ip, 6) << " " << tohex(buf[0], 2) << ' ' << " " 98 | << "Unrecognized command" 99 | << "\n"; 100 | buf.pop_front(); 101 | 102 | // ------------------------------------------------------------------ 4 103 | 104 | done: 105 | if (buf.size() < 0x20 && length != 0) { // assume all opcode is shorter than 0x20 bytes 106 | l1 = std::min(length, 0x10000); 107 | in.read(readbuf, l1); 108 | length -= l1; 109 | for (i = 0; i < l1; i++) { 110 | buf.push_back(readbuf[i]); 111 | } 112 | } 113 | } 114 | 115 | in.close(); 116 | out.close(); 117 | 118 | return 0; 119 | } 120 | -------------------------------------------------------------------------------- /emulator/Emulator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Config.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "Data/HardwareId.hpp" 15 | #include "Data/ModelInfo.hpp" 16 | #include "Data/SpriteInfo.hpp" 17 | 18 | #define MEM_EDIT_BASE_ADDR 0xD000 19 | #define MEM_EDIT_MEM_SIZE 0x2800 20 | #define LABEL_INPUT_BUF 0xd180 21 | 22 | namespace casioemu 23 | { 24 | class Chipset; 25 | class CPU; 26 | class MMU; 27 | 28 | /** 29 | * A mutex that ensures that a thread cannot get the mutex right after it's released if there are another waiting thread. 30 | */ 31 | class FairRecursiveMutex 32 | { 33 | std::mutex m; 34 | std::thread::id holding; 35 | int recursive_count; 36 | std::queue waiting; 37 | 38 | public: 39 | FairRecursiveMutex(); 40 | ~FairRecursiveMutex(); 41 | void lock(); 42 | void unlock(); 43 | }; 44 | 45 | class Emulator 46 | { 47 | 48 | SDL_Renderer *renderer; 49 | SDL_Texture *interface_texture; 50 | unsigned int timer_interval; 51 | bool running, paused; 52 | unsigned int last_frame_tick_count; 53 | std::string model_path; 54 | bool pause_on_mem_error; 55 | 56 | std::thread *tick_thread; 57 | 58 | SpriteInfo interface_background; 59 | int width, height; 60 | 61 | /** 62 | * A bunch of internally used methods for encapsulation purposes. 63 | */ 64 | void LoadModelDefition(); 65 | void TimerCallback(); 66 | void SetupLuaAPI(); 67 | void SetupInternals(); 68 | void RunStartupScript(); 69 | 70 | public: 71 | SDL_Window *window; 72 | Emulator(std::map &argv_map, bool paused = false); 73 | ~Emulator(); 74 | 75 | FairRecursiveMutex access_mx; 76 | lua_State *lua_state; 77 | int lua_model_ref, lua_pre_tick_ref, lua_post_tick_ref; 78 | HardwareId hardware_id; 79 | std::map &argv_map; 80 | 81 | private: 82 | /** 83 | * The cycle manager structure. This structure is reset every time the 84 | * emulator starts emulating CPU cycles and in every timer callback 85 | * it's queried for the number of cycles that need to be emulated in the 86 | * callback. This ensures that only as many cycles are emulated in a period 87 | * of time as many would be in real life. 88 | * 89 | * Note that it's assumed that the GetDelta function is called once every 90 | * timer_interval milliseconds. It's up to the timer to make sure that 91 | * there's no drift. 92 | */ 93 | struct Cycles 94 | { 95 | void Setup(Uint64 cycles_per_second, unsigned int timer_interval); 96 | void Reset(); 97 | Uint64 GetDelta(); 98 | Uint64 ticks_now, cycles_emulated, cycles_per_second; 99 | unsigned int timer_interval; 100 | } cycles; 101 | 102 | public: 103 | /** 104 | * A reference to the emulator chipset. This object holds all CPU, MMU, memory and 105 | * peripheral state. The emulator interfaces with the chipset by issuing interrupts 106 | * and rendering the screen buffer. It may also read internal state for testing purposes. 107 | */ 108 | Chipset &chipset; 109 | 110 | static Emulator *instance; 111 | 112 | bool Running(); 113 | void HandleMemoryError(); 114 | void Shutdown(); 115 | void Tick(); 116 | /** 117 | * Called when SDL_WINDOWEVENT_EXPOSED event is received. Does not re-frame. 118 | */ 119 | void Repaint(); 120 | void Frame(); 121 | void WindowResize(int width, int height); 122 | void ExecuteCommand(std::string command); 123 | unsigned int GetCyclesPerSecond(); 124 | bool GetPaused(); 125 | void SetPaused(bool paused); 126 | void UIEvent(SDL_Event &event); 127 | SDL_Renderer *GetRenderer(); 128 | SDL_Texture *GetInterfaceTexture(); 129 | ModelInfo GetModelInfo(std::string key); 130 | std::string GetModelFilePath(std::string relative_path); 131 | 132 | /** 133 | * We make the UI resizable by default 134 | * You can also specify a negative config parameter to disable this: resizable=0 135 | */ 136 | bool IsResizable(); 137 | 138 | friend class ModelInfo; 139 | friend class CPU; 140 | friend class MMU; 141 | }; 142 | } 143 | -------------------------------------------------------------------------------- /emulator/Config/Config.cpp: -------------------------------------------------------------------------------- 1 | #include "Config.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | void EmuConfig::initTranslate(){ 7 | translate[UI_TITLE]="CasioEmuNeo"; 8 | translate[UI_DISAS]="Disassembler"; 9 | translate[UI_DISAS_WAITING]="Please wait ..."; 10 | translate[UI_DISAS_GOTO_ADDR]="go to addr: "; 11 | translate[UI_DISAS_STEP]="step"; 12 | translate[UI_DISAS_TRACE]="trace"; 13 | translate[UI_REPORT_WINDOW]="Watch Window"; 14 | translate[UI_REPORT_RANGE]="set stack range"; 15 | translate[UI_REPORT_RANGE_SLIDER]="range"; 16 | translate[UI_STACK]="Stack Window"; 17 | translate[UI_INJECTOR]="Injector"; 18 | translate[UI_CHANGE_SCALE]="Change Scale"; 19 | translate[UI_CHANGE_SCALE_SLIDER]="scale"; 20 | translate[UI_ROP_INPUT]="Input your rop!"; 21 | translate[UI_ROP_SETINPUTRANGE]="Set rop size"; 22 | translate[UI_ROP_ANOFFSET]="set 'an' offset bytes"; 23 | translate[UI_ROP_ENTERAN]="Enter 'an'"; 24 | translate[UI_ROP_LOAD]="Load ROP"; 25 | translate[UI_ROP_LOADFROMSTR]="Load ROP from string"; 26 | translate[UI_INFO1]="Calculator is already set to Math I/O mode!"; 27 | translate[UI_INFO2]="'an' is entered!\nPlease make sure you're in Math I/O mode\n Back to emulator and press [->][=] to finish!"; 28 | translate[UI_INFO3]="ROP is entered!\nPlease press [->][=] to finish!"; 29 | translate[UI_MEMEDIT]="Memory Viewer"; 30 | translate[UI_REGS_BREAK_HINT] = "Please view registers while at a breakpoint"; 31 | translate[UI_BP_SELECT_MODE] = "Select breakpoint mode:"; 32 | translate[UI_BP_FIND_READ] = "Find what reads this address"; 33 | translate[UI_BP_FIND_WRITE] = "Find what writes to this address"; 34 | translate[UI_BP_DELETE] = "Delete this address"; 35 | translate[UI_BP_NOT_SET] = "No breakpoints set. Add an address, right-click it, and select a mode."; 36 | translate[UI_BP_LISTENING] = "Listening on address: %04x"; 37 | translate[UI_BP_CLEAR_RECORDS] = "Clear records"; 38 | translate[UI_BP_ADDR] = "Address:"; 39 | translate[UI_BP_ADD_ADDR] = "Add address"; 40 | translate[UI_BP_MODE] = "Mode:"; 41 | translate[UI_BP_WRITE] = "Write"; 42 | translate[UI_BP_READ] = "Read"; 43 | translate[UI_MEM_BP] = "Memory Breakpoint"; 44 | } 45 | 46 | void EmuConfig::update(){ 47 | file->write(root); 48 | } 49 | 50 | void EmuConfig::loadTranslate(std::string path){ 51 | std::ifstream fin(path); 52 | if(fin.fail()) 53 | return; 54 | char buf[200] = {0}; 55 | int ss = 0; 56 | while (!fin.eof()) { 57 | fin.getline(buf,199); 58 | for (int i = 0; i < 100 && buf[i]; i++) { 59 | if(buf[i]=='%') 60 | buf[i]='\n'; 61 | } 62 | fbuilder.AddText(buf); 63 | translate[ss] = std::string(buf); 64 | 65 | ss++; 66 | } 67 | fin.close(); 68 | } 69 | 70 | EmuConfig::EmuConfig(const char *f){ 71 | initTranslate(); 72 | path = f; 73 | file = new mINI::INIFile(path); 74 | if(!file->read(root)) 75 | return; 76 | format_succ = true; 77 | if(root.has("lang")){ 78 | auto& lang = root["lang"]; 79 | if(lang.has("lang")){ 80 | std::string prefix = lang["lang"]; 81 | loadTranslate("lang/"+prefix+".ini"); 82 | } 83 | } 84 | 85 | } 86 | 87 | std::string EmuConfig::GetFontPath(){ 88 | if(root.has("settings")){ 89 | if(root["settings"].has("font")){ 90 | return root["settings"]["font"]; 91 | } 92 | } 93 | return "unifont.otf"; 94 | } 95 | 96 | std::string EmuConfig::GetModulePath(){ 97 | if(root.has("settings")){ 98 | if(root["settings"].has("model")){ 99 | return root["settings"]["model"]; 100 | } 101 | } 102 | return "991cnx"; 103 | } 104 | 105 | float EmuConfig::GetScale(){ 106 | if(!root.has("settings")) 107 | return 1.0; 108 | if(!root["settings"].has("scale")) 109 | return 1.0; 110 | return std::stof(root["settings"]["scale"]); 111 | } 112 | 113 | void EmuConfig::SetScale(float num){ 114 | root["settings"]["scale"] = std::to_string(num); 115 | update(); 116 | } 117 | 118 | char* EmuConfig::operator[](int idx){ 119 | return translate[idx].data(); 120 | } 121 | 122 | ImFontGlyphRangesBuilder& EmuConfig::GetAtlas(){ 123 | return fbuilder; 124 | } 125 | 126 | EmuConfig EmuGloConfig("config.ini"); -------------------------------------------------------------------------------- /emulator/Gui/MemBreakPoint.cpp: -------------------------------------------------------------------------------- 1 | #include "MemBreakPoint.hpp" 2 | #include "../Emulator.hpp" 3 | #include "../Chipset/Chipset.hpp" 4 | #include "../Chipset/CPU.hpp" 5 | #include "imgui.h" 6 | #include 7 | #include 8 | #include 9 | #include "../Config/Config.hpp" 10 | 11 | UI_SINGLE_IMPL(MemBreakPoint) 12 | 13 | MemBreakPoint::MemBreakPoint(){ 14 | instance = this; 15 | } 16 | 17 | 18 | void MemBreakPoint::DrawContent(){ 19 | ImGuiListClipper c; 20 | static int selected = -1; 21 | c.Begin(break_point_hash.size()); 22 | ImDrawList *draw_list = ImGui::GetWindowDrawList(); 23 | char buf[5]={0}; 24 | while (c.Step()) { 25 | 26 | for (int i = c.DisplayStart;i>16, kv.first&0x0ffff); 86 | ImGui::TableSetColumnIndex(1); 87 | ImGui::Text(write?EmuGloConfig[UI_BP_WRITE]:EmuGloConfig[UI_BP_READ]); 88 | } 89 | ImGui::EndTable(); 90 | } 91 | 92 | } 93 | 94 | void MemBreakPoint::TryTrigBp(uint16_t addr,bool write){ 95 | if(target_addr == -1){ 96 | return; 97 | } 98 | MemBPData_t &bp = break_point_hash.at(target_addr); 99 | if(bp.addr == addr && bp.enableWrite == write){ 100 | bp.records[(casioemu::Emulator::instance->chipset.cpu.reg_csr<<16 ) 101 | | casioemu::Emulator::instance->chipset.cpu.reg_pc] = write; 102 | } 103 | } 104 | 105 | void MemBreakPoint::Show(){ 106 | static char buf[5]={0}; 107 | ImGui::Begin(EmuGloConfig[UI_MEM_BP]); 108 | ImGui::BeginChild("##srcollingmbp",ImVec2(0,ImGui::GetWindowHeight()/3)); 109 | DrawContent(); 110 | ImGui::EndChild(); 111 | ImGui::SetNextItemWidth(ImGui::CalcTextSize("F").x*4); 112 | ImGui::InputText(EmuGloConfig[UI_BP_ADDR], buf, 5,ImGuiInputTextFlags_CharsHexadecimal); 113 | ImGui::SameLine(); 114 | if(ImGui::Button(EmuGloConfig[UI_BP_ADD_ADDR])){ 115 | break_point_hash.push_back({ 116 | .addr = (uint16_t)strtol(buf, nullptr, 16) 117 | }); 118 | } 119 | ImGui::BeginChild("##findoutput"); 120 | DrawFindContent(); 121 | ImGui::EndChild(); 122 | ImGui::End(); 123 | } -------------------------------------------------------------------------------- /emulator/Gui/Injector.cpp: -------------------------------------------------------------------------------- 1 | #include "Injector.hpp" 2 | #include "imgui.h" 3 | #include "hex.hpp" 4 | #include "../Peripheral/BatteryBackedRAM.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../Config/Config.hpp" 11 | Injector::Injector() 12 | { 13 | instance = this; 14 | data_buf = new char[1024]; 15 | memset(data_buf, 0,1024); 16 | } 17 | 18 | UI_SINGLE_IMPL(Injector); 19 | 20 | void Injector::Show(){ 21 | ImGui::Begin(EmuGloConfig[UI_INJECTOR]); 22 | 23 | static float scale = -1.0f; 24 | static int range = 64; 25 | static char strbuf[1024]={0}; 26 | static char buf[10]={0}; 27 | static MemoryEditor editor; 28 | static char* info_msg; 29 | ImGui::BeginChild("##ropinput",ImVec2(0,ImGui::GetWindowHeight()*0.6)); 30 | ImGui::Text(EmuGloConfig[UI_CHANGE_SCALE]); 31 | ImGui::SameLine(); 32 | if(scale == -1.0f){ 33 | scale = EmuGloConfig.GetScale(); 34 | } 35 | if(ImGui::SliderFloat(EmuGloConfig[UI_CHANGE_SCALE_SLIDER], &scale, 0.01f, 2.0f)){ 36 | EmuGloConfig.SetScale(scale); 37 | } 38 | ImGui::NewLine(); 39 | ImGui::Text(EmuGloConfig[UI_ROP_INPUT]); 40 | ImGuiIO& io = ImGui::GetIO(); 41 | float ddpi, hdpi, vdpi; 42 | if (SDL_GetDisplayDPI(0, &ddpi, &hdpi, &vdpi) != 0) { 43 | fprintf(stderr, "Failed to obtain DPI information for display 0: %s\n", SDL_GetError()); 44 | exit(1); 45 | } 46 | float dpi_scaling = ddpi / 72.f; 47 | io.FontGlobalScale = scale*dpi_scaling; 48 | 49 | editor.DrawContents(data_buf, range); 50 | ImGui::EndChild(); 51 | ImGui::SliderInt(EmuGloConfig[UI_ROP_SETINPUTRANGE], &range, 64, 1024); 52 | ImGui::Text(EmuGloConfig[UI_ROP_ANOFFSET]); 53 | ImGui::SameLine(); 54 | ImGui::InputText("offset", buf, 9); 55 | char *base_addr = casioemu::BatteryBackedRAM::rom_addr; 56 | if(ImGui::Button("Math I/O")){ 57 | *(base_addr+0xd112 - 0xd000)= 0xc4; 58 | *(base_addr+0xd11e - 0xd000)= 0x00; 59 | info_msg = EmuGloConfig[UI_INFO1]; 60 | ImGui::OpenPopup("info"); 61 | } 62 | if(ImGui::Button(EmuGloConfig[UI_ROP_ENTERAN])){ 63 | int off = atoi(buf); 64 | if(off>100){ 65 | memset(base_addr+0xd180-0xd000, 0x31, 100); 66 | memset(base_addr+0xd180-0xd000+100, 0xa6, 1); 67 | memset(base_addr+0xd180-0xd000+101, 0x31, off-100); 68 | }else { 69 | memset(base_addr+0xd180-0xd000, 0x31, off); 70 | } 71 | *(base_addr+0xd180-0xd000+off)= 0xfd; 72 | *(base_addr+0xd180-0xd000+off+1)= 0x20; 73 | info_msg = EmuGloConfig[UI_INFO2]; 74 | ImGui::OpenPopup("info"); 75 | } 76 | if(ImGui::Button(EmuGloConfig[UI_ROP_LOAD])){ 77 | memcpy(base_addr+0xd180-0xd000,data_buf,range); 78 | info_msg = EmuGloConfig[UI_INFO3]; 79 | ImGui::OpenPopup("info"); 80 | } 81 | if(ImGui::Button(EmuGloConfig[UI_ROP_LOADFROMSTR])){ 82 | 83 | info_msg = EmuGloConfig[UI_INFO3]; 84 | 85 | 86 | auto valid_hex = [](char c) { 87 | if (c >= '0' && c <= '9') return true; 88 | if ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) return true; 89 | return false; 90 | }; 91 | size_t i = 0, j = 0; 92 | char hex_buf[3]; 93 | while (strbuf[i] != '\0' && strbuf[i + 1] != '\0') { 94 | if (strbuf[i] == ';' || strbuf[i + 1] == ';') { 95 | // begin comment; skip the rest of the line 96 | for (;; ++i) { 97 | if (strbuf[i] == '\0') goto exit; 98 | if (strbuf[i] == '\n') { 99 | ++i; 100 | break; 101 | } 102 | } 103 | } else { 104 | if (!(valid_hex(strbuf[i]) && valid_hex(strbuf[i + 1]))) { 105 | ++i; 106 | continue; 107 | } 108 | hex_buf[0] = strbuf[i], hex_buf[1] = strbuf[i + 1],hex_buf[2]='\0'; 109 | uint8_t byte = strtoul(hex_buf, nullptr, 16); 110 | *(base_addr - MEM_EDIT_BASE_ADDR + LABEL_INPUT_BUF + j) = (char) byte; 111 | *(data_buf + j) = (char) byte; 112 | i += 2; 113 | ++j; 114 | } 115 | } 116 | exit: 117 | ImGui::OpenPopup("info"); 118 | 119 | } 120 | ImGui::SameLine(); 121 | ImGui::InputTextMultiline("Input hex string.", strbuf, IM_ARRAYSIZE(strbuf) - 1); 122 | 123 | if(ImGui::BeginPopupModal("info",0,ImGuiWindowFlags_AlwaysAutoResize)){ 124 | ImGui::Text(info_msg); 125 | if(ImGui::Button("OK")){ 126 | ImGui::CloseCurrentPopup(); 127 | } 128 | ImGui::EndPopup(); 129 | } 130 | 131 | 132 | ImGui::End(); 133 | } -------------------------------------------------------------------------------- /emulator/Gui/WatchWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "WatchWindow.hpp" 2 | #include "../Chipset//Chipset.hpp" 3 | #include "../Chipset/CPU.hpp" 4 | #include "Ui.hpp" 5 | #include "CodeViewer.hpp" 6 | #include "imgui.h" 7 | #include "../Peripheral/BatteryBackedRAM.hpp" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "../Config/Config.hpp" 15 | 16 | WatchWindow::WatchWindow() 17 | { 18 | mem_editor.OptShowOptions = false; 19 | instance = this; 20 | } 21 | 22 | UI_SINGLE_IMPL(WatchWindow) 23 | 24 | void WatchWindow::PrepareRX(){ 25 | for (int i = 0; i<16; i++) { 26 | sprintf((char*)reg_rx[i], "%02x",casioemu::Emulator::instance 27 | ->chipset.cpu.reg_r[i] & 0x0ff); 28 | } 29 | sprintf(reg_pc, "%04x",casioemu::Emulator::instance 30 | ->chipset.cpu.reg_pc & 0xffff); 31 | sprintf(reg_lr, "%04x",casioemu::Emulator::instance 32 | ->chipset.cpu.reg_lr & 0xffff); 33 | sprintf(reg_sp, "%04x",casioemu::Emulator::instance 34 | ->chipset.cpu.reg_sp & 0xffff); 35 | sprintf(reg_ea, "%04x",casioemu::Emulator::instance 36 | ->chipset.cpu.reg_ea & 0xffff); 37 | sprintf(reg_psw, "%02x",casioemu::Emulator::instance 38 | ->chipset.cpu.reg_psw & 0xffff); 39 | } 40 | 41 | void WatchWindow::ShowRX(){ 42 | char id[10]; 43 | ImGui::TextColored(ImVec4(0,200,0,255) 44 | , "RXn: "); 45 | for (int i = 0; i<16; i++) { 46 | ImGui::SameLine(); 47 | sprintf(id, "##data%d",i); 48 | ImGui::SetNextItemWidth(char_width*3); 49 | ImGui::InputText(id, (char*)®_rx[i][0],3 50 | ,ImGuiInputTextFlags_CharsHexadecimal); 51 | 52 | } 53 | //ERn 54 | //不可编辑,必须通过Rn编辑 55 | ImGui::Text("ERn: "); 56 | for (int i = 0; i<16; i+=2) { 57 | ImGui::SameLine(); 58 | uint16_t val = casioemu::Emulator::instance->chipset.cpu.reg_r[i+1] 59 | <<8|casioemu::Emulator::instance->chipset.cpu.reg_r[i]; 60 | ImGui::Text("%04x ",val); 61 | 62 | } 63 | 64 | auto show_sfr = ([&](char *ptr, const char *label,int i,int width = 4){ 65 | ImGui::TextColored(ImVec4(0,200,0,255), "%s", label); 66 | ImGui::SameLine(); 67 | sprintf(id, "##sfr%d",i); 68 | ImGui::SetNextItemWidth(char_width*width+2); 69 | ImGui::InputText(id, (char*)ptr,width+1,ImGuiInputTextFlags_CharsHexadecimal); 70 | }); 71 | show_sfr(reg_pc, "PC: ", 1); 72 | ImGui::SameLine(); 73 | show_sfr(reg_lr, "LR: ", 2); 74 | ImGui::SameLine(); 75 | show_sfr(reg_ea, "EA: ", 3); 76 | ImGui::SameLine(); 77 | show_sfr(reg_sp, "SP: ", 4); 78 | ImGui::SameLine(); 79 | show_sfr(reg_psw, "PSW: ", 5,2); 80 | } 81 | 82 | void WatchWindow::UpdateRX(){ 83 | for (int i = 0; i<16; i++) { 84 | casioemu::Emulator::instance->chipset.cpu.reg_r[i] 85 | = (uint8_t)strtol((char*)reg_rx[i], nullptr, 16); 86 | } 87 | casioemu::Emulator::instance->chipset.cpu.reg_pc 88 | = (uint16_t)strtol((char*)reg_pc, nullptr, 16); 89 | casioemu::Emulator::instance->chipset.cpu.reg_lr 90 | = (uint16_t)strtol((char*)reg_lr, nullptr, 16); 91 | casioemu::Emulator::instance->chipset.cpu.reg_ea 92 | = (uint16_t)strtol((char*)reg_ea, nullptr, 16); 93 | casioemu::Emulator::instance->chipset.cpu.reg_sp 94 | = (uint16_t)strtol((char*)reg_sp, nullptr, 16); 95 | casioemu::Emulator::instance->chipset.cpu.reg_psw 96 | = (uint16_t)strtol((char*)reg_psw, nullptr, 16); 97 | } 98 | 99 | void WatchWindow::Show(){ 100 | char_width = ImGui::CalcTextSize("F").x; 101 | ImGui::Begin(EmuGloConfig[UI_REPORT_WINDOW]); 102 | ImGui::BeginChild("##stack_trace",ImVec2(0,ImGui::GetWindowHeight()/4)); 103 | casioemu::Chipset& chipset = casioemu::Emulator::instance->chipset; 104 | std::string s=chipset.cpu.GetBacktrace(); 105 | ImGui::InputTextMultiline("##as",(char*)s.c_str(),s.size(),ImVec2(ImGui::GetWindowWidth(),0),ImGuiInputTextFlags_ReadOnly); 106 | ImGui::EndChild(); 107 | ImGui::BeginChild("##reg_trace",ImVec2(0,ImGui::GetTextLineHeightWithSpacing()*4),ImGuiChildFlags_None,ImGuiWindowFlags_AlwaysHorizontalScrollbar); 108 | if(!CodeViewer::instance->isbreaked){ 109 | ImGui::TextColored(ImVec4(255,255,0,255), EmuGloConfig[UI_REGS_BREAK_HINT]); 110 | PrepareRX(); 111 | }else { 112 | ShowRX(); 113 | } 114 | 115 | 116 | ImGui::EndChild(); 117 | static int range=64; 118 | ImGui::BeginChild("##stack_view"); 119 | ImGui::Text(EmuGloConfig[UI_REPORT_RANGE]); 120 | ImGui::SameLine(); 121 | ImGui::SliderInt(EmuGloConfig[UI_REPORT_RANGE_SLIDER], &range, 64, 2048); 122 | uint16_t offset = chipset.cpu.reg_sp&0xffff; 123 | mem_editor.DrawContents(casioemu::BatteryBackedRAM::rom_addr+ offset-DebugUi::instance->ram_start, range,offset); 124 | ImGui::EndChild(); 125 | ImGui::End(); 126 | 127 | } -------------------------------------------------------------------------------- /emulator/Chipset/CPUControl.cpp: -------------------------------------------------------------------------------- 1 | #include "CPU.hpp" 2 | 3 | #include "../Emulator.hpp" 4 | #include "Chipset.hpp" 5 | #include "MMU.hpp" 6 | 7 | #include "../Gui/Ui.hpp" 8 | namespace casioemu 9 | { 10 | // * Control Register Access Instructions 11 | void CPU::OP_ADDSP() 12 | { 13 | impl_operands[0].value |= (impl_operands[0].value & 0x80) ? 0xFF00 : 0; 14 | reg_sp += impl_operands[0].value; 15 | reg_sp &= 0xfffe; 16 | } 17 | 18 | void CPU::OP_CTRL() 19 | { 20 | switch (impl_hint >> 8) 21 | { 22 | case 1: 23 | reg_ecsr[reg_psw & PSW_ELEVEL] = impl_operands[1].value; 24 | break; 25 | case 2: 26 | reg_elr[reg_psw & PSW_ELEVEL] = impl_operands[1].value; 27 | break; 28 | case 3: 29 | if (reg_psw & PSW_ELEVEL) 30 | reg_epsw[reg_psw & PSW_ELEVEL] = impl_operands[1].value; 31 | break; 32 | case 4: 33 | impl_operands[0].value = reg_elr[reg_psw & PSW_ELEVEL]; 34 | break; 35 | case 5: 36 | impl_operands[0].value = reg_sp; 37 | break; 38 | case 6: 39 | case 7: 40 | reg_psw = impl_operands[1].value; 41 | break; 42 | case 8: 43 | impl_operands[0].value = reg_ecsr[reg_psw & PSW_ELEVEL]; 44 | break; 45 | case 9: 46 | if (reg_psw & PSW_ELEVEL) 47 | impl_operands[0].value = reg_epsw[reg_psw & PSW_ELEVEL]; 48 | break; 49 | case 10: 50 | impl_operands[0].value = reg_psw; 51 | break; 52 | case 11: 53 | reg_sp = impl_operands[1].value; 54 | reg_sp &= 0xfffe; 55 | break; 56 | } 57 | } 58 | 59 | // * EA Register Data Transfer Instructions 60 | void CPU::OP_LEA() 61 | { 62 | reg_ea = 0; 63 | if (impl_operands[1].register_size) 64 | reg_ea += impl_operands[1].value; 65 | if (impl_hint & H_TI) 66 | reg_ea += impl_long_imm; 67 | } 68 | 69 | // * Coprocessor Data Transfer Instructions 70 | void CPU::OP_CR_R() 71 | { 72 | size_t op0_index = (impl_opcode >> 8) & 0x000F; 73 | size_t op1_index = (impl_opcode >> 4) & 0x000F; 74 | if (impl_hint & H_ST) 75 | reg_r[op0_index] = reg_cr[op1_index]; 76 | else 77 | reg_cr[op0_index] = reg_r[op1_index]; 78 | } 79 | 80 | void CPU::OP_CR_EA() 81 | { 82 | size_t op0_index = (impl_opcode >> 8) & 0x000F; 83 | size_t register_size = impl_opcode >> 8; 84 | 85 | if (impl_hint & H_ST) 86 | for (size_t ix = register_size - 1; ix != (size_t)-1; --ix) 87 | emulator.chipset.mmu.WriteData((((size_t)reg_dsr) << 16) | (uint16_t)(reg_ea + ix), reg_cr[op0_index + ix] 88 | ); 89 | else 90 | for (size_t ix = 0; ix != register_size; ++ix) 91 | reg_cr[op0_index + ix] = emulator.chipset.mmu.ReadData((((size_t)reg_dsr) << 16) | (uint16_t)(reg_ea + ix)); 92 | 93 | if (impl_hint & H_IA) 94 | BumpEA(register_size); 95 | } 96 | 97 | void CPU::BumpEA(size_t value_size) 98 | { 99 | reg_ea += value_size; 100 | if (value_size != 1) 101 | reg_ea &= ~1; 102 | } 103 | 104 | // * PSW Access Instructions 105 | void CPU::OP_PSW_OR() 106 | { 107 | reg_psw |= (impl_opcode & 0xFF); 108 | } 109 | 110 | void CPU::OP_PSW_AND() 111 | { 112 | reg_psw &= (impl_opcode & 0xFF); 113 | } 114 | 115 | void CPU::OP_CPLC() 116 | { 117 | reg_psw ^= PSW_C; 118 | } 119 | 120 | // * Conditional Relative Branch Instructions 121 | void CPU::OP_BC() 122 | { 123 | bool c = impl_flags_in & PSW_C; 124 | bool z = impl_flags_in & PSW_Z; 125 | bool s = impl_flags_in & PSW_S; 126 | bool ov = impl_flags_in & PSW_OV; 127 | bool le = z | c; 128 | bool lts = ov ^ s; 129 | bool les = lts | z; 130 | 131 | bool branch; 132 | switch ((impl_opcode >> 8) & 0x000F) 133 | { 134 | case 0: branch = !c; break; 135 | case 1: branch = c; break; 136 | case 2: branch = !le; break; 137 | case 3: branch = le; break; 138 | case 4: branch = !lts; break; 139 | case 5: branch = lts; break; 140 | case 6: branch = !les; break; 141 | case 7: branch = les; break; 142 | case 8: branch = !z; break; 143 | case 9: branch = z; break; 144 | case 10: branch = !ov; break; 145 | case 11: branch = ov; break; 146 | case 12: branch = !s; break; 147 | case 13: branch = s; break; 148 | default: branch = true; break; 149 | } 150 | 151 | if (branch) 152 | { 153 | impl_operands[0].value |= (impl_operands[0].value & 0x80) ? 0x7F00 : 0; 154 | reg_pc += impl_operands[0].value << 1; 155 | } 156 | } 157 | 158 | // * Software Interrupt Instructions 159 | void CPU::OP_SWI() 160 | { 161 | emulator.chipset.RaiseSoftware(impl_operands[0].value); 162 | } 163 | 164 | void CPU::OP_BRK() 165 | { 166 | emulator.chipset.Break(); 167 | } 168 | 169 | // * Branch Instructions 170 | void CPU::OP_B() 171 | { 172 | if (impl_hint & H_TI) 173 | { 174 | reg_csr = impl_operands[1].value; 175 | reg_pc = impl_long_imm; 176 | } 177 | else 178 | reg_pc = impl_operands[1].value; 179 | 180 | } 181 | 182 | void CPU::OP_BL() 183 | { 184 | reg_lr = reg_pc; 185 | reg_lcsr = reg_csr; 186 | if (!stack.empty() && !stack.back().lr_pushed) 187 | {} 188 | OP_B(); 189 | stack.push_back({false, 0, reg_csr, reg_pc}); 190 | } 191 | 192 | // * Miscellaneous Instructions 193 | void CPU::OP_RT() 194 | { 195 | if (stack.empty()) 196 | {} 197 | else 198 | { 199 | if (stack.back().lr_pushed) 200 | {} 201 | stack.pop_back(); 202 | } 203 | reg_csr = reg_lcsr; 204 | reg_pc = reg_lr; 205 | } 206 | 207 | void CPU::OP_RTI() 208 | { 209 | reg_csr = reg_ecsr[reg_psw & PSW_ELEVEL]; 210 | reg_pc = reg_elr[reg_psw & PSW_ELEVEL]; 211 | reg_psw = reg_epsw[reg_psw & PSW_ELEVEL]; 212 | } 213 | } 214 | 215 | -------------------------------------------------------------------------------- /disas/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "lib.h" 8 | 9 | #define separator "// ------------------------------------------------------------------ " 10 | 11 | int main(int argc, char** argv) { 12 | if (argc != 3) { 13 | char* st = new char[0x400]; 14 | std::ifstream fi {"help.txt"}; 15 | while (true) { 16 | fi.getline(st, 0x400); 17 | if (fi.fail() || std::strcmp(st, "*") == 0) { 18 | fi.close(); 19 | return 0; 20 | } 21 | std::cout << st << "\n"; 22 | } 23 | } 24 | 25 | std::ifstream in {argv[1]}, ex {"example.cpp"}; 26 | std::ofstream out {argv[2]}; 27 | char* st = new char[0x400]; 28 | while (true) { 29 | ex.getline(st, 0x400); 30 | out << st << "\n"; 31 | if (std::strcmp(st, separator "1") == 0) { 32 | break; 33 | } 34 | } 35 | while (true) { 36 | in.getline(st, 0x400); 37 | if (std::strcmp(st, "*") == 0) { 38 | break; 39 | } 40 | out << st << "\n"; 41 | } 42 | bool write = false; 43 | while (true) { 44 | ex.getline(st, 0x400); 45 | if (std::strcmp(st, separator "2") == 0) { 46 | write = true; 47 | } 48 | if (write) { 49 | out << st << "\n"; 50 | } 51 | if (std::strcmp(st, separator "3") == 0) { 52 | break; 53 | } 54 | } 55 | 56 | // start main part. 57 | char open, close, space; 58 | int maxlen, unitlen, opcodelen = -1, nspace = -1, unregnspace; 59 | in >> open >> close; // read exactly one char each time 60 | in >> maxlen >> unitlen; 61 | std::stringstream temp; 62 | unregnspace = 7 + 3 * (maxlen - unitlen); // const 63 | 64 | uint8_t mask, val, chardata [3][0x100]; 65 | // chardata: [0] : byte it is in (buf[k]) 66 | // [1] : shift length 67 | // [2] : number length (binary) 68 | 69 | do { 70 | in.getline(st, 0x400); 71 | 72 | if (std::strcmp(st, "*") == 0) break; 73 | if (std::strcmp(st, "") == 0) continue; 74 | if (st[0] == ';') continue; // allow for comments 75 | out << "// " << st << "\n"; 76 | if (st[0] == '#') { 77 | opcodelen = static_cast(std::strtol(&st[1], nullptr, 0)); 78 | nspace = 7 + 3 * (maxlen - opcodelen); 79 | continue; 80 | } 81 | // well, it is in usual format now. 82 | 83 | out << "if ("; 84 | // conditions 85 | 86 | temp.clear(); 87 | temp.str(st); 88 | for (int i = 0; i < 0x100; i++) 89 | chardata[0][i] = chardata[1][i] = chardata[2][i] = 0xFF; 90 | for (int i = 0; i < opcodelen; i++) { 91 | temp >> st; // must be 8 chars 92 | mask = val = 0; 93 | for (int j = 0; j < 8; j++) { 94 | unsigned char uc = static_cast(st[j]); 95 | if (st[j] == '0' || st[j] == '1') { 96 | mask = mask << 1 | 1; 97 | val = val << 1 | (st[j] - '0'); 98 | } else { 99 | if (chardata[0][uc] == 0xFF) { 100 | chardata[0][uc] = i; 101 | chardata[1][uc] = 7 - j; 102 | chardata[2][uc] = 1; 103 | } else { 104 | chardata[1][uc]--; 105 | chardata[2][uc]++; 106 | } 107 | mask <<= 1; 108 | val <<= 1; 109 | } 110 | } 111 | if (i != 0) out << " && "; 112 | out << "(buf[" << i << "] & 0b" << tobin(mask, 8) 113 | << ") == 0b" << tobin(val, 8); 114 | } 115 | temp >> space; 116 | if (space != ' ') temp.unget(); 117 | temp.getline(st, 0x400); 118 | 119 | out << ") {\n"; 120 | 121 | // implementation 122 | 123 | std::stringstream tempss {""}; 124 | 125 | // First line start. (assignment) 126 | tempss << " int "; 127 | bool notisfirst = false, declaredat = false; // declared anything 128 | for (char c = -128; c < 127; c++) { 129 | if (chardata[0][static_cast(c)] != 0xFF) { 130 | declaredat = true; 131 | if (notisfirst) { 132 | tempss << ", "; 133 | } else { 134 | notisfirst = true; 135 | } 136 | tempss << c << " = buf[" << static_cast(chardata[0][static_cast(c)]) 137 | << "] >> " << static_cast(chardata[1][static_cast(c)]) << " & 0b"; 138 | for (int j = 0; j < chardata[2][static_cast(c)]; j++) 139 | tempss << "1"; 140 | } 141 | } 142 | if (declaredat) { 143 | out << tempss.str() << ";\n"; 144 | } 145 | 146 | // Second line start. 147 | out << " out << tohex(ip, 6) << \" \" "; 148 | for (int i = 0; i < opcodelen; i++) { 149 | out << "<< tohex(buf[" << i << "], 2) << ' ' "; 150 | } 151 | 152 | // the string of spaces at last 153 | out << "<< \""; 154 | for (int i = 0; i < nspace; i++) out << ' '; // how long is determined by opcodelen and maxlen 155 | out << "\"\n"; 156 | 157 | // Third line start. 158 | out << " << \""; 159 | for (int i = 0; st[i] != 0; i++) { // st is C-style 160 | if (st[i] == open) out << "\" << ("; 161 | else if (st[i] == close) out << ") << \""; 162 | else out << st[i]; 163 | } 164 | 165 | // Fourth line start. 166 | out << "\"\n << \"\\n\";\n "; 167 | 168 | // Fifth line (repeat several times) 169 | for (int i = 0; i < opcodelen; i++) out << " buf.pop_front();"; 170 | 171 | // Sixth line 172 | out << "\n goto done;\n}\n"; 173 | } while (true); 174 | 175 | // "Unrecognized command" 176 | // First line start. 177 | out << ";\n out << tohex(ip, 6) << \" \" "; 178 | for (int i = 0; i < unitlen; i++) { 179 | out << "<< tohex(buf[" << i << "], 2) << ' ' "; 180 | } 181 | 182 | // the string of spaces at last 183 | out << "<< \""; 184 | for (int i = 0; i < unregnspace; i++) out << ' '; 185 | out << "\"\n"; 186 | 187 | // Second line start. 188 | out << " << \"Unrecognized command\"\n" 189 | 190 | // Third line start. 191 | " << \"\\n\";\n "; 192 | 193 | // Fourth line start. 194 | for (int i = 0; i < unitlen; i++) out << " buf.pop_front();\n"; 195 | 196 | // done. finalize it. 197 | 198 | write = false; 199 | do { 200 | if (!ex.getline(st, 0x400)) { 201 | in.close(); 202 | out.close(); 203 | ex.close(); 204 | return 0; 205 | } 206 | if (std::strcmp(st, separator "4") == 0) { 207 | write = true; 208 | } 209 | if (write) out << st << "\n"; 210 | } while (true); 211 | } 212 | -------------------------------------------------------------------------------- /emulator/Gui/Ui.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "CodeViewer.hpp" 4 | #include "imgui_impl_sdl2.h" 5 | #include "imgui_impl_sdlrenderer2.h" 6 | #include 7 | #include 8 | 9 | #include "Ui.hpp" 10 | #include "hex.hpp" 11 | #include "WatchWindow.hpp" 12 | #include "Injector.hpp" 13 | #include "MemBreakPoint.hpp" 14 | #include "../Peripheral/BatteryBackedRAM.hpp" 15 | 16 | #include "../Config/Config.hpp" 17 | ImVector ranges; 18 | UI_SINGLE_IMPL(DebugUi) 19 | DebugUi::DebugUi() 20 | { 21 | instance = this; 22 | ram_length = casioemu::Emulator::instance->GetModelInfo("ram_length"); 23 | ram_start = casioemu::Emulator::instance->GetModelInfo("ram_start"); 24 | window = SDL_CreateWindow(EmuGloConfig[UI_TITLE], SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); 25 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); 26 | if (renderer == nullptr) 27 | { 28 | SDL_Log("Error creating SDL_Renderer!"); 29 | exit(1); 30 | } 31 | IMGUI_CHECKVERSION(); 32 | ImGui::CreateContext(); 33 | ImGuiIO& io = ImGui::GetIO(); 34 | io.WantCaptureKeyboard=true; 35 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls 36 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls 37 | io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; 38 | io.ConfigDockingWithShift = true; 39 | io.FontGlobalScale = 1.0; 40 | 41 | EmuGloConfig.GetAtlas().AddRanges(io.Fonts->GetGlyphRangesChineseFull()); 42 | EmuGloConfig.GetAtlas().BuildRanges(&ranges); 43 | io.Fonts->AddFontFromFileTTF(EmuGloConfig.GetFontPath().data(), 16.0f, nullptr, ranges.Data); 44 | io.Fonts->Build(); 45 | 46 | ImGui::StyleColorsDark(); 47 | 48 | ImGui_ImplSDL2_InitForSDLRenderer(window, renderer); 49 | ImGui_ImplSDLRenderer2_Init(renderer); 50 | ui_components.push_back(new WatchWindow()); 51 | ui_components.push_back(new Injector()); 52 | ui_components.push_back(new MemBreakPoint()); 53 | ui_components.push_back(new CodeViewer(casioemu::Emulator::instance->GetModelFilePath("_disas.txt"))); 54 | rom_addr = casioemu::BatteryBackedRAM::rom_addr; 55 | // code_viewer=new CodeViewer(emulator->GetModelFilePath("_disas.txt"),emulator); 56 | } 57 | 58 | void DebugUi::DockerHelper(){ 59 | //p_open不需要,改成nullptr 60 | bool* p_open = nullptr; 61 | static bool opt_fullscreen = true; 62 | static bool opt_padding = false; 63 | static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_AutoHideTabBar; ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar| ImGuiWindowFlags_NoDocking; 64 | if (opt_fullscreen) 65 | { 66 | const ImGuiViewport* viewport = ImGui::GetMainViewport(); 67 | ImGui::SetNextWindowPos(viewport->WorkPos); 68 | ImGui::SetNextWindowSize(viewport->WorkSize); 69 | ImGui::SetNextWindowViewport(viewport->ID); 70 | ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); 71 | ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); 72 | window_flags |= ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove; 73 | window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; 74 | } 75 | 76 | if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) 77 | window_flags |= ImGuiWindowFlags_NoBackground; if (!opt_padding) 78 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); 79 | ImGui::Begin("按住SHIFT,拖拽来启用docking", p_open, window_flags); 80 | if (!opt_padding) 81 | ImGui::PopStyleVar(); 82 | if (opt_fullscreen) 83 | ImGui::PopStyleVar(2); // Submit the DockSpace 84 | ImGuiIO& io = ImGui::GetIO(); 85 | if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) 86 | { 87 | ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); 88 | ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); 89 | } 90 | ImGui::End(); 91 | } 92 | 93 | void DebugUi::PaintSDL(){ 94 | if(!need_paint) 95 | return; 96 | // SDL_RenderClear(renderer); 97 | 98 | 99 | SDL_RenderPresent(renderer); 100 | need_paint = false; 101 | } 102 | 103 | void DebugUi::PaintUi(){ 104 | if(need_paint) 105 | return; 106 | assert(MARKED_SPANS != nullptr /* initialized in casioemu.cpp */); 107 | auto &marked_spans = *MARKED_SPANS; 108 | static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); 109 | ImGuiIO& io = ImGui::GetIO(); 110 | SDL_RenderSetScale(renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); 111 | SDL_SetRenderDrawColor(renderer, (Uint8)(clear_color.x * 255), (Uint8)(clear_color.y * 255), (Uint8)(clear_color.z * 255), (Uint8)(clear_color.w * 255)); 112 | ImGui_ImplSDLRenderer2_NewFrame(); 113 | ImGui_ImplSDL2_NewFrame(); 114 | ImGui::NewFrame(); 115 | DockerHelper(); 116 | 117 | static MemoryEditor mem_edit; 118 | mem_edit.ReadOnly = false; 119 | mem_edit.DrawWindow(EmuGloConfig[UI_MEMEDIT], rom_addr, ram_length, ram_start, marked_spans); 120 | for(UiBase* a:ui_components){ 121 | a->Show(); 122 | } 123 | 124 | ImGui::Render(); 125 | ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData()); 126 | 127 | need_paint = true; 128 | 129 | } 130 | 131 | MemoryEditor::OptionalMarkedSpans* DebugUi::MARKED_SPANS = nullptr; 132 | 133 | void DebugUi::UpdateMarkedSpans(const MemoryEditor::OptionalMarkedSpans &spans) { 134 | delete MARKED_SPANS; 135 | auto leaked = new std::optional(spans); 136 | MARKED_SPANS = leaked; 137 | } 138 | 139 | void gui_loop(){ 140 | 141 | } 142 | int test_gui(){ 143 | //SDL_Delay(1000*5); 144 | 145 | //ImGui_ImplSDL2_InitForSDLRenderer(renderer); 146 | } 147 | 148 | // void gui_cleanup(){ 149 | // // Cleanup 150 | // ImGui_ImplSDLRenderer2_Shutdown(); 151 | // ImGui_ImplSDL2_Shutdown(); 152 | // ImGui::DestroyContext(); 153 | 154 | // SDL_DestroyRenderer(renderer); 155 | // SDL_DestroyWindow(window); 156 | // SDL_Quit(); 157 | // } 158 | -------------------------------------------------------------------------------- /asm/asm.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import keys 3 | import struct 4 | prompts = ''' 5 | Usage: 6 | asm 7 | ''' 8 | 9 | base_addr = 0xd522 10 | syms = [] 11 | debug =True 12 | showdump = print 13 | def loadsym(): 14 | global syms 15 | with open('./sym.txt','r',encoding="utf-8") as f: 16 | lines = f.readlines() 17 | for s in lines: 18 | flag = 0 19 | s = s.strip() 20 | if len(s) == 0 or s[0] == ';': 21 | continue 22 | if s[0] == '@': 23 | flag = 1 24 | s = s[1:] 25 | ps = s.split(' ') 26 | if len(ps)==2: 27 | syms.append([ps[0].lower(),ps[1].lower().strip(),flag]) 28 | 29 | def loopup(name:str): 30 | global syms 31 | flag = 0 32 | if name[0] == '@': 33 | flag = 1 34 | name = name[1:] 35 | for s in syms: 36 | if s[1] == name: 37 | if s[2] == 1: 38 | if flag == 1: 39 | return hex(int('3'+s[0],16)) 40 | return hex(int('3'+s[0],16)+2) 41 | elif s[2] == 2: 42 | return hex(int(s[0],16)) 43 | else: 44 | return hex(int('3'+s[0],16)) 45 | print('Unknown symbol name: '+name) 46 | exit() 47 | 48 | def preline(line:str): 49 | return nextstr(line[0:len(line)].strip().lower()) 50 | 51 | 52 | def next_arg(line:str,force16 = False): 53 | s = line.split(' ') 54 | if len(s)<2: 55 | return 0,'Require at least 1 argument here!\n'+'---> '+line 56 | s[1] = s[1].strip() 57 | i = 0 58 | if s[1].startswith('0x') or force16: 59 | i = int(s[1],16) 60 | else: 61 | i = int(s[1]) 62 | if len(s) > 2: 63 | if not s[2].strip().startswith(';'): 64 | return 0,'Too much arguments here!\n'+'---> '+line 65 | return i,'' 66 | 67 | def readall(path): 68 | with open(path, 'r',encoding="utf-8") as f: 69 | return f.readlines() 70 | def nextstr(line:str): 71 | idx = line.find(';') 72 | if idx == -1: 73 | return line.strip() 74 | return line[0:line.find(';')].strip() 75 | 76 | def process(strs,fstpass=True): 77 | global base_addr 78 | dump = '' 79 | hex_dump = '' 80 | line_num = 1 81 | for line in strs: 82 | line = preline(line) 83 | if line.startswith('#org'): 84 | if not fstpass: 85 | continue 86 | i,p = next_arg(line,True) 87 | base_addr = i 88 | print("base addr = "+str(i)) 89 | continue 90 | if line.startswith(';') or line == '': 91 | continue 92 | 93 | if line.startswith('space'): 94 | i,p = next_arg(line) 95 | if p != '': 96 | print(p) 97 | exit() 98 | #print('space '+str(i)+' bytes.') 99 | for j in range(i): 100 | dump+='0 ' 101 | hex_dump+='30 ' 102 | base_addr+=i 103 | elif line.startswith('hex') and line[3] == ' ': 104 | tokens = line[3:] 105 | #print(tokens) 106 | i = 0 107 | bin = '' 108 | while i < len(tokens): 109 | if len(bin)<2: 110 | if tokens[i]!=' ': 111 | bin+=tokens[i] 112 | if len(bin) == 2: 113 | #print('<'+bin+'>') 114 | dump+=keys.byte2keys(bin)[0]+' ' 115 | hex_dump+=str(bin)+' ' 116 | bin = '' 117 | base_addr+=1 118 | 119 | i+=1 120 | if len(bin)>0: 121 | print('hex require to be aligned with 2 bytes!') 122 | exit() 123 | elif line.endswith(':'): 124 | s = line[:-1] 125 | syms.append([hex(base_addr),s,2]) 126 | elif line.startswith('adr'): 127 | if fstpass: 128 | base_addr+=2 129 | continue 130 | p = line[3:].strip().split(' ') 131 | if len(p) >2 or len(p) == 0: 132 | print('Too many args for adr!') 133 | exit() 134 | off = 0 135 | if len(p) == 2: 136 | if p[1].startswith('0x'): 137 | off = int(p[1],16) 138 | else: 139 | off = int(p[1]) 140 | adr = loopup(p[0])[2:] 141 | adr = struct.pack(' 7 | #include 8 | #include 9 | #include 10 | 11 | /** 12 | * We need to setup break-point system in CPU 13 | * 14 | */ 15 | 16 | 17 | namespace casioemu 18 | { 19 | class Emulator; 20 | 21 | class CPU 22 | { 23 | Emulator &emulator; 24 | 25 | private: 26 | struct RegisterStub 27 | { 28 | size_t type_size; 29 | std::string name; 30 | 31 | uint16_t raw; 32 | }; 33 | 34 | template 35 | struct Register : public RegisterStub 36 | { 37 | Register() 38 | { 39 | type_size = sizeof(value_type); 40 | name = "?"; 41 | } 42 | 43 | operator value_type() 44 | { 45 | return raw; 46 | } 47 | 48 | Register &operator =(value_type value) 49 | { 50 | raw = value; 51 | return *this; 52 | } 53 | 54 | Register &operator &=(value_type value) 55 | { 56 | return *this = raw & value; 57 | } 58 | 59 | Register &operator |=(value_type value) 60 | { 61 | return *this = raw | value; 62 | } 63 | 64 | Register &operator ^=(value_type value) 65 | { 66 | return *this = raw ^ value; 67 | } 68 | 69 | Register &operator +=(value_type value) 70 | { 71 | return *this = raw + value; 72 | } 73 | 74 | Register &operator -=(value_type value) 75 | { 76 | return *this = raw - value; 77 | } 78 | }; 79 | 80 | typedef Register reg8_t; 81 | typedef Register reg16_t; 82 | 83 | uint8_t impl_last_dsr; 84 | uint8_t impl_flags_changed, impl_flags_out, impl_flags_in; 85 | uint8_t impl_shift_buffer; 86 | uint16_t impl_opcode, impl_long_imm; 87 | struct { 88 | uint64_t value; 89 | size_t register_index, register_size; 90 | } impl_operands[2]; 91 | size_t impl_hint; 92 | uint16_t impl_csr_mask; 93 | 94 | void SetupOpcodeDispatch(); 95 | void SetupRegisterProxies(); 96 | 97 | public: 98 | CPU(Emulator &emulator); 99 | ~CPU(); 100 | void SetupInternals(); 101 | 102 | /** 103 | * See 1.2.2.1 in the nX-U8 manual. 104 | */ 105 | enum StatusFlag 106 | { 107 | PSW_C = 0x80, 108 | PSW_Z = 0x40, 109 | PSW_S = 0x20, 110 | PSW_OV = 0x10, 111 | PSW_MIE = 0x8, 112 | PSW_HC = 0x4, 113 | PSW_ELEVEL = 0x3 114 | }; 115 | 116 | /** 117 | * See 1.3.6 in the nX-U8 manual. 118 | */ 119 | enum MemoryModel 120 | { 121 | MM_SMALL, 122 | MM_LARGE 123 | } memory_model; 124 | 125 | /** 126 | * See 1.2.1 in the nX-U8 manual. 127 | */ 128 | reg8_t reg_r[16], reg_cr[16]; 129 | reg16_t reg_pc, reg_elr[4], ®_lr; 130 | reg16_t reg_csr, reg_ecsr[4], ®_lcsr; 131 | reg8_t reg_epsw[4], ®_psw; 132 | reg16_t reg_sp, reg_ea; 133 | reg8_t reg_dsr; 134 | 135 | void SetMemoryModel(MemoryModel memory_model); 136 | void Next(); 137 | void Reset(); 138 | void Raise(size_t exception_level, size_t index); 139 | size_t GetExceptionLevel(); 140 | bool GetMasterInterruptEnable(); 141 | std::string GetBacktrace() const; 142 | 143 | private: 144 | struct StackFrame 145 | { 146 | bool lr_pushed; 147 | uint16_t lr_push_address, new_csr, new_pc; 148 | }; 149 | std::vector stack; 150 | 151 | 152 | 153 | uint16_t Fetch(); 154 | 155 | enum OpcodeHint 156 | { 157 | H_IE = 0x0001, // * Extend Immediate flag for arithmetic instructions. 158 | H_ST = 0x0002, // * Store flag for load/store/coprocessor instructions. 159 | H_DW = 0x0004, // * Store a new DSR value. 160 | H_DS = 0x0008, // * Instruction is a DSR prefix. 161 | H_IA = 0x0010, // * Increment EA flag for load/store/coprocessor instructions. 162 | H_TI = 0x0020, // * Instruction takes an external long immediate value. 163 | H_WB = 0x0040 // * Register Writeback flag for a lot of instructions to make life easier. 164 | }; 165 | 166 | struct OpcodeSource 167 | { 168 | void (CPU::*handler_function)(); 169 | /** 170 | * I know this should be an OpcodeHint, but the damn C++ initializer lists 171 | * convert literally everything to int if it's more than a single enum 172 | * value. Even binary OR'd values and 0. Pain in the ass. 173 | */ 174 | size_t hint; 175 | uint16_t opcode; 176 | struct OperandMask 177 | { 178 | /** 179 | * `register_size` determines whether an operand is a register 180 | * or an immediate. If it's 0, the operand is an immediate. Otherwise 181 | * the operand is a register of size `register_size`. 182 | */ 183 | size_t register_size; 184 | uint16_t mask, shift; 185 | } operands[2]; 186 | }; 187 | static OpcodeSource opcode_sources[]; 188 | OpcodeSource **opcode_dispatch; 189 | 190 | typedef RegisterStub CPU::*RegisterStubPointer; 191 | typedef RegisterStub (CPU::*RegisterStubArrayPointer)[]; 192 | struct RegisterRecord 193 | { 194 | std::string name; 195 | size_t array_size, array_base; 196 | RegisterStubPointer stub; 197 | RegisterStubArrayPointer stub_array; 198 | }; 199 | static RegisterRecord register_record_sources[]; 200 | std::map register_proxies; 201 | 202 | // * Arithmetic Instructions 203 | void OP_ADD(); 204 | void OP_ADD16(); 205 | void OP_ADDC(); 206 | void OP_AND(); 207 | void OP_MOV16(); 208 | void OP_MOV(); 209 | void OP_OR(); 210 | void OP_XOR(); 211 | void OP_CMP16(); 212 | void OP_SUB(); 213 | void OP_SUBC(); 214 | void Add8(); 215 | void ZSCheck(); 216 | void ShiftLeft8(); 217 | void ShiftRight8(); 218 | // * Shift Instructions 219 | void OP_SLL(); 220 | void OP_SLLC(); 221 | void OP_SRA(); 222 | void OP_SRL(); 223 | void OP_SRLC(); 224 | // * Load/Store Instructions 225 | void OP_LS_EA(); 226 | void OP_LS_R(); 227 | void OP_LS_I_R(); 228 | void OP_LS_BP(); 229 | void OP_LS_FP(); 230 | void OP_LS_I(); 231 | void LoadStore(uint16_t offset, size_t length); 232 | // * Control Register Access Instructions 233 | void OP_ADDSP(); 234 | void OP_CTRL(); 235 | // * PUSH/POP Instructions 236 | void OP_PUSH(); 237 | void OP_PUSHL(); 238 | void OP_POP(); 239 | void OP_POPL(); 240 | void Push16(uint16_t data); 241 | uint16_t Pop16(); 242 | // * Coprocessor Data Transfer Instructions 243 | void OP_CR_R(); 244 | void OP_CR_EA(); 245 | void BumpEA(size_t value_size); 246 | // * EA Register Data Transfer Instructions 247 | void OP_LEA(); 248 | // * ALU Instructions 249 | void OP_DAA(); 250 | void OP_DAS(); 251 | void OP_NEG(); 252 | // * Bit Access Instructions 253 | void OP_BITMOD(); 254 | // * PSW Access Instructions 255 | void OP_PSW_OR(); 256 | void OP_PSW_AND(); 257 | void OP_CPLC(); 258 | // * Conditional Relative Branch Instructions 259 | void OP_BC(); 260 | // * Sign Extension Instruction 261 | void OP_EXTBW(); 262 | // * Software Interrupt Instructions 263 | void OP_SWI(); 264 | void OP_BRK(); 265 | // * Branch Instructions 266 | void OP_B(); 267 | void OP_BL(); 268 | // * Multiplication and Division Instructions 269 | void OP_MUL(); 270 | void OP_DIV(); 271 | // * Miscellaneous Instructions 272 | void OP_INC_EA(); 273 | void OP_DEC_EA(); 274 | void OP_RT(); 275 | void OP_RTI(); 276 | void OP_NOP(); 277 | void OP_DSR(); 278 | }; 279 | } 280 | 281 | -------------------------------------------------------------------------------- /emulator/casioemu.cpp: -------------------------------------------------------------------------------- 1 | #include "Config.hpp" 2 | #include "Config/Config.hpp" 3 | #include "Gui/imgui_impl_sdl2.h" 4 | #include "Gui/Ui.hpp" 5 | #include "SDL_stdinc.h" 6 | #include "SDL_thread.h" 7 | #include "SDL_timer.h" 8 | #include "utils.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "Emulator.hpp" 25 | #include "Logger.hpp" 26 | #include "Data/EventCode.hpp" 27 | #include "SDL_events.h" 28 | #include "SDL_keyboard.h" 29 | #include "SDL_mouse.h" 30 | #include "SDL_video.h" 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | 39 | static bool abort_flag = false; 40 | 41 | void StartMemSpansConfigWatcherThread(const std::string &path); 42 | 43 | using namespace casioemu; 44 | 45 | 46 | int imgui_rendering(void* data){ 47 | DebugUi *ui = (DebugUi*)data; 48 | SDL_Event event; 49 | SDL_zero(event); 50 | event.type = SDL_USEREVENT; 51 | event.user.code = 6; 52 | while (true) { 53 | ui->PaintUi(); 54 | #ifndef _WIN32 55 | ui->PaintSDL(); 56 | #endif 57 | SDL_Delay(16); 58 | } 59 | } 60 | 61 | // #define DEBUG 62 | int main(int argc, char *argv[]) 63 | { 64 | std::map argv_map; 65 | for (int ix = 1; ix != argc; ++ix) 66 | { 67 | std::string key, value; 68 | char *eq_pos = strchr(argv[ix], '='); 69 | if (eq_pos) 70 | { 71 | key = std::string(argv[ix], eq_pos); 72 | value = eq_pos + 1; 73 | } 74 | else 75 | { 76 | key = "model"; 77 | value = argv[ix]; 78 | } 79 | 80 | if (argv_map.find(key) == argv_map.end()) 81 | argv_map[key] = value; 82 | else 83 | logger::Info("[argv] #%i: key '%s' already set\n", ix, key.c_str()); 84 | } 85 | 86 | if (argv_map.find("model") == argv_map.end()) 87 | { 88 | 89 | #ifdef DEBUG 90 | argv_map["model"]="E:/projects/CasioEmuX/models/fx991cncw"; 91 | #else 92 | argv_map["model"]=EmuGloConfig.GetModulePath(); 93 | #endif 94 | // printf("No model path supplied\n"); 95 | // exit(2); 96 | } 97 | argv_map["script"]="lua-common.lua"; 98 | 99 | int sdlFlags = SDL_INIT_VIDEO | SDL_INIT_TIMER; 100 | if (SDL_Init(sdlFlags) != 0) 101 | PANIC("SDL_Init failed: %s\n", SDL_GetError()); 102 | 103 | int imgFlags = IMG_INIT_PNG; 104 | if (IMG_Init(imgFlags) != imgFlags) 105 | PANIC("IMG_Init failed: %s\n", IMG_GetError()); 106 | 107 | std::string history_filename; 108 | auto history_filename_iter = argv_map.find("history"); 109 | if (history_filename_iter != argv_map.end()) 110 | history_filename = history_filename_iter->second; 111 | 112 | if (!history_filename.empty()) 113 | { 114 | 115 | } 116 | 117 | for (auto s: {SIGTERM, SIGINT}) { 118 | signal(s, [](int) { 119 | abort_flag = true; 120 | }); 121 | } 122 | // while(1) 123 | // ; 124 | 125 | 126 | { 127 | Emulator emulator(argv_map); 128 | std::thread console_t([&](){ 129 | char cmd_buf[128]; 130 | while (true) { 131 | memset(cmd_buf, 0, 128); 132 | std::cin.getline(cmd_buf,128); 133 | emulator.ExecuteCommand(std::string(cmd_buf)); 134 | } 135 | }); 136 | // Note: argv_map must be destructed after emulator. 137 | 138 | // start colored spans file watcher thread 139 | auto colored_spans_file = emulator.GetModelFilePath("mem-spans.txt"); 140 | StartMemSpansConfigWatcherThread(colored_spans_file); 141 | 142 | // Used to signal to the console input thread when to stop. 143 | static std::atomic running(true); 144 | 145 | DebugUi ui; 146 | 147 | SDL_Thread *uit = SDL_CreateThread(imgui_rendering, "uithread", &ui); 148 | 149 | while (emulator.Running()) 150 | { 151 | //std::cout< 21 | #include 22 | #include 23 | 24 | namespace casioemu 25 | { 26 | Chipset::Chipset(Emulator &_emulator) : emulator(_emulator), cpu(*new CPU(emulator)), mmu(*new MMU(emulator)) 27 | { 28 | } 29 | 30 | void Chipset::Setup() 31 | { 32 | for (size_t ix = 0; ix != INT_COUNT; ++ix) 33 | interrupts_active[ix] = false; 34 | pending_interrupt_count = 0; 35 | 36 | cpu.SetMemoryModel(CPU::MM_LARGE); 37 | 38 | std::initializer_list segments_es_plus{ 0, 1, 8 }, segments_classwiz{ 0, 1, 2, 3, 4, 5 }, segments_classwiz_ii{ 0,1,2,3,4,5,6,7,8 }; 39 | for (auto segment_index : emulator.hardware_id == HW_ES_PLUS ? segments_es_plus : emulator.hardware_id == HW_CLASSWIZ ? segments_classwiz : segments_classwiz_ii) 40 | mmu.GenerateSegmentDispatch(segment_index); 41 | 42 | ConstructPeripherals(); 43 | } 44 | 45 | Chipset::~Chipset() 46 | { 47 | DestructPeripherals(); 48 | DestructInterruptSFR(); 49 | 50 | delete &mmu; 51 | delete &cpu; 52 | } 53 | 54 | void Chipset::ConstructInterruptSFR() 55 | { 56 | region_int_mask.Setup(0xF010, 2, "Chipset/InterruptMask", &data_int_mask, MMURegion::DefaultRead, MMURegion::DefaultWrite, emulator); 57 | 58 | region_int_pending.Setup(0xF014, 2, "Chipset/InterruptPending", &data_int_pending, MMURegion::DefaultRead, MMURegion::DefaultWrite, emulator); 59 | } 60 | 61 | void Chipset::DestructInterruptSFR() 62 | { 63 | region_int_pending.Kill(); 64 | region_int_mask.Kill(); 65 | } 66 | 67 | void Chipset::ConstructPeripherals() 68 | { 69 | peripherals.push_front(new ROMWindow(emulator)); 70 | peripherals.push_front(new BatteryBackedRAM(emulator)); 71 | peripherals.push_front(CreateScreen(emulator)); 72 | peripherals.push_front(new Keyboard(emulator)); 73 | peripherals.push_front(new StandbyControl(emulator)); 74 | peripherals.push_front(new Miscellaneous(emulator)); 75 | peripherals.push_front(new Timer(emulator)); 76 | if (emulator.hardware_id == HW_CLASSWIZ_II) 77 | peripherals.push_front(new BCDCalc(emulator)); 78 | } 79 | 80 | void Chipset::DestructPeripherals() 81 | { 82 | for (auto &peripheral : peripherals) 83 | { 84 | peripheral->Uninitialise(); 85 | delete peripheral; 86 | } 87 | } 88 | 89 | void Chipset::SetupInternals() 90 | { 91 | std::ifstream rom_handle(emulator.GetModelFilePath(emulator.GetModelInfo("rom_path")), std::ifstream::binary); 92 | if (rom_handle.fail()) 93 | PANIC("std::ifstream failed: %s\n", std::strerror(errno)); 94 | rom_data = std::vector((std::istreambuf_iterator(rom_handle)), std::istreambuf_iterator()); 95 | 96 | for (auto &peripheral : peripherals) 97 | peripheral->Initialise(); 98 | 99 | ConstructInterruptSFR(); 100 | 101 | cpu.SetupInternals(); 102 | mmu.SetupInternals(); 103 | } 104 | 105 | void Chipset::Reset() 106 | { 107 | data_int_mask = 0; 108 | data_int_pending = 0; 109 | 110 | for (auto &peripheral : peripherals) 111 | peripheral->Reset(); 112 | 113 | cpu.Reset(); 114 | 115 | interrupts_active[INT_RESET] = true; 116 | pending_interrupt_count = 1; 117 | 118 | run_mode = RM_RUN; 119 | } 120 | 121 | void Chipset::Break() 122 | { 123 | if (cpu.GetExceptionLevel() > 1) 124 | { 125 | Reset(); 126 | return; 127 | } 128 | 129 | if (interrupts_active[INT_BREAK]) 130 | return; 131 | interrupts_active[INT_BREAK] = true; 132 | pending_interrupt_count++; 133 | } 134 | 135 | void Chipset::Halt() 136 | { 137 | run_mode = RM_HALT; 138 | } 139 | 140 | void Chipset::Stop() 141 | { 142 | run_mode = RM_STOP; 143 | } 144 | 145 | bool Chipset::GetRunningState() 146 | { 147 | if(run_mode == RM_RUN) 148 | return true; 149 | return false; 150 | } 151 | 152 | void Chipset::RaiseEmulator() 153 | { 154 | if (interrupts_active[INT_EMULATOR]) 155 | return; 156 | interrupts_active[INT_EMULATOR] = true; 157 | pending_interrupt_count++; 158 | } 159 | 160 | void Chipset::RaiseNonmaskable() 161 | { 162 | if (interrupts_active[INT_NONMASKABLE]) 163 | return; 164 | interrupts_active[INT_NONMASKABLE] = true; 165 | pending_interrupt_count++; 166 | } 167 | 168 | void Chipset::RaiseMaskable(size_t index) 169 | { 170 | if (index < INT_MASKABLE || index >= INT_SOFTWARE) 171 | PANIC("%zu is not a valid maskable interrupt index\n", index); 172 | if (interrupts_active[index]) 173 | return; 174 | interrupts_active[index] = true; 175 | pending_interrupt_count++; 176 | } 177 | 178 | void Chipset::RaiseSoftware(size_t index) 179 | { 180 | index += 0x40; 181 | if (interrupts_active[index]) 182 | return; 183 | interrupts_active[index] = true; 184 | pending_interrupt_count++; 185 | } 186 | 187 | void Chipset::AcceptInterrupt() 188 | { 189 | size_t old_exception_level = cpu.GetExceptionLevel(); 190 | 191 | size_t index = 0; 192 | // * Reset has priority over everything. 193 | if (interrupts_active[INT_RESET]) 194 | index = INT_RESET; 195 | // * Software interrupts are immediately accepted. 196 | if (!index) 197 | for (size_t ix = INT_SOFTWARE; ix != INT_COUNT; ++ix) 198 | if (interrupts_active[ix]) 199 | { 200 | if (old_exception_level > 1) 201 | PANIC("software interrupt while exception level was greater than 1\n"); 202 | index = ix; 203 | break; 204 | } 205 | // * No need to check the old exception level as NMICI has an exception level of 3. 206 | if (!index && interrupts_active[INT_EMULATOR]) 207 | index = INT_EMULATOR; 208 | // * No need to check the old exception level as BRK initiates a reset if 209 | // the currect exception level is greater than 1. 210 | if (!index && interrupts_active[INT_BREAK]) 211 | index = INT_BREAK; 212 | if (!index && interrupts_active[INT_NONMASKABLE] && old_exception_level <= 2) 213 | index = INT_NONMASKABLE; 214 | if (!index && old_exception_level <= 1) 215 | for (size_t ix = INT_MASKABLE; ix != INT_SOFTWARE; ++ix) 216 | if (interrupts_active[ix]) 217 | { 218 | index = ix; 219 | break; 220 | } 221 | 222 | size_t exception_level; 223 | switch (index) 224 | { 225 | case INT_RESET: 226 | exception_level = 0; 227 | break; 228 | 229 | case INT_BREAK: 230 | case INT_NONMASKABLE: 231 | exception_level = 2; 232 | break; 233 | 234 | case INT_EMULATOR: 235 | exception_level = 3; 236 | break; 237 | 238 | default: 239 | exception_level = 1; 240 | break; 241 | } 242 | 243 | if (index >= INT_MASKABLE && index < INT_SOFTWARE) 244 | { 245 | if (InterruptEnabledBySFR(index)) 246 | { 247 | SetInterruptPendingSFR(index); 248 | if (cpu.GetMasterInterruptEnable()) 249 | cpu.Raise(exception_level, index); 250 | } 251 | } 252 | else 253 | { 254 | cpu.Raise(exception_level, index); 255 | } 256 | 257 | run_mode = RM_RUN; 258 | 259 | // * TODO: introduce delay 260 | 261 | interrupts_active[index] = false; 262 | pending_interrupt_count--; 263 | } 264 | 265 | bool Chipset::InterruptEnabledBySFR(size_t index) 266 | { 267 | return data_int_mask & (1 << (index - managed_interrupt_base)); 268 | } 269 | 270 | bool Chipset::GetInterruptPendingSFR(size_t index) 271 | { 272 | return data_int_pending & (1 << (index - managed_interrupt_base)); 273 | } 274 | 275 | void Chipset::SetInterruptPendingSFR(size_t index) 276 | { 277 | data_int_pending |= (1 << (index - managed_interrupt_base)); 278 | } 279 | 280 | bool Chipset::GetRequireFrame() 281 | { 282 | return std::any_of(peripherals.begin(), peripherals.end(), [](Peripheral *peripheral){ 283 | return peripheral->GetRequireFrame(); 284 | }); 285 | } 286 | 287 | void Chipset::Frame() 288 | { 289 | for (auto peripheral : peripherals) 290 | peripheral->Frame(); 291 | } 292 | 293 | void Chipset::Tick() 294 | { 295 | // * TODO: decrement delay counter, return if it's not 0 296 | 297 | for (auto peripheral : peripherals) 298 | peripheral->Tick(); 299 | 300 | if (pending_interrupt_count) 301 | AcceptInterrupt(); 302 | 303 | for (auto peripheral : peripherals) 304 | peripheral->TickAfterInterrupts(); 305 | 306 | if (run_mode == RM_RUN) 307 | cpu.Next(); 308 | } 309 | 310 | void Chipset::UIEvent(SDL_Event &event) 311 | { 312 | for (auto peripheral : peripherals) 313 | peripheral->UIEvent(event); 314 | } 315 | } 316 | 317 | -------------------------------------------------------------------------------- /emulator/Gui/CodeViewer.cpp: -------------------------------------------------------------------------------- 1 | #include "CodeViewer.hpp" 2 | #include "../Config.hpp" 3 | #include "../Logger.hpp" 4 | #include "../Emulator.hpp" 5 | #include "../Chipset/Chipset.hpp" 6 | #include "../Chipset/CPU.hpp" 7 | #include "imgui.h" 8 | #include "WatchWindow.hpp" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "../Config/Config.hpp" 21 | 22 | std::unordered_map DebugBreakPoints; 23 | 24 | int get_real_pc(uint8_t seg,uint16_t off){ 25 | return (seg<<16)|off; 26 | } 27 | 28 | UI_SINGLE_IMPL(CodeViewer); 29 | 30 | CodeViewer::CodeViewer(std::string path) 31 | { 32 | instance = this; 33 | src_path = path; 34 | emu = casioemu::Emulator::instance; 35 | std::thread t1([this](){ 36 | std::ifstream f(src_path,std::ios::in); 37 | if(!f.is_open()){ 38 | PANIC("\nFail to open disassembly code src: %s\n",src_path.c_str()); 39 | } 40 | casioemu::logger::Info("Start to read code src ...\n"); 41 | char buf[200]{0}; 42 | char adr[6]{0}; 43 | while(!f.eof()){ 44 | f.getline(buf,200); 45 | // 1sf, extract segment number 46 | uint8_t seg = buf[1] - '0'; 47 | uint8_t len = strlen(buf); 48 | if(!len) 49 | break; 50 | if(len>max_col) 51 | max_col = len; 52 | memcpy(adr, buf+2, 4); 53 | //casioemu::logger::Info("[%s %d %d]\n",adr,seg,len); 54 | uint16_t offset = std::stoi( adr,0,16); 55 | CodeElem e; 56 | e.offset =offset; 57 | e.segment =seg; 58 | memset(e.srcbuf, 0, 40); 59 | memcpy(e.srcbuf, buf+28, len-28); 60 | codes.push_back(e); 61 | memset(buf, 0, 200); 62 | memset(adr, 0, 6); 63 | } 64 | f.close(); 65 | casioemu::logger::Info("Read src codes over!\n"); 66 | max_row = codes.size(); 67 | is_loaded=true; 68 | }); 69 | t1.detach(); 70 | } 71 | bool elem_cmp(const CodeElem& a, const CodeElem& b) { 72 | 73 | return get_real_pc(a.segment, a.offset)segment,it->offset,it->srcbuf); 90 | } 91 | if(idx) 92 | *idx = it-codes.begin(); 93 | return {.segment=it->segment,.offset=it->offset}; 94 | } 95 | 96 | bool CodeViewer::TryTrigBP(uint8_t seg,uint16_t offset,bool bp_mode){ 97 | int realpc = get_real_pc(seg, offset); 98 | auto it = DebugBreakPoints.find(realpc); 99 | if(it != DebugBreakPoints.end() && it->second==1){ 100 | //TODO: We ignore a second trigger 101 | (*it).second = 2; 102 | int idx=0; 103 | LookUp(seg, offset,&idx); 104 | cur_col = idx; 105 | need_roll=true; 106 | cur_break_real_pc = realpc; 107 | return true; 108 | } 109 | if( !bp_mode &&( debug_flags & DEBUG_STEP || debug_flags & DEBUG_RET_TRACE)){ 110 | int idx=0; 111 | LookUp(seg, offset,&idx); 112 | DebugBreakPoints[realpc] = 3; 113 | cur_col=idx; 114 | need_roll=true; 115 | cur_break_real_pc = realpc; 116 | return true; 117 | } 118 | return false; 119 | } 120 | 121 | void CodeViewer::DrawContent(){ 122 | 123 | ImGuiListClipper c; 124 | c.Begin(max_row,ImGui::GetTextLineHeight()); 125 | ImDrawList *draw_list =ImGui::GetWindowDrawList(); 126 | // if(cur_break_real_pc!=-1){ 127 | // c.IncludeItemByIndex(cur_col); 128 | // } 129 | while(c.Step()) 130 | { 131 | for (int line_i=c.DisplayStart; line_isecond == 1){ 136 | ImGui::TextColored(ImVec4(1.0,0.0,0.0,1.0), "[ x ]"); 137 | if(ImGui::IsItemHovered() && ImGui::IsMouseClicked(0)){ 138 | DebugBreakPoints.erase(realpc); 139 | } 140 | }else if(realpc != cur_break_real_pc){ 141 | ImGui::Text("[ o ]"); 142 | if(ImGui::IsItemHovered() && ImGui::IsMouseClicked(0)){ 143 | DebugBreakPoints[realpc] = 1; 144 | } 145 | 146 | } 147 | if(realpc == cur_break_real_pc){ 148 | isbreaked = true; 149 | ImVec2 pos = ImGui::GetCursorScreenPos(); 150 | ImGui::GetWindowDrawList() 151 | ->AddRectFilled(pos, ImVec2(pos.x + ImGui::GetWindowWidth(),pos.y +ImGui::GetTextLineHeight()),IM_COL32(255,255,0,50)); 152 | ImGui::TextColored(ImVec4(0.0,1.0,0.0,1.0),"[ > ]"); 153 | if(ImGui::IsItemHovered() && ImGui::IsMouseClicked(0)){ 154 | DebugBreakPoints.erase(realpc); 155 | } 156 | } 157 | 158 | ImGui::SameLine(); 159 | 160 | ImGui::TextColored(ImVec4(1.0,1.0,0.0,1.0), "%d:%04x",e.segment,e.offset); 161 | ImGui::SameLine(); 162 | ImGui::Text("%s",e.srcbuf); 163 | 164 | } 165 | } 166 | c.End(); 167 | if(need_roll){ 168 | float v=(float)cur_col/max_row*ImGui::GetScrollMaxY(); 169 | ImGui::SetScrollY(v); 170 | need_roll = false; 171 | selected_addr = codes[cur_col].segment*0x10000+codes[cur_col].offset; 172 | } 173 | 174 | } 175 | 176 | void CodeViewer::DrawMonitor(){ 177 | 178 | } 179 | 180 | static bool step_debug=false,trace_debug=false; 181 | 182 | void CodeViewer::Show(){ 183 | DrawWindow(); 184 | } 185 | 186 | void CodeViewer::DrawWindow(){ 187 | 188 | int h = ImGui::GetTextLineHeight()+4; 189 | int w = ImGui::CalcTextSize("F").x; 190 | if(!is_loaded){ 191 | ImGui::SetNextWindowSize(ImVec2(w*50,h*10)); 192 | ImGui::SetNextWindowContentSize(ImVec2(w*50,h*10)); 193 | ImGui::Begin(EmuGloConfig[UI_DISAS]); 194 | ImGui::SetCursorPos(ImVec2(w*2,h*5)); 195 | ImGui::Text(EmuGloConfig[UI_DISAS_WAITING]); 196 | ImGui::End(); 197 | return; 198 | } 199 | ImVec2 sz; 200 | h*=10; 201 | w*=max_col; 202 | sz.x = w; 203 | sz.y = h; 204 | //ImGui::SetNextWindowSize(sz); 205 | //ImGui::SetNextWindowContentSize(sz); 206 | ImGui::Begin(EmuGloConfig[UI_DISAS],0); 207 | ImGui::BeginChild("##scrolling",ImVec2(0,ImGui::GetWindowHeight()-ImGui::GetTextLineHeight()*2)); 208 | DrawContent(); 209 | ImGui::EndChild(); 210 | ImGui::Separator(); 211 | ImGui::Text(EmuGloConfig[UI_DISAS_GOTO_ADDR]); 212 | ImGui::SameLine(); 213 | ImGui::SetNextItemWidth(ImGui::CalcTextSize("000000").x); 214 | ImGui::InputText("##input", adrbuf, 8); 215 | if(adrbuf[0]!='\0' && ImGui::IsItemFocused()){ 216 | uint32_t addr = std::stoi(adrbuf,0,16); 217 | JumpTo(addr>>16, addr&0x0ffff); 218 | } 219 | ImGui::SameLine(); 220 | ImGui::Checkbox(EmuGloConfig[UI_DISAS_STEP], &step_debug); 221 | ImGui::SameLine(); 222 | ImGui::Checkbox(EmuGloConfig[UI_DISAS_TRACE], &trace_debug); 223 | if(cur_break_real_pc != -1){ 224 | ImGui::SameLine(); 225 | ImVec2 pos = ImGui::GetCursorScreenPos(); 226 | ImVec2 sz = ImGui::CalcTextSize("[next] "); 227 | //debug tool 228 | ImGui::GetWindowDrawList() 229 | ->AddRectFilled(pos, ImVec2(pos.x+sz.x,pos.y+sz.y),IM_COL32(255, 255, 0, 50)); 230 | 231 | ImGui::Text("[next]"); 232 | if(ImGui::IsItemHovered() && ImGui::IsMouseClicked(0)){ 233 | if(DebugBreakPoints.find(cur_break_real_pc) != DebugBreakPoints.end()){ 234 | if(DebugBreakPoints[cur_break_real_pc] == 3){ 235 | DebugBreakPoints.erase(cur_break_real_pc); 236 | }else{ 237 | DebugBreakPoints[cur_break_real_pc]=1; 238 | } 239 | } 240 | cur_break_real_pc = -1; 241 | isbreaked = false; 242 | WatchWindow::instance->UpdateRX(); 243 | emu->SetPaused(false); 244 | } 245 | ImGui::SameLine(); 246 | 247 | ImGui::Text(" "); 248 | } 249 | //ImGui::BeginChild("##scrolling"); 250 | DrawMonitor(); 251 | //ImGui::EndChild(); 252 | ImGui::End(); 253 | debug_flags = DEBUG_BREAKPOINT | (step_debug?DEBUG_STEP:0) | (trace_debug?DEBUG_RET_TRACE:0); 254 | 255 | } 256 | 257 | void CodeViewer::JumpTo(uint8_t seg,uint16_t offset){ 258 | int idx=0; 259 | //printf("jumpto:seg%d\n",seg); 260 | LookUp(seg, offset,&idx); 261 | cur_col=idx; 262 | need_roll=true; 263 | } -------------------------------------------------------------------------------- /emulator/Chipset/MMU.cpp: -------------------------------------------------------------------------------- 1 | #include "MMU.hpp" 2 | 3 | #include "../Gui/MemBreakPoint.hpp" 4 | 5 | #include 6 | #include 7 | #include "../Emulator.hpp" 8 | #include "Chipset.hpp" 9 | #include "../Logger.hpp" 10 | 11 | namespace casioemu 12 | { 13 | MMU::MMU(Emulator &_emulator) : emulator(_emulator) 14 | { 15 | segment_dispatch = new MemoryByte *[0x100]; 16 | for (size_t ix = 0; ix != 0x100; ++ix) 17 | segment_dispatch[ix] = nullptr; 18 | } 19 | 20 | MMU::~MMU() 21 | { 22 | for (size_t ix = 0; ix != 0x100; ++ix) 23 | if (segment_dispatch[ix]) 24 | delete[] segment_dispatch[ix]; 25 | 26 | delete[] segment_dispatch; 27 | } 28 | 29 | void MMU::GenerateSegmentDispatch(size_t segment_index) 30 | { 31 | segment_dispatch[segment_index] = new MemoryByte[0x10000]; 32 | for (size_t ix = 0; ix != 0x10000; ++ix) 33 | { 34 | segment_dispatch[segment_index][ix].region = nullptr; 35 | segment_dispatch[segment_index][ix].on_read = LUA_REFNIL; 36 | segment_dispatch[segment_index][ix].on_write = LUA_REFNIL; 37 | } 38 | } 39 | 40 | void MMU::SetupInternals() 41 | { 42 | *(MMU **)lua_newuserdata(emulator.lua_state, sizeof(MMU *)) = this; 43 | lua_newtable(emulator.lua_state); 44 | lua_pushcfunction(emulator.lua_state, [](lua_State *lua_state) { 45 | MMU *mmu = *(MMU **)lua_topointer(lua_state, 1); 46 | lua_pushinteger(lua_state, mmu->ReadCode(lua_tointeger(lua_state, 2))); 47 | return 1; 48 | }); 49 | lua_setfield(emulator.lua_state, -2, "__index"); 50 | lua_pushcfunction(emulator.lua_state, [](lua_State *) { 51 | return 0; 52 | }); 53 | lua_setfield(emulator.lua_state, -2, "__newindex"); 54 | lua_setmetatable(emulator.lua_state, -2); 55 | lua_setglobal(emulator.lua_state, "code"); 56 | 57 | *(MMU **)lua_newuserdata(emulator.lua_state, sizeof(MMU *)) = this; 58 | lua_newtable(emulator.lua_state); 59 | lua_pushcfunction(emulator.lua_state, [](lua_State *lua_state) { 60 | MMU *mmu = *(MMU **)lua_topointer(lua_state, 1); 61 | int isnum; 62 | size_t offset = lua_tointegerx(lua_state, 2, &isnum); 63 | if (isnum) 64 | { 65 | lua_pushinteger(lua_state, mmu->ReadData(offset)); 66 | return 1; 67 | } 68 | const char *key = lua_tostring(lua_state, 2); 69 | if (key == nullptr) // the key is not a string 70 | return 0; 71 | if (std::strcmp(key, "rwatch") == 0) 72 | { 73 | // execute Lua function whenever address is read from 74 | lua_pushcfunction(lua_state, [](lua_State *lua_state) { 75 | if (lua_gettop(lua_state) != 3) 76 | return luaL_error(lua_state, "rwatch function called with incorrect number of arguments"); 77 | 78 | MMU *mmu = *(MMU **)lua_topointer(lua_state, 1); 79 | size_t offset = lua_tointeger(lua_state, 2); 80 | int on_read = luaL_ref(lua_state, LUA_REGISTRYINDEX); 81 | 82 | size_t segment_index = offset >> 16; 83 | size_t segment_offset = offset & 0xFFFF; 84 | 85 | MemoryByte *segment = mmu->segment_dispatch[segment_index]; 86 | if (!segment) 87 | { 88 | logger::Info("attempt to set rwatch from offset %04zX of unmapped segment %02zX\n", 89 | segment_offset, segment_index); 90 | return 0; 91 | } 92 | 93 | MemoryByte &byte = segment[segment_offset]; 94 | luaL_unref(lua_state, LUA_REGISTRYINDEX, byte.on_read); 95 | byte.on_read = on_read; 96 | return 0; 97 | }); 98 | return 1; 99 | } 100 | else if (std::strcmp(key, "watch") == 0) 101 | { 102 | // execute Lua function whenever address is written to 103 | lua_pushcfunction(lua_state, [](lua_State *lua_state) { 104 | if (lua_gettop(lua_state) != 3) 105 | return luaL_error(lua_state, "watch function called with incorrect number of arguments"); 106 | 107 | MMU *mmu = *(MMU **)lua_topointer(lua_state, 1); 108 | size_t offset = lua_tointeger(lua_state, 2); 109 | int on_write = luaL_ref(lua_state, LUA_REGISTRYINDEX); 110 | 111 | size_t segment_index = offset >> 16; 112 | size_t segment_offset = offset & 0xFFFF; 113 | 114 | MemoryByte *segment = mmu->segment_dispatch[segment_index]; 115 | if (!segment) 116 | { 117 | logger::Info("attempt to set watch from offset %04zX of unmapped segment %02zX\n", 118 | segment_offset, segment_index); 119 | return 0; 120 | } 121 | 122 | MemoryByte &byte = segment[segment_offset]; 123 | luaL_unref(lua_state, LUA_REGISTRYINDEX, byte.on_write); 124 | byte.on_write = on_write; 125 | return 0; 126 | }); 127 | return 1; 128 | } 129 | else 130 | { 131 | return 0; 132 | } 133 | }); 134 | lua_setfield(emulator.lua_state, -2, "__index"); 135 | lua_pushcfunction(emulator.lua_state, [](lua_State *lua_state) { 136 | MMU *mmu = *(MMU **)lua_topointer(lua_state, 1); 137 | mmu->WriteData(lua_tointeger(lua_state, 2), lua_tointeger(lua_state, 3)); 138 | return 0; 139 | }); 140 | lua_setfield(emulator.lua_state, -2, "__newindex"); 141 | lua_setmetatable(emulator.lua_state, -2); 142 | lua_setglobal(emulator.lua_state, "data"); 143 | } 144 | 145 | uint16_t MMU::ReadCode(size_t offset) 146 | { 147 | if (offset >= (1 << 20)) 148 | PANIC("offset doesn't fit 20 bits\n"); 149 | if (offset & 1) 150 | PANIC("offset has LSB set\n"); 151 | 152 | size_t segment_index = offset >> 16; 153 | size_t segment_offset = offset & 0xFFFF; 154 | 155 | if (!segment_index) 156 | return (((uint16_t)emulator.chipset.rom_data[segment_offset + 1]) << 8) | emulator.chipset.rom_data[segment_offset]; 157 | 158 | MemoryByte *segment = segment_dispatch[segment_index]; 159 | if (!segment) 160 | { 161 | emulator.HandleMemoryError(); 162 | return 0; 163 | } 164 | 165 | MMURegion *region = segment[segment_offset].region; 166 | if (!region) 167 | { 168 | emulator.HandleMemoryError(); 169 | return 0; 170 | } 171 | 172 | return (((uint16_t)region->read(region, offset + 1)) << 8) | region->read(region, offset); 173 | } 174 | 175 | uint8_t MMU::ReadData(size_t offset) 176 | { 177 | if (offset >= (1 << 24)) 178 | PANIC("offset doesn't fit 24 bits\n"); 179 | 180 | size_t segment_index = offset >> 16; 181 | size_t segment_offset = offset & 0xFFFF; 182 | 183 | MemoryByte *segment = segment_dispatch[segment_index]; 184 | if (!segment) 185 | { 186 | //logger::Info("read from offset %04zX of unmapped segment %02zX\n", segment_offset, segment_index); 187 | emulator.HandleMemoryError(); 188 | return 0; 189 | } 190 | 191 | MemoryByte &byte = segment[segment_offset]; 192 | MMURegion *region = byte.region; 193 | if (byte.on_read != LUA_REFNIL) 194 | { 195 | lua_geti(emulator.lua_state, LUA_REGISTRYINDEX, byte.on_read); 196 | if (lua_pcall(emulator.lua_state, 0, 0, 0) != LUA_OK) 197 | { 198 | //logger::Info("calling commands on rwatch at %06zX failed: %s\n", 199 | //offset, lua_tostring(emulator.lua_state, -1)); 200 | lua_pop(emulator.lua_state, 1); 201 | } 202 | } 203 | if (!region) 204 | { 205 | //logger::Info("read from unmapped offset %04zX of segment %02zX\n", segment_offset, segment_index); 206 | emulator.HandleMemoryError(); 207 | return 0; 208 | } 209 | if(MemBreakPoint::instance){ 210 | MemBreakPoint::instance->TryTrigBp(segment_offset, false); 211 | } 212 | return region->read(region, offset); 213 | } 214 | 215 | void MMU::WriteData(size_t offset, uint8_t data) 216 | { 217 | if (offset >= (1 << 24)) 218 | PANIC("offset doesn't fit 24 bits\n"); 219 | 220 | size_t segment_index = offset >> 16; 221 | size_t segment_offset = offset & 0xFFFF; 222 | 223 | MemoryByte *segment = segment_dispatch[segment_index]; 224 | if (!segment) 225 | { 226 | //logger::Info("write to offset %04zX of unmapped segment %02zX (%02zX)\n", segment_offset, segment_index, data); 227 | emulator.HandleMemoryError(); 228 | return; 229 | } 230 | 231 | MemoryByte &byte = segment[segment_offset]; 232 | MMURegion *region = byte.region; 233 | if (byte.on_write != LUA_REFNIL) 234 | { 235 | lua_geti(emulator.lua_state, LUA_REGISTRYINDEX, byte.on_write); 236 | if (lua_pcall(emulator.lua_state, 0, 0, 0) != LUA_OK) 237 | { 238 | logger::Info("calling commands on watch at %06zX failed: %s\n", 239 | offset, lua_tostring(emulator.lua_state, -1)); 240 | lua_pop(emulator.lua_state, 1); 241 | } 242 | } 243 | if (!region) 244 | { 245 | //logger::Info("write to unmapped offset %04zX of segment %02zX (%02zX)\n", segment_offset, segment_index, data); 246 | emulator.HandleMemoryError(); 247 | return; 248 | } 249 | 250 | if(MemBreakPoint::instance){ 251 | MemBreakPoint::instance->TryTrigBp(segment_offset, true); 252 | } 253 | 254 | region->write(region, offset, data); 255 | } 256 | 257 | void MMU::RegisterRegion(MMURegion *region) 258 | { 259 | for (size_t ix = region->base; ix != region->base + region->size; ++ix) 260 | { 261 | if(ix == 561152){ 262 | printf("s"); 263 | } 264 | if (segment_dispatch[ix >> 16][ix & 0xFFFF].region) 265 | PANIC("MMU region overlap at %06zX\n", ix); 266 | segment_dispatch[ix >> 16][ix & 0xFFFF].region = region; 267 | } 268 | } 269 | 270 | void MMU::UnregisterRegion(MMURegion *region) 271 | { 272 | for (size_t ix = region->base; ix != region->base + region->size; ++ix) 273 | { 274 | if (!segment_dispatch[ix >> 16][ix & 0xFFFF].region) 275 | PANIC("MMU region double-hole at %06zX\n", ix); 276 | segment_dispatch[ix >> 16][ix & 0xFFFF].region = nullptr; 277 | } 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /emulator/Chipset/CPUArithmetic.cpp: -------------------------------------------------------------------------------- 1 | #include "CPU.hpp" 2 | 3 | #include "../Emulator.hpp" 4 | #include "Chipset.hpp" 5 | #include "MMU.hpp" 6 | 7 | namespace casioemu 8 | { 9 | // * Arithmetic Instructions 10 | void CPU::OP_ADD() 11 | { 12 | impl_flags_in &= ~PSW_C; 13 | impl_flags_in |= PSW_Z; 14 | OP_ADDC(); 15 | } 16 | 17 | void CPU::OP_ADD16() 18 | { 19 | if (impl_hint & H_IE) 20 | impl_operands[1].value |= (impl_operands[1].value & 0x40) ? 0xFF80 : 0; 21 | 22 | impl_flags_in &= ~PSW_C; 23 | 24 | uint8_t op_high_0 = impl_operands[0].value >> 8; 25 | uint8_t op_high_1 = impl_operands[1].value >> 8; 26 | Add8(); 27 | ZSCheck(); 28 | 29 | impl_flags_in = (impl_flags_in & ~PSW_C) | (impl_flags_out & PSW_C); 30 | 31 | uint8_t op_low_0 = impl_operands[0].value; 32 | impl_operands[0].value = op_high_0; 33 | impl_operands[1].value = op_high_1; 34 | Add8(); 35 | ZSCheck(); 36 | 37 | impl_operands[0].value = (impl_operands[0].value << 8) | op_low_0; 38 | } 39 | 40 | void CPU::OP_ADDC() 41 | { 42 | Add8(); 43 | if (!(impl_flags_in & PSW_Z)) 44 | impl_flags_out &= ~PSW_Z; 45 | ZSCheck(); 46 | } 47 | 48 | void CPU::OP_AND() 49 | { 50 | impl_operands[0].value &= impl_operands[1].value & 0xFF; 51 | ZSCheck(); 52 | } 53 | 54 | void CPU::OP_MOV16() 55 | { 56 | if (impl_hint & H_IE) 57 | impl_operands[1].value |= (impl_operands[1].value & 0x40) ? 0xFF80 : 0; 58 | 59 | impl_operands[0].value = impl_operands[1].value & 0xFF; 60 | ZSCheck(); 61 | 62 | uint8_t op_low_0 = impl_operands[0].value; 63 | impl_operands[0].value = (impl_operands[1].value >> 8) & 0xFF; 64 | ZSCheck(); 65 | 66 | impl_operands[0].value = (impl_operands[0].value << 8) | op_low_0; 67 | } 68 | 69 | void CPU::OP_MOV() 70 | { 71 | impl_operands[0].value = impl_operands[1].value & 0xFF; 72 | ZSCheck(); 73 | } 74 | 75 | void CPU::OP_OR() 76 | { 77 | impl_operands[0].value |= impl_operands[1].value & 0xFF; 78 | ZSCheck(); 79 | } 80 | 81 | void CPU::OP_XOR() 82 | { 83 | impl_operands[0].value ^= impl_operands[1].value & 0xFF; 84 | ZSCheck(); 85 | } 86 | 87 | void CPU::OP_CMP16() 88 | { 89 | impl_flags_in &= ~PSW_C; 90 | 91 | uint8_t op_high_0 = impl_operands[0].value >> 8; 92 | uint8_t op_high_1 = impl_operands[1].value >> 8; 93 | impl_operands[0].value ^= 0xFF; 94 | Add8(); 95 | impl_operands[0].value ^= 0xFF; 96 | ZSCheck(); 97 | 98 | impl_flags_in = (impl_flags_in & ~PSW_C) | (impl_flags_out & PSW_C); 99 | 100 | uint8_t op_low_0 = impl_operands[0].value; 101 | impl_operands[0].value = op_high_0; 102 | impl_operands[1].value = op_high_1; 103 | impl_operands[0].value ^= 0xFF; 104 | Add8(); 105 | impl_operands[0].value ^= 0xFF; 106 | ZSCheck(); 107 | 108 | impl_operands[0].value = (impl_operands[0].value << 8) | op_low_0; 109 | } 110 | 111 | void CPU::OP_SUB() 112 | { 113 | impl_flags_in &= ~PSW_C; 114 | impl_flags_in |= PSW_Z; 115 | OP_SUBC(); 116 | } 117 | 118 | void CPU::OP_SUBC() 119 | { 120 | impl_operands[0].value ^= 0xFF; 121 | Add8(); 122 | impl_operands[0].value ^= 0xFF; 123 | if (!(impl_flags_in & PSW_Z)) 124 | impl_flags_out &= ~PSW_Z; 125 | ZSCheck(); 126 | } 127 | 128 | // * Shift Instructions 129 | void CPU::OP_SLL() 130 | { 131 | impl_shift_buffer = 0; 132 | ShiftLeft8(); 133 | } 134 | 135 | void CPU::OP_SLLC() 136 | { 137 | size_t external_reg_index = (impl_operands[0].register_index - 1) & 15; 138 | impl_shift_buffer = reg_r[external_reg_index]; 139 | ShiftLeft8(); 140 | } 141 | 142 | void CPU::OP_SRA() 143 | { 144 | size_t shift_by = impl_operands[1].value & 7; 145 | uint64_t msb = impl_operands[0].value & 0x80; 146 | impl_shift_buffer = 0; 147 | ShiftRight8(); 148 | if (msb) 149 | impl_operands[0].value |= (0xFF >> shift_by) ^ 0xFF; 150 | } 151 | 152 | void CPU::OP_SRL() 153 | { 154 | impl_shift_buffer = 0; 155 | ShiftRight8(); 156 | } 157 | 158 | void CPU::OP_SRLC() 159 | { 160 | size_t external_reg_index = (impl_operands[0].register_index + 1) & 15; 161 | impl_shift_buffer = reg_r[external_reg_index]; 162 | ShiftRight8(); 163 | } 164 | 165 | // * ALU Instructions 166 | void CPU::OP_DAA() 167 | { 168 | impl_operands[1].value = 0; 169 | if ((impl_operands[0].value & 0x0F) > 0x09 || (impl_flags_in & PSW_HC)) impl_operands[1].value |= 0x06; 170 | if ((impl_operands[0].value & 0xF0) > 0x90 || (impl_flags_in & PSW_C)) impl_operands[1].value |= 0x60; 171 | if ((impl_operands[0].value & 0xF0) == 0x90 && (impl_operands[0].value & 0x0F) > 0x09 && !(impl_flags_in & PSW_HC)) impl_operands[1].value |= 0x60; 172 | uint8_t flags_in_backup = impl_flags_in; 173 | OP_ADD(); 174 | impl_flags_out |= flags_in_backup & PSW_C; 175 | impl_flags_changed &= ~PSW_OV; 176 | } 177 | 178 | void CPU::OP_DAS() 179 | { 180 | impl_operands[1].value = 0; 181 | if ((impl_operands[0].value & 0x0F) > 0x09 || (impl_flags_in & PSW_HC)) impl_operands[1].value |= 0x06; 182 | if ((impl_operands[0].value & 0xF0) > 0x90 || (impl_flags_in & PSW_C)) impl_operands[1].value |= 0x60; 183 | uint8_t flags_in_backup = impl_flags_in; 184 | OP_SUB(); 185 | impl_flags_out |= flags_in_backup & PSW_C; 186 | impl_flags_changed &= ~PSW_OV; 187 | } 188 | 189 | void CPU::OP_NEG() 190 | { 191 | impl_operands[1].value = impl_operands[0].value; 192 | impl_operands[0].value = 0; 193 | OP_SUB(); 194 | } 195 | 196 | // * Bit Access Instructions 197 | void CPU::OP_BITMOD() 198 | { 199 | size_t src_index; 200 | uint64_t bit_in = 1 << impl_operands[1].value; 201 | if (impl_hint & H_TI) 202 | { 203 | src_index = impl_long_imm; 204 | impl_operands[0].value = emulator.chipset.mmu.ReadData((((size_t)reg_dsr) << 16) | src_index); 205 | } 206 | else 207 | { 208 | src_index = impl_operands[0].value; 209 | impl_operands[0].value = reg_r[src_index]; 210 | } 211 | 212 | impl_flags_changed |= PSW_Z; 213 | impl_flags_out = (impl_operands[0].value & bit_in) ? 0 : PSW_Z; 214 | 215 | switch (impl_opcode & 0x000F) 216 | { 217 | case 0: 218 | impl_operands[0].value |= bit_in; 219 | break; 220 | case 2: 221 | impl_operands[0].value &= ~bit_in; 222 | break; 223 | } 224 | 225 | if ((impl_opcode & 0x000F) != 1) 226 | { 227 | if (impl_hint & H_TI) 228 | emulator.chipset.mmu.WriteData((((size_t)reg_dsr) << 16) | src_index, impl_operands[0].value); 229 | else 230 | reg_r[src_index] = impl_operands[0].value; 231 | } 232 | } 233 | 234 | // * Sign Extension Instruction 235 | void CPU::OP_EXTBW() 236 | { 237 | size_t index = (impl_opcode & 0x00E0) >> 4; 238 | impl_operands[0].value = (reg_r[index] & 0x80) ? 0xFF : 0x00; 239 | reg_r[index + 1] = impl_operands[0].value; 240 | ZSCheck(); 241 | } 242 | 243 | // * Multiplication and Division Instructions 244 | void CPU::OP_MUL() 245 | { 246 | impl_operands[0].value &= 0xFF; 247 | impl_operands[0].value *= impl_operands[1].value; 248 | 249 | impl_flags_changed |= PSW_Z; 250 | impl_flags_out = impl_operands[0].value ? 0 : PSW_Z; 251 | } 252 | 253 | void CPU::OP_DIV() 254 | { 255 | impl_flags_changed |= PSW_Z | PSW_C; 256 | if (!impl_operands[1].value) 257 | { 258 | impl_flags_out |= PSW_C; 259 | return; 260 | } 261 | 262 | uint16_t quotient = impl_operands[0].value / impl_operands[1].value; 263 | uint16_t remainder = impl_operands[0].value % impl_operands[1].value; 264 | 265 | impl_operands[0].value = quotient; 266 | if (impl_operands[0].value) 267 | impl_flags_out &= ~PSW_Z; 268 | 269 | size_t remainder_reg_index = (impl_opcode >> 4) & 0x000F; 270 | reg_r[remainder_reg_index] = remainder; 271 | } 272 | 273 | // * Miscellaneous Instructions 274 | void CPU::OP_INC_EA() 275 | { 276 | impl_operands[0].value = emulator.chipset.mmu.ReadData((((size_t)reg_dsr) << 16) | reg_ea); 277 | impl_operands[1].value = 1; 278 | OP_ADD(); 279 | impl_flags_changed &= ~PSW_C; 280 | emulator.chipset.mmu.WriteData((((size_t)reg_dsr) << 16) | reg_ea, impl_operands[0].value); 281 | } 282 | 283 | void CPU::OP_DEC_EA() 284 | { 285 | impl_operands[0].value = emulator.chipset.mmu.ReadData((((size_t)reg_dsr) << 16) | reg_ea); 286 | impl_operands[1].value = 1; 287 | OP_SUB(); 288 | impl_flags_changed &= ~PSW_C; 289 | emulator.chipset.mmu.WriteData((((size_t)reg_dsr) << 16) | reg_ea, impl_operands[0].value); 290 | } 291 | 292 | void CPU::Add8() 293 | { 294 | uint8_t op8[2] = {(uint8_t)impl_operands[0].value, (uint8_t)impl_operands[1].value}; 295 | uint16_t c_in = (impl_flags_in & PSW_C) ? 1 : 0; 296 | 297 | bool carry_8 = (((uint16_t)op8[0] & 0xFF) + (op8[1] & 0xFF) + c_in) >> 8; 298 | bool carry_7 = (((uint16_t)op8[0] & 0x7F) + (op8[1] & 0x7F) + c_in) >> 7; 299 | bool carry_4 = (((uint16_t)op8[0] & 0x0F) + (op8[1] & 0x0F) + c_in) >> 4; 300 | 301 | impl_flags_changed |= PSW_C | PSW_OV | PSW_HC; 302 | impl_flags_out = (impl_flags_out & ~PSW_C) | (carry_8 ? PSW_C : 0); 303 | impl_flags_out = (impl_flags_out & ~PSW_OV) | ((carry_8 ^ carry_7) ? PSW_OV : 0); 304 | impl_flags_out = (impl_flags_out & ~PSW_HC) | (carry_4 ? PSW_HC : 0); 305 | 306 | impl_operands[0].value = (uint8_t)(op8[0] + op8[1] + c_in); 307 | } 308 | 309 | void CPU::ZSCheck() 310 | { 311 | impl_flags_changed |= PSW_Z | PSW_S; 312 | if (impl_operands[0].value & 0xFF) 313 | impl_flags_out &= ~PSW_Z; 314 | impl_flags_out = (impl_flags_out & ~PSW_S) | ((impl_operands[0].value & 0x80) ? PSW_S : 0); 315 | } 316 | 317 | void CPU::ShiftLeft8() 318 | { 319 | impl_operands[0].value &= 0xFF; 320 | size_t shift_by = impl_operands[1].value & 7; 321 | uint16_t result = (uint16_t)impl_operands[0].value << shift_by; 322 | result |= impl_shift_buffer >> (8 - shift_by); 323 | impl_flags_changed |= PSW_C; 324 | if (result & 0x100) 325 | impl_flags_out |= PSW_C; 326 | impl_operands[0].value = (uint8_t)result; 327 | } 328 | 329 | void CPU::ShiftRight8() 330 | { 331 | impl_operands[0].value &= 0xFF; 332 | size_t shift_by = impl_operands[1].value & 7; 333 | uint16_t result = (uint16_t)impl_operands[0].value << (8 - shift_by); 334 | result |= (uint16_t)impl_shift_buffer << (16 - shift_by); 335 | impl_flags_changed |= PSW_C; 336 | if (result & 0x80) 337 | impl_flags_out |= PSW_C; 338 | impl_operands[0].value = (uint8_t)(result >> 8); 339 | } 340 | } 341 | 342 | -------------------------------------------------------------------------------- /disas/temp: -------------------------------------------------------------------------------- 1 | // Independent from the project. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define ip (totallen - length - buf.size()) 10 | 11 | std::string tohex(int n, int len) { 12 | std::string retval = ""; 13 | for (int x = 0; x < len; x++) { 14 | retval = "0123456789ABCDEF"[n & 0xF] + retval; 15 | n >>= 4; 16 | } 17 | return retval; 18 | } 19 | // Those are important to the disassembler. 20 | 21 | // ------------------------------------------------------------------ 1 22 | // This program doesn't have IP, but the disassembler use ip (instruction pointer). 23 | 24 | #define pc ip 25 | 26 | /* Have zero padding at first if necessary. 27 | The number with maximum length is (1 << (binlen - 1)), equal to the smallest 28 | number. It is a power of 2, thus first digit cannot be hexadecimal. 29 | The number has binlen digits. So, hexlen = ceil(binlen/4). 30 | */ 31 | std::string signedtohex(int n, int binlen) { 32 | // n satisfy (unsigned)n < (1 << binlen) 33 | binlen--; 34 | bool ispositive = (n >> binlen) == 0; 35 | if (!ispositive) n = (2 << binlen) - n; 36 | std::string retval = ""; 37 | binlen = 1 + binlen / 4; // ceil of (old)binlen/4 38 | // now binlen <- hexlen 39 | for (int x = 0; x < binlen; x++) { 40 | retval = "0123456789ABCDEF"[n & 0xF] + retval; 41 | n >>= 4; 42 | } 43 | return ispositive ? retval : ("-" + retval); 44 | } 45 | 46 | std::string cond [16] = {"GE", "LT", "GT", "LE", "GES", "LTS", "GTS", "LES", 47 | "NE", "EQ", "NV", "OV", "PS", "NS", "AL", ""}; 48 | 49 | * 50 | {} 4 2 51 | 52 | ; The first "*" denote end of C++ functions (and some kinds of const) part. 53 | 54 | ; Opening and closing bracket that is not used in opcode. Must be characters. 55 | ; "4": maximum opcode length. 56 | ; "2": in case of unrecognized command, skip that many bytes. 57 | 58 | 59 | ; Special notation: 60 | ; Because the disassembler use int as internal data type, then "i << 25 >> 25" is "(signed7) i". 61 | ; The disassembler-creater does not recognize semicolon in middle of line as comment, so it 62 | ; is used for comment in the disassembled file. 63 | 64 | 65 | # 2 66 | 67 | ; ----------- Special: DSR Prefix Instructions ----------- 68 | ; That is not possible in source code anyway but is for 69 | ; simplifying the instruction set file. 70 | 71 | iiiiiiii 11100011 DSR<- 0{tohex(i, 2)}h 72 | dddd1111 10010000 DSR<- R{d} 73 | 10011111 11111110 DSR<- DSR 74 | ; If there is no DSR prefix instruction then "DSR<- 0" 75 | 76 | ; ----------- Arithmetic instructions ----------- 77 | ; In some of those, #imm8's signedness is not determined, but that is not important 78 | ; because it is just added, subtracted or compared to a 8-bit register. 79 | ; It affect C and OV flag differently. 80 | 81 | mmmm0001 1000nnnn ADD R{n}, R{m} 82 | iiiiiiii 0001nnnn ADD R{n}, #{i} 83 | mmm00110 1111nnn0 ADD ER{n*2}, ER{m*2} 84 | 1iiiiiii 1110nnn0 ADD ER{n*2}, #{i << 25 >> 25} 85 | mmmm0110 1000nnnn ADDC R{n}, R{m} 86 | iiiiiiii 0110nnnn ADDC R{n}, #{i} 87 | mmmm0010 1000nnnn AND R{n}, R{m} 88 | iiiiiiii 0010nnnn AND R{n}, #{i} 89 | mmmm0111 1000nnnn CMP R{n}, R{m} 90 | iiiiiiii 0111nnnn CMP R{n}, #{i} 91 | mmmm0101 1000nnnn CMPC R{n}, R{m} 92 | iiiiiiii 0101nnnn CMPC R{n}, #{i} 93 | mmm00101 1111nnn0 MOV ER{n*2}, ER{m*2} 94 | 0iiiiiii 1110nnn0 MOV ER{n*2}, #{i} 95 | mmmm0000 1000nnnn MOV R{n}, R{m} 96 | iiiiiiii 0000nnnn MOV R{n}, #{i} 97 | mmmm0011 1000nnnn OR R{n}, R{m} 98 | iiiiiiii 0011nnnn OR R{n}, #{i} 99 | mmmm0100 1000nnnn XOR R{n}, R{m} 100 | iiiiiiii 0100nnnn XOR R{n}, #{i} 101 | mmm00111 1111nnn0 CMP ER{n*2}, ER{m*2} 102 | mmmm1000 1000nnnn SUB R{n}, R{m} 103 | mmmm1001 1000nnnn SUBC R{n}, R{m} 104 | 105 | ; ----------- Shift Instructions ----------- 106 | mmmm1010 1000nnnn SLL R{n}, R{m} 107 | 0www1010 1001nnnn SLL R{n}, #{w} 108 | mmmm1011 1000nnnn SLLC R{n}, R{m} 109 | 0www1011 1001nnnn SLLC R{n}, #{w} 110 | mmmm1110 1000nnnn SRA R{n}, R{m} 111 | 0www1110 1001nnnn SRA R{n}, #{w} 112 | mmmm1100 1000nnnn SRL R{n}, R{m} 113 | 0www1100 1001nnnn SRL R{n}, #{w} 114 | mmmm1101 1000nnnn SRLC R{n}, R{m} 115 | 0www1101 1001nnnn SRLC R{n}, #{w} 116 | 117 | ; ----------- Load/Store Instructions (2 bytes) ----------- 118 | 119 | 00110010 1001nnn0 L ER{n*2}, [EA] 120 | 01010010 1001nnn0 L ER{n*2}, [EA+] 121 | mmm00010 1001nnn0 L ER{n*2}, [ER{m*2}] 122 | 123 | ; Not support same character over multiple bytes 124 | ; C++ operators (+, -, *, <<, >>, >>>) 125 | 126 | 00DDDDDD 1011nnn0 L ER{n*2}, {signedtohex(D, 6)}h[BP] 127 | 01DDDDDD 1011nnn0 L ER{n*2}, {signedtohex(D, 6)}h[FP] 128 | 129 | ; What is Dadr? -> Direct address 130 | 131 | 00110000 1001nnnn L R{n}, [EA] 132 | 01010000 1001nnnn L R{n}, [EA+] 133 | mmm00000 1001nnnn L R{n}, [ER{m*2}] 134 | 00DDDDDD 1101nnnn L R{n}, {signedtohex(D, 6)}h[BP] 135 | 01DDDDDD 1101nnnn L R{n}, {signedtohex(D, 6)}h[FP] 136 | 137 | 00110100 1001nn00 L XR{n*4}, [EA] 138 | 01010100 1001nn00 L XR{n*4}, [EA+] 139 | 00110110 1001n000 L QR{n*8}, [EA] 140 | 01010110 1001n000 L QR{n*8}, [EA+] 141 | 142 | 143 | 144 | 00110011 1001nnn0 ST ER{n*2}, [EA] 145 | 01010011 1001nnn0 ST ER{n*2}, [EA+] 146 | mmm00011 1001nnn0 ST ER{n*2}, [ER{m*2}] 147 | 10DDDDDD 1011nnn0 ST ER{n*2}, {signedtohex(D, 6)}h[BP] 148 | ; D : Disp6 149 | 11DDDDDD 1011nnn0 ST ER{n*2}, {signedtohex(D, 6)}h[FP] 150 | 151 | 00110001 1001nnnn ST R{n}, [EA] 152 | 01010001 1001nnnn ST R{n}, [EA+] 153 | mmm00001 1001nnnn ST R{n}, [ER{m*2}] 154 | 10DDDDDD 1101nnnn ST R{n}, {signedtohex(D, 6)}h[BP] 155 | 11DDDDDD 1101nnnn ST R{n}, {signedtohex(D, 6)}h[FP] 156 | 157 | 00110101 1001nn00 ST XR{n*4}, [EA] 158 | 01010101 1001nn00 ST XR{n*4}, [EA+] 159 | 160 | 00110111 1001n000 ST QR{n*8}, [EA] 161 | 01010111 1001n000 ST QR{n*8}, [EA+] 162 | 163 | 164 | iiiiiiii 11100001 ADD SP, #{signedtohex(i, 8)}h 165 | ; #imm8 = #signed8 166 | mmmm1111 10100000 MOV ECSR, R{m} 167 | 00001101 1010mmm0 MOV ELR, ER{m*2} 168 | mmmm1100 10100000 MOV EPSW, R{m} 169 | 170 | 00000101 1010nnn0 MOV ER{n*2}, ELR 171 | 00011010 1010nnn0 MOV ER{n*2}, SP 172 | mmmm1011 10100000 MOV PSW, R{m} 173 | iiiiiiii 11101001 MOV PSW, #{i} 174 | ; #imm8 = #unsigned8 175 | 176 | 00000111 1010nnnn MOV R{n}, ECSR 177 | 00000100 1010nnnn MOV R{n}, EPSW 178 | 00000011 1010nnnn MOV R{n}, PSW 179 | 180 | mmm01010 10100001 MOV SP, ER{m*2} 181 | 182 | ; ----------- PUSH/POP Instructions ----------- 183 | 184 | 01011110 1111nnn0 PUSH ER{n*2} 185 | 01111110 1111n000 PUSH QR{n*8} 186 | 01001110 1111nnnn PUSH R{n} 187 | 01101110 1111nn00 PUSH XR{n*4} 188 | 189 | 11001110 1111lep1 PUSH {l==1?"LR, ":""}{e==1?"EPSW, ":""}{p==1?"ELR, ":""}EA 190 | 11001110 1111le10 PUSH {l==1?"LR, ":""}{e==1?"EPSW, ":""}ELR 191 | 11001110 1111l100 PUSH {l==1?"LR, ":""}EPSW 192 | 11001110 11111000 PUSH LR 193 | ; 4 cases because they have different separator (comma) rule 194 | 195 | 00011110 1111nnn0 POP ER{n*2} 196 | 00111110 1111n000 POP QR{n*8} 197 | 00001110 1111nnnn POP R{n} 198 | 00101110 1111nn00 POP XR{n*4} 199 | 10001110 1111lep1 POP {l==1?"LR, ":""}{e==1?"PSW, ":""}{p==1?"PC, ":""}EA 200 | 10001110 1111le10 POP {l==1?"LR, ":""}{e==1?"PSW, ":""}PC 201 | 10001110 1111l100 POP {l==1?"LR, ":""}PSW 202 | 10001110 11111000 POP LR 203 | 204 | ; ----------- Coprocessor Data Transfer Instructions ----------- 205 | 206 | mmmm1110 1010nnnn MOV CR{n}, R{m} 207 | 00101101 1111nnn0 MOV CER{n*2}, [EA] 208 | 00111101 1111nnn0 MOV CER{n*2}, [EA+] 209 | 00001101 1111nnnn MOV CR{n}, [EA] 210 | 00011101 1111nnnn MOV CR{n}, [EA+] 211 | 01001101 1111nn00 MOV CXR{n*4}, [EA] 212 | 01011101 1111nn00 MOV CXR{n*4}, [EA+] 213 | 01101101 1111n000 MOV CQR{n*8}, [EA] 214 | 01111101 1111n000 MOV CQR{n*8}, [EA+] 215 | 216 | mmmm0110 1010nnnn MOV R{n}, CR{m} 217 | 10101101 1111mmm0 MOV [EA], CER{m*2} 218 | 10111101 1111mmm0 MOV [EA+], CER{m*2} 219 | 10001101 1111mmmm MOV [EA], CR{m} 220 | 10011101 1111mmmm MOV [EA+], CR{m} 221 | 11001101 1111mm00 MOV [EA], CXR{m*4} 222 | 11011101 1111mm00 MOV [EA+], CXR{m*4} 223 | 11101101 1111m000 MOV [EA], CQR{m*8} 224 | 11111101 1111m000 MOV [EA+], CQR{m*8} 225 | 226 | 227 | ; ----------- EA Register Data Transfer Instructions ----------- 228 | 229 | mmm01010 11110000 LEA [ER{m*2}] 230 | 231 | 232 | ; ----------- ALU Instructions ----------- 233 | 234 | 00011111 1000nnnn DAA R{n} 235 | 00111111 1000nnnn DAS R{n} 236 | 01011111 1000nnnn NEG R{n} 237 | 238 | 239 | ; ----------- Bit Access Instructions ----------- 240 | 241 | 0bbb0000 1010nnnn SB R{n}.{b} 242 | ; b : bit_offset 243 | 0bbb0010 1010nnnn RB R{n}.{b} 244 | 0bbb0001 1010nnnn TB R{n}.{b} 245 | 246 | 247 | ; ----------- PSW Access Instructions ----------- 248 | 249 | 00001000 11101101 EI 250 | 11110111 11101011 DI 251 | 10000000 11101101 SC 252 | 01111111 11101011 RC 253 | 11001111 11111110 CPLC 254 | 255 | ; ----------- Conditional Relative Branch Instructions ----------- 256 | rrrrrrrr 1100cccc BC {cond[c]}, {tohex(2 + pc + ((int)(signed char)r << 1), 4+1)}h 257 | 258 | ; nextPC = (2 + pc). 259 | 260 | ; The above ; is for comment in the assembly file, not this file. 261 | 262 | ; !*!*!*!*!*!*!*!*!*!*!*!*!*!*!* Test that carefully! !*!*!*!*!*!*!*!*!*!*!*!*!*!*!* 263 | 264 | 265 | ; ----------- Sign Extension Instruction ----------- 266 | nnn01111 1000mmm1 {m == n ? "" : "Wrong format - "}EXTBW ER{n*2} 267 | 268 | ; ----------- Software Interrupt Instructions ----------- 269 | 00iiiiii 11100101 SWI #{i} 270 | 11111111 11111111 BRK 271 | 272 | 273 | ; ----------- Branch Instructions ----------- 274 | nnn00010 11110000 B ER{n*2} 275 | nnn00011 11110000 BL ER{n*2} 276 | 277 | 278 | ; ----------- Multiplication and Division Instructions ----------- 279 | mmmm0100 1111nnn0 MUL ER{n*2}, R{m} 280 | mmmm1001 1111nnn0 DIV ER{n*2}, R{m} 281 | 282 | ; ----------- Miscellaneous ----------- 283 | 00101111 11111110 INC [EA] 284 | 00111111 11111110 DEC [EA] 285 | 00011111 11111110 RT 286 | 00001111 11111110 RTI 287 | 10001111 11111110 NOP 288 | 289 | # 4 290 | ; All commands in this part contains Dadr. 291 | 292 | 293 | ; ----------- Load/Store Instructions ----------- 294 | 295 | mmm01000 1010nnn0 DDDDDDDD EEEEEEEE L ER{n*2}, {signedtohex(E*256+D, 16)}h[ER{m*2}] 296 | 00010010 1001nnn0 DDDDDDDD EEEEEEEE L ER{n*2}, 0{tohex(E*256+D, 4)}h 297 | mmm01000 1001nnnn DDDDDDDD EEEEEEEE L R{n}, {signedtohex(E*256+D, 16)}h[ER{m*2}] 298 | 00010000 1001nnnn DDDDDDDD EEEEEEEE L R{n}, 0{tohex(E*256+D, 4)}h 299 | 300 | mmm01001 1010nnn0 DDDDDDDD EEEEEEEE ST ER{n*2}, {signedtohex(E*256+D, 16)}h[ER{m*2}] 301 | 00010011 1001nnn0 DDDDDDDD EEEEEEEE ST ER{n*2}, 0{tohex(E*256+D, 4)}h 302 | mmm01001 1001nnnn DDDDDDDD EEEEEEEE ST R{n}, {signedtohex(E*256+D, 16)}h[ER{m*2}] 303 | 00010001 1001nn -------------------------------------------------------------------------------- /disas/nX-U8_is_split.txt: -------------------------------------------------------------------------------- 1 | // split version: display 4-byte commands as two 2-byte commands. 2 | 3 | // This program doesn't have IP, but the disassembler use ip (instruction pointer). 4 | 5 | #define pc ip 6 | 7 | /* Have zero padding at first if necessary. 8 | The number with maximum length is (1 << (binlen - 1)), equal to the smallest 9 | number. It is a power of 2, thus first digit cannot be hexadecimal. 10 | The number has binlen digits. So, hexlen = ceil(binlen/4). 11 | */ 12 | std::string signedtohex(int n, int binlen) { 13 | // n satisfy (unsigned)n < (1 << binlen) 14 | binlen--; 15 | bool ispositive = (n >> binlen) == 0; 16 | if (!ispositive) n = (2 << binlen) - n; 17 | std::string retval = ""; 18 | binlen = 1 + binlen / 4; // ceil of (old)binlen/4 19 | // now binlen <- hexlen 20 | for (int x = 0; x < binlen; x++) { 21 | retval = "0123456789ABCDEF"[n & 0xF] + retval; 22 | n >>= 4; 23 | } 24 | return ispositive ? retval : ("-" + retval); 25 | } 26 | 27 | std::string cond [16] = {"GE", "LT", "GT", "LE", "GES", "LTS", "GTS", "LES", 28 | "NE", "EQ", "NV", "OV", "PS", "NS", "AL", ""}; 29 | 30 | * 31 | {} 4 2 32 | 33 | ; The first "*" denote end of C++ functions (and some kinds of const) part. 34 | 35 | ; Opening and closing bracket that is not used in opcode. Must be characters. 36 | ; "4": maximum opcode length. 37 | ; "2": in case of unrecognized command, skip that many bytes. 38 | 39 | 40 | ; Special notation: 41 | ; Because the disassembler use int as internal data type, then "i << 25 >> 25" is "(signed7) i". 42 | ; The disassembler-creater does not recognize semicolon in middle of line as comment, so it 43 | ; is used for comment in the disassembled file. 44 | 45 | 46 | # 2 47 | 48 | ; ----------- Special: DSR Prefix Instructions ----------- 49 | ; That is not possible in source code anyway but is for 50 | ; simplifying the instruction set file. 51 | 52 | iiiiiiii 11100011 DSR<- 0{tohex(i, 2)}h 53 | dddd1111 10010000 DSR<- R{d} 54 | 10011111 11111110 DSR<- DSR 55 | ; If there is no DSR prefix instruction then "DSR<- 0" 56 | 57 | ; ----------- Arithmetic instructions ----------- 58 | ; In some of those, #imm8's signedness is not determined, but that is not important 59 | ; because it is just added, subtracted or compared to a 8-bit register. 60 | ; It affect C and OV flag differently. 61 | 62 | mmmm0001 1000nnnn ADD R{n}, R{m} 63 | iiiiiiii 0001nnnn ADD R{n}, #{i} 64 | mmm00110 1111nnn0 ADD ER{n*2}, ER{m*2} 65 | 1iiiiiii 1110nnn0 ADD ER{n*2}, #{i << 25 >> 25} 66 | mmmm0110 1000nnnn ADDC R{n}, R{m} 67 | iiiiiiii 0110nnnn ADDC R{n}, #{i} 68 | mmmm0010 1000nnnn AND R{n}, R{m} 69 | iiiiiiii 0010nnnn AND R{n}, #{i} 70 | mmmm0111 1000nnnn CMP R{n}, R{m} 71 | iiiiiiii 0111nnnn CMP R{n}, #{i} 72 | mmmm0101 1000nnnn CMPC R{n}, R{m} 73 | iiiiiiii 0101nnnn CMPC R{n}, #{i} 74 | mmm00101 1111nnn0 MOV ER{n*2}, ER{m*2} 75 | 0iiiiiii 1110nnn0 MOV ER{n*2}, #{i} 76 | mmmm0000 1000nnnn MOV R{n}, R{m} 77 | iiiiiiii 0000nnnn MOV R{n}, #{i} 78 | mmmm0011 1000nnnn OR R{n}, R{m} 79 | iiiiiiii 0011nnnn OR R{n}, #{i} 80 | mmmm0100 1000nnnn XOR R{n}, R{m} 81 | iiiiiiii 0100nnnn XOR R{n}, #{i} 82 | mmm00111 1111nnn0 CMP ER{n*2}, ER{m*2} 83 | mmmm1000 1000nnnn SUB R{n}, R{m} 84 | mmmm1001 1000nnnn SUBC R{n}, R{m} 85 | 86 | ; ----------- Shift Instructions ----------- 87 | mmmm1010 1000nnnn SLL R{n}, R{m} 88 | 0www1010 1001nnnn SLL R{n}, #{w} 89 | mmmm1011 1000nnnn SLLC R{n}, R{m} 90 | 0www1011 1001nnnn SLLC R{n}, #{w} 91 | mmmm1110 1000nnnn SRA R{n}, R{m} 92 | 0www1110 1001nnnn SRA R{n}, #{w} 93 | mmmm1100 1000nnnn SRL R{n}, R{m} 94 | 0www1100 1001nnnn SRL R{n}, #{w} 95 | mmmm1101 1000nnnn SRLC R{n}, R{m} 96 | 0www1101 1001nnnn SRLC R{n}, #{w} 97 | 98 | ; ----------- Load/Store Instructions (2 bytes) ----------- 99 | 100 | 00110010 1001nnn0 L ER{n*2}, [EA] 101 | 01010010 1001nnn0 L ER{n*2}, [EA+] 102 | mmm00010 1001nnn0 L ER{n*2}, [ER{m*2}] 103 | 104 | ; Not support same character over multiple bytes 105 | ; C++ operators (+, -, *, <<, >>, >>>) 106 | 107 | 00DDDDDD 1011nnn0 L ER{n*2}, {signedtohex(D, 6)}h[BP] 108 | 01DDDDDD 1011nnn0 L ER{n*2}, {signedtohex(D, 6)}h[FP] 109 | 110 | ; What is Dadr? -> Direct address 111 | 112 | 00110000 1001nnnn L R{n}, [EA] 113 | 01010000 1001nnnn L R{n}, [EA+] 114 | mmm00000 1001nnnn L R{n}, [ER{m*2}] 115 | 00DDDDDD 1101nnnn L R{n}, {signedtohex(D, 6)}h[BP] 116 | 01DDDDDD 1101nnnn L R{n}, {signedtohex(D, 6)}h[FP] 117 | 118 | 00110100 1001nn00 L XR{n*4}, [EA] 119 | 01010100 1001nn00 L XR{n*4}, [EA+] 120 | 00110110 1001n000 L QR{n*8}, [EA] 121 | 01010110 1001n000 L QR{n*8}, [EA+] 122 | 123 | 124 | 125 | 00110011 1001nnn0 ST ER{n*2}, [EA] 126 | 01010011 1001nnn0 ST ER{n*2}, [EA+] 127 | mmm00011 1001nnn0 ST ER{n*2}, [ER{m*2}] 128 | 10DDDDDD 1011nnn0 ST ER{n*2}, {signedtohex(D, 6)}h[BP] 129 | ; D : Disp6 130 | 11DDDDDD 1011nnn0 ST ER{n*2}, {signedtohex(D, 6)}h[FP] 131 | 132 | 00110001 1001nnnn ST R{n}, [EA] 133 | 01010001 1001nnnn ST R{n}, [EA+] 134 | mmm00001 1001nnnn ST R{n}, [ER{m*2}] 135 | 10DDDDDD 1101nnnn ST R{n}, {signedtohex(D, 6)}h[BP] 136 | 11DDDDDD 1101nnnn ST R{n}, {signedtohex(D, 6)}h[FP] 137 | 138 | 00110101 1001nn00 ST XR{n*4}, [EA] 139 | 01010101 1001nn00 ST XR{n*4}, [EA+] 140 | 141 | 00110111 1001n000 ST QR{n*8}, [EA] 142 | 01010111 1001n000 ST QR{n*8}, [EA+] 143 | 144 | 145 | iiiiiiii 11100001 ADD SP, #{signedtohex(i, 8)}h 146 | ; #imm8 = #signed8 147 | mmmm1111 10100000 MOV ECSR, R{m} 148 | 00001101 1010mmm0 MOV ELR, ER{m*2} 149 | mmmm1100 10100000 MOV EPSW, R{m} 150 | 151 | 00000101 1010nnn0 MOV ER{n*2}, ELR 152 | 00011010 1010nnn0 MOV ER{n*2}, SP 153 | mmmm1011 10100000 MOV PSW, R{m} 154 | iiiiiiii 11101001 MOV PSW, #{i} 155 | ; #imm8 = #unsigned8 156 | 157 | 00000111 1010nnnn MOV R{n}, ECSR 158 | 00000100 1010nnnn MOV R{n}, EPSW 159 | 00000011 1010nnnn MOV R{n}, PSW 160 | 161 | mmm01010 10100001 MOV SP, ER{m*2} 162 | 163 | ; ----------- PUSH/POP Instructions ----------- 164 | 165 | 01011110 1111nnn0 PUSH ER{n*2} 166 | 01111110 1111n000 PUSH QR{n*8} 167 | 01001110 1111nnnn PUSH R{n} 168 | 01101110 1111nn00 PUSH XR{n*4} 169 | 170 | 11001110 1111lep1 PUSH {l==1?"LR, ":""}{e==1?"EPSW, ":""}{p==1?"ELR, ":""}EA 171 | 11001110 1111le10 PUSH {l==1?"LR, ":""}{e==1?"EPSW, ":""}ELR 172 | 11001110 1111l100 PUSH {l==1?"LR, ":""}EPSW 173 | 11001110 11111000 PUSH LR 174 | ; 4 cases because they have different separator (comma) rule 175 | 176 | 00011110 1111nnn0 POP ER{n*2} 177 | 00111110 1111n000 POP QR{n*8} 178 | 00001110 1111nnnn POP R{n} 179 | 00101110 1111nn00 POP XR{n*4} 180 | 10001110 1111lep1 POP {l==1?"LR, ":""}{e==1?"PSW, ":""}{p==1?"PC, ":""}EA 181 | 10001110 1111le10 POP {l==1?"LR, ":""}{e==1?"PSW, ":""}PC 182 | 10001110 1111l100 POP {l==1?"LR, ":""}PSW 183 | 10001110 11111000 POP LR 184 | 185 | ; ----------- Coprocessor Data Transfer Instructions ----------- 186 | 187 | mmmm1110 1010nnnn MOV CR{n}, R{m} 188 | 00101101 1111nnn0 MOV CER{n*2}, [EA] 189 | 00111101 1111nnn0 MOV CER{n*2}, [EA+] 190 | 00001101 1111nnnn MOV CR{n}, [EA] 191 | 00011101 1111nnnn MOV CR{n}, [EA+] 192 | 01001101 1111nn00 MOV CXR{n*4}, [EA] 193 | 01011101 1111nn00 MOV CXR{n*4}, [EA+] 194 | 01101101 1111n000 MOV CQR{n*8}, [EA] 195 | 01111101 1111n000 MOV CQR{n*8}, [EA+] 196 | 197 | mmmm0110 1010nnnn MOV R{n}, CR{m} 198 | 10101101 1111mmm0 MOV [EA], CER{m*2} 199 | 10111101 1111mmm0 MOV [EA+], CER{m*2} 200 | 10001101 1111mmmm MOV [EA], CR{m} 201 | 10011101 1111mmmm MOV [EA+], CR{m} 202 | 11001101 1111mm00 MOV [EA], CXR{m*4} 203 | 11011101 1111mm00 MOV [EA+], CXR{m*4} 204 | 11101101 1111m000 MOV [EA], CQR{m*8} 205 | 11111101 1111m000 MOV [EA+], CQR{m*8} 206 | 207 | 208 | ; ----------- EA Register Data Transfer Instructions ----------- 209 | 210 | mmm01010 11110000 LEA [ER{m*2}] 211 | 212 | 213 | ; ----------- ALU Instructions ----------- 214 | 215 | 00011111 1000nnnn DAA R{n} 216 | 00111111 1000nnnn DAS R{n} 217 | 01011111 1000nnnn NEG R{n} 218 | 219 | 220 | ; ----------- Bit Access Instructions ----------- 221 | 222 | 0bbb0000 1010nnnn SB R{n}.{b} 223 | ; b : bit_offset 224 | 0bbb0010 1010nnnn RB R{n}.{b} 225 | 0bbb0001 1010nnnn TB R{n}.{b} 226 | 227 | 228 | ; ----------- PSW Access Instructions ----------- 229 | 230 | 00001000 11101101 EI 231 | 11110111 11101011 DI 232 | 10000000 11101101 SC 233 | 01111111 11101011 RC 234 | 11001111 11111110 CPLC 235 | 236 | ; ----------- Conditional Relative Branch Instructions ----------- 237 | rrrrrrrr 1100cccc BC {cond[c]}, {tohex(2 + pc + ((int)(signed char)r << 1), 4+1)}h 238 | 239 | ; nextPC = (2 + pc). 240 | 241 | ; The above ; is for comment in the assembly file, not this file. 242 | 243 | ; !*!*!*!*!*!*!*!*!*!*!*!*!*!*!* Test that carefully! !*!*!*!*!*!*!*!*!*!*!*!*!*!*!* 244 | 245 | 246 | ; ----------- Sign Extension Instruction ----------- 247 | nnn01111 1000mmm1 {m == n ? "" : "Wrong format - "}EXTBW ER{n*2} 248 | 249 | ; ----------- Software Interrupt Instructions ----------- 250 | 00iiiiii 11100101 SWI #{i} 251 | 11111111 11111111 BRK 252 | 253 | 254 | ; ----------- Branch Instructions ----------- 255 | nnn00010 11110000 B ER{n*2} 256 | nnn00011 11110000 BL ER{n*2} 257 | 258 | 259 | ; ----------- Multiplication and Division Instructions ----------- 260 | mmmm0100 1111nnn0 MUL ER{n*2}, R{m} 261 | mmmm1001 1111nnn0 DIV ER{n*2}, R{m} 262 | 263 | ; ----------- Miscellaneous ----------- 264 | 00101111 11111110 INC [EA] 265 | 00111111 11111110 DEC [EA] 266 | 00011111 11111110 RT 267 | 00001111 11111110 RTI 268 | 10001111 11111110 NOP 269 | 270 | ; ----------- Load/Store Instructions ----------- 271 | 272 | mmm01000 1010nnn0 L ER{n*2}, simm16[ER{m*2}] 273 | 00010010 1001nnn0 L ER{n*2}, imm16 274 | mmm01000 1001nnnn L R{n}, simm16[ER{m*2}] 275 | 00010000 1001nnnn L R{n}, imm16 276 | 277 | mmm01001 1010nnn0 ST ER{n*2}, simm16[ER{m*2}] 278 | 00010011 1001nnn0 ST ER{n*2}, imm16 279 | mmm01001 1001nnnn ST R{n}, simm16[ER{m*2}] 280 | 00010001 1001nnnn ST R{n}, imm16 281 | 282 | ; ----------- EA Register Data Transfer Instructions ----------- 283 | mmm01011 11110000 LEA simm16[ER{m*2}] 284 | 00001100 11110000 LEA imm16 285 | 286 | ; ----------- Bit Access Instructions ----------- 287 | 288 | 1bbb0000 10100000 SB imm16.{b} 289 | 1bbb0010 10100000 RB imm16.{b} 290 | 1bbb0001 10100000 TB imm16.{b} 291 | 292 | ; ----------- Branch Instructions ----------- 293 | 294 | 00000000 1111gggg B 0{tohex(g, 1)}h:cadr_imm16 295 | 00000001 1111gggg BL 0{tohex(g, 1)}h:cadr_imm16 296 | 297 | * 298 | ^ That (second) "star" denote the end of the file, so later 299 | comments don't need ; or // at first. 300 | 301 | 302 | Comment: 303 | # k : start block of k-bytes commands. 304 | All commands must be big-endian. 305 | Each byte is separated by a space. 306 | Empty lines are allowed. 307 | ";" can only be used at beginning of line. 308 | --------------------------------------------------------------------------------