├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .dockerignore ├── .github └── workflows │ ├── docker.yaml │ ├── main.yaml │ └── wasm.yaml ├── .gitignore ├── .gitmodules ├── .vscode ├── c_cpp_properties.json ├── launch.json ├── settings.json └── tasks.json ├── CMakeLists.txt ├── Dockerfile ├── LICENSE ├── README.md ├── cmake └── bundle.cmake ├── docs └── screenshot.jpg ├── dumps ├── ficr.bin ├── ficr.h ├── secret.bin ├── secret.h ├── uicr.bin └── uicr.h ├── frontend ├── desktop │ ├── .gitignore │ ├── cmd │ │ ├── infiniemu │ │ │ └── main.go │ │ └── previewer │ │ │ └── main.go │ ├── emulator │ │ ├── emulator.c │ │ ├── emulator.go │ │ ├── pinetime.go │ │ ├── program.go │ │ └── user_config.h │ ├── go.mod │ ├── go.sum │ ├── gui │ │ └── gui.go │ ├── libinfiniemu.a │ └── script │ │ └── script.go └── wasm │ ├── .gitignore │ ├── .vscode │ └── extensions.json │ ├── README.md │ ├── env.d.ts │ ├── index.html │ ├── infiniemu.d.ts │ ├── package.json │ ├── pnpm-lock.yaml │ ├── public │ └── favicon.ico │ ├── src │ ├── App.vue │ ├── common.ts │ ├── components │ │ ├── Console.vue │ │ ├── Display.vue │ │ ├── Emulator.vue │ │ └── FileBrowser.vue │ ├── main.ts │ ├── pinetime.ts │ ├── utils.ts │ └── worker.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── include ├── arm.h ├── bus_i2c.h ├── bus_spi.h ├── byte_util.h ├── circular_buffer.h ├── commander.h ├── components │ ├── i2c │ │ ├── bma425.h │ │ ├── cst816s.h │ │ └── hrs3300.h │ └── spi │ │ ├── spinorflash.h │ │ └── st7789.h ├── config.h ├── cpu.h ├── demangle.h ├── dma.h ├── fault.h ├── gdb.h ├── ie_time.h ├── lua.h ├── lualibs │ ├── lua_display.h │ ├── lua_image.h │ ├── lua_touch.h │ └── lualibs.h ├── memory.h ├── nrf52832.h ├── pcap.h ├── peripherals │ ├── dcb.h │ ├── dwt.h │ ├── nrf52832 │ │ ├── ccm.h │ │ ├── clock.h │ │ ├── comp.h │ │ ├── easydma.h │ │ ├── ecb.h │ │ ├── gpio.h │ │ ├── gpiote.h │ │ ├── nvmc.h │ │ ├── power.h │ │ ├── ppi.h │ │ ├── pwm.h │ │ ├── radio.h │ │ ├── rng.h │ │ ├── rtc.h │ │ ├── saadc.h │ │ ├── spi.h │ │ ├── spim.h │ │ ├── temp.h │ │ ├── timer.h │ │ ├── twim.h │ │ └── wdt.h │ ├── nvic.h │ ├── peripheral.h │ ├── scb.h │ └── scb_fp.h ├── pinetime.h ├── pins.h ├── program.h ├── pseudocode.h ├── runlog.h ├── scheduler.h ├── segger_rtt.h ├── state_store.h ├── ticker.h └── util.h ├── src ├── .gitignore ├── CMakeLists.txt ├── bus_i2c.c ├── bus_spi.c ├── circular_buffer.c ├── commander.c ├── component │ ├── i2c │ │ ├── bma425.c │ │ ├── cst816s.c │ │ └── hrs3300.c │ └── spi │ │ ├── spinorflash.c │ │ └── st7789.c ├── cpu.c ├── demangle.cpp ├── dma.c ├── fault.c ├── gdb.c ├── lua.c ├── lualibs │ ├── lua_display.c │ ├── lua_image.c │ ├── lua_pinetime.c │ └── lua_touch.c ├── main.c ├── memory.c ├── nrf52832.c ├── pcap.c ├── peripherals │ ├── dcb.c │ ├── dwt.c │ ├── nrf52832 │ │ ├── ccm.c │ │ ├── clock.c │ │ ├── comp.c │ │ ├── ecb.c │ │ ├── gpio.c │ │ ├── gpiote.c │ │ ├── nvmc.c │ │ ├── power.c │ │ ├── ppi.c │ │ ├── pwm.c │ │ ├── radio.c │ │ ├── rng.c │ │ ├── rtc.c │ │ ├── saadc.c │ │ ├── spi.c │ │ ├── spim.c │ │ ├── temp.c │ │ ├── timer.c │ │ ├── twim.c │ │ └── wdt.c │ ├── nvic.c │ ├── scb.c │ └── scb_fp.c ├── pinetime.c ├── pins.c ├── program.c ├── runlog.c ├── scheduler.c ├── segger_rtt.c ├── state_store.c ├── ticker.c ├── time.c └── wasm.c ├── test ├── .gitignore ├── firmware │ ├── .gitignore │ ├── FreeRTOS │ │ ├── heap_4_infinitime.c │ │ ├── port.c │ │ ├── port_cmsis.c │ │ ├── port_cmsis_systick.c │ │ ├── portmacro.h │ │ └── portmacro_cmsis.h │ ├── FreeRTOSConfig.h │ ├── Makefile │ ├── main.c │ └── sdk_config.h ├── generate_tests.py └── suites │ ├── adc.yaml │ ├── add.yaml │ ├── and.yaml │ ├── asr.yaml │ ├── bfc.yaml │ ├── bfi.yaml │ ├── bic.yaml │ ├── clz.yaml │ ├── cmn.yaml │ ├── cmp.yaml │ ├── eor.yaml │ ├── it.yaml │ ├── ldm.yaml │ ├── ldmdb.yaml │ ├── ldr.yaml │ ├── ldrb.yaml │ ├── rsb.yaml │ ├── str.yaml │ ├── strd.yaml │ └── teq.yaml ├── tools ├── .gitignore ├── runlog-analyzer │ ├── .gitignore │ ├── asm.go │ ├── commands.go │ ├── expression.go │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── runlog.go ├── simulrun │ ├── .gitignore │ ├── asm │ │ ├── assembler.go │ │ └── instructions.go │ ├── exchange.go │ ├── gdb.go │ ├── go.mod │ └── main.go └── web-previewer │ ├── cache.go │ ├── github.go │ ├── go.mod │ ├── go.sum │ └── main.go └── user_config.h /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:12 2 | ARG USERNAME=vscode 3 | ARG USER_UID=1000 4 | ARG USER_GID=$USER_UID 5 | 6 | RUN apt update && apt install -y git wget gcc g++ gdb-multiarch cmake make valgrind xz-utils unzip python3 python3-yaml libpng-dev 7 | 8 | ARG toolchain_ver=13.2.rel1 9 | RUN wget -q -O toolchain.tar.xz https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu/${toolchain_ver}/binrel/arm-gnu-toolchain-${toolchain_ver}-x86_64-arm-none-eabi.tar.xz 10 | RUN tar xf toolchain.tar.xz -C /opt && mv /opt/arm-gnu-toolchain* /opt/arm-gnu-toolchain 11 | RUN rm toolchain.tar.xz 12 | 13 | RUN cd opt && \ 14 | mkdir nrfx && cd nrfx && \ 15 | wget -q -O nrfx.zip https://github.com/NordicSemiconductor/nrfx/releases/download/v3.4.0/nrfx-v3.4.0-7c47cc0a.zip && \ 16 | unzip nrfx.zip && \ 17 | rm nrfx.zip 18 | 19 | RUN wget -q -O sdk.zip https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip && \ 20 | unzip -q sdk.zip -d /opt && \ 21 | rm sdk.zip 22 | 23 | RUN cd /tmp && \ 24 | wget -q -O capstone.tar.gz https://github.com/capstone-engine/capstone/archive/5.0.1.tar.gz && \ 25 | tar xf capstone.tar.gz && \ 26 | cd capstone* && \ 27 | CAPSTONE_ARCHS=arm ./make.sh install && \ 28 | rm -r capstone* 29 | 30 | RUN cd /opt && \ 31 | git clone https://github.com/emscripten-core/emsdk.git && \ 32 | cd emsdk && \ 33 | ./emsdk install latest && \ 34 | ./emsdk activate latest && \ 35 | echo "source /opt/emsdk/emsdk_env.sh" >> /etc/profile 36 | 37 | ENV NRFX_PATH /opt/nrfx 38 | ENV NRF5_SDK_PATH /opt/nRF5_SDK_15.3.0_59ac345 39 | ENV ARM_TOOLCHAIN_PATH /opt/arm-gnu-toolchain 40 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "dockerfile": "Dockerfile" 4 | }, 5 | "customizations": { 6 | "vscode": { 7 | "extensions": [ 8 | "ms-vscode.cmake-tools", 9 | "ms-vscode.cpptools-extension-pack", 10 | "ms-vscode.hexeditor", 11 | "ms-vscode.makefile-tools", 12 | "ms-python.python", 13 | "vadimcn.vscode-lldb", 14 | "twxs.cmake" 15 | ] 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | build 3 | Dockerfile 4 | tools/simulrun/toolchain 5 | frontend/wasm/node_modules -------------------------------------------------------------------------------- /.github/workflows/docker.yaml: -------------------------------------------------------------------------------- 1 | name: docker 2 | on: [push] 3 | jobs: 4 | build-docker: 5 | runs-on: ubuntu-22.04 6 | permissions: 7 | contents: read 8 | packages: write 9 | steps: 10 | - uses: actions/checkout@v4 11 | with: 12 | submodules: recursive 13 | 14 | - uses: macbre/push-to-ghcr@master 15 | with: 16 | image_name: ${{ github.repository }} # it will be lowercased internally 17 | github_token: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: main 2 | on: [push] 3 | jobs: 4 | build-viewer: 5 | runs-on: ubuntu-22.04 6 | steps: 7 | - uses: actions/checkout@v4 8 | with: 9 | submodules: recursive 10 | 11 | - name: Install sudo if running on Act 12 | if: ${{ env.ACT }} 13 | run: apt-get update && apt-get install -y sudo 14 | 15 | - name: Install dependencies 16 | run: sudo apt-get update && sudo apt-get install -y wget gcc-12 g++ make cmake pkg-config libx11-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libopengl-dev libglx-dev libglu1-mesa-dev freeglut3-dev libxxf86vm-dev libpng-dev 17 | 18 | - name: Setup Golang with cache 19 | uses: magnetikonline/action-golang-cache@v5 20 | with: 21 | go-version-file: frontend/desktop/go.mod 22 | 23 | - name: Build libinfiniemu 24 | run: | 25 | gcc --version 26 | mkdir build 27 | cd build 28 | cmake -DCMAKE_BUILD_TYPE=Release .. 29 | make -j$(nproc) bundling_target 30 | 31 | - name: Build frontends 32 | run: | 33 | cd frontend/desktop 34 | go build -o infiniemu ./cmd/infiniemu 35 | go build -o previewer ./cmd/previewer 36 | 37 | - uses: actions/upload-artifact@v4 38 | with: 39 | name: InfiniEmu 40 | path: | 41 | frontend/desktop/infiniemu 42 | frontend/desktop/previewer 43 | if-no-files-found: error 44 | -------------------------------------------------------------------------------- /.github/workflows/wasm.yaml: -------------------------------------------------------------------------------- 1 | name: wasm 2 | on: [push] 3 | 4 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 5 | permissions: 6 | contents: read 7 | pages: write 8 | id-token: write 9 | 10 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 11 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 12 | concurrency: 13 | group: "pages" 14 | cancel-in-progress: false 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-22.04 19 | env: 20 | EMSCRIPTEN: 3.1.61 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | submodules: recursive 25 | 26 | - name: Install Node.js 27 | uses: actions/setup-node@v4 28 | with: 29 | node-version: "20.x" 30 | 31 | - name: Install dependencies 32 | run: sudo apt-get update && sudo apt-get install -y python3 git wget gcc g++ make cmake xz-utils libpng-dev 33 | 34 | - name: Cache Emscripten 35 | id: cache-emscripten 36 | uses: actions/cache@v4 37 | with: 38 | path: /tmp/emsdk 39 | key: emscripten-${{ env.EMSCRIPTEN }} 40 | 41 | - name: Install Emscripten 42 | if: steps.cache-emscripten.outputs.cache-hit != 'true' 43 | run: | 44 | cd /tmp 45 | git clone https://github.com/emscripten-core/emsdk.git 46 | cd emsdk 47 | ./emsdk install $EMSCRIPTEN 48 | ./emsdk activate $EMSCRIPTEN 49 | 50 | - name: Build InfiniEmu 51 | run: | 52 | source /tmp/emsdk/emsdk_env.sh 53 | mkdir -p build 54 | cd build 55 | emcmake cmake -DCMAKE_BUILD_TYPE=Release .. 56 | make -j$(nproc) infiniemu-wasm 57 | cd .. 58 | cp build/src/infiniemu.js build/src/infiniemu.wasm frontend/wasm/ 59 | 60 | - name: Build frontend 61 | run: | 62 | cd frontend/wasm 63 | npm i -g pnpm 64 | pnpm install 65 | pnpm run build 66 | 67 | - name: Upload GitHub Pages artifact 68 | uses: actions/upload-pages-artifact@v3 69 | with: 70 | path: frontend/wasm/dist 71 | 72 | deploy: 73 | runs-on: ubuntu-22.04 74 | needs: build 75 | if: github.ref == 'refs/heads/master' 76 | environment: 77 | name: github-pages 78 | url: ${{ steps.deployment.outputs.page_url }} 79 | steps: 80 | - name: Setup Pages 81 | uses: actions/configure-pages@v5 82 | - name: Deploy to GitHub Pages 83 | id: deployment 84 | uses: actions/deploy-pages@v4 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*.bin 2 | /*.dis 3 | /*.out 4 | /*.elf 5 | /*.so 6 | /*.o 7 | build 8 | build-wasm 9 | obj 10 | ./infiniemu 11 | tests.out 12 | 13 | screen*.raw 14 | screen*.png 15 | 16 | __debug_bin* 17 | 18 | callgrind.out.* 19 | vgcore.* 20 | 21 | *.pcap 22 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/RTT"] 2 | path = lib/RTT 3 | url = https://github.com/SEGGERMicro/RTT 4 | [submodule "lib/tiny-AES-c"] 5 | path = lib/tiny-AES-c 6 | url = git@github.com:kokke/tiny-AES-c.git 7 | [submodule "lib/capstone"] 8 | path = lib/capstone 9 | url = git@github.com:capstone-engine/capstone.git 10 | branch = 5.0.1 11 | [submodule "lib/littlefs"] 12 | path = lib/littlefs 13 | url = https://github.com/littlefs-project/littlefs 14 | [submodule "lib/lua"] 15 | path = lib/lua 16 | url = https://github.com/lua/lua.git 17 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/include", 7 | "${workspaceFolder}/lib", 8 | "${workspaceFolder}/lib/capstone/include", 9 | "${workspaceFolder}" 10 | ], 11 | "defines": [ 12 | "_POSIX_C_SOURCE=200809L" 13 | ], 14 | "compilerPath": "/usr/bin/gcc", 15 | "cStandard": "c11", 16 | "cppStandard": "gnu++17", 17 | "intelliSenseMode": "linux-gcc-x64", 18 | "configurationProvider": "ms-vscode.cmake-tools" 19 | } 20 | ], 21 | "version": 4 22 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | // Resolved by CMake Tools: 12 | "program": "${command:cmake.launchTargetPath}", 13 | "args": ["-bf", "${workspaceFolder}/infinitime.out"], 14 | "stopAtEntry": false, 15 | "cwd": "${workspaceFolder}", 16 | "environment": [ 17 | { 18 | // add the directory where our target was built to the PATHs 19 | // it gets resolved by CMake Tools: 20 | "name": "PATH", 21 | "value": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}" 22 | } 23 | ], 24 | "MIMode": "gdb", 25 | "setupCommands": [ 26 | { 27 | "description": "Enable pretty-printing for gdb", 28 | "text": "-enable-pretty-printing", 29 | "ignoreFailures": true 30 | } 31 | ] 32 | }, 33 | { 34 | "name": "(gdb) Launch tests", 35 | "type": "cppdbg", 36 | "request": "launch", 37 | "program": "${workspaceFolder}/tests.out", 38 | "args": [], 39 | "stopAtEntry": false, 40 | "cwd": "${workspaceFolder}", 41 | "environment": [], 42 | "externalConsole": false, 43 | "MIMode": "gdb", 44 | "setupCommands": [ 45 | { 46 | "description": "Enable pretty-printing for gdb", 47 | "text": "-enable-pretty-printing", 48 | "ignoreFailures": true 49 | }, 50 | { 51 | "description": "Set Disassembly Flavor to Intel", 52 | "text": "-gdb-set disassembly-flavor intel", 53 | "ignoreFailures": true 54 | } 55 | ], 56 | "preLaunchTask": "build tests" 57 | }, 58 | ] 59 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.h": "c", 4 | "*.tcc": "c", 5 | "charconv": "c", 6 | "chrono": "c", 7 | "expected": "c", 8 | "optional": "c", 9 | "format": "c", 10 | "ratio": "c", 11 | "system_error": "c", 12 | "array": "c", 13 | "functional": "c", 14 | "regex": "c", 15 | "tuple": "c", 16 | "type_traits": "c", 17 | "utility": "c" 18 | }, 19 | "lldb.showDisassembly": "never", 20 | "lldb.dereferencePointers": true, 21 | "lldb.consoleMode": "evaluate", 22 | "lldb.displayFormat": "hex" 23 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cmake", 6 | "label": "CMake: build", 7 | "command": "build", 8 | "targets": [ 9 | "all" 10 | ], 11 | "group": "build", 12 | "problemMatcher": [], 13 | "detail": "CMake template build task" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | project(infiniemu) 4 | 5 | if(CMAKE_TOOLCHAIN_FILE MATCHES "Emscripten.cmake") 6 | set(EMSCRIPTEN TRUE) 7 | message("[*] Building using Emscripten toolchain") 8 | endif() 9 | 10 | find_package(PNG) 11 | if(PNG_FOUND) 12 | add_compile_definitions(ENABLE_PNG=1) 13 | endif() 14 | 15 | include("./cmake/bundle.cmake") 16 | 17 | include_directories(include lib .) 18 | 19 | add_subdirectory(lib/tiny-AES-c) 20 | 21 | option(CAPSTONE_ARCHITECTURE_DEFAULT "" OFF) 22 | option(CAPSTONE_ARM_SUPPORT "" ON) 23 | add_subdirectory(lib/capstone) 24 | 25 | add_library(lua STATIC lib/lua/onelua.c) 26 | target_compile_options(lua PRIVATE -DMAKE_LIB) 27 | 28 | add_subdirectory(src) 29 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.23.2-bookworm AS builder 2 | RUN apt-get update && apt-get install -y wget git gcc-12 g++ make cmake pkg-config libx11-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libopengl-dev libglx-dev libglu1-mesa-dev freeglut3-dev libxxf86vm-dev libpng-dev 3 | 4 | WORKDIR /app 5 | COPY . . 6 | RUN mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j$(nproc) bundling_target 7 | 8 | RUN cd frontend/desktop && go build -o /previewer ./cmd/previewer 9 | RUN cd tools/web-previewer && go build -o /preview-server . 10 | 11 | FROM debian:12 AS runner 12 | WORKDIR /app 13 | RUN apt-get update && apt-get -y install ca-certificates 14 | COPY --from=builder /previewer /preview-server ./ 15 | 16 | CMD ["/app/preview-server", "-previewer", "/app/previewer"] 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # InfiniEmu 2 | 3 | InfiniEmu is an emulator that emulates a full [PineTime](https://pine64.org/devices/pinetime/) smartwatch, which includes: 4 | 5 | * [NRF52832](https://www.nordicsemi.com/Products/nRF52832) along with its ARM Cortex M4 CPU and peripherals, 6 | * BMA425 I2C accelerometer 7 | * CST816S I2C touch screen controller 8 | * HRS3300 I2C heart rate sensor 9 | * ST7789 SPI LCD display controller 10 | * A generic SPI flash based on the XT25F32B-S 11 | 12 | ## Disclaimer 13 | 14 | This project isn't production ready by any means, and the emulation almost definitely doesn't completely match a real device. 15 | 16 | The goal is to be able to run the same image with the emulator as you would flash on a real device, however InfiniTime 1.14.0 doesn't work on InfiniEmu because of [an issue](https://github.com/InfiniTimeOrg/InfiniTime/pull/2070) that's already fixed but hasn't yet been released on an InfiniTime version. Compiling the latest commit from the InfiniTime repo works fine. 17 | 18 | Many things are yet to be implemented, including but not limited to: 19 | 20 | * ~Adjusting battery voltage input~ 21 | * Sending heartrate data 22 | * Bluetooth 23 | * ~Saving and loading the contents of the SPI flash~ 24 | 25 | # Usage 26 | 27 | There are currently no releases, the easiest way to run InfiniEmu is to download the AppImage artifact from the latest successful [action run](https://github.com/pipe01/InfiniEmu/actions/workflows/main.yaml). 28 | 29 | To run InfiniEmu, use the following command: `./InfiniEmu-x86_64.AppImage `, where `` is the path to the ELF or binary file containing the firmware to run. If an ELF file with symbols is passed, additional information such as FreeRTOS free heap size will be available. 30 | 31 | ## Online previewer 32 | 33 | There is a hosted version of the web previewer at `tools/web-previewer` available at `https://pipe01.net/infiniemu/preview`. This endpoint can take the following parameters: 34 | 35 | - `fw`: specifies what firmware to run. This can be either a pull request number in the form `pr/123`, a Git commit hash or a Git ref like `heads/main` or `tags/1.10.0`. 36 | - `info`: include information about the run at the bottom of the image. 37 | - `no-cache`: do not reuse cached data about the run. 38 | - `script`: the script to run. A script consists of one or more commands separated by commas. You can use underscores where spaces would usually go for convenience when pasting the script into a URL. For an up to date list of commands check the [`script.go`](https://github.com/pipe01/InfiniEmu/blob/master/frontend/desktop/script/script.go) file, specifically the `commands` map. 39 | 40 | > [!CAUTION] 41 | > When using a preview URL inside a GitHub markdown document (READMEs, issues, pull requests, etc) make sure to visit the URL on your browser before adding it to the markdown text, otherwise GitHub's servers will time out the request before the preview is generated. 42 | 43 | > [!NOTE] 44 | > Previews can take a bit to generate depending on your script, please allow up to 30 seconds before giving up. 45 | 46 | ### Example: 47 | 48 | `https://pipe01.net/infiniemu/preview?fw=heads/main&script=run_1s,screenshot` 49 | 50 | ![](https://pipe01.net/infiniemu/preview?fw=heads/main&script=run_1s,screenshot) 51 | 52 | `https://pipe01.net/infiniemu/preview?fw=heads/main&info=true&script=run_1s,screenshot,swipe_right,run_300ms,screenshot,tap_180_180,run_300ms,screenshot,tap_120_200,run_300ms,screenshot,tap_36_203,run_300ms,screenshot,button_1s,run_300ms,screenshot` 53 | 54 | ![](https://pipe01.net/infiniemu/preview?fw=heads/main&info=true&script=run_1s,screenshot,swipe_right,run_300ms,screenshot,tap_180_180,run_300ms,screenshot,tap_120_200,run_300ms,screenshot,tap_36_203,run_300ms,screenshot,button_1s,run_300ms,screenshot) 55 | 56 | # Screenshots 57 | 58 | ![](docs/screenshot.jpg) 59 | -------------------------------------------------------------------------------- /cmake/bundle.cmake: -------------------------------------------------------------------------------- 1 | function(bundle_static_library tgt_name bundled_tgt_name) 2 | list(APPEND static_libs ${tgt_name}) 3 | 4 | function(_recursively_collect_dependencies input_target) 5 | set(_input_link_libraries LINK_LIBRARIES) 6 | get_target_property(_input_type ${input_target} TYPE) 7 | if (${_input_type} STREQUAL "INTERFACE_LIBRARY") 8 | set(_input_link_libraries INTERFACE_LINK_LIBRARIES) 9 | endif() 10 | get_target_property(public_dependencies ${input_target} ${_input_link_libraries}) 11 | foreach(dependency IN LISTS public_dependencies) 12 | if(TARGET ${dependency}) 13 | get_target_property(alias ${dependency} ALIASED_TARGET) 14 | if (TARGET ${alias}) 15 | set(dependency ${alias}) 16 | endif() 17 | get_target_property(_type ${dependency} TYPE) 18 | if (${_type} STREQUAL "STATIC_LIBRARY") 19 | list(APPEND static_libs ${dependency}) 20 | endif() 21 | 22 | get_property(library_already_added 23 | GLOBAL PROPERTY _${tgt_name}_static_bundle_${dependency}) 24 | if (NOT library_already_added) 25 | set_property(GLOBAL PROPERTY _${tgt_name}_static_bundle_${dependency} ON) 26 | _recursively_collect_dependencies(${dependency}) 27 | endif() 28 | endif() 29 | endforeach() 30 | set(static_libs ${static_libs} PARENT_SCOPE) 31 | endfunction() 32 | 33 | _recursively_collect_dependencies(${tgt_name}) 34 | 35 | list(REMOVE_DUPLICATES static_libs) 36 | 37 | set(bundled_tgt_full_name 38 | ${CMAKE_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${bundled_tgt_name}${CMAKE_STATIC_LIBRARY_SUFFIX}) 39 | 40 | if (CMAKE_CXX_COMPILER_ID MATCHES "^(Clang|GNU)$") 41 | file(WRITE ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in 42 | "CREATE ${bundled_tgt_full_name}\n" ) 43 | 44 | foreach(tgt IN LISTS static_libs) 45 | file(APPEND ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in 46 | "ADDLIB $\n") 47 | endforeach() 48 | 49 | file(APPEND ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in "SAVE\n") 50 | file(APPEND ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in "END\n") 51 | 52 | file(GENERATE 53 | OUTPUT ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar 54 | INPUT ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in) 55 | 56 | set(ar_tool ${CMAKE_AR}) 57 | if (CMAKE_INTERPROCEDURAL_OPTIMIZATION) 58 | set(ar_tool ${CMAKE_CXX_COMPILER_AR}) 59 | endif() 60 | 61 | add_custom_command( 62 | COMMAND ${ar_tool} -M < ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar 63 | DEPENDS ${static_libs} 64 | OUTPUT ${bundled_tgt_full_name} 65 | COMMENT "Bundling ${bundled_tgt_name}" 66 | VERBATIM) 67 | elseif(MSVC) 68 | find_program(lib_tool lib) 69 | 70 | foreach(tgt IN LISTS static_libs) 71 | list(APPEND static_libs_full_names $) 72 | endforeach() 73 | 74 | add_custom_command( 75 | COMMAND ${lib_tool} /NOLOGO /OUT:${bundled_tgt_full_name} ${static_libs_full_names} 76 | DEPENDS ${static_libs} 77 | OUTPUT ${bundled_tgt_full_name} 78 | COMMENT "Bundling ${bundled_tgt_name}" 79 | VERBATIM) 80 | else() 81 | message(FATAL_ERROR "Unknown bundle scenario!") 82 | endif() 83 | 84 | add_custom_target(bundling_target ALL DEPENDS ${bundled_tgt_full_name}) 85 | add_dependencies(bundling_target ${tgt_name}) 86 | 87 | add_library(${bundled_tgt_name} STATIC IMPORTED) 88 | set_target_properties(${bundled_tgt_name} 89 | PROPERTIES 90 | IMPORTED_LOCATION ${bundled_tgt_full_name} 91 | INTERFACE_INCLUDE_DIRECTORIES $) 92 | add_dependencies(${bundled_tgt_name} bundling_target) 93 | 94 | endfunction() -------------------------------------------------------------------------------- /docs/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipe01/InfiniEmu/e87555a05d6d8a17cab1c1c5e9e967bb6a0240d7/docs/screenshot.jpg -------------------------------------------------------------------------------- /dumps/ficr.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipe01/InfiniEmu/e87555a05d6d8a17cab1c1c5e9e967bb6a0240d7/dumps/ficr.bin -------------------------------------------------------------------------------- /dumps/secret.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipe01/InfiniEmu/e87555a05d6d8a17cab1c1c5e9e967bb6a0240d7/dumps/secret.bin -------------------------------------------------------------------------------- /dumps/uicr.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipe01/InfiniEmu/e87555a05d6d8a17cab1c1c5e9e967bb6a0240d7/dumps/uicr.bin -------------------------------------------------------------------------------- /frontend/desktop/.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | __debug_bin* 3 | imgui.ini 4 | spiflash.bin 5 | .vscode 6 | state.bin -------------------------------------------------------------------------------- /frontend/desktop/cmd/infiniemu/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | "github.com/pipe01/InfiniEmu/frontend/desktop/emulator" 10 | "github.com/pipe01/InfiniEmu/frontend/desktop/gui" 11 | ) 12 | 13 | func main() { 14 | runGDB := flag.Bool("gdb", false, "") 15 | analyzeHeap := flag.Bool("heap", false, "") 16 | emitRunlog := flag.Bool("runlog", false, "") 17 | noScheduler := flag.Bool("no-sched", false, "") 18 | flag.Parse() 19 | 20 | if flag.NArg() != 1 { 21 | log.Fatal("Usage: infiniemu [options] ") 22 | } 23 | 24 | f, err := os.Open(flag.Arg(0)) 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | defer f.Close() 29 | 30 | program, err := emulator.LoadProgram(f) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | 35 | fmt.Printf("Loaded %d symbols and %d functions\n", len(program.Symbols), len(program.Functions)) 36 | 37 | var extflashInit []byte 38 | if v, err := os.ReadFile("spiflash.bin"); err == nil { 39 | extflashInit = v 40 | } 41 | 42 | e := emulator.NewEmulator(program, extflashInit, true) 43 | 44 | if *emitRunlog { 45 | e.RecordRunlog("runlog.bin") 46 | defer e.CloseRunlog() 47 | } 48 | 49 | if *analyzeHeap { 50 | e.EnableHeapTracker() 51 | } 52 | 53 | err = gui.RunGUI(e, *analyzeHeap, *runGDB, *noScheduler) 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /frontend/desktop/cmd/previewer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "image" 6 | "image/draw" 7 | "image/png" 8 | "log" 9 | "os" 10 | 11 | "github.com/pipe01/InfiniEmu/frontend/desktop/emulator" 12 | "github.com/pipe01/InfiniEmu/frontend/desktop/script" 13 | ) 14 | 15 | func main() { 16 | screenshotPath := flag.String("screenshot", "screenshot.png", "Path to save screenshot") 17 | flag.Parse() 18 | 19 | if flag.NArg() != 2 { 20 | log.Fatal("Usage: previewer [options] ") 21 | } 22 | 23 | f, err := os.Open(flag.Arg(0)) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | defer f.Close() 28 | 29 | program, err := emulator.LoadProgram(f) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | scriptBytes, err := os.ReadFile(flag.Arg(1)) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | e := emulator.NewEmulator(program, nil, true) 40 | 41 | screenshots, err := script.Execute(e, scriptBytes) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | 46 | if len(screenshots) > 0 { 47 | combined := combineImages(screenshots) 48 | 49 | f, err := os.Create(*screenshotPath) 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | 54 | err = png.Encode(f, combined) 55 | if err != nil { 56 | log.Fatal(err) 57 | } 58 | } 59 | } 60 | 61 | func combineImages(images []image.Image) image.Image { 62 | const separation = 5 63 | 64 | width := 0 65 | height := 0 66 | 67 | for i, img := range images { 68 | if img.Bounds().Dy() > height { 69 | height = img.Bounds().Dy() 70 | } 71 | 72 | width += img.Bounds().Dx() 73 | 74 | if i > 0 { 75 | width += separation 76 | } 77 | } 78 | 79 | dst := image.NewRGBA(image.Rect(0, 0, width, height)) 80 | 81 | x := 0 82 | for _, img := range images { 83 | rect := img.Bounds() 84 | 85 | draw.Draw(dst, image.Rect(x, 0, x+rect.Dx(), rect.Dy()), img, image.Point{0, 0}, draw.Src) 86 | 87 | x += rect.Dx() + separation 88 | } 89 | 90 | return dst 91 | } 92 | -------------------------------------------------------------------------------- /frontend/desktop/emulator/emulator.c: -------------------------------------------------------------------------------- 1 | #include "fault.h" 2 | #include "gdb.h" 3 | #include "ie_time.h" 4 | #include "pinetime.h" 5 | #include "segger_rtt.h" 6 | #include "scheduler.h" 7 | 8 | #include 9 | 10 | unsigned long inst_counter = 0; 11 | bool stop_loop = false; 12 | 13 | bool found_rtt = false; 14 | 15 | static char rtt_buffer[1024]; 16 | static int rtt_read; 17 | 18 | extern void branch_callback(cpu_t *cpu, unsigned int old_pc, unsigned int new_pc, void *userdata); 19 | 20 | static inline void loop_step(pinetime_t *pt, rtt_t *rtt) 21 | { 22 | pinetime_step(pt); 23 | inst_counter++; 24 | 25 | if (rtt && (found_rtt || inst_counter < 1000000)) 26 | { 27 | if (inst_counter % 1000 == 0) 28 | { 29 | if (!found_rtt) 30 | found_rtt = rtt_find_control(rtt); 31 | 32 | rtt_read = rtt_flush_buffers(rtt, rtt_buffer, sizeof(rtt_buffer)); 33 | if (rtt_read > 0) 34 | { 35 | fwrite(rtt_buffer, 1, rtt_read, stdout); 36 | fflush(stdout); 37 | } 38 | } 39 | } 40 | } 41 | 42 | static void loop(pinetime_t *pt, rtt_t *rtt) 43 | { 44 | stop_loop = false; 45 | 46 | while (!stop_loop) 47 | { 48 | loop_step(pt, rtt); 49 | } 50 | } 51 | 52 | int run_iterations(pinetime_t *pt, rtt_t *rtt, unsigned long iterations, unsigned long iterations_per_us) 53 | { 54 | unsigned long i; 55 | 56 | jmp_buf fault_jmp; 57 | 58 | int fault = setjmp(fault_jmp); 59 | 60 | if (fault) 61 | { 62 | fault_clear_jmp(); 63 | 64 | return fault; 65 | } 66 | else 67 | { 68 | fault_set_jmp(&fault_jmp); 69 | 70 | for (i = 0; i < iterations; i++) 71 | { 72 | loop_step(pt, rtt); 73 | } 74 | } 75 | 76 | fault_clear_jmp(); 77 | 78 | return 0; 79 | } 80 | 81 | scheduler_t *create_sched(pinetime_t *pt, size_t freq) 82 | { 83 | return scheduler_new((scheduler_cb_t)pinetime_step, pt, freq); 84 | } 85 | 86 | int run(int type, void *arg, rtt_t *rtt) 87 | { 88 | jmp_buf fault_jmp; 89 | 90 | int fault = setjmp(fault_jmp); 91 | 92 | if (fault) 93 | { 94 | fault_clear_jmp(); 95 | 96 | return fault; 97 | } 98 | else 99 | { 100 | fault_set_jmp(&fault_jmp); 101 | 102 | switch (type) 103 | { 104 | case 0: 105 | loop((pinetime_t *)arg, rtt); 106 | break; 107 | 108 | case 1: 109 | scheduler_run((scheduler_t *)arg); 110 | break; 111 | 112 | case 2: 113 | gdb_start((gdb_t *)arg); 114 | break; 115 | } 116 | } 117 | 118 | fault_clear_jmp(); 119 | 120 | return 0; 121 | } 122 | 123 | void set_cpu_branch_cb(cpu_t *cpu, void *userdata) 124 | { 125 | if (userdata == NULL) 126 | cpu_set_branch_cb(cpu, NULL, NULL); 127 | else 128 | cpu_set_branch_cb(cpu, branch_callback, userdata); 129 | } 130 | -------------------------------------------------------------------------------- /frontend/desktop/emulator/pinetime.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | const BaseFrequencyHZ = 64_000_000 4 | 5 | const ( 6 | PinCharging = 12 7 | PinCst816sReset = 10 8 | PinButton = 13 9 | PinButtonEnable = 15 10 | PinCst816sIrq = 28 11 | PinPowerPresent = 19 12 | PinBma421Irq = 8 13 | PinMotor = 16 14 | PinLcdBacklightLow = 14 15 | PinLcdBacklightMedium = 22 16 | PinLcdBacklightHigh = 23 17 | PinSpiSck = 2 18 | PinSpiMosi = 3 19 | PinSpiMiso = 4 20 | PinSpiFlashCsn = 5 21 | PinSpiLcdCsn = 25 22 | PinLcdDataCommand = 18 23 | PinLcdReset = 26 24 | PinTwiScl = 7 25 | PinTwiSda = 6 26 | ) 27 | -------------------------------------------------------------------------------- /frontend/desktop/emulator/user_config.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipe01/InfiniEmu/e87555a05d6d8a17cab1c1c5e9e967bb6a0240d7/frontend/desktop/emulator/user_config.h -------------------------------------------------------------------------------- /frontend/desktop/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pipe01/InfiniEmu/frontend/desktop 2 | 3 | go 1.22.2 4 | 5 | require ( 6 | github.com/AllenDang/imgui-go v1.12.1-0.20221124025851-59b862ca5a0c 7 | github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 8 | ) 9 | 10 | require ( 11 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect 12 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b // indirect 13 | github.com/stretchr/testify v1.8.4 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /frontend/desktop/go.sum: -------------------------------------------------------------------------------- 1 | github.com/AllenDang/imgui-go v1.12.1-0.20221124025851-59b862ca5a0c h1:kiXjH0n0KzOpvhgy3nDFkPmKfg4A+QWsEOwxwWy6yuI= 2 | github.com/AllenDang/imgui-go v1.12.1-0.20221124025851-59b862ca5a0c/go.mod h1:kuPs9RWleaUuK7D49bE6HPxyRA36Lp4ICKGp+5OnnbY= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= 7 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= 8 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 9 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b h1:GgabKamyOYguHqHjSkDACcgoPIz3w0Dis/zJ1wyHHHU= 10 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 11 | github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE= 12 | github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= 13 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 15 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 16 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 17 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 18 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 19 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 20 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 21 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 22 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | -------------------------------------------------------------------------------- /frontend/desktop/libinfiniemu.a: -------------------------------------------------------------------------------- 1 | ../../build/libinfiniemu_bundle.a -------------------------------------------------------------------------------- /frontend/wasm/.gitignore: -------------------------------------------------------------------------------- 1 | infiniemu.js 2 | infiniemu.wasm 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | pnpm-debug.log* 10 | lerna-debug.log* 11 | 12 | node_modules 13 | .DS_Store 14 | dist 15 | dist-ssr 16 | coverage 17 | *.local 18 | .vite 19 | 20 | /cypress/videos/ 21 | /cypress/screenshots/ 22 | 23 | # Editor directories and files 24 | .vscode/* 25 | !.vscode/extensions.json 26 | .idea 27 | *.suo 28 | *.ntvs* 29 | *.njsproj 30 | *.sln 31 | *.sw? 32 | 33 | *.tsbuildinfo 34 | -------------------------------------------------------------------------------- /frontend/wasm/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /frontend/wasm/README.md: -------------------------------------------------------------------------------- 1 | # infiniemu 2 | 3 | This template should help get you started developing with Vue 3 in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). 8 | 9 | ## Type Support for `.vue` Imports in TS 10 | 11 | TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types. 12 | 13 | ## Customize configuration 14 | 15 | See [Vite Configuration Reference](https://vitejs.dev/config/). 16 | 17 | ## Project Setup 18 | 19 | ```sh 20 | pnpm install 21 | ``` 22 | 23 | ### Compile and Hot-Reload for Development 24 | 25 | ```sh 26 | pnpm dev 27 | ``` 28 | 29 | ### Type-Check, Compile and Minify for Production 30 | 31 | ```sh 32 | pnpm build 33 | ``` 34 | -------------------------------------------------------------------------------- /frontend/wasm/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /frontend/wasm/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | InfiniEmu 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /frontend/wasm/infiniemu.d.ts: -------------------------------------------------------------------------------- 1 | declare const tag: unique symbol; 2 | 3 | export type Pointer = { readonly [tag]: "Pointer" }; 4 | export type Program = { readonly [tag]: "Program" }; 5 | export type Pinetime = { readonly [tag]: "Pinetime" }; 6 | export type ST7789 = { readonly [tag]: "ST7789" }; 7 | export type CST816S = { readonly [tag]: "CST816S" }; 8 | export type NRF52832 = { readonly [tag]: "NRF52832" }; 9 | export type SPINorFlash = { readonly [tag]: "SPINorFlash" }; 10 | export type CPU = { readonly [tag]: "CPU" }; 11 | export type Memory = { readonly [tag]: "Memory" }; 12 | export type Pins = { readonly [tag]: "Pins" }; 13 | export type Commander = { readonly [tag]: "Commander" }; 14 | export type RTT = { readonly [tag]: "RTT" }; 15 | export type LFS = { readonly [tag]: "LFS" }; 16 | export type LFSDir = { readonly [tag]: "LFSDir" }; 17 | export type LFSInfo = { readonly [tag]: "LFSInfo" }; 18 | export type LFSFile = { readonly [tag]: "LFSInfo" }; 19 | 20 | const module: EmscriptenModuleFactory; 85 | 86 | export default module; 87 | -------------------------------------------------------------------------------- /frontend/wasm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infiniemu", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "run-p type-check \"build-only {@}\" --", 9 | "preview": "vite preview", 10 | "build-only": "vite build", 11 | "type-check": "vue-tsc --build --force" 12 | }, 13 | "dependencies": { 14 | "@types/emscripten": "^1.39.13", 15 | "@vue/language-plugin-pug": "^2.0.22", 16 | "@zip.js/zip.js": "^2.7.45", 17 | "bootstrap": "^5.3.3", 18 | "bootstrap-icons": "^1.11.3", 19 | "pug": "^3.0.3", 20 | "sass": "^1.77.6", 21 | "vue": "^3.4.29" 22 | }, 23 | "devDependencies": { 24 | "@tsconfig/node20": "^20.1.4", 25 | "@types/node": "^20.14.5", 26 | "@vitejs/plugin-vue": "^5.0.5", 27 | "@vue/tsconfig": "^0.5.1", 28 | "npm-run-all2": "^6.2.0", 29 | "typescript": "~5.4.0", 30 | "vite": "^5.3.1", 31 | "vue-tsc": "^2.0.21" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /frontend/wasm/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipe01/InfiniEmu/e87555a05d6d8a17cab1c1c5e9e967bb6a0240d7/frontend/wasm/public/favicon.ico -------------------------------------------------------------------------------- /frontend/wasm/src/App.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 115 | -------------------------------------------------------------------------------- /frontend/wasm/src/common.ts: -------------------------------------------------------------------------------- 1 | export type FileInfo = { 2 | name: string; 3 | fullPath: string; 4 | size: number; 5 | type: "file" | "dir"; 6 | }; 7 | 8 | export type MessageToWorkerType = { messageId?: number } & ( 9 | { type: "setCanvas", data: OffscreenCanvas } | 10 | { type: "setBackupTime", data: Date } | 11 | { type: "start", data: void } | 12 | { type: "stop", data: void } | 13 | { type: "doTouch", data: { gesture: number, x: number, y: number } } | 14 | { type: "clearTouch", data: void } | 15 | { type: "setPinVoltage", data: { pin: number, value: number } } | 16 | { type: "setProgram", data: ArrayBuffer } | 17 | { type: "readDir", data: string } | 18 | { type: "readFile", data: string } | 19 | { type: "writeFile", data: { path: string, data: ArrayBuffer } } | 20 | { type: "createDir", data: string } | 21 | { type: "backupFS", data: void } | 22 | { type: "restoreFS", data: ArrayBuffer } | 23 | { type: "loadArchiveFS", data: { path: string, zipData: ArrayBuffer } } | 24 | { type: "reset", data: void } | 25 | { type: "runCommand", data: string } 26 | ); 27 | 28 | export type MessageFromWorkerType = { replyToId?: number } & ( 29 | { type: "ready", data: void } | 30 | { type: "done", data: void } | 31 | { type: "error", data: { message: string | undefined, stack: string | undefined, string: string | undefined } } | 32 | { type: "dirFiles", data: FileInfo[] } | 33 | { type: "fileData", data: { path: string, data: ArrayBuffer } } | 34 | { type: "running", data: boolean } | 35 | { type: "rttFound", data: void } | 36 | { type: "rttData", data: string } | 37 | { type: "lcdSleeping", data: boolean } | 38 | { type: "cpuSleeping", data: boolean } | 39 | { type: "performance", data: { loopTime: number, cps: number, totalSRAM: number } } | 40 | { type: "backupData", data: ArrayBuffer } | 41 | { type: "commandOutput", data: string } | 42 | { type: "pins", data: (number | boolean)[] } | 43 | { type: "aborted", data: any } 44 | ); 45 | -------------------------------------------------------------------------------- /frontend/wasm/src/components/Console.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 22 | 23 | 52 | 53 | 81 | -------------------------------------------------------------------------------- /frontend/wasm/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | import "bootstrap-icons/font/bootstrap-icons.min.css" 5 | 6 | createApp(App).mount('#app') 7 | -------------------------------------------------------------------------------- /frontend/wasm/src/pinetime.ts: -------------------------------------------------------------------------------- 1 | // From the perspective of the MCU 2 | export type PinDirection = "i" | "o" | "io"; 3 | 4 | export function isInput(dir: PinDirection) { 5 | return dir == "i" || dir == "io"; 6 | } 7 | 8 | export function isOutput(dir: PinDirection) { 9 | return dir == "o" || dir == "io"; 10 | } 11 | 12 | export type Pin = { number: number; name: string; } & ( 13 | { dir: "i"; canChange?: boolean; analog?: false; pull?: "up" | "down" } | 14 | { dir: "i"; canChange?: boolean; analog: true, intialValue?: number } | 15 | { dir: "o"; } | 16 | { dir: "io"; canChange?: boolean; } 17 | ); 18 | 19 | export const pinetimePins: Pin[] = [ 20 | { number: 2, name: "SpiSck", dir: "o" }, 21 | { number: 3, name: "SpiMosi", dir: "o" }, 22 | { number: 4, name: "SpiMiso", dir: "i" }, 23 | { number: 5, name: "SpiFlashCsn", dir: "o" }, 24 | { number: 6, name: "TwiSda", dir: "io" }, 25 | { number: 7, name: "TwiScl", dir: "io" }, 26 | { number: 8, name: "Bma421Irq", dir: "i" }, 27 | { number: 10, name: "Cst816sReset", dir: "o" }, 28 | { number: 12, name: "Charging", dir: "i", canChange: true, pull: "up" }, 29 | { number: 13, name: "Button", dir: "i", canChange: true }, 30 | { number: 14, name: "LcdBacklightLow", dir: "o" }, 31 | { number: 15, name: "ButtonEnable", dir: "o" }, 32 | { number: 16, name: "Motor", dir: "o" }, 33 | { number: 18, name: "LcdDataCommand", dir: "o" }, 34 | { number: 19, name: "PowerPresent", dir: "i", canChange: true, pull: "up" }, 35 | { number: 22, name: "LcdBacklightMedium", dir: "o" }, 36 | { number: 23, name: "LcdBacklightHigh", dir: "o" }, 37 | { number: 25, name: "SpiLcdCsn", dir: "o" }, 38 | { number: 26, name: "LcdReset", dir: "o" }, 39 | { number: 28, name: "Cst816sIrq", dir: "i" }, 40 | { number: 31, name: "BatteryVoltage", dir: "i", canChange: true, analog: true, intialValue: 3.9 / 2 }, 41 | ]; 42 | -------------------------------------------------------------------------------- /frontend/wasm/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 8 | 9 | "baseUrl": ".", 10 | "paths": { 11 | "@/*": ["./src/*"] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/wasm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.node.json" 6 | }, 7 | { 8 | "path": "./tsconfig.app.json" 9 | } 10 | ], 11 | "vueCompilerOptions": { 12 | "plugins": [ 13 | "@vue/language-plugin-pug" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /frontend/wasm/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node20/tsconfig.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "nightwatch.conf.*", 8 | "playwright.config.*" 9 | ], 10 | "compilerOptions": { 11 | "composite": true, 12 | "noEmit": true, 13 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 14 | 15 | "module": "ESNext", 16 | "moduleResolution": "Bundler", 17 | "types": ["node"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /frontend/wasm/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [ 9 | vue(), 10 | ], 11 | resolve: { 12 | alias: { 13 | '@': fileURLToPath(new URL('./src', import.meta.url)) 14 | } 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /include/arm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define APSR_N 31 7 | #define APSR_Z 30 8 | #define APSR_C 29 9 | #define APSR_V 28 10 | #define APSR_Q 27 11 | #define APSR_GE (0b1111 << 16) 12 | 13 | #define IPSR_MASK ((1 << 9) - 1) 14 | 15 | #define EPSR_T 24 16 | 17 | #define CONTROL_nPRIV 0 18 | #define CONTROL_SPSEL 1 19 | #define CONTROL_FPCA 2 20 | 21 | #define ARM_MAX_EXCEPTIONS 512 22 | #define ARM_MAX_PRIORITY 255 23 | #define ARM_EXTERNAL_INTERRUPT_NUMBER(n) (16 + (n)) 24 | 25 | // TODO: Move this to nrf52832.h 26 | #define ARM_SRAM_START 0x20000000 27 | #define ARM_SRAM_END 0x20010000 28 | 29 | typedef union 30 | { 31 | struct 32 | { 33 | unsigned int ipsr : 9; 34 | unsigned int : 1; 35 | unsigned int epsr_iciit_l : 6; 36 | unsigned int apsr_ge0 : 1; 37 | unsigned int apsr_ge1 : 1; 38 | unsigned int apsr_ge2 : 1; 39 | unsigned int apsr_ge3 : 1; 40 | unsigned int : 4; 41 | unsigned int epsr_t : 1; 42 | unsigned int epsr_iciit_h : 2; 43 | unsigned int apsr_q : 1; 44 | unsigned int apsr_v : 1; 45 | unsigned int apsr_c : 1; 46 | unsigned int apsr_z : 1; 47 | unsigned int apsr_n : 1; 48 | }; 49 | uint32_t value; 50 | } xPSR_t; 51 | 52 | static_assert(sizeof(xPSR_t) == 4, "xPSR register size has invalid size"); 53 | 54 | #define xPSR_IT(xpsr) ((xpsr).epsr_iciit_l | ((xpsr).epsr_iciit_h) << 6) 55 | 56 | typedef union 57 | { 58 | struct 59 | { 60 | unsigned int nPRIV : 1; 61 | unsigned int SPSEL : 1; 62 | unsigned int FPCA : 1; 63 | }; 64 | uint32_t value; 65 | } CONTROL_t; 66 | 67 | static_assert(sizeof(CONTROL_t) == 4, "CONTROL register size has invalid size"); 68 | 69 | typedef union 70 | { 71 | struct 72 | { 73 | unsigned int LSPACT : 1; 74 | unsigned int USER : 1; 75 | unsigned int : 1; 76 | unsigned int THREAD : 1; 77 | unsigned int HFRDY : 1; 78 | unsigned int MMRDY : 1; 79 | unsigned int BFRDY : 1; 80 | unsigned int : 1; 81 | unsigned int MONRDY : 1; 82 | unsigned int : 21; 83 | unsigned int LSPEN : 1; 84 | unsigned int ASPEN : 1; 85 | }; 86 | uint32_t value; 87 | } FPCCR_t; 88 | 89 | static_assert(sizeof(FPCCR_t) == 4, "FPCCR register size has invalid size"); 90 | 91 | typedef enum 92 | { 93 | ARM_MODE_THREAD = 0, 94 | ARM_MODE_HANDLER = 1, 95 | } arm_mode; 96 | 97 | typedef enum 98 | { 99 | ARM_EXC_NONE = 0, 100 | 101 | ARM_EXC_RESET = 1, 102 | ARM_EXC_NMI = 2, 103 | ARM_EXC_HARDFAULT = 3, 104 | ARM_EXC_MEMMANAGE = 4, 105 | ARM_EXC_BUSFAULT = 5, 106 | ARM_EXC_USAGEFAULT = 6, 107 | ARM_EXC_SVC = 11, 108 | ARM_EXC_DEBUGMONITOR = 12, 109 | ARM_EXC_PENDSV = 14, 110 | ARM_EXC_SYSTICK = 15, 111 | 112 | ARM_EXC_EXTERNAL_START = 16, 113 | ARM_EXC_EXTERNAL_END = 512 114 | } arm_exception; 115 | 116 | #define ARM_EXCEPTION_COUNT 512 117 | 118 | #define ARM_EXC_EXTERNAL(n) (ARM_EXC_EXTERNAL_START + (n)) 119 | 120 | static inline arm_cc invert_cc(arm_cc cc) 121 | { 122 | switch (cc) 123 | { 124 | case ARM_CC_EQ: 125 | return ARM_CC_NE; 126 | case ARM_CC_NE: 127 | return ARM_CC_EQ; 128 | case ARM_CC_HS: 129 | return ARM_CC_LO; 130 | case ARM_CC_LO: 131 | return ARM_CC_HS; 132 | case ARM_CC_MI: 133 | return ARM_CC_PL; 134 | case ARM_CC_PL: 135 | return ARM_CC_MI; 136 | case ARM_CC_VS: 137 | return ARM_CC_VC; 138 | case ARM_CC_VC: 139 | return ARM_CC_VS; 140 | case ARM_CC_HI: 141 | return ARM_CC_LS; 142 | case ARM_CC_LS: 143 | return ARM_CC_HI; 144 | case ARM_CC_GE: 145 | return ARM_CC_LT; 146 | case ARM_CC_LT: 147 | return ARM_CC_GE; 148 | case ARM_CC_GT: 149 | return ARM_CC_LE; 150 | case ARM_CC_LE: 151 | return ARM_CC_GT; 152 | default: 153 | return ARM_CC_INVALID; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /include/bus_i2c.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef struct bus_i2c_t bus_i2c_t; 7 | 8 | typedef void (*i2c_write_f)(uint8_t *data, size_t data_size, void *userdata); 9 | typedef size_t (*i2c_read_f)(uint8_t *data, size_t data_size, void *userdata); 10 | typedef void (*i2c_reset_f)(void *userdata); 11 | 12 | typedef struct 13 | { 14 | void *userdata; 15 | i2c_write_f write; 16 | i2c_read_f read; 17 | i2c_reset_f reset; 18 | } i2c_slave_t; 19 | 20 | bus_i2c_t *i2c_new(uint8_t *ram, size_t ram_size); 21 | void i2c_reset(bus_i2c_t *); 22 | void i2c_free(bus_i2c_t *); 23 | void i2c_add_slave(bus_i2c_t *, uint8_t address, i2c_slave_t slave); 24 | void i2c_write(bus_i2c_t *, uint8_t address, uint32_t data_address, size_t data_size); 25 | size_t i2c_read(bus_i2c_t *, uint8_t address, uint32_t data_address, size_t data_size); 26 | 27 | #define RETURN_REG(periph, reg) \ 28 | do \ 29 | { \ 30 | if (data_size == 1) \ 31 | { \ 32 | periph->next_read[0] = periph->reg; \ 33 | periph->next_read_size = 1; \ 34 | } \ 35 | else \ 36 | periph->reg = data[1]; \ 37 | } while (0) 38 | -------------------------------------------------------------------------------- /include/bus_spi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "pins.h" 8 | #include "state_store.h" 9 | 10 | typedef struct bus_spi_t bus_spi_t; 11 | 12 | typedef void (*spi_write_f)(uint8_t byte, void *userdata); 13 | typedef uint8_t (*spi_read_f)(void *userdata); 14 | typedef void (*spi_reset_f)(void *userdata); 15 | typedef void (*spi_cs_changed_f)(bool selected, void *userdata); 16 | 17 | typedef enum 18 | { 19 | SPI_RESULT_OK = 0, 20 | SPI_RESULT_NO_SELECTED, 21 | } spi_result_t; 22 | 23 | typedef struct 24 | { 25 | void *userdata; 26 | spi_write_f write; 27 | spi_read_f read; 28 | spi_reset_f reset; 29 | spi_cs_changed_f cs_changed; 30 | } spi_slave_t; 31 | 32 | bus_spi_t *bus_spi_new(pins_t *pins, uint8_t *ram, size_t ram_size, state_store_t *store); 33 | void bus_spi_reset(bus_spi_t *); 34 | void bus_spi_free(bus_spi_t *); 35 | void bus_spi_step(bus_spi_t *); 36 | void bus_spi_add_slave(bus_spi_t *, uint8_t cs_pin, spi_slave_t slave); 37 | spi_result_t bus_spi_write_dma(bus_spi_t *, uint32_t address, size_t size); 38 | spi_result_t bus_spi_write(bus_spi_t *, uint8_t byte); 39 | size_t bus_spi_read_dma(bus_spi_t *, uint32_t address, size_t size); 40 | bool bus_spi_read(bus_spi_t *, uint8_t *data); 41 | -------------------------------------------------------------------------------- /include/byte_util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if !defined(__BYTE_ORDER__) || (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__) 6 | #error This program only works on little endian systems 7 | #endif 8 | 9 | typedef union 10 | { 11 | struct 12 | { 13 | uint8_t a : 8; 14 | uint8_t b : 8; 15 | uint8_t c : 8; 16 | uint8_t d : 8; 17 | }; 18 | uint8_t u8[4]; 19 | uint32_t u32; 20 | } Register4; 21 | 22 | typedef union 23 | { 24 | struct 25 | { 26 | uint16_t a : 16; 27 | uint16_t b : 16; 28 | }; 29 | uint16_t u16[2]; 30 | uint32_t u32; 31 | } Register2; 32 | 33 | #define READ_UINT16(arr, addr) (*((uint16_t *)((uint8_t *)(arr) + (addr)))) 34 | #define WRITE_UINT16(arr, addr, value) (*(uint16_t *)((uint8_t *)(arr) + (addr)) = (value)) 35 | 36 | #define READ_UINT32(arr, addr) (*((uint32_t *)((uint8_t *)(arr) + (addr)))) 37 | #define WRITE_UINT32(arr, addr, value) (*(uint32_t *)((uint8_t *)(arr) + (addr)) = (value)) 38 | 39 | #define x(high, low) 0x##high##low 40 | 41 | // x & (0xFFFFFFFF << log2(n)) 42 | #define ALIGN2(x) ((x) & (0xFFFFFFFF << 1)) 43 | #define ALIGN4(x) ((x) & (0xFFFFFFFF << 2)) 44 | #define ALIGN8(x) ((x) & (0xFFFFFFFF << 3)) 45 | 46 | #define SET(x, n) ((x) = ((x) | (1 << (n)))) 47 | #define CLEAR(x, n) ((x) = ((x) & ~(1 << (n)))) 48 | #define IS_SET(x, n) (((x) & (1 << (n))) != 0) 49 | 50 | #define MASK(n) (1 << (n)) 51 | -------------------------------------------------------------------------------- /include/circular_buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct circular_buffer_t circular_buffer_t; 8 | 9 | circular_buffer_t *circular_buffer_new(size_t size); 10 | void circular_buffer_free(circular_buffer_t *); 11 | 12 | bool circular_buffer_read(circular_buffer_t *, uint8_t *data); 13 | bool circular_buffer_write(circular_buffer_t *, uint8_t data); 14 | void circular_buffer_clear(circular_buffer_t *); 15 | size_t circular_buffer_remaining(circular_buffer_t *); 16 | -------------------------------------------------------------------------------- /include/commander.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pinetime.h" 4 | 5 | typedef void (*commander_output_t)(const char *msg, void *userdata); 6 | 7 | typedef struct commander_t commander_t; 8 | 9 | commander_t *commander_new(pinetime_t *pt); 10 | void commander_set_output(commander_t *, commander_output_t, void *userdata); 11 | void commander_free(commander_t *); 12 | 13 | void commander_run_command(commander_t *, const char *command); 14 | -------------------------------------------------------------------------------- /include/components/i2c/bma425.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bus_i2c.h" 4 | #include "state_store.h" 5 | 6 | i2c_slave_t bma425_new(state_store_t *store); 7 | -------------------------------------------------------------------------------- /include/components/i2c/cst816s.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bus_i2c.h" 4 | #include "pins.h" 5 | #include "state_store.h" 6 | 7 | typedef enum 8 | { 9 | GESTURE_NONE = 0x00, 10 | GESTURE_SLIDEDOWN = 0x01, 11 | GESTURE_SLIDEUP = 0x02, 12 | GESTURE_SLIDELEFT = 0x03, 13 | GESTURE_SLIDERIGHT = 0x04, 14 | GESTURE_SINGLETAP = 0x05, 15 | GESTURE_DOUBLETAP = 0x0B, 16 | GESTURE_LONGPRESS = 0x0C, 17 | } touch_gesture_t; 18 | 19 | typedef struct cst816s_t cst816s_t; 20 | 21 | cst816s_t *cst816s_new(pins_t *pins, state_store_t *store, int irqPin); 22 | i2c_slave_t cst816s_get_slave(cst816s_t *); 23 | 24 | void cst816s_do_touch(cst816s_t *, touch_gesture_t gesture, uint16_t x, uint16_t y); 25 | void cst816s_release_touch(cst816s_t *); 26 | -------------------------------------------------------------------------------- /include/components/i2c/hrs3300.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bus_i2c.h" 4 | #include "state_store.h" 5 | 6 | typedef struct hrs3300_t hrs3300_t; 7 | 8 | hrs3300_t *hrs3300_new(state_store_t *store); 9 | i2c_slave_t hrs3300_get_slave(hrs3300_t *); 10 | 11 | void hrs3300_set_ch0(hrs3300_t *, uint32_t value); 12 | void hrs3300_set_ch1(hrs3300_t *, uint32_t value); 13 | -------------------------------------------------------------------------------- /include/components/spi/spinorflash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bus_spi.h" 4 | #include "state_store.h" 5 | 6 | #include 7 | 8 | typedef struct spinorflash_t spinorflash_t; 9 | 10 | spinorflash_t *spinorflash_new(state_store_t *store, size_t size, size_t sector_size); 11 | spi_slave_t spinorflash_get_slave(spinorflash_t *); 12 | 13 | size_t spinorflash_get_write_count(spinorflash_t *); 14 | void spinorflash_set_buffer(spinorflash_t *, uint8_t *data); 15 | uint8_t *spinorflash_get_buffer(spinorflash_t *); 16 | size_t spinorflash_get_buffer_size(spinorflash_t *); 17 | -------------------------------------------------------------------------------- /include/components/spi/st7789.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bus_spi.h" 4 | 5 | typedef struct st7789_t st7789_t; 6 | 7 | #define DISPLAY_BUFFER_WIDTH 240 8 | #define DISPLAY_BUFFER_HEIGHT 320 9 | #define BYTES_PER_PIXEL 2 // We assume 16bpp format 10 | 11 | st7789_t* st7789_new(state_store_t *store); 12 | spi_slave_t st7789_get_slave(st7789_t *); 13 | 14 | void st7789_read_screen(st7789_t *, uint8_t *data, size_t width, size_t height); 15 | bool st7789_is_sleeping(st7789_t *); 16 | 17 | size_t st7789_get_write_count(st7789_t *); 18 | -------------------------------------------------------------------------------- /include/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "user_config.h" 4 | 5 | #ifndef ENABLE_LOG_GDB 6 | #define ENABLE_LOG_GDB 0 7 | #endif 8 | 9 | #ifndef ENABLE_LOG_CPU_EXCEPTIONS 10 | #define ENABLE_LOG_CPU_EXCEPTIONS 0 11 | #endif 12 | 13 | #ifndef ENABLE_LOG_CPU_INSTRUCTIONS 14 | #define ENABLE_LOG_CPU_INSTRUCTIONS 0 15 | #endif 16 | 17 | #ifndef ENABLE_LOG_CPU_IT 18 | #define ENABLE_LOG_CPU_IT 0 19 | #endif 20 | 21 | #ifndef ENABLE_LOG_SEGGER_RTT 22 | #define ENABLE_LOG_SEGGER_RTT 1 23 | #endif 24 | 25 | #ifndef ENABLE_LOG_SPI_FLASH 26 | #define ENABLE_LOG_SPI_FLASH 0 27 | #endif 28 | 29 | #ifndef ENABLE_LOG_BMA425 30 | #define ENABLE_LOG_BMA425 0 31 | #endif 32 | 33 | #ifndef ENABLE_SEGGER_RTT 34 | #define ENABLE_SEGGER_RTT 1 35 | #endif 36 | 37 | #ifndef ENABLE_MEASUREMENT 38 | #define ENABLE_MEASUREMENT 1 39 | #endif 40 | 41 | #ifndef ABORT_ON_INVALID_MEM_ACCESS 42 | #define ABORT_ON_INVALID_MEM_ACCESS 1 43 | #endif 44 | 45 | #ifndef ASSERT_EXCEPTION_REGISTERS 46 | #define ASSERT_EXCEPTION_REGISTERS 0 47 | #endif 48 | -------------------------------------------------------------------------------- /include/cpu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "arm.h" 8 | #include "memory.h" 9 | #include "runlog.h" 10 | #include "state_store.h" 11 | 12 | typedef struct cpu_inst_t cpu_t; 13 | 14 | typedef void (*branch_cb_t)(cpu_t *, uint32_t old_pc, uint32_t new_pc, void *userdata); 15 | typedef void (*mem_watchpoint_cb_t)(cpu_t *, bool isWrite, uint32_t addr, size_t size, uint32_t value_old, uint32_t value_new, void *userdata); 16 | 17 | cpu_t *cpu_new(const uint8_t *program, size_t program_size, memory_map_t *mem, state_store_t *store, size_t max_external_interrupts, size_t priority_bits); 18 | void cpu_free(cpu_t *); 19 | void cpu_reset(cpu_t *); 20 | // Returns the number of cycles that the CPU ran for 21 | int cpu_step(cpu_t *); 22 | 23 | void cpu_clear_instruction_cache(cpu_t *); 24 | 25 | #if ENABLE_RUNLOG 26 | void cpu_set_runlog(cpu_t *, runlog_t *runlog); 27 | #endif 28 | 29 | void cpu_set_branch_cb(cpu_t *, branch_cb_t cb, void *userdata); 30 | void cpu_set_memory_watchpoint(cpu_t *, uint32_t addr, bool read, bool write, mem_watchpoint_cb_t cb, void *userdata); 31 | void cpu_clear_memory_watchpoint(cpu_t *); 32 | 33 | memory_map_t *cpu_mem(cpu_t *cpu); 34 | bool cpu_mem_read(cpu_t *, uint32_t addr, uint8_t *value); 35 | bool cpu_mem_write(cpu_t *, uint32_t addr, uint8_t value); 36 | 37 | uint32_t cpu_reg_read(cpu_t *, arm_reg reg); 38 | void cpu_reg_write(cpu_t *, arm_reg reg, uint32_t value); 39 | 40 | uint32_t cpu_sysreg_read(cpu_t *, arm_sysreg reg); 41 | void cpu_sysreg_write(cpu_t *, arm_sysreg reg, uint32_t value, bool can_update_it); 42 | 43 | arm_exception cpu_get_top_running_exception(cpu_t *); 44 | void cpu_jump_exception(cpu_t *, arm_exception ex); 45 | int16_t cpu_get_exception_priority(cpu_t *, arm_exception ex); 46 | void cpu_set_exception_priority(cpu_t *, arm_exception ex, int16_t priority); 47 | void cpu_exception_set_pending(cpu_t *, arm_exception ex); 48 | void cpu_exception_clear_pending(cpu_t *, arm_exception ex); 49 | bool cpu_exception_is_pending(cpu_t *, arm_exception ex); 50 | uint32_t cpu_exception_get_pending_block(cpu_t *, int block_num); 51 | bool cpu_exception_is_active(cpu_t *, arm_exception ex); 52 | void cpu_exception_set_enabled(cpu_t *, arm_exception ex, bool enabled); 53 | bool cpu_exception_get_enabled(cpu_t *, arm_exception ex); 54 | 55 | bool cpu_is_sleeping(cpu_t *); 56 | -------------------------------------------------------------------------------- /include/demangle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" 5 | #endif 6 | 7 | char *demangle(const char *mangled); 8 | -------------------------------------------------------------------------------- /include/dma.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef struct dma_t dma_t; 7 | 8 | dma_t *dma_new(uint32_t start_addr, uint8_t *ram, size_t ram_size); 9 | void dma_free(dma_t *); 10 | void dma_read(dma_t *, uint32_t addr, size_t count, uint8_t *data); 11 | void dma_write(dma_t *, uint32_t addr, size_t count, const uint8_t *data); 12 | -------------------------------------------------------------------------------- /include/fault.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef enum 7 | { 8 | FAULT_UNKNOWN = 1, 9 | 10 | FAULT_NOT_IMPLEMENTED, 11 | 12 | FAULT_MEMORY_INVALID_ACCESS, 13 | FAULT_MEMORY_INVALID_SIZE, 14 | FAULT_MEMORY_UNHANDLED, 15 | 16 | FAULT_CPU_INVALID_INSTRUCTION, 17 | FAULT_CPU_INVALID_CC, 18 | FAULT_CPU_INVALID_EXCEPTION_RETURN, 19 | FAULT_CPU_INVALID_FP_REGISTER, 20 | FAULT_CPU_INVALID_SYSREG, 21 | FAULT_CPU_FIXED_EXCEPTION, 22 | FAULT_CPU_PC_ALIGNMENT, 23 | FAULT_CPU_FP_DISABLED, 24 | FAULT_CPU_DIVIDE_BY_ZERO, 25 | 26 | FAULT_DMA_INVALID_ADDRESS, 27 | 28 | FAULT_SPI_COMMAND_TOO_LONG, 29 | FAULT_SPI_UNKNOWN_COMMAND, 30 | FAULT_SPI_OUTPUT_BUFFER_FULL, 31 | FAULT_SPI_INPUT_BUFFER_FULL, 32 | 33 | FAULT_I2C_DUPLICATE_ADDRESS, 34 | FAULT_I2C_UNKNOWN_ADDRESS, 35 | FAULT_I2C_UNKNOWN_COMMAND, 36 | FAULT_I2C_INVALID_DATA, 37 | 38 | FAULT_PPI_DUPLICATE_PERIPHERAL, 39 | 40 | FAULT_RTC_INVALID_STATE, 41 | 42 | FAULT_ST7789_INVALID_COORDS, 43 | } fault_type_t; 44 | 45 | #define assert_fault(cond, fault) \ 46 | do \ 47 | { \ 48 | if (!(cond)) \ 49 | { \ 50 | printf(__FILE__ ":%d: Assertion `" #cond "` failed.\n", __LINE__); \ 51 | fault_take(fault); \ 52 | } \ 53 | } while (0) 54 | 55 | #define fault_take(t) \ 56 | do \ 57 | { \ 58 | printf(__FILE__ ":%d: took fault %d\n", __LINE__, t); \ 59 | fault_take_(t); \ 60 | } while (0) 61 | 62 | void fault_set_jmp(jmp_buf *buf); 63 | void fault_clear_jmp(void); 64 | void fault_take_(fault_type_t t) __attribute__((__nothrow__, __leaf__, __noreturn__)); 65 | -------------------------------------------------------------------------------- /include/gdb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pinetime.h" 4 | 5 | typedef struct gdb_t gdb_t; 6 | 7 | gdb_t *gdb_new(pinetime_t *pt, bool start_paused); 8 | void gdb_start(gdb_t *gdb); 9 | -------------------------------------------------------------------------------- /include/ie_time.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __EMSCRIPTEN__ 6 | 7 | #include 8 | 9 | static inline uint64_t microseconds_now() 10 | { 11 | return EM_ASM_INT(return performance.now() * 1e3); 12 | } 13 | 14 | #else 15 | 16 | #include 17 | 18 | uint64_t microseconds_now_real(); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/lua.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pinetime.h" 4 | 5 | void run_lua(const char *script, size_t script_size, const char *name); 6 | void run_lua_file(const char *script_path); 7 | -------------------------------------------------------------------------------- /include/lualibs/lua_display.h: -------------------------------------------------------------------------------- 1 | #include "lualibs.h" 2 | 3 | int l_display_new(lua_State *L); 4 | -------------------------------------------------------------------------------- /include/lualibs/lua_image.h: -------------------------------------------------------------------------------- 1 | #include "lualibs.h" 2 | 3 | int l_image_new(lua_State *L); 4 | -------------------------------------------------------------------------------- /include/lualibs/lua_touch.h: -------------------------------------------------------------------------------- 1 | #include "lualibs.h" 2 | 3 | int l_touch_new(lua_State *L); 4 | -------------------------------------------------------------------------------- /include/lualibs/lualibs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef LIB_NAME 10 | 11 | #define METATABLE macro_string(LIB_NAME) 12 | 13 | #define DEF_FN_PUBLIC(name) int l_##name(lua_State *L) 14 | #define DEF_FN(name) static DEF_FN_PUBLIC(name) 15 | 16 | #define REG_FN(name) \ 17 | lua_pushcfunction(L, l_##name); \ 18 | lua_setglobal(L, #name); 19 | 20 | #define DEF_FUNCS static const luaL_Reg functions[] = 21 | #define DEF_METHODS static const luaL_Reg methods[] = 22 | 23 | #define FN2(name, fn) {#name, l_##fn} 24 | #define FN(name) {#name, l_##name} 25 | #define END_FN {NULL, NULL} 26 | 27 | #define DEF_LIB(name) \ 28 | int luaopen_##name(lua_State *L) \ 29 | { \ 30 | luaL_newmetatable(L, METATABLE); \ 31 | lua_pushstring(L, "__index"); \ 32 | lua_pushvalue(L, -2); \ 33 | lua_settable(L, -3); \ 34 | luaL_setfuncs(L, methods, 0); \ 35 | lua_newtable(L); \ 36 | luaL_setfuncs(L, functions, 0); \ 37 | lua_setglobal(L, macro_string(LIB_NAME)); \ 38 | return 1; \ 39 | } 40 | 41 | static inline DATA_TYPE *lua_getdata_p(lua_State *L, int index) 42 | { 43 | void *data = luaL_checkudata(L, index, METATABLE); 44 | luaL_argcheck(L, data != NULL, index, "invalid data for " macro_string(LIB_NAME)); 45 | 46 | return *(DATA_TYPE **)data; 47 | } 48 | 49 | static inline DATA_TYPE *lua_getdata(lua_State *L, int index) 50 | { 51 | void *data = luaL_checkudata(L, index, METATABLE); 52 | luaL_argcheck(L, data != NULL, index, "invalid data for " macro_string(LIB_NAME)); 53 | 54 | return (DATA_TYPE *)data; 55 | } 56 | 57 | #else 58 | 59 | #define DEF_LIB(name) int luaopen_##name(lua_State *L) 60 | 61 | DEF_LIB(pinetime); 62 | DEF_LIB(display); 63 | DEF_LIB(image); 64 | DEF_LIB(touch); 65 | 66 | #undef DEF_LIB 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /include/nrf52832.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cpu.h" 4 | #include "bus_i2c.h" 5 | #include "bus_spi.h" 6 | #include "program.h" 7 | #include "state_store.h" 8 | #include "peripherals/nrf52832/rtc.h" 9 | 10 | #define NRF52832_SRAM_SIZE 0x10000 11 | #define NRF52832_FLASH_SIZE 0x80000 12 | 13 | #define NRF52832_PRIORITY_BITS 3 14 | 15 | #define NRF52832_MAX_EXTERNAL_INTERRUPTS 496 16 | 17 | #define NRF52832_HFCLK_FREQUENCY (64 * 1000 * 1000) 18 | #define NRF52832_LFCLK_FREQUENCY 32768 19 | 20 | enum 21 | { 22 | INSTANCE_CLOCK = 0x00, 23 | INSTANCE_POWER = 0x00, 24 | INSTANCE_BPROT = 0x00, 25 | INSTANCE_RADIO = 0x01, 26 | INSTANCE_UARTE0 = 0x02, 27 | INSTANCE_UART0 = 0x02, 28 | INSTANCE_SPIM0 = 0x03, 29 | INSTANCE_SPIS0 = 0x03, 30 | INSTANCE_TWIM0 = 0x03, 31 | INSTANCE_TWI0 = 0x03, 32 | INSTANCE_SPI0 = 0x03, 33 | INSTANCE_TWIS0 = 0x03, 34 | INSTANCE_SPIM1 = 0x04, 35 | INSTANCE_TWI1 = 0x04, 36 | INSTANCE_SPIS1 = 0x04, 37 | INSTANCE_TWIS1 = 0x04, 38 | INSTANCE_TWIM1 = 0x04, 39 | INSTANCE_SPI1 = 0x04, 40 | INSTANCE_NFCT = 0x05, 41 | INSTANCE_GPIOTE = 0x06, 42 | INSTANCE_SAADC = 0x07, 43 | INSTANCE_TIMER0 = 0x08, 44 | INSTANCE_TIMER1 = 0x09, 45 | INSTANCE_TIMER2 = 0x0A, 46 | INSTANCE_RTC0 = 0x0B, 47 | INSTANCE_TEMP = 0x0C, 48 | INSTANCE_RNG = 0x0D, 49 | INSTANCE_ECB = 0x0E, 50 | INSTANCE_CCM = 0x0F, 51 | INSTANCE_AAR = 0x0F, 52 | INSTANCE_WDT = 0x10, 53 | INSTANCE_RTC1 = 0x11, 54 | INSTANCE_QDEC = 0x12, 55 | INSTANCE_LPCOMP = 0x13, 56 | INSTANCE_COMP = 0x13, 57 | INSTANCE_SWI0 = 0x14, 58 | INSTANCE_EGU0 = 0x14, 59 | INSTANCE_EGU1 = 0x15, 60 | INSTANCE_SWI1 = 0x15, 61 | INSTANCE_SWI2 = 0x16, 62 | INSTANCE_EGU2 = 0x16, 63 | INSTANCE_SWI3 = 0x17, 64 | INSTANCE_EGU3 = 0x17, 65 | INSTANCE_EGU4 = 0x18, 66 | INSTANCE_SWI4 = 0x18, 67 | INSTANCE_SWI5 = 0x19, 68 | INSTANCE_EGU5 = 0x19, 69 | INSTANCE_TIMER3 = 0x1A, 70 | INSTANCE_TIMER4 = 0x1B, 71 | INSTANCE_PWM0 = 0x1C, 72 | INSTANCE_PDM = 0x1D, 73 | INSTANCE_NVMC = 0x1E, 74 | INSTANCE_PPI = 0x1F, 75 | INSTANCE_MWU = 0x20, 76 | INSTANCE_PWM1 = 0x21, 77 | INSTANCE_PWM2 = 0x22, 78 | INSTANCE_SPI2 = 0x23, 79 | INSTANCE_SPIS2 = 0x23, 80 | INSTANCE_SPIM2 = 0x23, 81 | INSTANCE_RTC2 = 0x24, 82 | INSTANCE_I2S = 0x25, 83 | INSTANCE_FPU = 0x26, 84 | }; 85 | 86 | typedef struct NRF52832_inst_t NRF52832_t; 87 | 88 | NRF52832_t *nrf52832_new(const program_t *flash, size_t sram_size, state_store_t *store); 89 | void nrf52832_free(NRF52832_t *); 90 | void nrf52832_reset(NRF52832_t *); 91 | int nrf52832_step(NRF52832_t *); 92 | 93 | cpu_t *nrf52832_get_cpu(NRF52832_t *); 94 | bus_spi_t *nrf52832_get_spi(NRF52832_t *); 95 | bus_i2c_t *nrf52832_get_i2c(NRF52832_t *); 96 | pins_t *nrf52832_get_pins(NRF52832_t *); 97 | void *nrf52832_get_peripheral(NRF52832_t *, uint8_t instance_id); 98 | 99 | size_t nrf52832_get_used_sram(NRF52832_t *); 100 | size_t nrf52832_get_sram_size(NRF52832_t *); 101 | uint64_t nrf52832_get_cycle_counter(NRF52832_t *); 102 | 103 | bool nrf52832_flash_write(NRF52832_t *, uint32_t addr, uint8_t value); 104 | 105 | void nrf52832_reload_state(NRF52832_t *); 106 | -------------------------------------------------------------------------------- /include/pcap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef struct pcap_t pcap_t; 7 | 8 | pcap_t *pcap_create(const char *path); 9 | void pcap_destroy(pcap_t *); 10 | 11 | void pcap_write_packet(pcap_t *, const uint8_t *data, size_t length); 12 | -------------------------------------------------------------------------------- /include/peripherals/dcb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | PERIPHERAL(DCB, dcb, state_store_t *store) 6 | -------------------------------------------------------------------------------- /include/peripherals/dwt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | #define DWT_CYCCNTENA 0 6 | 7 | PERIPHERAL(DWT, dwt, state_store_t *store) 8 | 9 | void dwt_increment_cycle(DWT_t *dwt, unsigned int count); 10 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/ccm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | NRF52_PERIPHERAL(CCM, ccm) 6 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/clock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | NRF52_PERIPHERAL(CLOCK, clock) 6 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/comp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | NRF52_PERIPHERAL(COMP, comp) 6 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/easydma.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct 6 | { 7 | uint32_t ptr; // Data pointer 8 | uint32_t maxcnt; // Maximum number of bytes in buffer 9 | uint32_t amount; // Number of bytes transferred in the last transaction 10 | uint32_t list; // EasyDMA list type 11 | } easydma_reg_t; 12 | 13 | #define EASYDMA_CASES(periph) \ 14 | case 0x534: /*RXD.PTR*/ \ 15 | OP_RETURN_REG_RESULT((periph)->rx.ptr, WORD, MEMREG_RESULT_OK_CONTINUE); \ 16 | case 0x538: /*RXD.MAXCNT*/ \ 17 | OP_RETURN_REG_RESULT((periph)->rx.maxcnt, WORD, MEMREG_RESULT_OK_CONTINUE); \ 18 | case 0x540: /*RXD.LIST*/ \ 19 | OP_RETURN_REG_RESULT((periph)->rx.list, WORD, MEMREG_RESULT_OK_CONTINUE); \ 20 | case 0x544: /*TXD.PTR*/ \ 21 | OP_RETURN_REG_RESULT((periph)->tx.ptr, WORD, MEMREG_RESULT_OK_CONTINUE); \ 22 | case 0x548: /*TXD.MAXCNT*/ \ 23 | OP_RETURN_REG_RESULT((periph)->tx.maxcnt, WORD, MEMREG_RESULT_OK_CONTINUE); \ 24 | case 0x550: /*TXD.LIST*/ \ 25 | OP_RETURN_REG_RESULT((periph)->tx.list, WORD, MEMREG_RESULT_OK_CONTINUE); 26 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/ecb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | NRF52_PERIPHERAL(ECB, ecb) 6 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/gpio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | #include "pins.h" 6 | 7 | NRF52_PERIPHERAL(GPIO, gpio) 8 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/gpiote.h: -------------------------------------------------------------------------------- 1 | #include "peripherals/peripheral.h" 2 | 3 | NRF52_PERIPHERAL(GPIOTE, gpiote) 4 | 5 | void gpiote_step(GPIOTE_t *); 6 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/nvmc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | NRF52_PERIPHERAL(NVMC, nvmc, uint8_t *data, size_t size) 6 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/power.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | typedef enum 6 | { 7 | RESETREASON_RESETPIN = 1 << 0, // Reset from pin-reset detected 8 | RESETREASON_DOG = 1 << 1, // Reset from watchdog detected 9 | RESETREASON_SREQ = 1 << 2, // Reset from soft reset detected 10 | RESETREASON_LOCKUP = 1 << 3, // Reset from CPU lock-up detected 11 | RESETREASON_OFF = 1 << 16, // Reset due to wake up from System OFF mode when wakeup is triggered from DETECT signal from GPIO 12 | RESETREASON_LPCOMP = 1 << 17, // Reset due to wake up from System OFF mode when wakeup is triggered from ANADETECT signal from LPCOMP 13 | RESETREASON_DIF = 1 << 18, // Reset due to wake up from System OFF mode when wakeup is triggered from entering into debug interface mode 14 | RESETREASON_NFC = 1 << 19, // Reset due to wake up from System OFF mode by NFC field detect 15 | } nrf_resetreason; 16 | 17 | NRF52_PERIPHERAL(POWER, power) 18 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/ppi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | typedef struct PPI_t PPI_t; 6 | 7 | typedef enum 8 | { 9 | SHORT_RADIO_READY_START = 0, 10 | SHORT_RADIO_END_DISABLE, 11 | SHORT_RADIO_DISABLED_TXEN, 12 | SHORT_RADIO_DISABLED_RXEN, 13 | SHORT_RADIO_ADDRESS_RSSISTART, 14 | SHORT_RADIO_END_START, 15 | SHORT_RADIO_ADDRESS_BCSTART, 16 | SHORT_RADIO_DISABLED_RSSISTOP, 17 | } ppi_short_t; 18 | 19 | // OPERATION(ppi); 20 | memreg_op_result_t ppi_operation(uint32_t base, uint32_t offset, uint32_t *value, memreg_op_t op, void *userdata); 21 | 22 | _Thread_local extern PPI_t *current_ppi; // TODO: Remove this and pass PPI instance to peripherals 23 | 24 | typedef void (*ppi_task_cb_t)(PPI_t *, uint8_t peripheral, uint8_t task, void *userdata); 25 | 26 | #define PPI_TASK_HANDLER(name) void name(PPI_t *ppi, uint8_t peripheral, uint8_t task, void *userdata) 27 | 28 | #define TASK_ID(offset) (((offset) & 0xFF) / 4) 29 | #define EVENT_ID(offset) (((offset) & 0xFF) / 4) 30 | 31 | PPI_t *ppi_new(cpu_t **cpu); 32 | 33 | void ppi_add_peripheral(PPI_t *, uint8_t id, ppi_task_cb_t cb, void *userdata); 34 | void ppi_replace_peripheral(PPI_t *, uint8_t id, ppi_task_cb_t cb, void *userdata); 35 | void ppi_remove_peripheral(PPI_t *, uint8_t id); 36 | void ppi_fire_task(PPI_t *, uint8_t peripheral_id, uint8_t task_id); 37 | 38 | void ppi_fire_event(PPI_t *, uint8_t peripheral_id, uint8_t event_id, bool pend_exception); 39 | void ppi_clear_event(PPI_t *, uint8_t peripheral_id, uint8_t event_id); 40 | bool ppi_event_is_set(PPI_t *, uint8_t peripheral_id, uint8_t event_id); 41 | 42 | void ppi_shorts_set_enabled(PPI_t *, ppi_short_t short_id, bool enabled); 43 | bool ppi_shorts_is_enabled(PPI_t *, ppi_short_t short_id); 44 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/pwm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | NRF52_PERIPHERAL(PWM, pwm) 6 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/radio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pcap.h" 4 | #include "peripherals/peripheral.h" 5 | 6 | enum 7 | { 8 | RADIO_TASKS_TXEN = 0x000, 9 | RADIO_TASKS_RXEN = 0x004, 10 | RADIO_TASKS_START = 0x008, 11 | RADIO_TASKS_STOP = 0x00C, 12 | RADIO_TASKS_DISABLE = 0x010, 13 | RADIO_TASKS_RSSISTART = 0x014, 14 | RADIO_TASKS_RSSISTOP = 0x018, 15 | RADIO_TASKS_BCSTART = 0x01C, 16 | RADIO_TASKS_BCSTOP = 0x020, 17 | RADIO_EVENTS_READY = 0x100, 18 | RADIO_EVENTS_ADDRESS = 0x104, 19 | RADIO_EVENTS_PAYLOAD = 0x108, 20 | RADIO_EVENTS_END = 0x10C, 21 | RADIO_EVENTS_DISABLED = 0x110, 22 | RADIO_EVENTS_DEVMATCH = 0x114, 23 | RADIO_EVENTS_DEVMISS = 0x118, 24 | RADIO_EVENTS_RSSIEND = 0x11C, 25 | RADIO_EVENTS_BCMATCH = 0x128, 26 | RADIO_EVENTS_CRCOK = 0x130, 27 | RADIO_EVENTS_CRCERROR = 0x134, 28 | }; 29 | 30 | NRF52_PERIPHERAL(RADIO, radio) 31 | 32 | #if ENABLE_PCAP 33 | void radio_set_pcap(RADIO_t *, pcap_t *pcap); 34 | #endif 35 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/rng.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | NRF52_PERIPHERAL(RNG, rng) 6 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/rtc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | #include "cpu.h" 6 | 7 | #define RTC_MAX_CC 4 8 | 9 | enum 10 | { 11 | RTC_TASKS_START = 0x000, 12 | RTC_TASKS_STOP = 0x004, 13 | RTC_TASKS_CLEAR = 0x008, 14 | RTC_TASKS_TRIGOVRFLW = 0x00C, 15 | RTC_EVENTS_TICK = 0x100, 16 | RTC_EVENTS_OVRFLW = 0x104, 17 | RTC_EVENTS_COMPARE0 = 0x140, 18 | RTC_EVENTS_COMPARE1 = 0x144, 19 | RTC_EVENTS_COMPARE2 = 0x148, 20 | RTC_EVENTS_COMPARE3 = 0x14C, 21 | }; 22 | 23 | NRF52_PERIPHERAL(RTC, rtc, size_t cc_num) 24 | 25 | uint32_t rtc_is_running(RTC_t *); 26 | uint32_t rtc_get_counter(RTC_t *); 27 | double rtc_get_tick_interval_us(RTC_t *); 28 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/saadc.h: -------------------------------------------------------------------------------- 1 | #include "peripherals/peripheral.h" 2 | 3 | #include "pins.h" 4 | 5 | NRF52_PERIPHERAL(SAADC, saadc) 6 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/spi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | #define SPI_ENABLE_VALUE 1 6 | 7 | NRF52_PERIPHERAL(SPI, spi) 8 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/spim.h: -------------------------------------------------------------------------------- 1 | #include "peripherals/peripheral.h" 2 | 3 | #include "bus_spi.h" 4 | 5 | #define SPIM_ENABLE_VALUE 7 6 | 7 | NRF52_PERIPHERAL(SPIM, spim) 8 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/temp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | NRF52_PERIPHERAL(TEMP, temp) 6 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | #define TIMER_MAX_CC 6 6 | 7 | enum 8 | { 9 | TIMER_TASKS_START = 0x000, 10 | TIMER_TASKS_STOP = 0x004, 11 | TIMER_TASKS_COUNT = 0x008, 12 | TIMER_TASKS_CLEAR = 0x00C, 13 | TIMER_TASKS_SHUTDOWN = 0x010, 14 | TIMER_TASKS_CAPTURE0 = 0x040, 15 | TIMER_TASKS_CAPTURE1 = 0x044, 16 | TIMER_TASKS_CAPTURE2 = 0x048, 17 | TIMER_TASKS_CAPTURE3 = 0x04C, 18 | TIMER_TASKS_CAPTURE4 = 0x050, 19 | TIMER_TASKS_CAPTURE5 = 0x054, 20 | TIMER_EVENTS_COMPARE0 = 0x140, 21 | TIMER_EVENTS_COMPARE1 = 0x144, 22 | TIMER_EVENTS_COMPARE2 = 0x148, 23 | TIMER_EVENTS_COMPARE3 = 0x14C, 24 | TIMER_EVENTS_COMPARE4 = 0x150, 25 | TIMER_EVENTS_COMPARE5 = 0x154, 26 | }; 27 | 28 | NRF52_PERIPHERAL(TIMER, timer, size_t cc_num) 29 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/twim.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | #include "bus_i2c.h" 5 | 6 | #define TWIM_ENABLE_VALUE 6 7 | 8 | NRF52_PERIPHERAL(TWIM, twim) 9 | -------------------------------------------------------------------------------- /include/peripherals/nrf52832/wdt.h: -------------------------------------------------------------------------------- 1 | #include "peripherals/peripheral.h" 2 | 3 | NRF52_PERIPHERAL(WDT, wdt) 4 | -------------------------------------------------------------------------------- /include/peripherals/nvic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripherals/peripheral.h" 4 | 5 | #include "cpu.h" 6 | 7 | PERIPHERAL(NVIC, nvic, cpu_t *cpu, state_store_t *store, size_t priority_bits) -------------------------------------------------------------------------------- /include/peripherals/peripheral.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "bus_i2c.h" 7 | #include "bus_spi.h" 8 | #include "cpu.h" 9 | #include "dma.h" 10 | #include "memory.h" 11 | #include "pins.h" 12 | #include "ticker.h" 13 | #include "state_store.h" 14 | 15 | typedef struct 16 | { 17 | uint8_t id; 18 | cpu_t **cpu; 19 | pins_t *pins; 20 | struct PPI_t *ppi; 21 | ticker_t *ticker; 22 | bus_i2c_t *i2c; 23 | bus_spi_t *spi; 24 | dma_t *dma; 25 | state_store_t *state_store; 26 | } nrf52_peripheral_context_t; 27 | 28 | #define OPERATION(name) memreg_op_result_t name##_operation(uint32_t base, uint32_t offset, uint32_t *value, memreg_op_t op, void *userdata) 29 | 30 | #define NRF52_PERIPHERAL_CONSTRUCTOR(type, name, ...) type##_t *name##_new(nrf52_peripheral_context_t ctx, ##__VA_ARGS__) 31 | 32 | #define PERIPHERAL(type, name, ...) \ 33 | typedef struct type##_inst_t type##_t; \ 34 | OPERATION(name); \ 35 | type##_t *name##_new(__VA_ARGS__); 36 | 37 | #define NRF52_PERIPHERAL(type, name, ...) \ 38 | typedef struct type##_inst_t type##_t; \ 39 | OPERATION(name); \ 40 | type##_t *name##_new(nrf52_peripheral_context_t ctx, ##__VA_ARGS__); 41 | 42 | #define NEW_PERIPH(chip, type, name, field, addr, size, ...) \ 43 | (chip)->field = name##_new(__VA_ARGS__); \ 44 | memory_map_add_region((chip)->mem, memreg_new_operation(addr, size, name##_operation, (chip)->field)); 45 | 46 | #define OP_TASK_RESULT(offset, result) \ 47 | case offset: \ 48 | if (OP_IS_READ(op)) \ 49 | *value = 0; \ 50 | else if (*value) \ 51 | ppi_fire_task(current_ppi, (base & 0xFF000) >> 12, TASK_ID(offset)); \ 52 | return result; 53 | 54 | #define OP_TASK(offset) OP_TASK_RESULT(offset, MEMREG_RESULT_OK) 55 | 56 | #define OP_EVENT_RESULT(offset, result) \ 57 | case offset: \ 58 | if (OP_IS_READ(op)) \ 59 | *value = ppi_event_is_set(current_ppi, (base & 0xFF000) >> 12, EVENT_ID(offset)) ? 1 : 0; \ 60 | else if (*value == 0) \ 61 | ppi_clear_event(current_ppi, (base & 0xFF000) >> 12, EVENT_ID(offset)); \ 62 | return result; 63 | 64 | #define OP_EVENT(offset) OP_EVENT_RESULT(offset, MEMREG_RESULT_OK) 65 | 66 | #define OP_RETURN_REG_SET(reg, size) \ 67 | if (OP_IS_READ(op)) \ 68 | *value = reg; \ 69 | else \ 70 | reg |= *value; \ 71 | return MEMREG_RESULT_OK; 72 | 73 | #define OP_RETURN_REG_CLR(reg, size) \ 74 | if (OP_IS_READ(op)) \ 75 | *value = reg; \ 76 | else \ 77 | reg &= ~*value; \ 78 | return MEMREG_RESULT_OK; 79 | 80 | #define OP_INTEN(peripheral) \ 81 | case 0x300: \ 82 | OP_RETURN_REG(peripheral->inten.value, WORD); 83 | 84 | #define OP_INTENSET(peripheral) \ 85 | case 0x304: \ 86 | OP_RETURN_REG_SET(peripheral->inten.value, WORD) 87 | 88 | #define OP_INTENCLR(peripheral) \ 89 | case 0x308: \ 90 | OP_RETURN_REG_CLR(peripheral->inten.value, WORD) 91 | -------------------------------------------------------------------------------- /include/peripherals/scb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "peripherals/peripheral.h" 6 | #include "cpu.h" 7 | 8 | typedef union 9 | { 10 | struct 11 | { 12 | unsigned int NONBASETHRDENA : 1; 13 | unsigned int USERSETMPEND : 1; 14 | unsigned int : 1; 15 | unsigned int UNALIGN_TRP : 1; 16 | unsigned int DIV_0_TRP : 1; 17 | unsigned int : 3; 18 | unsigned int BFHFNMIGN : 3; 19 | unsigned int STKALIGN : 3; 20 | unsigned int : 6; 21 | unsigned int DC : 1; 22 | unsigned int IC : 1; 23 | unsigned int BP : 1; 24 | }; 25 | uint32_t value; 26 | } SCB_CCR_t; 27 | 28 | static_assert(sizeof(SCB_CCR_t) == 4, "SCB_CCR_t size is incorrect"); 29 | 30 | PERIPHERAL(SCB, scb, cpu_t *cpu, state_store_t *store) 31 | 32 | uint32_t scb_get_prigroup(SCB_t *); 33 | SCB_CCR_t scb_get_ccr(SCB_t *); 34 | uint32_t scb_get_cpacr(SCB_t *); 35 | uint32_t scb_get_vtor_tbloff(SCB_t *); 36 | -------------------------------------------------------------------------------- /include/peripherals/scb_fp.h: -------------------------------------------------------------------------------- 1 | #include "peripherals/peripheral.h" 2 | 3 | #include "arm.h" 4 | 5 | PERIPHERAL(SCB_FP, scb_fp, state_store_t *store) 6 | 7 | FPCCR_t scb_fp_get_fpccr(SCB_FP_t *scb_fp); 8 | uint32_t scb_fp_get_fpscr(SCB_FP_t *scb_fp); 9 | void scb_fp_set_fpscr(SCB_FP_t *scb_fp, uint32_t value); 10 | -------------------------------------------------------------------------------- /include/pinetime.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "nrf52832.h" 4 | #include "components/spi/spinorflash.h" 5 | #include "components/spi/st7789.h" 6 | #include "components/i2c/cst816s.h" 7 | #include "components/i2c/hrs3300.h" 8 | 9 | #define PINETIME_EXTFLASH_CS_PIN 5 10 | #define PINETIME_EXTFLASH_SIZE (4 * 1024 * 1024) 11 | #define PINETIME_EXTFLASH_SECTOR_SIZE (4 * 1024) 12 | #define PINETIME_LCD_CS_PIN 25 13 | #define PINETIME_CST816S_IRQ_PIN 28 14 | 15 | #define PINETIME_LCD_WIDTH 240 16 | #define PINETIME_LCD_HEIGHT 240 17 | 18 | #define PINETIME_CST816S_I2C_ADDR 0x15 19 | #define PINETIME_BMA425_I2C_ADDR 0x18 20 | #define PINETIME_HRS3300_I2C_ADDR 0x44 21 | 22 | typedef struct pinetime_t pinetime_t; 23 | 24 | pinetime_t *pinetime_new(const program_t *program); 25 | void pinetime_free(pinetime_t *); 26 | void pinetime_reset(pinetime_t *); 27 | int pinetime_step(pinetime_t *); 28 | 29 | NRF52832_t *pinetime_get_nrf52832(pinetime_t *); 30 | st7789_t *pinetime_get_st7789(pinetime_t *); 31 | cst816s_t *pinetime_get_cst816s(pinetime_t *); 32 | hrs3300_t *pinetime_get_hrs3300(pinetime_t *); 33 | spinorflash_t *pinetime_get_spinorflash(pinetime_t *pt); 34 | 35 | uint8_t *pinetime_save_state(pinetime_t *pt, size_t *size); 36 | bool pinetime_load_state(pinetime_t *pt, uint8_t *data, size_t size); 37 | -------------------------------------------------------------------------------- /include/pins.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PINS_COUNT 32 4 | 5 | #include "state_store.h" 6 | 7 | #include 8 | #include 9 | 10 | typedef struct pins_t pins_t; 11 | 12 | typedef enum 13 | { 14 | SENSE_DISABLED = 0, 15 | SENSE_HIGH = 2, 16 | SENSE_LOW = 3 17 | } pinsense_t; 18 | 19 | typedef enum 20 | { 21 | PIN_INPUT = 0, 22 | PIN_OUTPUT = 1, 23 | PIN_PULLDOWN = 2, 24 | PIN_PULLUP = 4, 25 | } pindir_t; 26 | 27 | typedef enum 28 | { 29 | PINOWNER_NONE = 0, 30 | PINOWNER_GPIOTE = 1 << 0, 31 | 32 | PINOWNER_ANY = ~0, 33 | } pinowner_t; 34 | 35 | #define pins_is_input(pins, pin) ((pins_get_dir(pins, pin) & 1) == 0) 36 | #define pins_set_input(pins, pin) pins_set_dir(pins, pin, pins_get_dir(pins, pin) & ~PIN_OUTPUT) 37 | #define pins_set_output(pins, pin) pins_set_dir(pins, pin, pins_get_dir(pins, pin) | PIN_OUTPUT) 38 | 39 | pins_t *pins_new(state_store_t *store, uint16_t high_voltage_mv, uint16_t high_threshold_mv); 40 | void pins_free(pins_t *); 41 | 42 | void pins_reset(pins_t *); 43 | 44 | void pins_set(pins_t *, int pin); 45 | void pins_clear(pins_t *, int pin); 46 | void pins_toggle(pins_t *, int pin); 47 | void pins_set_voltage(pins_t *pins, int pin, uint16_t mv); 48 | 49 | bool pins_is_analog(pins_t *, int pin); 50 | void pins_set_analog(pins_t *, int pin, bool analog); 51 | 52 | pindir_t pins_get_dir(pins_t *, int pin); 53 | void pins_set_dir(pins_t *, int pin, pindir_t dir); 54 | 55 | void pins_set_sense(pins_t *, int pin, pinsense_t sense); 56 | pinsense_t pins_get_sense(pins_t *, int pin); 57 | 58 | uint32_t pins_get_latch(pins_t *); 59 | void pins_set_latch(pins_t *, uint32_t latch); 60 | 61 | bool pins_is_set(pins_t *, int pin); 62 | uint32_t pins_read_all(pins_t *); 63 | uint16_t pins_get_voltage(pins_t *, int pin); 64 | 65 | bool pins_acquire(pins_t *, int pin, pinowner_t owner); 66 | bool pins_release(pins_t *, int pin, pinowner_t owner); 67 | bool pins_is_owned(pins_t *, int pin, pinowner_t owner); 68 | -------------------------------------------------------------------------------- /include/program.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct program_t program_t; 8 | 9 | program_t *program_new(size_t size); 10 | void program_free(program_t *); 11 | 12 | size_t program_size(const program_t *); 13 | void program_write_to(const program_t *, uint8_t *data, size_t size); 14 | 15 | void program_load(program_t *, size_t offset, const uint8_t *data, size_t size); 16 | void program_load_binary(program_t *, size_t offset, const uint8_t *data, size_t size); 17 | bool program_load_elf(program_t *, size_t offset, const uint8_t *data, size_t size); 18 | 19 | bool program_find_symbol(const program_t *program, const char *name, size_t *address, size_t *size); 20 | -------------------------------------------------------------------------------- /include/pseudocode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | uint32_t AddWithCarry(uint32_t x, uint32_t y, bool *carry, bool *overflow) 9 | { 10 | uint64_t unsigned_sum = (uint64_t)x + (uint64_t)y + (uint64_t)(*carry ? 1 : 0); 11 | int64_t signed_sum = (int64_t)(int32_t)x + (int64_t)(int32_t)y + (int64_t)(*carry ? 1 : 0); 12 | 13 | uint32_t result = unsigned_sum & 0xFFFFFFFF; 14 | assert(result == (signed_sum & 0xFFFFFFFF)); 15 | 16 | *carry = result != unsigned_sum; 17 | *overflow = (int64_t)(int32_t)result != signed_sum; 18 | 19 | return result; 20 | } 21 | 22 | uint32_t Shift_C(uint32_t value, arm_shifter type, uint32_t amount, bool *carry) 23 | { 24 | if (type == ARM_SFT_RRX) 25 | { 26 | uint32_t result = (value >> 1) | (*carry << 31); 27 | *carry = value & 1; 28 | return result; 29 | } 30 | 31 | if (amount == 0) 32 | return value; 33 | 34 | // assert(amount < 32); 35 | 36 | switch (type) 37 | { 38 | case ARM_SFT_LSL: 39 | assert(amount < 32); 40 | 41 | *carry = (value >> (32 - amount)) & 1; 42 | return value << amount; 43 | 44 | case ARM_SFT_LSR: 45 | assert(amount < 32); 46 | 47 | *carry = (value >> (amount - 1)) & 1; 48 | return value >> amount; 49 | 50 | case ARM_SFT_ASR: 51 | if (amount > 31) 52 | { 53 | *carry = value >> 31; 54 | return (int32_t)value >> 31; 55 | } 56 | 57 | *carry = (value >> (amount - 1)) & 1; 58 | return (int32_t)value >> amount; 59 | 60 | case ARM_SFT_ROR: 61 | *carry = (value >> (amount - 1)) & 1; 62 | return (value >> amount) | (value << (32 - amount)); 63 | 64 | default: 65 | fprintf(stderr, "Unhandled shift type %d\n", type); 66 | abort(); 67 | } 68 | } 69 | 70 | bool UnsignedSatQ(int32_t i, uint32_t n, uint32_t *result) 71 | { 72 | int64_t i64 = (int64_t)i; 73 | 74 | if (i64 > (1 << n) - 1) 75 | { 76 | *result = (1 << n) - 1; 77 | return true; 78 | } 79 | else if (i64 < 0) 80 | { 81 | *result = 0; 82 | return true; 83 | } 84 | else 85 | { 86 | *result = i; 87 | return false; 88 | } 89 | } 90 | 91 | // Capstone doesn't provide the carry bit so we must calculate it ourselves 92 | static inline bool CalculateThumbExpandCarry(uint8_t *bytes, uint32_t imm32, bool carry_in) 93 | { 94 | bool bit1 = (bytes[1] & (1 << 2)) != 0; 95 | bool bit2 = (bytes[3] & (1 << 6)) != 0; 96 | 97 | if (bit1 || bit2) 98 | return (imm32 & (1 << 31)) != 0; 99 | 100 | return carry_in; 101 | } 102 | -------------------------------------------------------------------------------- /include/runlog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if ENABLE_RUNLOG 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "memory.h" 10 | #include "program.h" 11 | 12 | typedef struct runlog_t runlog_t; 13 | 14 | typedef enum __attribute__((packed)) 15 | { 16 | RUNLOG_REG_R0, 17 | RUNLOG_REG_R1, 18 | RUNLOG_REG_R2, 19 | RUNLOG_REG_R3, 20 | RUNLOG_REG_R4, 21 | RUNLOG_REG_R5, 22 | RUNLOG_REG_R6, 23 | RUNLOG_REG_R7, 24 | RUNLOG_REG_R8, 25 | RUNLOG_REG_R9, 26 | RUNLOG_REG_R10, 27 | RUNLOG_REG_R11, 28 | RUNLOG_REG_R12, 29 | RUNLOG_REG_SP, 30 | RUNLOG_REG_LR, 31 | RUNLOG_REG_PC, 32 | RUNLOG_REG_XPSR, 33 | RUNLOG_REG_MSP, 34 | RUNLOG_REG_PSP, 35 | 36 | RUNLOG_REG_MIN = RUNLOG_REG_R0, 37 | RUNLOG_REG_MAX = RUNLOG_REG_PSP, 38 | 39 | RUNLOG_REG_UNKNOWN = 255, 40 | } runlog_register_t; 41 | 42 | typedef struct 43 | { 44 | uint32_t core[RUNLOG_REG_MAX + 1]; 45 | } runlog_registers_t; 46 | 47 | runlog_t *runlog_new(FILE *file); 48 | void runlog_free(runlog_t *); 49 | 50 | void runlog_record_reset(runlog_t *, runlog_registers_t regs); 51 | void runlog_record_load_program(runlog_t *, program_t *program); 52 | void runlog_record_fetch(runlog_t *, uint32_t pc); 53 | void runlog_record_execute(runlog_t *, runlog_registers_t regs); 54 | void runlog_record_memory_load(runlog_t *, uint32_t addr, uint32_t value, runlog_register_t dst, byte_size_t size); 55 | void runlog_record_memory_store(runlog_t *, runlog_register_t src, uint32_t value, uint32_t addr, byte_size_t size); 56 | void runlog_exception_enter(runlog_t *, uint16_t ex_num); 57 | void runlog_exception_exit(runlog_t *, uint16_t ex_num); 58 | 59 | #endif // ENABLE_RUNLOG 60 | -------------------------------------------------------------------------------- /include/scheduler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef int (*scheduler_cb_t)(void *userdata); 7 | 8 | typedef struct scheduler_t scheduler_t; 9 | 10 | scheduler_t *scheduler_new(scheduler_cb_t cb, void *userdata, size_t target_hz); 11 | void scheduler_run(scheduler_t *); 12 | void scheduler_stop(scheduler_t *); 13 | void scheduler_set_frequency(scheduler_t *, size_t target_hz); 14 | -------------------------------------------------------------------------------- /include/segger_rtt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "memory.h" 4 | 5 | typedef struct rtt_inst_t rtt_t; 6 | 7 | typedef void (*rtt_output_t)(const char *msg, void *userdata); 8 | 9 | rtt_t *rtt_new(memory_map_t *mem); 10 | void rtt_free(rtt_t *rtt); 11 | bool rtt_find_control(rtt_t *rtt); 12 | size_t rtt_flush_buffers(rtt_t *rtt, char *buffer, size_t buffer_size); 13 | -------------------------------------------------------------------------------- /include/state_store.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct state_store_t state_store_t; 8 | typedef uint16_t state_key_t; 9 | 10 | #define PERIPHERAL_KEY(id) (0xFF00 | (id)) 11 | 12 | enum 13 | { 14 | STATE_KEY_CPU = 1, 15 | STATE_KEY_MEMORY, 16 | STATE_KEY_PINS, 17 | STATE_KEY_BUS_SPI, 18 | STATE_KEY_DCB, 19 | STATE_KEY_DWT, 20 | STATE_KEY_NVIC, 21 | STATE_KEY_SCB_FP, 22 | STATE_KEY_SCB, 23 | STATE_KEY_BMA425, 24 | STATE_KEY_CST816S, 25 | STATE_KEY_HRS3300, 26 | STATE_KEY_SPINORFLASH, 27 | STATE_KEY_ST7789, 28 | 29 | STATE_KEY_POWER = 0x0100, 30 | STATE_KEY_CLOCK, 31 | 32 | STATE_KEY_SPIM0 = 0x0200, 33 | STATE_KEY_TWIM0 = 0x0300, 34 | }; 35 | 36 | state_store_t *state_store_new(); 37 | void state_store_free(state_store_t *); 38 | 39 | void state_store_register(state_store_t *store, state_key_t key, void *data, size_t size); 40 | void state_store_freeze(state_store_t *); 41 | 42 | uint8_t *state_store_save(state_store_t *, size_t *size); 43 | bool state_store_load(state_store_t *, const uint8_t *data, size_t size); 44 | -------------------------------------------------------------------------------- /include/ticker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef struct ticker_t ticker_t; 7 | 8 | typedef enum 9 | { 10 | CLOCK_HFCLK, 11 | CLOCK_LFCLK, 12 | } clock_type; 13 | 14 | typedef void (*ticker_cb_t)(void *); 15 | 16 | ticker_t *ticker_new(int32_t lfclk_cycles); 17 | void ticker_free(ticker_t *); 18 | void ticker_reset(ticker_t *); 19 | 20 | void ticker_hftick(ticker_t *, unsigned int count); 21 | void ticker_lftick(ticker_t *); 22 | 23 | void ticker_add(ticker_t *, clock_type clock, ticker_cb_t cb, void *userdata, uint32_t interval, bool auto_reload); 24 | void ticker_remove(ticker_t *, clock_type clock, ticker_cb_t cb); 25 | -------------------------------------------------------------------------------- /include/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define macro_string2(x) #x 9 | #define macro_string(x) macro_string2(x) 10 | 11 | static inline uint8_t *read_file_u8(const char *path, size_t *size) 12 | { 13 | FILE *f = fopen(path, "rb"); 14 | if (f == NULL) 15 | { 16 | fprintf(stderr, "Failed to open %s\n", path); 17 | return NULL; 18 | } 19 | 20 | fseek(f, 0, SEEK_END); 21 | long fsize = ftell(f); 22 | fseek(f, 0, SEEK_SET); 23 | 24 | uint8_t *data = malloc(fsize); 25 | if (fread(data, 1, fsize, f) != (size_t)fsize) 26 | { 27 | fprintf(stderr, "Failed to read %s\n", path); 28 | return NULL; 29 | } 30 | fclose(f); 31 | 32 | *size = fsize; 33 | return data; 34 | } 35 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipe01/InfiniEmu/e87555a05d6d8a17cab1c1c5e9e967bb6a0240d7/src/.gitignore -------------------------------------------------------------------------------- /src/bus_i2c.c: -------------------------------------------------------------------------------- 1 | #include "bus_i2c.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "arm.h" 7 | #include "fault.h" 8 | 9 | #define MAX_SLAVES 256 10 | 11 | struct bus_i2c_t 12 | { 13 | uint8_t *ram; 14 | size_t ram_size; 15 | 16 | i2c_slave_t *slaves[MAX_SLAVES]; 17 | }; 18 | 19 | bus_i2c_t *i2c_new(uint8_t *ram, size_t ram_size) 20 | { 21 | bus_i2c_t *i2c = (bus_i2c_t *)calloc(1, sizeof(bus_i2c_t)); 22 | i2c->ram = ram; 23 | i2c->ram_size = ram_size; 24 | return i2c; 25 | } 26 | 27 | void i2c_reset(bus_i2c_t *i2c) 28 | { 29 | for (size_t i = 0; i < MAX_SLAVES; i++) 30 | { 31 | if (i2c->slaves[i]) 32 | i2c->slaves[i]->reset(i2c->slaves[i]->userdata); 33 | } 34 | } 35 | 36 | void i2c_free(bus_i2c_t *i2c) 37 | { 38 | for (size_t i = 0; i < MAX_SLAVES; i++) 39 | { 40 | if (i2c->slaves[i]) 41 | free(i2c->slaves[i]); 42 | } 43 | 44 | free(i2c); 45 | } 46 | 47 | void i2c_add_slave(bus_i2c_t *i2c, uint8_t address, i2c_slave_t slave) 48 | { 49 | if (i2c->slaves[address]) 50 | fault_take(FAULT_I2C_DUPLICATE_ADDRESS); 51 | 52 | i2c_slave_t *copy = malloc(sizeof(i2c_slave_t)); 53 | memcpy(copy, &slave, sizeof(i2c_slave_t)); 54 | 55 | i2c->slaves[address] = copy; 56 | } 57 | 58 | void i2c_write(bus_i2c_t *i2c, uint8_t address, uint32_t data_address, size_t data_size) 59 | { 60 | if (!i2c->slaves[address]) 61 | fault_take(FAULT_I2C_UNKNOWN_ADDRESS); 62 | 63 | if (data_address < ARM_SRAM_START || data_address >= ARM_SRAM_END) // TODO: Check end too 64 | { 65 | printf("Invalid EasyDMA address 0x%08X\n", data_address); 66 | fault_take(FAULT_DMA_INVALID_ADDRESS); 67 | } 68 | 69 | uint32_t offset = data_address - ARM_SRAM_START; 70 | 71 | i2c->slaves[address]->write(i2c->ram + offset, data_size, i2c->slaves[address]->userdata); 72 | } 73 | 74 | size_t i2c_read(bus_i2c_t *i2c, uint8_t address, uint32_t data_address, size_t data_size) 75 | { 76 | if (!i2c->slaves[address]) 77 | fault_take(FAULT_I2C_UNKNOWN_ADDRESS); 78 | 79 | if (data_address < ARM_SRAM_START || data_address >= ARM_SRAM_END) // TODO: Check end too 80 | { 81 | printf("Invalid EasyDMA address 0x%08X\n", data_address); 82 | fault_take(FAULT_DMA_INVALID_ADDRESS); 83 | } 84 | 85 | uint32_t offset = data_address - ARM_SRAM_START; 86 | 87 | return i2c->slaves[address]->read(i2c->ram + offset, data_size, i2c->slaves[address]->userdata); 88 | } 89 | -------------------------------------------------------------------------------- /src/circular_buffer.c: -------------------------------------------------------------------------------- 1 | #include "circular_buffer.h" 2 | 3 | #include 4 | 5 | struct circular_buffer_t 6 | { 7 | uint8_t *data; 8 | size_t size; 9 | size_t head; 10 | size_t tail; 11 | size_t remaining; 12 | }; 13 | 14 | circular_buffer_t *circular_buffer_new(size_t size) 15 | { 16 | circular_buffer_t *buf = malloc(sizeof(circular_buffer_t)); 17 | buf->data = malloc(size); 18 | buf->size = size; 19 | buf->head = buf->tail = 0; 20 | buf->remaining = 0; 21 | 22 | return buf; 23 | } 24 | 25 | void circular_buffer_free(circular_buffer_t *buf) 26 | { 27 | free(buf->data); 28 | free(buf); 29 | } 30 | 31 | bool circular_buffer_read(circular_buffer_t *buf, uint8_t *data) 32 | { 33 | if (buf->head == buf->tail) 34 | return false; 35 | 36 | *data = buf->data[buf->tail]; 37 | buf->tail = (buf->tail + 1) % buf->size; 38 | buf->remaining--; 39 | 40 | return true; 41 | } 42 | 43 | bool circular_buffer_write(circular_buffer_t *buf, uint8_t data) 44 | { 45 | size_t next_head = (buf->head + 1) % buf->size; 46 | 47 | if (next_head == buf->tail) 48 | return false; 49 | 50 | buf->data[buf->head] = data; 51 | buf->head = next_head; 52 | buf->remaining++; 53 | 54 | return true; 55 | } 56 | 57 | void circular_buffer_clear(circular_buffer_t *buf) 58 | { 59 | buf->head = buf->tail = 0; 60 | } 61 | 62 | size_t circular_buffer_remaining(circular_buffer_t *buf) 63 | { 64 | return buf->remaining; 65 | } 66 | -------------------------------------------------------------------------------- /src/component/i2c/cst816s.c: -------------------------------------------------------------------------------- 1 | #include "components/i2c/cst816s.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "fault.h" 8 | 9 | #define CHIPID 0xB4 10 | #define VENDORID 0x00 11 | #define FWVERSION 0x01 12 | 13 | #define MAX_READ_SIZE 50 14 | 15 | typedef struct __attribute__((packed)) 16 | { 17 | uint8_t _unknown; 18 | uint8_t gesture; 19 | uint8_t pointNum; 20 | uint8_t xHigh; 21 | uint8_t xLow; 22 | uint8_t yHigh; 23 | uint8_t yLow; 24 | } touchdata_t; 25 | 26 | struct cst816s_t 27 | { 28 | struct state 29 | { 30 | uint8_t next_read[MAX_READ_SIZE]; 31 | size_t next_read_size; 32 | 33 | touchdata_t touchdata; 34 | bool has_touch; 35 | }; 36 | 37 | pins_t *pins; 38 | int irqPin; 39 | }; 40 | 41 | void cst816s_reset(void *userdata) 42 | { 43 | cst816s_t *cst816s = userdata; 44 | 45 | pins_set(cst816s->pins, cst816s->irqPin); // Active low 46 | } 47 | 48 | void cst816s_write(uint8_t *data, size_t data_size, void *userdata) 49 | { 50 | cst816s_t *cst816s = (cst816s_t *)userdata; 51 | 52 | assert(data_size >= 1); 53 | 54 | uint8_t reg = data[0]; 55 | 56 | switch (reg) 57 | { 58 | case 0x00: // Read touch data 59 | { 60 | if (cst816s->has_touch) 61 | { 62 | pins_set(cst816s->pins, cst816s->irqPin); 63 | cst816s->has_touch = false; 64 | } 65 | 66 | memcpy(cst816s->next_read, &cst816s->touchdata, sizeof(touchdata_t)); 67 | cst816s->next_read_size = sizeof(touchdata_t); 68 | break; 69 | } 70 | 71 | case 0x15: // Unknown 72 | // Do nothing 73 | break; 74 | 75 | case 0xA5: // Sleep? 76 | // Do nothing 77 | break; 78 | 79 | case 0xA7: // ChipID 80 | cst816s->next_read[0] = CHIPID; 81 | cst816s->next_read_size = 1; 82 | cst816s->next_read_size = 1; 83 | break; 84 | 85 | case 0xA8: // VendorID 86 | cst816s->next_read[0] = VENDORID; 87 | cst816s->next_read_size = 1; 88 | break; 89 | 90 | case 0xA9: // FWVersion 91 | cst816s->next_read[0] = FWVERSION; 92 | cst816s->next_read_size = 1; 93 | break; 94 | 95 | case 0xEC: // MotionMask 96 | // Do nothing 97 | break; 98 | 99 | case 0xFA: // IrqCtl 100 | // Do nothing 101 | break; 102 | 103 | default: 104 | fault_take(FAULT_I2C_UNKNOWN_COMMAND); 105 | } 106 | } 107 | 108 | size_t cst816s_read(uint8_t *data, size_t data_size, void *userdata) 109 | { 110 | cst816s_t *cst816s = (cst816s_t *)userdata; 111 | 112 | if (data_size > cst816s->next_read_size) 113 | return 0; 114 | 115 | memcpy(data, cst816s->next_read, data_size); 116 | 117 | return data_size; 118 | } 119 | 120 | cst816s_t *cst816s_new(pins_t *pins, state_store_t *store, int irqPin) 121 | { 122 | cst816s_t *cst816s = calloc(1, sizeof(cst816s_t)); 123 | cst816s->pins = pins; 124 | cst816s->irqPin = irqPin; 125 | 126 | state_store_register(store, STATE_KEY_CST816S, cst816s, sizeof(struct state)); 127 | 128 | return cst816s; 129 | } 130 | 131 | i2c_slave_t cst816s_get_slave(cst816s_t *cst816s) 132 | { 133 | return (i2c_slave_t){ 134 | .userdata = cst816s, 135 | .write = cst816s_write, 136 | .read = cst816s_read, 137 | .reset = cst816s_reset, 138 | }; 139 | } 140 | 141 | void cst816s_do_touch(cst816s_t *cst, touch_gesture_t gesture, uint16_t x, uint16_t y) 142 | { 143 | cst->touchdata = (touchdata_t){ 144 | .gesture = gesture, 145 | .pointNum = 1, 146 | .xHigh = (x >> 8) & 0xFF, 147 | .xLow = x & 0xFF, 148 | .yHigh = (y >> 8) & 0xFF, 149 | .yLow = y & 0xFF, 150 | }; 151 | cst->has_touch = true; 152 | 153 | pins_clear(cst->pins, cst->irqPin); 154 | } 155 | 156 | void cst816s_release_touch(cst816s_t *cst) 157 | { 158 | cst->touchdata = (touchdata_t){ 159 | .gesture = GESTURE_NONE, 160 | .pointNum = 0, 161 | }; 162 | cst->has_touch = true; 163 | 164 | pins_clear(cst->pins, cst->irqPin); 165 | } 166 | -------------------------------------------------------------------------------- /src/component/i2c/hrs3300.c: -------------------------------------------------------------------------------- 1 | #include "components/i2c/hrs3300.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "fault.h" 9 | 10 | #define MAX_READ_SIZE 100 11 | 12 | struct hrs3300_t 13 | { 14 | uint8_t enable, pdriver, res, hgain; 15 | 16 | uint8_t next_read[MAX_READ_SIZE]; 17 | size_t next_read_size; 18 | 19 | uint32_t ch0, ch1; 20 | }; 21 | 22 | void hrs3300_reset(void *userdata) 23 | { 24 | hrs3300_t *hrs3300 = (hrs3300_t *)userdata; 25 | hrs3300->enable = 0x68; 26 | } 27 | 28 | void hrs3300_write(uint8_t *data, size_t data_size, void *userdata) 29 | { 30 | hrs3300_t *hrs3300 = (hrs3300_t *)userdata; 31 | 32 | uint8_t reg = data[0]; 33 | 34 | switch (reg) 35 | { 36 | case 0x01: // ENABLE 37 | RETURN_REG(hrs3300, enable); 38 | break; 39 | 40 | case 0x08: // C1DATAM 41 | assert_fault(data_size == 1, FAULT_I2C_INVALID_DATA); 42 | hrs3300->next_read[0] = (hrs3300->ch1 >> 3) & 0xFF; 43 | hrs3300->next_read_size = 1; 44 | break; 45 | 46 | case 0x09: // C0DATAM 47 | assert_fault(data_size == 1, FAULT_I2C_INVALID_DATA); 48 | hrs3300->next_read[0] = (hrs3300->ch0 >> 8) & 0xFF; 49 | hrs3300->next_read_size = 1; 50 | break; 51 | 52 | case 0x0A: // C0DATAH 53 | assert_fault(data_size == 1, FAULT_I2C_INVALID_DATA); 54 | hrs3300->next_read[0] = (hrs3300->ch0 >> 4) & 0xF; 55 | hrs3300->next_read_size = 1; 56 | break; 57 | 58 | case 0x0C: // PDRIVER 59 | RETURN_REG(hrs3300, pdriver); 60 | break; 61 | 62 | case 0x0D: // C1DATAH 63 | assert_fault(data_size == 1, FAULT_I2C_INVALID_DATA); 64 | hrs3300->next_read[0] = (hrs3300->ch1 >> 11) & 0x7F; 65 | hrs3300->next_read_size = 1; 66 | break; 67 | 68 | case 0x0E: // C1DATAL 69 | assert_fault(data_size == 1, FAULT_I2C_INVALID_DATA); 70 | hrs3300->next_read[0] = hrs3300->ch1 & 0x7; 71 | hrs3300->next_read_size = 1; 72 | break; 73 | 74 | case 0x0F: // C0DATAL 75 | assert_fault(data_size == 1, FAULT_I2C_INVALID_DATA); 76 | hrs3300->next_read[0] = (hrs3300->ch0 & 0xF) | (((hrs3300->ch0 >> 16) & 0x3) << 4); 77 | hrs3300->next_read_size = 1; 78 | break; 79 | 80 | case 0x16: // RES 81 | RETURN_REG(hrs3300, res); 82 | break; 83 | 84 | case 0x17: // HGAIN 85 | RETURN_REG(hrs3300, hgain); 86 | break; 87 | 88 | default: 89 | printf("hrs3300: unknown register 0x%02x\n", reg); 90 | fault_take(FAULT_I2C_UNKNOWN_COMMAND); 91 | } 92 | } 93 | 94 | size_t hrs3300_read(uint8_t *data, size_t data_size, void *userdata) 95 | { 96 | hrs3300_t *hrs3300 = (hrs3300_t *)userdata; 97 | 98 | assert(data_size >= hrs3300->next_read_size); 99 | 100 | memcpy(data, hrs3300->next_read, data_size); 101 | 102 | return data_size; 103 | } 104 | 105 | hrs3300_t *hrs3300_new(state_store_t *store) 106 | { 107 | hrs3300_t *hrs = calloc(1, sizeof(hrs3300_t)); 108 | 109 | state_store_register(store, STATE_KEY_HRS3300, hrs, sizeof(hrs3300_t)); 110 | 111 | return hrs; 112 | } 113 | 114 | i2c_slave_t hrs3300_get_slave(hrs3300_t *hrs3300) 115 | { 116 | return (i2c_slave_t){ 117 | .userdata = hrs3300, 118 | .write = hrs3300_write, 119 | .read = hrs3300_read, 120 | .reset = hrs3300_reset, 121 | }; 122 | } 123 | 124 | void hrs3300_set_ch0(hrs3300_t *hrs, uint32_t value) 125 | { 126 | hrs->ch0 = value & 0xFFFFFF; 127 | } 128 | 129 | void hrs3300_set_ch1(hrs3300_t *hrs, uint32_t value) 130 | { 131 | hrs->ch1 = value & 0xFFFFFF; 132 | } 133 | -------------------------------------------------------------------------------- /src/demangle.cpp: -------------------------------------------------------------------------------- 1 | #include "demangle.h" 2 | 3 | #include 4 | 5 | extern "C" 6 | char *demangle(const char *mangled) 7 | { 8 | int status; 9 | return abi::__cxa_demangle(mangled, NULL, NULL, &status); 10 | } 11 | -------------------------------------------------------------------------------- /src/dma.c: -------------------------------------------------------------------------------- 1 | #include "dma.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct dma_t 9 | { 10 | uint32_t start_addr; 11 | uint8_t *ram; 12 | size_t ram_size; 13 | }; 14 | 15 | dma_t *dma_new(uint32_t start_addr, uint8_t *ram, size_t ram_size) 16 | { 17 | dma_t *edma = malloc(sizeof(dma_t)); 18 | edma->start_addr = start_addr; 19 | edma->ram = ram; 20 | edma->ram_size = ram_size; 21 | 22 | return edma; 23 | } 24 | 25 | void dma_free(dma_t *edma) 26 | { 27 | free(edma); 28 | } 29 | 30 | void dma_read(dma_t *edma, uint32_t addr, size_t count, uint8_t *data) 31 | { 32 | assert(addr >= edma->start_addr); 33 | uint32_t offset = addr - edma->start_addr; 34 | assert(offset + count <= edma->ram_size); 35 | 36 | memcpy(data, edma->ram + offset, count); 37 | } 38 | 39 | void dma_write(dma_t *edma, uint32_t addr, size_t count, const uint8_t *data) 40 | { 41 | assert(addr >= edma->start_addr); 42 | uint32_t offset = addr - edma->start_addr; 43 | assert(offset + count <= edma->ram_size); 44 | 45 | memcpy(edma->ram + offset, data, count); 46 | } 47 | -------------------------------------------------------------------------------- /src/fault.c: -------------------------------------------------------------------------------- 1 | #include "fault.h" 2 | 3 | #include 4 | #include 5 | 6 | _Thread_local jmp_buf *current_jmp = NULL; 7 | 8 | void fault_set_jmp(jmp_buf *buf) 9 | { 10 | current_jmp = buf; 11 | } 12 | 13 | void fault_clear_jmp() 14 | { 15 | current_jmp = NULL; 16 | } 17 | 18 | void fault_take_(fault_type_t t) 19 | { 20 | fflush(NULL); 21 | 22 | if (current_jmp) 23 | longjmp(*current_jmp, t); 24 | else 25 | abort(); 26 | } 27 | -------------------------------------------------------------------------------- /src/lua.c: -------------------------------------------------------------------------------- 1 | #include "lua.h" 2 | #include "lualibs/lualibs.h" 3 | 4 | #include "util.h" 5 | 6 | void run_lua(const char *script, size_t script_size, const char *name) 7 | { 8 | lua_State *L = luaL_newstate(); 9 | 10 | luaL_openselectedlibs(L, LUA_GLIBK | LUA_MATHLIBK | LUA_TABLIBK | LUA_STRLIBK, 0); 11 | 12 | luaopen_display(L); 13 | luaopen_pinetime(L); 14 | luaopen_image(L); 15 | luaopen_touch(L); 16 | 17 | if (luaL_loadbuffer(L, script, script_size, name) || lua_pcall(L, 0, 0, 0)) 18 | { 19 | fprintf(stderr, "Failed to run Lua script: %s\n", lua_tostring(L, -1)); 20 | } 21 | 22 | // lua_close(L); 23 | } 24 | 25 | void run_lua_file(const char *script_path) 26 | { 27 | size_t script_size; 28 | uint8_t *script = read_file_u8(script_path, &script_size); 29 | 30 | if (script == NULL) 31 | { 32 | fprintf(stderr, "Failed to read Lua script: %s\n", script_path); 33 | return; 34 | } 35 | 36 | run_lua((const char *)script, script_size, script_path); 37 | 38 | free(script); 39 | } 40 | -------------------------------------------------------------------------------- /src/lualibs/lua_display.c: -------------------------------------------------------------------------------- 1 | #include "pinetime.h" 2 | 3 | #define LIB_NAME display 4 | #define DATA_TYPE st7789_t 5 | #include "lualibs/lualibs.h" 6 | 7 | #include "lualibs/lua_image.h" 8 | 9 | DEF_FN_PUBLIC(display_new) 10 | { 11 | if (!lua_isuserdata(L, 1)) 12 | luaL_error(L, "Invalid argument: expected userdata"); 13 | 14 | st7789_t **display = lua_newuserdata(L, sizeof(st7789_t **)); 15 | 16 | luaL_getmetatable(L, METATABLE); 17 | lua_setmetatable(L, -2); 18 | 19 | *display = lua_touserdata(L, 1); 20 | 21 | return 1; 22 | } 23 | 24 | DEF_FN(capture) 25 | { 26 | st7789_t *st = lua_getdata_p(L, 1); 27 | 28 | uint8_t screen_buffer[PINETIME_LCD_WIDTH * PINETIME_LCD_HEIGHT * BYTES_PER_PIXEL]; 29 | st7789_read_screen(st, screen_buffer, PINETIME_LCD_WIDTH, PINETIME_LCD_HEIGHT); 30 | 31 | lua_pushcclosure(L, l_image_new, 0); 32 | lua_pushinteger(L, PINETIME_LCD_WIDTH); 33 | lua_pushinteger(L, PINETIME_LCD_HEIGHT); 34 | lua_pushlightuserdata(L, screen_buffer); 35 | lua_call(L, 3, 1); 36 | 37 | return 1; 38 | } 39 | 40 | DEF_FUNCS{ 41 | END_FN, 42 | }; 43 | 44 | DEF_METHODS{ 45 | FN(capture), 46 | END_FN, 47 | }; 48 | 49 | DEF_LIB(display) 50 | -------------------------------------------------------------------------------- /src/lualibs/lua_pinetime.c: -------------------------------------------------------------------------------- 1 | #include "pinetime.h" 2 | 3 | #define LIB_NAME pinetime 4 | #define DATA_TYPE pinetime_t 5 | #include "lualibs/lualibs.h" 6 | #include "lualibs/lua_display.h" 7 | #include "lualibs/lua_touch.h" 8 | 9 | #include 10 | 11 | DEF_FN(new) 12 | { 13 | if (!lua_istable(L, 1)) 14 | luaL_error(L, "Invalid argument: expected table"); 15 | 16 | lua_pushstring(L, "firmware"); 17 | lua_gettable(L, 1); 18 | 19 | if (lua_isnil(L, -1)) 20 | luaL_error(L, "Invalid argument: expected 'firmware'"); 21 | 22 | const char *firmware_path = luaL_checkstring(L, -1); 23 | 24 | size_t flash_size = NRF52832_FLASH_SIZE; 25 | 26 | lua_pushstring(L, "flash_size"); 27 | lua_gettable(L, 1); 28 | if (!lua_isnil(L, -1)) 29 | flash_size = luaL_checkinteger(L, -1); 30 | 31 | program_t *program = program_new(flash_size); 32 | 33 | size_t firmware_size = 0; 34 | uint8_t *firmware = read_file_u8(firmware_path, &firmware_size); 35 | if (firmware == NULL) 36 | luaL_error(L, "Failed to read firmware: %s", firmware_path); 37 | 38 | program_load(program, 0, firmware, firmware_size); 39 | 40 | pinetime_t **pt = lua_newuserdata(L, sizeof(pinetime_t **)); 41 | 42 | luaL_getmetatable(L, METATABLE); 43 | lua_setmetatable(L, -2); 44 | 45 | *pt = pinetime_new(program); 46 | 47 | pinetime_reset(*pt); 48 | 49 | return 1; 50 | } 51 | 52 | DEF_FN(run) 53 | { 54 | pinetime_t *pt = lua_getdata_p(L, 1); 55 | 56 | int cycles = 0; 57 | 58 | if (lua_isnumber(L, 2)) 59 | { 60 | cycles = NRF52832_HFCLK_FREQUENCY * luaL_checknumber(L, 2); 61 | } 62 | else if (lua_istable(L, 2)) 63 | { 64 | lua_pushstring(L, "seconds"); 65 | lua_gettable(L, 2); 66 | if (!lua_isnil(L, -1)) 67 | { 68 | cycles = NRF52832_HFCLK_FREQUENCY * luaL_checknumber(L, -1); 69 | } 70 | else 71 | { 72 | lua_pushstring(L, "cycles"); 73 | lua_gettable(L, 2); 74 | 75 | if (lua_isnil(L, -1)) 76 | luaL_error(L, "Invalid argument: expected 'seconds' or 'cycles'"); 77 | 78 | cycles = luaL_checkinteger(L, -1); 79 | } 80 | } 81 | else 82 | { 83 | luaL_error(L, "Invalid argument: expected number or table"); 84 | } 85 | 86 | int rem_cycles = cycles; 87 | 88 | while (rem_cycles > 0) 89 | { 90 | rem_cycles -= pinetime_step(pt); 91 | } 92 | 93 | lua_pushinteger(L, cycles - rem_cycles); 94 | return 1; 95 | } 96 | 97 | DEF_FN(reset) 98 | { 99 | pinetime_t *pt = lua_getdata_p(L, 1); 100 | 101 | pinetime_reset(pt); 102 | 103 | return 0; 104 | } 105 | 106 | DEF_FN(__index) 107 | { 108 | pinetime_t *pt = lua_getdata_p(L, 1); 109 | 110 | luaL_argcheck(L, lua_isstring(L, 2), 2, "Expected string"); 111 | 112 | const char *key = lua_tostring(L, 2); 113 | 114 | if (strcmp(key, "display") == 0) 115 | { 116 | lua_pushcclosure(L, l_display_new, 0); 117 | lua_pushlightuserdata(L, pinetime_get_st7789(pt)); 118 | lua_call(L, 1, 1); 119 | return 1; 120 | } 121 | if (strcmp(key, "touch") == 0) 122 | { 123 | lua_pushcclosure(L, l_touch_new, 0); 124 | lua_pushlightuserdata(L, pinetime_get_cst816s(pt)); 125 | lua_call(L, 1, 1); 126 | return 1; 127 | } 128 | 129 | // Delegate to metatable 130 | lua_getmetatable(L, 1); 131 | lua_pushvalue(L, 2); 132 | lua_gettable(L, -2); 133 | 134 | return 1; 135 | } 136 | 137 | DEF_FN(__gc) 138 | { 139 | pinetime_t *pt = lua_getdata_p(L, 1); 140 | 141 | pinetime_free(pt); 142 | 143 | return 0; 144 | } 145 | 146 | DEF_FUNCS{ 147 | FN(new), 148 | END_FN, 149 | }; 150 | 151 | DEF_METHODS{ 152 | FN(run), 153 | FN(reset), 154 | FN(__index), 155 | FN(__gc), 156 | END_FN, 157 | }; 158 | 159 | DEF_LIB(pinetime) 160 | -------------------------------------------------------------------------------- /src/lualibs/lua_touch.c: -------------------------------------------------------------------------------- 1 | #include "pinetime.h" 2 | 3 | #define LIB_NAME touch 4 | #define DATA_TYPE cst816s_t 5 | #include "lualibs/lualibs.h" 6 | 7 | #include 8 | 9 | DEF_FN_PUBLIC(touch_new) 10 | { 11 | if (!lua_isuserdata(L, 1)) 12 | luaL_error(L, "Invalid argument: expected userdata"); 13 | 14 | cst816s_t **touch = lua_newuserdata(L, sizeof(cst816s_t **)); 15 | 16 | luaL_getmetatable(L, METATABLE); 17 | lua_setmetatable(L, -2); 18 | 19 | *touch = lua_touserdata(L, 1); 20 | 21 | return 1; 22 | } 23 | 24 | DEF_FN(swipe) 25 | { 26 | cst816s_t *ts = lua_getdata_p(L, 1); 27 | 28 | touch_gesture_t gesture = GESTURE_NONE; 29 | 30 | if (lua_isstring(L, 2)) 31 | { 32 | const char *gesture_str = lua_tostring(L, 2); 33 | 34 | if (strcmp(gesture_str, "down") == 0) 35 | gesture = GESTURE_SLIDEDOWN; 36 | else if (strcmp(gesture_str, "up") == 0) 37 | gesture = GESTURE_SLIDEUP; 38 | else if (strcmp(gesture_str, "left") == 0) 39 | gesture = GESTURE_SLIDELEFT; 40 | else if (strcmp(gesture_str, "right") == 0) 41 | gesture = GESTURE_SLIDERIGHT; 42 | else 43 | luaL_error(L, "Invalid argument: expected 'down', 'up', 'left' or 'right'"); 44 | } 45 | else 46 | { 47 | luaL_error(L, "Invalid argument: expected string"); 48 | } 49 | 50 | uint32_t x = 0, y = 0; 51 | 52 | if (lua_istable(L, 3)) 53 | { 54 | lua_pushinteger(L, 1); 55 | lua_gettable(L, 3); 56 | x = luaL_checkinteger(L, -1); 57 | 58 | lua_pushinteger(L, 2); 59 | lua_gettable(L, 3); 60 | y = luaL_checkinteger(L, -1); 61 | } 62 | 63 | cst816s_do_touch(ts, gesture, x, y); 64 | 65 | return 0; 66 | } 67 | 68 | DEF_FN(tap) 69 | { 70 | cst816s_t *ts = lua_getdata_p(L, 1); 71 | 72 | uint32_t x = luaL_checkinteger(L, 2); 73 | uint32_t y = luaL_checkinteger(L, 3); 74 | 75 | cst816s_do_touch(ts, GESTURE_SINGLETAP, x, y); 76 | 77 | return 0; 78 | } 79 | 80 | DEF_FN(release) 81 | { 82 | cst816s_t *ts = lua_getdata_p(L, 1); 83 | 84 | cst816s_release_touch(ts); 85 | 86 | return 0; 87 | } 88 | 89 | DEF_FUNCS{ 90 | END_FN, 91 | }; 92 | 93 | DEF_METHODS{ 94 | FN(swipe), 95 | FN(tap), 96 | FN(release), 97 | END_FN, 98 | }; 99 | 100 | DEF_LIB(touch) 101 | -------------------------------------------------------------------------------- /src/pcap.c: -------------------------------------------------------------------------------- 1 | #include "pcap.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ie_time.h" 10 | 11 | typedef struct pcap_hdr_s 12 | { 13 | uint32_t magic_number; /* magic number */ 14 | uint16_t version_major; /* major version number */ 15 | uint16_t version_minor; /* minor version number */ 16 | int32_t thiszone; /* GMT to local correction */ 17 | uint32_t sigfigs; /* accuracy of timestamps */ 18 | uint32_t snaplen; /* max length of captured packets, in octets */ 19 | uint32_t network; /* data link type */ 20 | } pcap_hdr_t; 21 | 22 | typedef struct pcaprec_hdr_s 23 | { 24 | uint32_t ts_sec; /* timestamp seconds */ 25 | uint32_t ts_usec; /* timestamp microseconds */ 26 | uint32_t incl_len; /* number of octets of packet saved in file */ 27 | uint32_t orig_len; /* actual length of packet */ 28 | } pcaprec_hdr_t; 29 | 30 | #define LINKTYPE_BLUETOOTH_LE_LL 251 31 | 32 | struct pcap_t 33 | { 34 | int fd; 35 | time_t start; 36 | }; 37 | 38 | pcap_t *pcap_create(const char *path) 39 | { 40 | pcap_t *pcap = malloc(sizeof(pcap_t)); 41 | 42 | int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC | O_RSYNC, 0644); 43 | if (fd < 0) 44 | { 45 | perror("open"); 46 | exit(1); 47 | } 48 | 49 | pcap->fd = fd; 50 | 51 | pcap_hdr_t hdr = { 52 | .magic_number = 0xa1b2c3d4, 53 | .version_major = 2, 54 | .version_minor = 4, 55 | .thiszone = 0, 56 | .sigfigs = 0, 57 | .snaplen = 65535, 58 | .network = LINKTYPE_BLUETOOTH_LE_LL, 59 | }; 60 | (void)!write(fd, &hdr, sizeof(hdr)); 61 | 62 | return pcap; 63 | } 64 | 65 | void pcap_destroy(pcap_t *pcap) 66 | { 67 | close(pcap->fd); 68 | free(pcap); 69 | } 70 | 71 | void pcap_write_packet(pcap_t *pcap, const uint8_t *data, size_t length) 72 | { 73 | pcaprec_hdr_t hdr = { 74 | .ts_sec = pcap->start, 75 | .ts_usec = microseconds_now_real(), 76 | .incl_len = length, 77 | .orig_len = length, 78 | }; 79 | 80 | (void)!write(pcap->fd, &hdr, sizeof(hdr)); 81 | (void)!write(pcap->fd, data, length); 82 | fsync(pcap->fd); 83 | } 84 | -------------------------------------------------------------------------------- /src/peripherals/dcb.c: -------------------------------------------------------------------------------- 1 | #include "peripherals/dcb.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "memory.h" 7 | 8 | struct DCB_inst_t 9 | { 10 | uint32_t demcr; 11 | }; 12 | 13 | OPERATION(dcb) 14 | { 15 | DCB_t *dcb = userdata; 16 | 17 | if (op == OP_RESET) 18 | { 19 | dcb->demcr = 0x01000000; 20 | return MEMREG_RESULT_OK; 21 | } 22 | 23 | OP_IGNORE_LOAD_DATA 24 | OP_ASSERT_SIZE(op, WORD); 25 | 26 | switch (offset) 27 | { 28 | case 0x0: // DHCSR 29 | if (OP_IS_READ(op)) 30 | *value = 0; 31 | printf("DHCSR: %08X\n", *value); 32 | return MEMREG_RESULT_OK; 33 | 34 | case 0xC: // DEMCR 35 | OP_RETURN_REG(dcb->demcr, WORD); 36 | } 37 | 38 | return MEMREG_RESULT_UNHANDLED; 39 | } 40 | 41 | DCB_t *dcb_new(state_store_t *store) 42 | { 43 | DCB_t *dcb = malloc(sizeof(DCB_t)); 44 | 45 | state_store_register(store, STATE_KEY_DCB, dcb, sizeof(DCB_t)); 46 | 47 | return dcb; 48 | } 49 | -------------------------------------------------------------------------------- /src/peripherals/dwt.c: -------------------------------------------------------------------------------- 1 | #include "peripherals/dwt.h" 2 | 3 | #include 4 | 5 | #include "memory.h" 6 | 7 | struct DWT_inst_t 8 | { 9 | uint32_t ctrl; 10 | uint32_t cyccnt; 11 | }; 12 | 13 | OPERATION(dwt) 14 | { 15 | DWT_t *dwt = userdata; 16 | 17 | if (op == OP_RESET) 18 | { 19 | dwt->ctrl = 0x40000001; 20 | dwt->cyccnt = 0; 21 | return MEMREG_RESULT_OK; 22 | } 23 | 24 | OP_IGNORE_LOAD_DATA 25 | OP_ASSERT_SIZE(op, WORD); 26 | 27 | switch (offset) 28 | { 29 | case 0x0: // CTRL 30 | OP_RETURN_REG(dwt->ctrl, WORD); 31 | 32 | case 0x4: // CYCCNT 33 | OP_RETURN_REG(dwt->cyccnt, WORD); 34 | } 35 | 36 | return MEMREG_RESULT_UNHANDLED; 37 | } 38 | 39 | DWT_t *dwt_new(state_store_t *store) 40 | { 41 | DWT_t *dwt = malloc(sizeof(DWT_t)); 42 | 43 | state_store_register(store, STATE_KEY_DWT, dwt, sizeof(DWT_t)); 44 | 45 | return dwt; 46 | } 47 | 48 | void dwt_increment_cycle(DWT_t *dwt, unsigned int count) 49 | { 50 | if ((dwt->ctrl & (1 << DWT_CYCCNTENA)) != 0) 51 | dwt->cyccnt += count; 52 | } -------------------------------------------------------------------------------- /src/peripherals/nrf52832/ccm.c: -------------------------------------------------------------------------------- 1 | #include "peripherals/nrf52832/ccm.h" 2 | 3 | #include 4 | 5 | struct CCM_inst_t 6 | { 7 | uint32_t foo; 8 | }; 9 | 10 | OPERATION(ccm) 11 | { 12 | if (op == OP_RESET) 13 | { 14 | return MEMREG_RESULT_OK; 15 | } 16 | 17 | OP_IGNORE_LOAD_DATA 18 | OP_ASSERT_SIZE(op, WORD); 19 | 20 | return MEMREG_RESULT_OK; 21 | } 22 | 23 | NRF52_PERIPHERAL_CONSTRUCTOR(CCM, ccm) 24 | { 25 | return malloc(sizeof(CCM_t)); 26 | } 27 | -------------------------------------------------------------------------------- /src/peripherals/nrf52832/comp.c: -------------------------------------------------------------------------------- 1 | #include "peripherals/nrf52832/comp.h" 2 | 3 | struct COMP_inst_t 4 | { 5 | uint32_t foo; 6 | }; 7 | 8 | OPERATION(comp) 9 | { 10 | if (op == OP_RESET) 11 | return MEMREG_RESULT_OK; 12 | 13 | OP_IGNORE_LOAD_DATA 14 | OP_ASSERT_SIZE(op, WORD); 15 | 16 | // COMP_t *comp = (COMP_t *)userdata; 17 | 18 | switch (offset) 19 | { 20 | case 0x540: // Unknown, do nothing 21 | return MEMREG_RESULT_OK; 22 | } 23 | 24 | return MEMREG_RESULT_UNHANDLED; 25 | } 26 | 27 | NRF52_PERIPHERAL_CONSTRUCTOR(COMP, comp) 28 | { 29 | return malloc(sizeof(COMP_t)); 30 | } 31 | 32 | void comp_reset(COMP_t *comp) 33 | { 34 | } 35 | -------------------------------------------------------------------------------- /src/peripherals/nrf52832/ecb.c: -------------------------------------------------------------------------------- 1 | #include "peripherals/nrf52832/ecb.h" 2 | 3 | #include 4 | 5 | #include "peripherals/nrf52832/ppi.h" 6 | 7 | #define CBC 0 8 | #define CTR 0 9 | #include "tiny-AES-c/aes.h" 10 | 11 | enum 12 | { 13 | TASKS_STARTECB = 0x000, // Start ECB block encrypt 14 | TASKS_STOPECB = 0x004, // Abort a possible executing ECB operation 15 | EVENTS_ENDECB = 0x100, // ECB block encrypt complete 16 | EVENTS_ERRORECB = 0x104, // ECB block encrypt aborted because of a STOPECB task or due to an error 17 | }; 18 | 19 | typedef struct 20 | { 21 | union 22 | { 23 | unsigned int ENDECB : 1; 24 | unsigned int ERRORECB : 1; 25 | }; 26 | uint32_t value; 27 | } inten_t; 28 | 29 | typedef struct 30 | { 31 | uint8_t key[16]; 32 | uint8_t cleartext[16]; 33 | uint8_t ciphertext[16]; 34 | } ecbdata_t; 35 | static_assert(sizeof(ecbdata_t) == 48, "ecbdata_t size is not 48 bytes"); 36 | 37 | typedef struct 38 | { 39 | uint32_t ecbdataptr; 40 | inten_t inten; 41 | } state_t; 42 | 43 | struct ECB_inst_t 44 | { 45 | state_t; 46 | 47 | dma_t *dma; 48 | }; 49 | 50 | OPERATION(ecb) 51 | { 52 | ECB_t *ecb = userdata; 53 | 54 | if (op == OP_RESET) 55 | { 56 | memset(ecb, 0, sizeof(state_t)); 57 | return MEMREG_RESULT_OK; 58 | } 59 | 60 | OP_IGNORE_LOAD_DATA 61 | OP_ASSERT_SIZE(op, WORD); 62 | 63 | switch (offset) 64 | { 65 | OP_TASK(TASKS_STARTECB) 66 | OP_TASK(TASKS_STOPECB) 67 | OP_EVENT(EVENTS_ENDECB) 68 | OP_EVENT(EVENTS_ERRORECB) 69 | 70 | OP_INTENSET(ecb) 71 | OP_INTENCLR(ecb) 72 | 73 | case 0x504: 74 | OP_RETURN_REG(ecb->ecbdataptr, WORD); 75 | 76 | default: 77 | break; 78 | } 79 | 80 | return MEMREG_RESULT_OK; 81 | } 82 | 83 | PPI_TASK_HANDLER(ecb_task_handler) 84 | { 85 | ECB_t *ecb = (ECB_t *)userdata; 86 | 87 | switch (task) 88 | { 89 | case TASK_ID(TASKS_STARTECB): 90 | { 91 | ecbdata_t ecbdata; 92 | static_assert(sizeof(ecbdata.cleartext) == AES_BLOCKLEN, "ecbdata.cleartext size is not AES_BLOCKLEN bytes"); 93 | 94 | dma_read(ecb->dma, ecb->ecbdataptr, sizeof(ecbdata_t), (uint8_t *)&ecbdata); 95 | 96 | uint8_t data[AES_BLOCKLEN]; 97 | memcpy(data, ecbdata.cleartext, sizeof(data)); 98 | 99 | struct AES_ctx ctx; 100 | AES_init_ctx(&ctx, ecbdata.key); 101 | AES_ECB_encrypt(&ctx, data); 102 | 103 | memcpy(ecbdata.ciphertext, data, sizeof(data)); 104 | 105 | dma_write(ecb->dma, ecb->ecbdataptr, sizeof(ecbdata_t), (const uint8_t *)&ecbdata); 106 | 107 | ppi_fire_event(ppi, peripheral, EVENT_ID(EVENTS_ENDECB), ecb->inten.ENDECB); 108 | break; 109 | } 110 | 111 | default: 112 | break; 113 | } 114 | } 115 | 116 | NRF52_PERIPHERAL_CONSTRUCTOR(ECB, ecb) 117 | { 118 | ECB_t *ecb = malloc(sizeof(ECB_t)); 119 | ecb->dma = ctx.dma; 120 | 121 | state_store_register(ctx.state_store, PERIPHERAL_KEY(ctx.id), ecb, sizeof(state_t)); 122 | 123 | ppi_add_peripheral(ctx.ppi, ctx.id, ecb_task_handler, ecb); 124 | 125 | return ecb; 126 | } -------------------------------------------------------------------------------- /src/peripherals/nrf52832/nvmc.c: -------------------------------------------------------------------------------- 1 | #include "peripherals/nrf52832/nvmc.h" 2 | 3 | #include "byte_util.h" 4 | 5 | typedef union 6 | { 7 | struct 8 | { 9 | unsigned int wen : 1; 10 | unsigned int een : 1; 11 | }; 12 | uint32_t value; 13 | } config_t; 14 | 15 | struct NVMC_inst_t 16 | { 17 | config_t config; 18 | 19 | uint8_t *data; 20 | size_t size; 21 | }; 22 | 23 | OPERATION(nvmc) 24 | { 25 | NVMC_t *nvmc = userdata; 26 | 27 | if (op == OP_RESET) 28 | { 29 | nvmc->config.value = 0; 30 | return MEMREG_RESULT_OK; 31 | } 32 | 33 | if (base == 0 && offset <= nvmc->size) 34 | { 35 | if (OP_IS_READ(op)) 36 | { 37 | switch (op) 38 | { 39 | case OP_READ_BYTE: 40 | *value = nvmc->data[offset]; 41 | break; 42 | 43 | case OP_READ_HALFWORD: 44 | *value = READ_UINT16(nvmc->data, offset); 45 | break; 46 | 47 | case OP_READ_WORD: 48 | *value = READ_UINT32(nvmc->data, offset); 49 | break; 50 | 51 | default: 52 | return MEMREG_RESULT_INVALID_SIZE; 53 | } 54 | } 55 | else 56 | { 57 | OP_ASSERT_SIZE(op, WORD); 58 | 59 | if (!nvmc->config.wen) 60 | return MEMREG_RESULT_INVALID_ACCESS; 61 | 62 | nvmc->data[offset] &= *value & 0xFF; 63 | nvmc->data[offset + 1] &= (*value >> 8) & 0xFF; 64 | nvmc->data[offset + 2] &= (*value >> 16) & 0xFF; 65 | nvmc->data[offset + 3] &= (*value >> 24) & 0xFF; 66 | } 67 | 68 | return MEMREG_RESULT_OK; 69 | } 70 | 71 | switch (offset) 72 | { 73 | case 0x400: // READY 74 | OP_ASSERT_READ(op); 75 | *value = 1; // Always ready 76 | return MEMREG_RESULT_OK; 77 | 78 | case 0x504: // CONFIG 79 | OP_RETURN_REG(nvmc->config.value, WORD); 80 | 81 | default: 82 | break; 83 | } 84 | 85 | return MEMREG_RESULT_UNHANDLED; 86 | } 87 | 88 | NRF52_PERIPHERAL_CONSTRUCTOR(NVMC, nvmc, uint8_t *data, size_t size) 89 | { 90 | NVMC_t *nvmc = malloc(sizeof(NVMC_t)); 91 | nvmc->data = data; 92 | nvmc->size = size; 93 | 94 | state_store_register(ctx.state_store, PERIPHERAL_KEY(ctx.id), nvmc, sizeof(config_t)); 95 | 96 | return nvmc; 97 | } 98 | -------------------------------------------------------------------------------- /src/peripherals/nrf52832/power.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "peripherals/nrf52832/power.h" 4 | #include "arm.h" 5 | 6 | struct POWER_inst_t 7 | { 8 | nrf_resetreason resetreason; 9 | }; 10 | 11 | OPERATION(power) 12 | { 13 | POWER_t *power = (POWER_t *)userdata; 14 | 15 | if (op == OP_RESET) 16 | { 17 | power->resetreason = RESETREASON_SREQ; 18 | return MEMREG_RESULT_OK; 19 | } 20 | 21 | OP_IGNORE_LOAD_DATA 22 | OP_ASSERT_SIZE(op, WORD); 23 | 24 | switch (offset) 25 | { 26 | case 0x400: // RESETREAS 27 | if (OP_IS_READ(op)) 28 | *value = power->resetreason; 29 | else 30 | power->resetreason &= ~*value; 31 | 32 | return MEMREG_RESULT_OK; 33 | 34 | case 0x578: // DCDCEN 35 | // Do nothing 36 | return MEMREG_RESULT_OK; 37 | } 38 | 39 | return MEMREG_RESULT_UNHANDLED; 40 | } 41 | 42 | NRF52_PERIPHERAL_CONSTRUCTOR(POWER, power) 43 | { 44 | POWER_t *power = malloc(sizeof(POWER_t)); 45 | 46 | state_store_register(ctx.state_store, STATE_KEY_POWER, power, sizeof(POWER_t)); 47 | 48 | return power; 49 | } 50 | -------------------------------------------------------------------------------- /src/peripherals/nrf52832/pwm.c: -------------------------------------------------------------------------------- 1 | #include "peripherals/nrf52832/pwm.h" 2 | 3 | struct PWM_inst_t 4 | { 5 | char dummy; 6 | }; 7 | 8 | OPERATION(pwm) 9 | { 10 | if (op == OP_RESET) 11 | { 12 | return MEMREG_RESULT_OK; 13 | } 14 | 15 | OP_IGNORE_LOAD_DATA 16 | OP_ASSERT_SIZE(op, WORD); 17 | 18 | return MEMREG_RESULT_OK; 19 | } 20 | 21 | NRF52_PERIPHERAL_CONSTRUCTOR(PWM, pwm) 22 | { 23 | return malloc(sizeof(PWM_t)); 24 | } 25 | -------------------------------------------------------------------------------- /src/peripherals/nrf52832/rng.c: -------------------------------------------------------------------------------- 1 | #include "peripherals/nrf52832/rng.h" 2 | 3 | #include "nrf52832.h" 4 | #include "peripherals/nrf52832/ppi.h" 5 | 6 | #include 7 | #include 8 | 9 | enum 10 | { 11 | TASKS_START = 0x000, 12 | TASKS_STOP = 0x004, 13 | EVENTS_VALRDY = 0x100, 14 | }; 15 | 16 | typedef union 17 | { 18 | struct 19 | { 20 | unsigned int VALRDY : 1; 21 | }; 22 | uint32_t value; 23 | } inten_t; 24 | 25 | struct RNG_inst_t 26 | { 27 | cpu_t **cpu; 28 | PPI_t *ppi; 29 | 30 | uint32_t value; 31 | 32 | bool running; 33 | uint32_t config; 34 | 35 | inten_t inten; 36 | }; 37 | 38 | static inline void rng_new_value(RNG_t *rng) 39 | { 40 | rng->value = (uint32_t)rand(); 41 | 42 | ppi_fire_event(rng->ppi, INSTANCE_RNG, EVENT_ID(EVENTS_VALRDY), rng->inten.VALRDY); 43 | } 44 | 45 | OPERATION(rng) 46 | { 47 | RNG_t *rng = (RNG_t *)userdata; 48 | 49 | if (op == OP_RESET) 50 | { 51 | *rng = (RNG_t){ 52 | .cpu = rng->cpu, 53 | .ppi = rng->ppi, 54 | }; 55 | return MEMREG_RESULT_OK; 56 | } 57 | 58 | OP_IGNORE_LOAD_DATA 59 | OP_ASSERT_SIZE(op, WORD); 60 | 61 | switch (offset) 62 | { 63 | OP_TASK(TASKS_START) 64 | OP_TASK(TASKS_STOP) 65 | OP_EVENT(EVENTS_VALRDY) 66 | 67 | OP_INTENSET(rng) 68 | OP_INTENCLR(rng) 69 | 70 | case 0x504: // CONFIG 71 | OP_RETURN_REG(rng->config, WORD); 72 | 73 | case 0x508: 74 | OP_ASSERT_READ(op); 75 | 76 | *value = rng->value; 77 | 78 | if (rng->running) 79 | rng_new_value(rng); 80 | 81 | return MEMREG_RESULT_OK; 82 | 83 | default: 84 | break; 85 | } 86 | 87 | return MEMREG_RESULT_UNHANDLED; 88 | } 89 | 90 | PPI_TASK_HANDLER(rng_task_cb) 91 | { 92 | RNG_t *rng = (RNG_t *)userdata; 93 | 94 | switch (task) 95 | { 96 | case TASK_ID(TASKS_START): 97 | rng->running = true; 98 | rng_new_value(rng); 99 | break; 100 | 101 | case TASK_ID(TASKS_STOP): 102 | rng->running = false; 103 | break; 104 | 105 | default: 106 | break; 107 | } 108 | } 109 | 110 | NRF52_PERIPHERAL_CONSTRUCTOR(RNG, rng) 111 | { 112 | RNG_t *rng = malloc(sizeof(RNG_t)); 113 | rng->cpu = ctx.cpu; 114 | rng->ppi = ctx.ppi; 115 | 116 | ppi_add_peripheral(ctx.ppi, ctx.id, rng_task_cb, rng); 117 | 118 | return rng; 119 | } 120 | -------------------------------------------------------------------------------- /src/peripherals/nrf52832/spi.c: -------------------------------------------------------------------------------- 1 | #include "peripherals/nrf52832/spi.h" 2 | 3 | #include "fault.h" 4 | #include "peripherals/nrf52832/ppi.h" 5 | 6 | enum 7 | { 8 | EVENTS_READY = 0x108, 9 | }; 10 | 11 | #define READ_BUFFER_SIZE 50 12 | 13 | typedef struct 14 | { 15 | bool enabled; 16 | } state_t; 17 | 18 | struct SPI_inst_t 19 | { 20 | state_t; 21 | 22 | uint8_t id; 23 | bus_spi_t *bus; 24 | }; 25 | 26 | PPI_TASK_HANDLER(spi_task_handler) 27 | { 28 | } 29 | 30 | OPERATION(spi) 31 | { 32 | SPI_t *spi = userdata; 33 | 34 | if (op == OP_RESET) 35 | { 36 | spi->enabled = false; 37 | return MEMREG_RESULT_OK; 38 | } 39 | 40 | switch (offset) 41 | { 42 | OP_EVENT(EVENTS_READY) 43 | 44 | case 0x500: // ENABLE 45 | if (OP_IS_READ(op)) 46 | { 47 | if (spi->enabled) 48 | { 49 | *value = SPI_ENABLE_VALUE; 50 | return MEMREG_RESULT_OK; 51 | } 52 | else 53 | { 54 | *value = 0; 55 | return MEMREG_RESULT_OK_CONTINUE; 56 | } 57 | } 58 | else if (*value == SPI_ENABLE_VALUE) 59 | { 60 | if (!spi->enabled) 61 | ppi_add_peripheral(current_ppi, spi->id, spi_task_handler, spi); 62 | 63 | spi->enabled = true; 64 | return MEMREG_RESULT_OK; 65 | } 66 | else 67 | { 68 | if (spi->enabled) 69 | ppi_remove_peripheral(current_ppi, spi->id); 70 | 71 | spi->enabled = false; 72 | return MEMREG_RESULT_OK_CONTINUE; 73 | } 74 | break; 75 | 76 | case 0x518: // RXD 77 | if (spi->enabled) 78 | { 79 | OP_ASSERT_READ(op); 80 | 81 | uint8_t byte; 82 | bus_spi_read(spi->bus, &byte); 83 | *value = byte; 84 | 85 | printf("SPI RXD 0x%02X\n", byte); 86 | 87 | // ppi_fire_event(current_ppi, spi->id, EVENT_ID(EVENTS_READY), false); // TODO: Should we fire this event here? 88 | return MEMREG_RESULT_OK; 89 | } 90 | break; 91 | 92 | case 0x51C: // TXD 93 | if (spi->enabled) 94 | { 95 | OP_ASSERT_WRITE(op); 96 | 97 | printf("SPI TXD: %02X\n", *value); 98 | 99 | spi_result_t result = bus_spi_write(spi->bus, *value); 100 | if (result != SPI_RESULT_OK) 101 | fault_take(FAULT_NOT_IMPLEMENTED); // TODO: Handle better 102 | 103 | ppi_fire_event(current_ppi, spi->id, EVENT_ID(EVENTS_READY), false); 104 | 105 | return MEMREG_RESULT_OK; 106 | } 107 | break; 108 | 109 | default: 110 | break; 111 | } 112 | 113 | return MEMREG_RESULT_UNHANDLED; 114 | } 115 | 116 | NRF52_PERIPHERAL_CONSTRUCTOR(SPI, spi) 117 | { 118 | SPI_t *spi = malloc(sizeof(SPI_t)); 119 | spi->bus = ctx.spi; 120 | spi->id = ctx.id; 121 | 122 | state_store_register(ctx.state_store, PERIPHERAL_KEY(ctx.id), spi, sizeof(state_t)); 123 | 124 | return spi; 125 | } 126 | -------------------------------------------------------------------------------- /src/peripherals/nrf52832/temp.c: -------------------------------------------------------------------------------- 1 | #include "peripherals/nrf52832/temp.h" 2 | 3 | #include 4 | #include 5 | 6 | struct TEMP_inst_t 7 | { 8 | uint32_t a[6]; 9 | uint32_t b[6]; 10 | uint32_t t[5]; 11 | }; 12 | 13 | OPERATION(temp) 14 | { 15 | TEMP_t *temp = (TEMP_t *)userdata; 16 | 17 | if (op == OP_RESET) 18 | { 19 | memset(temp, 0, sizeof(TEMP_t)); 20 | return MEMREG_RESULT_OK; 21 | } 22 | 23 | OP_IGNORE_LOAD_DATA 24 | OP_ASSERT_SIZE(op, WORD); 25 | 26 | if (offset >= 0x520 && offset <= 0x534) 27 | { 28 | OP_RETURN_REG(temp->a[(offset - 0x520) / 4], WORD); 29 | } 30 | if (offset >= 0x540 && offset <= 0x554) 31 | { 32 | OP_RETURN_REG(temp->b[(offset - 0x540) / 4], WORD); 33 | } 34 | if (offset >= 0x560 && offset <= 0x570) 35 | { 36 | OP_RETURN_REG(temp->t[(offset - 0x560) / 4], WORD); 37 | } 38 | 39 | return MEMREG_RESULT_UNHANDLED; 40 | } 41 | 42 | NRF52_PERIPHERAL_CONSTRUCTOR(TEMP, temp) 43 | { 44 | TEMP_t *temp = malloc(sizeof(TEMP_t)); 45 | 46 | state_store_register(ctx.state_store, PERIPHERAL_KEY(ctx.id), temp, sizeof(TEMP_t)); 47 | 48 | return temp; 49 | } 50 | -------------------------------------------------------------------------------- /src/peripherals/nrf52832/wdt.c: -------------------------------------------------------------------------------- 1 | #include "peripherals/nrf52832/wdt.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "byte_util.h" 7 | 8 | struct WDT_inst_t 9 | { 10 | uint32_t config; 11 | uint32_t crv; 12 | uint32_t rren; 13 | 14 | bool started; 15 | }; 16 | 17 | OPERATION(wdt) 18 | { 19 | WDT_t *wdt = (WDT_t *)userdata; 20 | 21 | if (op == OP_RESET) 22 | { 23 | memset(wdt, 0, sizeof(WDT_t)); 24 | return MEMREG_RESULT_OK; 25 | } 26 | 27 | OP_IGNORE_LOAD_DATA 28 | OP_ASSERT_SIZE(op, WORD); 29 | 30 | // TODO: Make watchdog do something 31 | 32 | switch (offset) 33 | { 34 | case 0x000: // TASKS_START 35 | OP_ASSERT_WRITE(op); 36 | 37 | wdt->started = true; 38 | return MEMREG_RESULT_OK; 39 | 40 | case 0x504: // CRV 41 | OP_RETURN_REG(wdt->crv, WORD); 42 | 43 | case 0x508: // RREN 44 | OP_RETURN_REG(wdt->rren, WORD); 45 | 46 | case 0x50C: // CONFIG 47 | OP_RETURN_REG(wdt->config, WORD); 48 | 49 | case 0x600: // RR[0] 50 | OP_ASSERT_WRITE(op); 51 | return MEMREG_RESULT_OK; 52 | } 53 | 54 | return MEMREG_RESULT_UNHANDLED; 55 | } 56 | 57 | NRF52_PERIPHERAL_CONSTRUCTOR(WDT, wdt) 58 | { 59 | WDT_t *wdt = malloc(sizeof(WDT_t)); 60 | 61 | state_store_register(ctx.state_store, PERIPHERAL_KEY(ctx.id), wdt, sizeof(WDT_t)); 62 | 63 | return wdt; 64 | } 65 | -------------------------------------------------------------------------------- /src/peripherals/scb_fp.c: -------------------------------------------------------------------------------- 1 | #include "peripherals/scb_fp.h" 2 | 3 | #include "arm.h" 4 | 5 | #include 6 | #include 7 | 8 | #include "byte_util.h" 9 | 10 | struct SCB_FP_inst_t 11 | { 12 | FPCCR_t fpccr; 13 | uint32_t fpscr; 14 | }; 15 | 16 | OPERATION(scb_fp) 17 | { 18 | SCB_FP_t *scb_fp = userdata; 19 | 20 | if (op == OP_RESET) 21 | { 22 | scb_fp->fpccr.ASPEN = 1; 23 | scb_fp->fpccr.LSPEN = 1; 24 | scb_fp->fpccr.LSPACT = 0; 25 | 26 | return MEMREG_RESULT_OK; 27 | } 28 | 29 | switch (offset) 30 | { 31 | case 0x34: // FPCCR 32 | OP_RETURN_REG(scb_fp->fpccr.value, WORD); 33 | } 34 | 35 | return MEMREG_RESULT_UNHANDLED; 36 | } 37 | 38 | SCB_FP_t *scb_fp_new(state_store_t *store) 39 | { 40 | SCB_FP_t *scb_fp = malloc(sizeof(SCB_FP_t)); 41 | 42 | state_store_register(store, STATE_KEY_SCB_FP, scb_fp, sizeof(SCB_FP_t)); 43 | 44 | return scb_fp; 45 | } 46 | 47 | FPCCR_t scb_fp_get_fpccr(SCB_FP_t *scb_fp) 48 | { 49 | return scb_fp->fpccr; 50 | } 51 | 52 | uint32_t scb_fp_get_fpscr(SCB_FP_t *scb_fp) 53 | { 54 | return scb_fp->fpscr; 55 | } 56 | 57 | void scb_fp_set_fpscr(SCB_FP_t *scb_fp, uint32_t value) 58 | { 59 | scb_fp->fpscr = value; 60 | } 61 | -------------------------------------------------------------------------------- /src/pinetime.c: -------------------------------------------------------------------------------- 1 | #include "pinetime.h" 2 | 3 | #include "byte_util.h" 4 | #include "components/i2c/bma425.h" 5 | #include "components/i2c/cst816s.h" 6 | #include "components/i2c/hrs3300.h" 7 | #include "components/spi/spinorflash.h" 8 | #include "components/spi/st7789.h" 9 | 10 | struct pinetime_t 11 | { 12 | NRF52832_t *nrf; 13 | state_store_t *state_store; 14 | 15 | st7789_t *lcd; 16 | cst816s_t *touch; 17 | hrs3300_t *hrs; 18 | spinorflash_t *extflash; 19 | }; 20 | 21 | pinetime_t *pinetime_new(const program_t *program) 22 | { 23 | uint32_t initial_sp; 24 | program_write_to(program, (uint8_t *)&initial_sp, sizeof(initial_sp)); 25 | 26 | printf("Initial SP: 0x%08x\n", initial_sp); 27 | 28 | size_t sram_size = initial_sp - x(2000, 0000); 29 | if (sram_size < NRF52832_SRAM_SIZE) 30 | sram_size = NRF52832_SRAM_SIZE; 31 | 32 | printf("SRAM size: %ld bytes\n", sram_size); 33 | 34 | pinetime_t *pt = malloc(sizeof(pinetime_t)); 35 | pt->state_store = state_store_new(); 36 | pt->nrf = nrf52832_new(program, sram_size, pt->state_store); 37 | pt->lcd = st7789_new(pt->state_store); 38 | pt->touch = cst816s_new(nrf52832_get_pins(pt->nrf), pt->state_store, PINETIME_CST816S_IRQ_PIN); 39 | pt->hrs = hrs3300_new(pt->state_store); 40 | pt->extflash = spinorflash_new(pt->state_store, PINETIME_EXTFLASH_SIZE, PINETIME_EXTFLASH_SECTOR_SIZE); 41 | 42 | bus_spi_add_slave(nrf52832_get_spi(pt->nrf), PINETIME_EXTFLASH_CS_PIN, spinorflash_get_slave(pt->extflash)); 43 | bus_spi_add_slave(nrf52832_get_spi(pt->nrf), PINETIME_LCD_CS_PIN, st7789_get_slave(pt->lcd)); 44 | i2c_add_slave(nrf52832_get_i2c(pt->nrf), PINETIME_CST816S_I2C_ADDR, cst816s_get_slave(pt->touch)); 45 | i2c_add_slave(nrf52832_get_i2c(pt->nrf), PINETIME_BMA425_I2C_ADDR, bma425_new(pt->state_store)); 46 | i2c_add_slave(nrf52832_get_i2c(pt->nrf), PINETIME_HRS3300_I2C_ADDR, hrs3300_get_slave(pt->hrs)); 47 | 48 | nrf52832_reset(pt->nrf); 49 | 50 | return pt; 51 | } 52 | 53 | void pinetime_free(pinetime_t *pt) 54 | { 55 | nrf52832_free(pt->nrf); 56 | state_store_free(pt->state_store); 57 | 58 | free(pt); 59 | } 60 | 61 | void pinetime_reset(pinetime_t *pt) 62 | { 63 | nrf52832_reset(pt->nrf); 64 | } 65 | 66 | int pinetime_step(pinetime_t *pt) 67 | { 68 | return nrf52832_step(pt->nrf); 69 | } 70 | 71 | NRF52832_t *pinetime_get_nrf52832(pinetime_t *pt) 72 | { 73 | return pt->nrf; 74 | } 75 | 76 | st7789_t *pinetime_get_st7789(pinetime_t *pt) 77 | { 78 | return pt->lcd; 79 | } 80 | 81 | cst816s_t *pinetime_get_cst816s(pinetime_t *pt) 82 | { 83 | return pt->touch; 84 | } 85 | 86 | hrs3300_t *pinetime_get_hrs3300(pinetime_t *pt) 87 | { 88 | return pt->hrs; 89 | } 90 | 91 | spinorflash_t *pinetime_get_spinorflash(pinetime_t *pt) 92 | { 93 | return pt->extflash; 94 | } 95 | 96 | uint8_t *pinetime_save_state(pinetime_t *pt, size_t *size) 97 | { 98 | return state_store_save(pt->state_store, size); 99 | } 100 | 101 | bool pinetime_load_state(pinetime_t *pt, uint8_t *data, size_t size) 102 | { 103 | if (state_store_load(pt->state_store, data, size)) 104 | { 105 | nrf52832_reload_state(pt->nrf); 106 | return true; 107 | } 108 | 109 | return false; 110 | } 111 | -------------------------------------------------------------------------------- /src/scheduler.c: -------------------------------------------------------------------------------- 1 | #include "scheduler.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "ie_time.h" 12 | 13 | #define US_PER_ITERATION 100000 14 | 15 | struct scheduler_t 16 | { 17 | scheduler_cb_t cb; 18 | void *userdata; 19 | 20 | bool stop; 21 | 22 | size_t cycles_per_iteration; 23 | }; 24 | 25 | scheduler_t *scheduler_new(scheduler_cb_t cb, void *userdata, size_t target_hz) 26 | { 27 | scheduler_t *scheduler = malloc(sizeof(scheduler_t)); 28 | scheduler->cb = cb; 29 | scheduler->userdata = userdata; 30 | scheduler->stop = false; 31 | 32 | scheduler_set_frequency(scheduler, target_hz); 33 | 34 | return scheduler; 35 | } 36 | 37 | void scheduler_run(scheduler_t *sched) 38 | { 39 | struct timespec ts_req = {0}; 40 | 41 | sched->stop = false; 42 | 43 | ssize_t fuel; 44 | 45 | while (!sched->stop) 46 | { 47 | fuel = sched->cycles_per_iteration; 48 | 49 | uint64_t start = microseconds_now_real(); 50 | 51 | while (fuel > 0) 52 | { 53 | fuel -= sched->cb(sched->userdata); 54 | } 55 | 56 | uint64_t elapsed_us = microseconds_now_real() - start; 57 | 58 | if (elapsed_us < US_PER_ITERATION) 59 | { 60 | ts_req.tv_nsec = (US_PER_ITERATION - elapsed_us) * 1000; 61 | while (nanosleep(&ts_req, &ts_req)); 62 | } 63 | } 64 | } 65 | 66 | void scheduler_stop(scheduler_t *sched) 67 | { 68 | sched->stop = true; 69 | } 70 | 71 | void scheduler_set_frequency(scheduler_t *sched, size_t target_hz) 72 | { 73 | sched->cycles_per_iteration = (target_hz * US_PER_ITERATION) / 1000000; 74 | } 75 | -------------------------------------------------------------------------------- /src/segger_rtt.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #if ENABLE_SEGGER_RTT 4 | 5 | #if ENABLE_LOG_SEGGER_RTT 6 | #define LOG(msg, ...) printf("[RTT] " msg, __VA_ARGS__) 7 | #else 8 | #define LOG(...) 9 | #endif 10 | 11 | #include "segger_rtt.h" 12 | 13 | #include 14 | #include 15 | 16 | #include "../lib/RTT/RTT/SEGGER_RTT.h" 17 | #include "byte_util.h" 18 | 19 | #define BUFFER_UP_FIELD(cb_addr, buf_index, offset) ((cb_addr) + offsetof(SEGGER_RTT_CB, aUp) + (24 * (buf_index)) + (offset)) 20 | 21 | #define min(a, b) ((a) < (b) ? (a) : (b)) 22 | 23 | struct rtt_inst_t 24 | { 25 | memory_map_t *mem; 26 | uint32_t control_block_addr; 27 | uint32_t up_buf_size; 28 | }; 29 | 30 | rtt_t *rtt_new(memory_map_t *mem) 31 | { 32 | rtt_t *rtt = malloc(sizeof(rtt_t)); 33 | rtt->mem = mem; 34 | rtt->control_block_addr = 0; 35 | 36 | return rtt; 37 | } 38 | 39 | void rtt_free(rtt_t *rtt) 40 | { 41 | free(rtt); 42 | } 43 | 44 | bool rtt_find_control(rtt_t *rtt) 45 | { 46 | if (rtt->control_block_addr != 0) 47 | return true; 48 | 49 | uint32_t addr = memory_map_find_data(rtt->mem, x(2000, 0000), 0x10000, (const uint8_t *)"SEGGER RTT", 10); 50 | if (addr == MEMREG_FIND_NOT_FOUND) 51 | return false; 52 | 53 | LOG("Found SEGGER RTT control block at 0x%08x\n", addr); 54 | 55 | rtt->control_block_addr = addr; 56 | 57 | uint32_t upName = memory_map_read(rtt->mem, BUFFER_UP_FIELD(addr, 0, 0)); 58 | 59 | char name[32]; 60 | for (size_t i = 0; i < sizeof(name); i++) 61 | { 62 | if ((name[i] = memory_map_read(rtt->mem, upName + i)) == 0) 63 | break; 64 | } 65 | 66 | rtt->up_buf_size = memory_map_read(rtt->mem, BUFFER_UP_FIELD(addr, 0, 8)); 67 | 68 | LOG("Up buffer name: %s, %d bytes\n", name, rtt->up_buf_size); 69 | 70 | return true; 71 | } 72 | 73 | size_t rtt_flush_buffers(rtt_t *rtt, char *buffer, size_t buffer_size) 74 | { 75 | if (rtt->control_block_addr == 0) 76 | return 0; 77 | 78 | uint32_t cb_addr = rtt->control_block_addr; 79 | 80 | uint32_t bufferAddr = memory_map_read(rtt->mem, BUFFER_UP_FIELD(cb_addr, 0, 4)); 81 | uint32_t wrOff = memory_map_read(rtt->mem, BUFFER_UP_FIELD(cb_addr, 0, 12)); 82 | uint32_t rdOff = memory_map_read(rtt->mem, BUFFER_UP_FIELD(cb_addr, 0, 16)); 83 | 84 | size_t numBytesRem, numBytesRead = 0; 85 | 86 | if (rdOff > wrOff) 87 | { 88 | numBytesRem = rtt->up_buf_size - rdOff; 89 | numBytesRem = min(numBytesRem, buffer_size); 90 | bufferAddr += rdOff; 91 | numBytesRead += numBytesRem; 92 | buffer_size -= numBytesRem; 93 | rdOff += numBytesRem; 94 | 95 | while (numBytesRem--) 96 | { 97 | *buffer++ = memory_map_read_byte(rtt->mem, bufferAddr++); 98 | } 99 | 100 | if (rdOff == rtt->up_buf_size) 101 | rdOff = 0; 102 | } 103 | 104 | numBytesRem = wrOff - rdOff; 105 | numBytesRem = min(numBytesRem, buffer_size); 106 | 107 | if (numBytesRem > 0) 108 | { 109 | bufferAddr += rdOff; 110 | numBytesRead += numBytesRem; 111 | buffer_size -= numBytesRem; 112 | rdOff += numBytesRem; 113 | 114 | while (numBytesRem--) 115 | { 116 | *buffer++ = memory_map_read_byte(rtt->mem, bufferAddr++); 117 | } 118 | } 119 | 120 | if (numBytesRead) 121 | { 122 | memory_map_write(rtt->mem, BUFFER_UP_FIELD(cb_addr, 0, 16), rdOff, SIZE_WORD); 123 | } 124 | 125 | return numBytesRead; 126 | } 127 | 128 | #else 129 | 130 | void pedantic() {} 131 | 132 | #endif -------------------------------------------------------------------------------- /src/state_store.c: -------------------------------------------------------------------------------- 1 | #include "state_store.h" 2 | 3 | #include 4 | #include 5 | 6 | typedef struct 7 | { 8 | uint16_t key; 9 | size_t size; 10 | } entry_header_t; 11 | 12 | typedef struct 13 | { 14 | entry_header_t header; 15 | void *data; 16 | } entry_t; 17 | 18 | #define MAX_ENTRIES 100 19 | 20 | struct state_store_t 21 | { 22 | entry_t entries[MAX_ENTRIES]; 23 | size_t entries_count; 24 | 25 | bool frozen; 26 | }; 27 | 28 | state_store_t *state_store_new() 29 | { 30 | state_store_t *store = malloc(sizeof(state_store_t)); 31 | store->entries_count = 0; 32 | store->frozen = false; 33 | 34 | return store; 35 | } 36 | 37 | void state_store_free(state_store_t *store) 38 | { 39 | for (size_t i = 0; i < store->entries_count; i++) 40 | free(store->entries[i].data); 41 | 42 | free(store); 43 | } 44 | 45 | static entry_t *find_entry(state_store_t *store, state_key_t key) 46 | { 47 | for (size_t i = 0; i < store->entries_count; i++) 48 | if (store->entries[i].header.key == key) 49 | return &store->entries[i]; 50 | 51 | return NULL; 52 | } 53 | 54 | void state_store_register(state_store_t *store, state_key_t key, void *data, size_t size) 55 | { 56 | if (store->frozen) 57 | abort(); 58 | 59 | if (find_entry(store, key)) 60 | abort(); 61 | 62 | store->entries[store->entries_count++] = (entry_t){ 63 | .header.key = key, 64 | .header.size = size, 65 | .data = data, 66 | }; 67 | } 68 | 69 | void state_store_freeze(state_store_t *store) 70 | { 71 | store->frozen = true; 72 | } 73 | 74 | uint8_t *state_store_save(state_store_t *store, size_t *size) 75 | { 76 | *size = 0; 77 | 78 | for (size_t i = 0; i < store->entries_count; i++) 79 | *size += sizeof(entry_header_t) + store->entries[i].header.size; 80 | 81 | uint8_t *data = malloc(*size); 82 | 83 | size_t offset = 0; 84 | for (size_t i = 0; i < store->entries_count; i++) 85 | { 86 | memcpy(data + offset, &store->entries[i].header, sizeof(entry_header_t)); 87 | offset += sizeof(entry_header_t); 88 | 89 | memcpy(data + offset, store->entries[i].data, store->entries[i].header.size); 90 | offset += store->entries[i].header.size; 91 | } 92 | 93 | return data; 94 | } 95 | 96 | bool state_store_load(state_store_t *store, const uint8_t *data, size_t size) 97 | { 98 | entry_header_t header; 99 | 100 | size_t offset = 0; 101 | while (offset < size) 102 | { 103 | memcpy(&header, data + offset, sizeof(entry_header_t)); 104 | offset += sizeof(entry_header_t); 105 | 106 | entry_t *entry = find_entry(store, header.key); 107 | if (!entry || entry->header.size != header.size) { 108 | return false; 109 | } 110 | 111 | memcpy(entry->data, data + offset, header.size); 112 | offset += header.size; 113 | } 114 | 115 | return true; 116 | } 117 | -------------------------------------------------------------------------------- /src/ticker.c: -------------------------------------------------------------------------------- 1 | #include "ticker.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define MAX_ENTRIES 10 9 | 10 | typedef struct 11 | { 12 | ticker_cb_t cb; 13 | void *userdata; 14 | uint32_t interval, counter; 15 | bool auto_reload; 16 | } ticker_entry_t; 17 | 18 | struct ticker_t 19 | { 20 | ticker_entry_t lf_entries[MAX_ENTRIES]; 21 | size_t lf_entries_count; 22 | ticker_entry_t hf_entries[MAX_ENTRIES]; 23 | size_t hf_entries_count; 24 | 25 | uint32_t lfclk_divider, lfclk_counter; 26 | bool lfclk_enabled; 27 | }; 28 | 29 | ticker_t *ticker_new(int32_t lfclk_divider) 30 | { 31 | ticker_t *ticker = calloc(1, sizeof(ticker_t)); 32 | 33 | if (lfclk_divider > 0) 34 | { 35 | ticker->lfclk_divider = lfclk_divider; 36 | ticker->lfclk_enabled = true; 37 | } 38 | 39 | return ticker; 40 | } 41 | 42 | void ticker_free(ticker_t *ticker) 43 | { 44 | free(ticker); 45 | } 46 | 47 | void ticker_reset(ticker_t *ticker) 48 | { 49 | ticker->lf_entries_count = 0; 50 | ticker->hf_entries_count = 0; 51 | } 52 | 53 | #define CLOCK_ENTRIES(clock) (clock == CLOCK_HFCLK ? ticker->hf_entries : ticker->lf_entries) 54 | #define CLOCK_ENTRIES_COUNT(clock) (clock == CLOCK_HFCLK ? &ticker->hf_entries_count : &ticker->lf_entries_count) 55 | 56 | void ticker_add(ticker_t *ticker, clock_type clock, ticker_cb_t cb, void *userdata, uint32_t interval, bool auto_reload) 57 | { 58 | ticker_entry_t *entries = CLOCK_ENTRIES(clock); 59 | size_t *entries_count = CLOCK_ENTRIES_COUNT(clock); 60 | 61 | assert(*entries_count < MAX_ENTRIES); 62 | 63 | ticker_entry_t *entry = &entries[(*entries_count)++]; 64 | 65 | entry->cb = cb; 66 | entry->userdata = userdata; 67 | entry->interval = interval; 68 | entry->counter = 0; 69 | entry->auto_reload = auto_reload; 70 | } 71 | 72 | void ticker_remove(ticker_t *ticker, clock_type clock, ticker_cb_t cb) 73 | { 74 | ticker_entry_t *entries = CLOCK_ENTRIES(clock); 75 | size_t *entries_count = CLOCK_ENTRIES_COUNT(clock); 76 | 77 | for (size_t i = 0; i < *entries_count; i++) 78 | { 79 | if (entries[i].cb == cb) 80 | { 81 | if (*entries_count == 1) 82 | { 83 | *entries_count = 0; 84 | return; 85 | } 86 | 87 | // Move last entry to the removed entry 88 | memcpy(&entries[i], &entries[*entries_count - 1], sizeof(ticker_entry_t)); 89 | 90 | (*entries_count)--; 91 | break; 92 | } 93 | } 94 | } 95 | 96 | static inline void tick_entries(ticker_entry_t *entries, size_t *entries_count) 97 | { 98 | for (size_t i = 0; i < *entries_count; i++) 99 | { 100 | ticker_entry_t *entry = &entries[i]; 101 | 102 | if (++entry->counter == entry->interval) 103 | { 104 | entry->cb(entry->userdata); 105 | 106 | if (entry->auto_reload) 107 | { 108 | entry->counter = 0; 109 | } 110 | else 111 | { 112 | // Move last entry to the removed entry 113 | memcpy(entry, &entries[*entries_count - 1], sizeof(ticker_entry_t)); 114 | 115 | (*entries_count)--; 116 | i--; 117 | } 118 | } 119 | } 120 | } 121 | 122 | void ticker_hftick(ticker_t *ticker, unsigned int count) 123 | { 124 | for (size_t i = 0; i < count; i++) 125 | { 126 | tick_entries(ticker->hf_entries, &ticker->hf_entries_count); 127 | } 128 | 129 | if (!ticker->lfclk_enabled) 130 | return; 131 | 132 | ticker->lfclk_counter += count; 133 | 134 | while (ticker->lfclk_counter >= ticker->lfclk_divider) 135 | { 136 | ticker_lftick(ticker); 137 | ticker->lfclk_counter -= ticker->lfclk_divider; 138 | } 139 | } 140 | 141 | void ticker_lftick(ticker_t *ticker) 142 | { 143 | tick_entries(ticker->lf_entries, &ticker->lf_entries_count); 144 | } 145 | -------------------------------------------------------------------------------- /src/time.c: -------------------------------------------------------------------------------- 1 | #include "ie_time.h" 2 | 3 | #include 4 | 5 | static _Thread_local struct timeval tv; 6 | 7 | uint64_t microseconds_now_real() 8 | { 9 | gettimeofday(&tv, NULL); 10 | return tv.tv_sec * 1e6 + tv.tv_usec; 11 | } 12 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | /main.o 2 | /main.c -------------------------------------------------------------------------------- /test/firmware/.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | *.dis 3 | *.elf 4 | *.readelf -------------------------------------------------------------------------------- /test/firmware/FreeRTOS/portmacro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeRTOS Kernel V10.0.0 3 | * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | * the Software, and to permit persons to whom the Software is furnished to do so, 10 | * subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. If you wish to use our Amazon 14 | * FreeRTOS name, please do so in a fair use way that does not cause confusion. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | * 23 | * http://www.FreeRTOS.org 24 | * http://aws.amazon.com/freertos 25 | * 26 | * 1 tab == 4 spaces! 27 | */ 28 | 29 | 30 | #ifndef PORTMACRO_H 31 | #define PORTMACRO_H 32 | 33 | #include "portmacro_cmsis.h" 34 | 35 | #endif /* PORTMACRO_H */ 36 | 37 | -------------------------------------------------------------------------------- /test/firmware/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | void vTaskCode(void *) 7 | { 8 | for (;;) 9 | { 10 | SEGGER_RTT_WriteString(0, "Tick\n"); 11 | vTaskDelay(pdMS_TO_TICKS(1000)); 12 | } 13 | } 14 | 15 | int main() 16 | { 17 | SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL); 18 | 19 | SEGGER_RTT_WriteString(0, "SEGGER Real-Time-Terminal Sample\n"); 20 | 21 | TaskHandle_t xHandle = NULL; 22 | xTaskCreate(vTaskCode, "NAME", 300, NULL, 1, &xHandle); 23 | 24 | vTaskStartScheduler(); 25 | 26 | __asm volatile("svc 0"); 27 | 28 | for (;;) 29 | { 30 | } 31 | } 32 | 33 | void vApplicationMallocFailedHook() 34 | { 35 | } 36 | 37 | void vApplicationStackOverflowHook(void *xTask, char *pcTaskName) 38 | { 39 | } 40 | -------------------------------------------------------------------------------- /test/suites/adc.yaml: -------------------------------------------------------------------------------- 1 | "ADC (register)": 2 | - setup: 3 | registers: 4 | r0: 0 5 | r1: 10 6 | execute: 7 | - "adcs r0, r1, #4" 8 | expect: 9 | registers: 10 | r0: 14 11 | r1: 10 12 | nczv: "0000" 13 | 14 | - setup: 15 | registers: 16 | r0: 10 17 | execute: 18 | - "adcs r0, #4" 19 | expect: 20 | registers: 21 | r0: 14 22 | nczv: "0000" 23 | 24 | "ADC (immediate)": 25 | - setup: 26 | registers: 27 | r0: 0 28 | r1: 0 29 | execute: 30 | - "adcs r0, r1" 31 | expect: 32 | registers: 33 | r0: 0 34 | nczv: "0010" 35 | 36 | - setup: 37 | registers: 38 | r0: 1 39 | r1: 2 40 | execute: 41 | - "adcs r0, r1" 42 | expect: 43 | registers: 44 | r0: 3 45 | nczv: "0000" 46 | 47 | - setup: 48 | registers: 49 | r0: -1 50 | r1: 2 51 | execute: 52 | - "adcs r0, r1" 53 | expect: 54 | registers: 55 | r0: 1 56 | nczv: "0100" 57 | 58 | - setup: 59 | registers: 60 | r0: 0xFFFFFFFF 61 | r1: 1 62 | execute: 63 | - "adcs r0, r1" 64 | expect: 65 | registers: 66 | r0: 0 67 | nczv: "0110" 68 | 69 | -------------------------------------------------------------------------------- /test/suites/add.yaml: -------------------------------------------------------------------------------- 1 | "ADD (immediate)": 2 | - setup: 3 | registers: 4 | r0: 0 5 | r1: 10 6 | execute: 7 | - "adds r0, r1, #4" 8 | expect: 9 | registers: 10 | r0: 14 11 | r1: 10 12 | nczv: "0000" 13 | 14 | - setup: 15 | registers: 16 | r0: 10 17 | execute: 18 | - "adds r0, #4" 19 | expect: 20 | registers: 21 | r0: 14 22 | nczv: "0000" 23 | 24 | "ADD (register)": 25 | - setup: 26 | registers: 27 | r0: 0 28 | r1: 0 29 | execute: 30 | - "adds r0, r1" 31 | expect: 32 | registers: 33 | r0: 0 34 | nczv: "0010" 35 | 36 | - setup: 37 | registers: 38 | r0: 1 39 | r1: 2 40 | execute: 41 | - "adds r0, r1" 42 | expect: 43 | registers: 44 | r0: 3 45 | nczv: "0000" 46 | 47 | - setup: 48 | registers: 49 | r0: -1 50 | r1: 2 51 | execute: 52 | - "adds r0, r1" 53 | expect: 54 | registers: 55 | r0: 1 56 | r1: 2 57 | nczv: "0100" 58 | 59 | - setup: 60 | registers: 61 | r0: 0xFFFFFFFF 62 | r1: 0xFFFFFFFF 63 | execute: 64 | - "adds r0, r1" 65 | expect: 66 | registers: 67 | r0: 0xFFFFFFFE 68 | r1: 0xFFFFFFFF 69 | nczv: "1100" 70 | 71 | - setup: 72 | registers: 73 | r0: 0xFFFFFFFF 74 | r1: 1 75 | execute: 76 | - "adds r0, r1" 77 | expect: 78 | registers: 79 | r0: 0 80 | nczv: "0110" 81 | 82 | "ADD (SP plus immediate)": 83 | - setup: 84 | registers: 85 | r0: 0 86 | sp: 100 87 | execute: 88 | - "adds r0, sp, #4" 89 | expect: 90 | registers: 91 | r0: 104 92 | sp: 100 93 | nczv: "0000" 94 | 95 | "ADD (SP plus register)": 96 | - setup: 97 | registers: 98 | r0: 10 99 | sp: 100 100 | execute: 101 | - "adds r0, sp, r0" 102 | expect: 103 | registers: 104 | r0: 110 105 | sp: 100 106 | nczv: "0000" 107 | -------------------------------------------------------------------------------- /test/suites/and.yaml: -------------------------------------------------------------------------------- 1 | "AND (immediate)": 2 | - name: "same source and destination register" 3 | setup: 4 | registers: 5 | r0: 0xFF 6 | execute: 7 | - "and r0, #5" 8 | expect: 9 | registers: 10 | r0: 5 11 | 12 | - name: "different source and destination register" 13 | setup: 14 | registers: 15 | r0: 0xFF 16 | r1: 0x00 17 | execute: 18 | - "and r1, r0, #5" 19 | expect: 20 | registers: 21 | r0: 0xFF 22 | r1: 5 23 | 24 | "AND (register)": 25 | - setup: 26 | registers: 27 | r0: 0xFF 28 | r1: 0x0A 29 | execute: 30 | - "and r0, r1" 31 | expect: 32 | registers: 33 | r0: 0x0A 34 | r1: 0x0A 35 | 36 | - setup: 37 | registers: 38 | r0: 0xF0 39 | r1: 0x0F 40 | execute: 41 | - "ands r0, r1" 42 | expect: 43 | registers: 44 | r0: 0x00 45 | r1: 0x0F 46 | ncz: "001" 47 | 48 | - name: "different source and destination register" 49 | setup: 50 | registers: 51 | r0: 0xFF 52 | r1: 0x01 53 | r2: 0x00 54 | execute: 55 | - "ands r2, r0, r1, LSL #1" 56 | expect: 57 | registers: 58 | r0: 0xFF 59 | r1: 0x01 60 | r2: 0x02 61 | ncz: "000" 62 | -------------------------------------------------------------------------------- /test/suites/asr.yaml: -------------------------------------------------------------------------------- 1 | "ASR (immediate)": 2 | - name: "same source and destination register" 3 | setup: 4 | registers: 5 | r0: 0xF0 6 | execute: 7 | - "asrs r0, r0, #4" 8 | expect: 9 | registers: 10 | r0: 0x0F 11 | ncz: "000" 12 | 13 | - name: "different source and destination register" 14 | setup: 15 | registers: 16 | r0: 0x00 17 | r1: 0xF0 18 | execute: 19 | - "asrs r0, r1, #4" 20 | expect: 21 | registers: 22 | r0: 0x0F 23 | r1: 0xF0 24 | ncz: "000" 25 | 26 | - name: "shift by 0" 27 | setup: 28 | registers: 29 | r0: 0xF0 30 | execute: 31 | - "asrs r0, r0, #0" 32 | expect: 33 | registers: 34 | r0: 0xF0 35 | ncz: "000" 36 | 37 | - name: "result is zero" 38 | setup: 39 | registers: 40 | r0: 0x01 41 | execute: 42 | - "asrs r0, r0, #1" 43 | expect: 44 | registers: 45 | r0: 0x00 46 | ncz: "011" 47 | 48 | "ASR (register)": 49 | - name: "same source and destination register" 50 | setup: 51 | registers: 52 | r0: 0xF0 53 | r1: 0x04 54 | execute: 55 | - "asrs r0, r0, r1" 56 | expect: 57 | registers: 58 | r0: 0x0F 59 | ncz: "000" 60 | 61 | - name: "shift by 0" 62 | setup: 63 | registers: 64 | r0: 0xF0 65 | r1: 0x00 66 | r2: 0x00 67 | execute: 68 | - "asrs r2, r0, r1" 69 | expect: 70 | registers: 71 | r0: 0xF0 72 | r1: 0x00 73 | r2: 0xF0 74 | ncz: "000" 75 | -------------------------------------------------------------------------------- /test/suites/bfc.yaml: -------------------------------------------------------------------------------- 1 | "BFC": 2 | - setup: 3 | registers: 4 | r0: 0xF00 5 | execute: 6 | - "bfc r0, #8, #4" 7 | expect: 8 | registers: 9 | r0: 0x000 10 | ncz: "000" 11 | 12 | - setup: 13 | registers: 14 | r0: 0b111111 15 | execute: 16 | - "bfc r0, #2, #3" 17 | expect: 18 | registers: 19 | r0: 0b100011 20 | ncz: "000" 21 | -------------------------------------------------------------------------------- /test/suites/bfi.yaml: -------------------------------------------------------------------------------- 1 | "BFI": 2 | - setup: 3 | registers: 4 | r0: 0b0000011 5 | r1: 0b1111000 6 | execute: 7 | - "bfi r0, r1, #3, #4" 8 | expect: 9 | registers: 10 | r0: 0b1111011 11 | r1: 0b1111000 12 | ncz: "000" 13 | 14 | - setup: 15 | registers: 16 | r0: 0b0000011 17 | r1: 0b1101000 18 | execute: 19 | - "bfi r0, r1, #3, #4" 20 | expect: 21 | registers: 22 | r0: 0b1101011 23 | r1: 0b1101000 24 | ncz: "000" 25 | -------------------------------------------------------------------------------- /test/suites/bic.yaml: -------------------------------------------------------------------------------- 1 | "BIC (immediate)": 2 | - name: "same source and destination register" 3 | setup: 4 | registers: 5 | r0: 0b0011100 6 | execute: 7 | - "bics r0, #20" 8 | expect: 9 | registers: 10 | r0: 0b0001000 11 | ncz: "000" 12 | 13 | - name: "different source and destination register" 14 | setup: 15 | registers: 16 | r0: 0 17 | r1: 0b0011100 18 | execute: 19 | - "bics r0, r1, #20" 20 | expect: 21 | registers: 22 | r0: 0b0001000 23 | r1: 0b0011100 24 | ncz: "000" 25 | 26 | - setup: 27 | registers: 28 | r0: 0b111 29 | execute: 30 | - "bics r0, #7" 31 | expect: 32 | registers: 33 | r0: 0 34 | ncz: "001" 35 | 36 | "BIC (register)": 37 | - name: "same source and destination register" 38 | setup: 39 | registers: 40 | r0: 0b0111110 41 | r1: 0b0010100 42 | execute: 43 | - "bic r0, r0, r1" 44 | expect: 45 | registers: 46 | r0: 0b0101010 47 | r1: 0b0010100 48 | ncz: "000" 49 | 50 | - name: "different source and destination register" 51 | setup: 52 | registers: 53 | r0: 0b0011100 54 | r1: 0b0010100 55 | r2: 0b0000000 56 | execute: 57 | - "bic r2, r0, r1" 58 | expect: 59 | registers: 60 | r0: 0b0011100 61 | r1: 0b0010100 62 | r2: 0b0001000 63 | ncz: "000" 64 | 65 | - name: "with shift" 66 | setup: 67 | registers: 68 | r0: 0b0111110 69 | r1: 0b0010100 70 | execute: 71 | - "bic r0, r0, r1, lsl #1" 72 | expect: 73 | registers: 74 | r0: 0b0010110 75 | r1: 0b0010100 76 | ncz: "000" 77 | -------------------------------------------------------------------------------- /test/suites/clz.yaml: -------------------------------------------------------------------------------- 1 | "CLZ": 2 | - setup: 3 | registers: 4 | r0: 0b1000 5 | execute: 6 | - "clz r0, r0" 7 | expect: 8 | registers: 9 | r0: 28 10 | 11 | - setup: 12 | registers: 13 | r0: 0 14 | r1: 0b1000 15 | execute: 16 | - "clz r0, r1" 17 | expect: 18 | registers: 19 | r0: 28 20 | r1: 0b1000 21 | 22 | - setup: 23 | registers: 24 | r0: 0 25 | r1: 0 26 | execute: 27 | - "clz r0, r1" 28 | expect: 29 | registers: 30 | r0: 32 31 | r1: 0 32 | -------------------------------------------------------------------------------- /test/suites/cmn.yaml: -------------------------------------------------------------------------------- 1 | "CMN (immediate)": 2 | - setup: 3 | registers: 4 | r0: 0x20 5 | execute: 6 | - "cmn r0, #0x10" 7 | expect: 8 | registers: 9 | r0: == 10 | nczv: "0000" 11 | 12 | - setup: 13 | registers: 14 | r0: 0xFFFF_FFFF 15 | execute: 16 | - "cmn r0, #1" 17 | expect: 18 | registers: 19 | r0: == 20 | nczv: "0110" 21 | 22 | - setup: 23 | registers: 24 | r0: 0xFFFF_FFFE 25 | execute: 26 | - "cmn r0, #1" 27 | expect: 28 | registers: 29 | r0: == 30 | nczv: "1000" 31 | 32 | "CMN (register)": 33 | - setup: 34 | registers: 35 | r0: 0x20 36 | r1: 0x10 37 | execute: 38 | - "cmn r0, r1" 39 | expect: 40 | registers: 41 | r0: == 42 | r1: == 43 | nczv: "0000" 44 | 45 | - setup: 46 | registers: 47 | r0: 0xFFFF_FFFF 48 | r1: 1 49 | execute: 50 | - "cmn r0, r1" 51 | expect: 52 | registers: 53 | r0: == 54 | r1: == 55 | nczv: "0110" 56 | 57 | - setup: 58 | registers: 59 | r0: 0xFFFF_FFFE 60 | r1: 1 61 | execute: 62 | - "cmn r0, r1" 63 | expect: 64 | registers: 65 | r0: == 66 | r1: == 67 | nczv: "1000" 68 | 69 | - name: "shifted" 70 | setup: 71 | registers: 72 | r0: 0xFFFF_FFFC 73 | r1: 1 74 | execute: 75 | - "cmn r0, r1, LSL 2" 76 | expect: 77 | registers: 78 | r0: == 79 | r1: == 80 | nczv: "0110" 81 | -------------------------------------------------------------------------------- /test/suites/cmp.yaml: -------------------------------------------------------------------------------- 1 | "CMP (immediate)": 2 | - setup: 3 | registers: 4 | r0: 0x20 5 | execute: 6 | - "cmp r0, #0x10" 7 | expect: 8 | registers: 9 | r0: == 10 | nczv: "0100" 11 | 12 | - setup: 13 | registers: 14 | r0: 0 15 | execute: 16 | - "cmp r0, #1" 17 | expect: 18 | registers: 19 | r0: == 20 | nczv: "1000" 21 | 22 | - setup: 23 | registers: 24 | r0: 0xFFFF_FFFF 25 | execute: 26 | - "cmp r0, #1" 27 | expect: 28 | registers: 29 | r0: == 30 | nczv: "1100" 31 | 32 | "CMP (register)": 33 | - setup: 34 | registers: 35 | r0: 0x20 36 | r1: 0x10 37 | execute: 38 | - "cmp r0, r1" 39 | expect: 40 | registers: 41 | r0: == 42 | r1: == 43 | nczv: "0100" 44 | 45 | - setup: 46 | registers: 47 | r0: 0x10 48 | r1: 0x20 49 | execute: 50 | - "cmp r0, r1" 51 | expect: 52 | registers: 53 | r0: == 54 | r1: == 55 | nczv: "0000" 56 | 57 | - setup: 58 | registers: 59 | r0: 0x10 60 | r1: 0x10 61 | execute: 62 | - "cmp r0, r1" 63 | expect: 64 | registers: 65 | r0: == 66 | r1: == 67 | nczv: "0010" 68 | 69 | - name: "shifted" 70 | setup: 71 | registers: 72 | r0: 0x4 73 | r1: 1 74 | execute: 75 | - "cmp r0, r1, LSL 2" 76 | expect: 77 | registers: 78 | r0: == 79 | r1: == 80 | nczv: "0110" 81 | -------------------------------------------------------------------------------- /test/suites/eor.yaml: -------------------------------------------------------------------------------- 1 | "EOR (immediate)": 2 | - setup: 3 | registers: 4 | r0: 0b111 5 | execute: 6 | - "eor r0, #0b101" 7 | expect: 8 | registers: 9 | r0: 0b010 10 | nczv: "0000" 11 | 12 | - setup: 13 | registers: 14 | r0: 0b111 15 | execute: 16 | - "eors r0, #0b111" 17 | expect: 18 | registers: 19 | r0: 0b000 20 | nczv: "0010" 21 | 22 | "EOR (register)": 23 | - setup: 24 | registers: 25 | r0: 0b111 26 | r1: 0b101 27 | execute: 28 | - "eor r0, r1" 29 | expect: 30 | registers: 31 | r0: 0b010 32 | r1: == 33 | nczv: "0000" 34 | 35 | - setup: 36 | registers: 37 | r0: 0b111 38 | r1: 0b111 39 | execute: 40 | - "eors r0, r1" 41 | expect: 42 | registers: 43 | r0: 0b000 44 | r1: == 45 | nczv: "0010" 46 | 47 | - name: "shifted" 48 | setup: 49 | registers: 50 | r0: 0b1110 51 | r1: 0b111 52 | execute: 53 | - "eors r0, r0, r1, LSL 1" 54 | expect: 55 | registers: 56 | r0: 0b0000 57 | r1: == 58 | nczv: "0010" 59 | -------------------------------------------------------------------------------- /test/suites/it.yaml: -------------------------------------------------------------------------------- 1 | "IT": 2 | - name: "1 instruction" 3 | setup: 4 | registers: 5 | r0: 0 6 | r1: 0 7 | execute: 8 | - "cmp r0, r0" 9 | - "it eq" 10 | - "addeq r1, #1" 11 | expect: 12 | registers: 13 | r0: == 14 | r1: 1 15 | nczv: "0000" 16 | 17 | - name: "2 instructions" 18 | setup: 19 | registers: 20 | r0: 0 21 | r1: 0 22 | execute: 23 | - "cmp r0, r0" 24 | - "itt eq" 25 | - "addeq r1, #1" 26 | - "addeq r1, #1" 27 | expect: 28 | registers: 29 | r0: == 30 | r1: 2 31 | nczv: "0000" 32 | 33 | - name: "3 instructions" 34 | setup: 35 | registers: 36 | r0: 0 37 | r1: 0 38 | execute: 39 | - "cmp r0, r0" 40 | - "ittt eq" 41 | - "addeq r1, #1" 42 | - "addeq r1, #1" 43 | - "addeq r1, #1" 44 | expect: 45 | registers: 46 | r0: == 47 | r1: 3 48 | nczv: "0000" 49 | 50 | - name: "4 instructions" 51 | setup: 52 | registers: 53 | r0: 0 54 | r1: 0 55 | execute: 56 | - "cmp r0, r0" 57 | - "itttt eq" 58 | - "addeq r1, #1" 59 | - "addeq r1, #1" 60 | - "addeq r1, #1" 61 | - "addeq r1, #1" 62 | expect: 63 | registers: 64 | r0: == 65 | r1: 4 66 | nczv: "0000" 67 | -------------------------------------------------------------------------------- /test/suites/ldm.yaml: -------------------------------------------------------------------------------- 1 | "LDM": 2 | - setup: 3 | registers: 4 | r0: 0x100 5 | memory: 6 | "0x100": [1, 0, 0, 0] 7 | "0x104": [2, 0, 0, 0] 8 | "0x108": [3, 0, 0, 0] 9 | execute: 10 | - "ldm r0, {r1, r2, r3}" 11 | expect: 12 | registers: 13 | r1: 1 14 | r2: 2 15 | r3: 3 16 | 17 | - name: "writeback" 18 | setup: 19 | registers: 20 | r0: 0x100 21 | memory: 22 | "0x100": [1, 0, 0, 0] 23 | "0x104": [2, 0, 0, 0] 24 | "0x108": [3, 0, 0, 0] 25 | execute: 26 | - "ldm r0!, {r1, r2, r3}" 27 | expect: 28 | registers: 29 | r0: 0x10C 30 | r1: 1 31 | r2: 2 32 | r3: 3 33 | -------------------------------------------------------------------------------- /test/suites/ldmdb.yaml: -------------------------------------------------------------------------------- 1 | "LDMDB": 2 | - setup: 3 | registers: 4 | r0: 0x100 5 | memory: 6 | "0x0FC": [1, 0, 0, 0] 7 | "0x0F8": [2, 0, 0, 0] 8 | "0x0F4": [3, 0, 0, 0] 9 | execute: 10 | - "ldmdb r0, {r1, r2, r3}" 11 | expect: 12 | registers: 13 | r0: == 14 | r1: 3 15 | r2: 2 16 | r3: 1 17 | 18 | - name: "writeback" 19 | setup: 20 | registers: 21 | r0: 0x100 22 | memory: 23 | "0x0FC": [1, 0, 0, 0] 24 | "0x0F8": [2, 0, 0, 0] 25 | "0x0F4": [3, 0, 0, 0] 26 | execute: 27 | - "ldmdb r0!, {r1, r2, r3}" 28 | expect: 29 | registers: 30 | r0: 0x0F4 31 | r1: 3 32 | r2: 2 33 | r3: 1 34 | -------------------------------------------------------------------------------- /test/suites/ldr.yaml: -------------------------------------------------------------------------------- 1 | "LDR (immediate)": 2 | - setup: 3 | registers: 4 | r0: 100 5 | memory: 6 | "100": [123, 0, 0, 0] 7 | execute: 8 | - "ldr r1, [r0]" 9 | expect: 10 | registers: 11 | r0: == 12 | r1: 123 13 | 14 | - setup: 15 | registers: 16 | r0: 100 17 | memory: 18 | "104": [123, 0, 0, 0] 19 | execute: 20 | - "ldr r1, [r0, #4]" 21 | expect: 22 | registers: 23 | r0: == 24 | r1: 123 25 | 26 | - name: "pre-indexed, writeback" 27 | setup: 28 | registers: 29 | r0: 100 30 | memory: 31 | "104": [123, 0, 0, 0] 32 | execute: 33 | - "ldr r1, [r0, #4]!" 34 | expect: 35 | registers: 36 | r0: 104 37 | r1: 123 38 | 39 | - name: "post-indexed, writeback" 40 | setup: 41 | registers: 42 | r0: 100 43 | memory: 44 | "100": [123, 0, 0, 0] 45 | execute: 46 | - "ldr r1, [r0], #4" 47 | expect: 48 | registers: 49 | r0: 104 50 | r1: 123 51 | 52 | "LDR (register)": 53 | - setup: 54 | registers: 55 | r0: 100 56 | r1: 4 57 | memory: 58 | "104": [123, 0, 0, 0] 59 | execute: 60 | - "ldr r2, [r0, r1]" 61 | expect: 62 | registers: 63 | r0: == 64 | r1: == 65 | r2: 123 66 | 67 | - name: "shifted" 68 | setup: 69 | registers: 70 | r0: 100 71 | r1: 1 72 | memory: 73 | "104": [123, 0, 0, 0] 74 | execute: 75 | - "ldr r2, [r0, r1, LSL 2]" 76 | expect: 77 | registers: 78 | r0: == 79 | r1: == 80 | r2: 123 81 | -------------------------------------------------------------------------------- /test/suites/ldrb.yaml: -------------------------------------------------------------------------------- 1 | "LDRB (immediate)": 2 | - setup: 3 | registers: 4 | r0: 100 5 | memory: 6 | "100": [123, 0, 0, 0] 7 | execute: 8 | - "ldrb r1, [r0]" 9 | expect: 10 | registers: 11 | r0: == 12 | r1: 123 13 | 14 | - setup: 15 | registers: 16 | r0: 100 17 | memory: 18 | "104": [123, 0, 0, 0] 19 | execute: 20 | - "ldrb r1, [r0, #4]" 21 | expect: 22 | registers: 23 | r0: == 24 | r1: 123 25 | 26 | - name: "pre-indexed, writeback" 27 | setup: 28 | registers: 29 | r0: 100 30 | memory: 31 | "104": [123, 0, 0, 0] 32 | execute: 33 | - "ldrb r1, [r0, #4]!" 34 | expect: 35 | registers: 36 | r0: 104 37 | r1: 123 38 | 39 | - name: "post-indexed, writeback" 40 | setup: 41 | registers: 42 | r0: 100 43 | memory: 44 | "100": [123, 0, 0, 0] 45 | execute: 46 | - "ldrb r1, [r0], #4" 47 | expect: 48 | registers: 49 | r0: 104 50 | r1: 123 51 | 52 | "LDRB (register)": 53 | - setup: 54 | registers: 55 | r0: 100 56 | r1: 4 57 | memory: 58 | "104": [123, 0, 0, 0] 59 | execute: 60 | - "ldrb r2, [r0, r1]" 61 | expect: 62 | registers: 63 | r0: == 64 | r1: == 65 | r2: 123 66 | 67 | - name: "shifted" 68 | setup: 69 | registers: 70 | r0: 100 71 | r1: 1 72 | memory: 73 | "104": [123, 0, 0, 0] 74 | execute: 75 | - "ldrb r2, [r0, r1, LSL 2]" 76 | expect: 77 | registers: 78 | r0: == 79 | r1: == 80 | r2: 123 81 | -------------------------------------------------------------------------------- /test/suites/rsb.yaml: -------------------------------------------------------------------------------- 1 | "RSB (immediate)": 2 | - setup: 3 | registers: 4 | r0: 10 5 | execute: 6 | - "rsb r0, #30" 7 | expect: 8 | registers: 9 | r0: 20 10 | 11 | - setup: 12 | registers: 13 | r0: 10 14 | execute: 15 | - "rsbs r0, #30" 16 | expect: 17 | registers: 18 | r0: 20 19 | nczv: "0100" 20 | 21 | - setup: 22 | registers: 23 | r0: 0 24 | r1: 10 25 | execute: 26 | - "rsb r0, r1, #30" 27 | expect: 28 | registers: 29 | r0: 20 30 | r1: == 31 | -------------------------------------------------------------------------------- /test/suites/str.yaml: -------------------------------------------------------------------------------- 1 | "STR (immediate)": 2 | - name: "index, !writeback" 3 | setup: 4 | memory: 5 | 200: [0, 0, 0, 0] 6 | registers: 7 | r0: 190 8 | r1: 123 9 | execute: 10 | - "str r1, [r0, #10]" 11 | expect: 12 | memory: 13 | 200: [123, 0, 0, 0] 14 | registers: 15 | r0: == 16 | r1: == 17 | 18 | - name: "index, !writeback, subtract" 19 | setup: 20 | memory: 21 | 200: [0, 0, 0, 0] 22 | registers: 23 | r0: 210 24 | r1: 123 25 | execute: 26 | - "str r1, [r0, #-10]" 27 | expect: 28 | memory: 29 | 200: [123, 0, 0, 0] 30 | registers: 31 | r0: == 32 | r1: == 33 | 34 | - name: "index, !writeback, no offset" 35 | setup: 36 | memory: 37 | 200: [0, 0, 0, 0] 38 | registers: 39 | r0: 200 40 | r1: 123 41 | execute: 42 | - "str r1, [r0]" 43 | expect: 44 | memory: 45 | 200: [123, 0, 0, 0] 46 | registers: 47 | r0: == 48 | r1: == 49 | 50 | - name: "index, writeback" 51 | setup: 52 | memory: 53 | 200: [0, 0, 0, 0] 54 | registers: 55 | r0: 190 56 | r1: 123 57 | execute: 58 | - "str r1, [r0, #10]!" 59 | expect: 60 | memory: 61 | 200: [123, 0, 0, 0] 62 | registers: 63 | r0: 200 64 | r1: == 65 | 66 | - name: "index, writeback, subtract" 67 | setup: 68 | memory: 69 | 200: [0, 0, 0, 0] 70 | registers: 71 | r0: 210 72 | r1: 123 73 | execute: 74 | - "str r1, [r0, #-10]!" 75 | expect: 76 | memory: 77 | 200: [123, 0, 0, 0] 78 | registers: 79 | r0: 200 80 | r1: == 81 | 82 | - name: "!index, writeback" 83 | setup: 84 | memory: 85 | 190: [0, 0, 0, 0] 86 | registers: 87 | r0: 190 88 | r1: 123 89 | execute: 90 | - "str r1, [r0], #10" 91 | expect: 92 | memory: 93 | 190: [123, 0, 0, 0] 94 | registers: 95 | r0: 200 96 | r1: == 97 | 98 | - name: "!index, writeback, subtract" 99 | setup: 100 | memory: 101 | 190: [0, 0, 0, 0] 102 | registers: 103 | r0: 190 104 | r1: 123 105 | execute: 106 | - "str r1, [r0], #-10" 107 | expect: 108 | memory: 109 | 190: [123, 0, 0, 0] 110 | registers: 111 | r0: 180 112 | r1: == 113 | 114 | "STR (register)": 115 | - setup: 116 | memory: 117 | 200: [0, 0, 0, 0] 118 | registers: 119 | r0: 190 120 | r1: 10 121 | r2: 123 122 | execute: 123 | - "str r2, [r0, r1]" 124 | expect: 125 | memory: 126 | 200: [123, 0, 0, 0] 127 | registers: 128 | r0: == 129 | r1: == 130 | r2: == 131 | 132 | - name: "shifted offset" 133 | setup: 134 | memory: 135 | 200: [0, 0, 0, 0] 136 | registers: 137 | r0: 190 138 | r1: 5 139 | r2: 123 140 | execute: 141 | - "str r2, [r0, r1, LSL #1]" 142 | expect: 143 | memory: 144 | 200: [123, 0, 0, 0] 145 | registers: 146 | r0: == 147 | r1: == 148 | r2: == 149 | -------------------------------------------------------------------------------- /test/suites/strd.yaml: -------------------------------------------------------------------------------- 1 | "STRD (immediate)": 2 | - name: "index, !writeback" 3 | setup: 4 | memory: 5 | 200: [0, 0, 0, 0] 6 | 204: [0, 0, 0, 0] 7 | registers: 8 | r0: 196 9 | r1: 10 10 | r2: 20 11 | execute: 12 | - "strd r1, r2, [r0, #4]" 13 | expect: 14 | memory: 15 | 200: [10, 0, 0, 0] 16 | 204: [20, 0, 0, 0] 17 | registers: 18 | r0: == 19 | r1: == 20 | r2: == 21 | 22 | - name: "index, !writeback, subtract" 23 | setup: 24 | memory: 25 | 200: [0, 0, 0, 0] 26 | 204: [0, 0, 0, 0] 27 | registers: 28 | r0: 208 29 | r1: 10 30 | r2: 20 31 | execute: 32 | - "strd r1, r2, [r0, #-8]" 33 | expect: 34 | memory: 35 | 200: [10, 0, 0, 0] 36 | 204: [20, 0, 0, 0] 37 | registers: 38 | r0: == 39 | r1: == 40 | r2: == 41 | 42 | - name: "index, !writeback, no offset" 43 | setup: 44 | memory: 45 | 200: [0, 0, 0, 0] 46 | 204: [0, 0, 0, 0] 47 | registers: 48 | r0: 200 49 | r1: 10 50 | r2: 20 51 | execute: 52 | - "strd r1, r2, [r0]" 53 | expect: 54 | memory: 55 | 200: [10, 0, 0, 0] 56 | 204: [20, 0, 0, 0] 57 | registers: 58 | r0: == 59 | r1: == 60 | r2: == 61 | 62 | - name: "index, writeback" 63 | setup: 64 | memory: 65 | 200: [0, 0, 0, 0] 66 | 204: [20, 0, 0, 0] 67 | registers: 68 | r0: 192 69 | r1: 10 70 | r2: 20 71 | execute: 72 | - "strd r1, r2, [r0, #8]!" 73 | expect: 74 | memory: 75 | 200: [10, 0, 0, 0] 76 | 204: [20, 0, 0, 0] 77 | registers: 78 | r0: 200 79 | r1: == 80 | r2: == 81 | 82 | - name: "index, writeback, subtract" 83 | setup: 84 | memory: 85 | 200: [0, 0, 0, 0] 86 | 204: [0, 0, 0, 0] 87 | registers: 88 | r0: 208 89 | r1: 10 90 | r2: 20 91 | execute: 92 | - "strd r1, r2, [r0, #-8]!" 93 | expect: 94 | memory: 95 | 200: [10, 0, 0, 0] 96 | 204: [20, 0, 0, 0] 97 | registers: 98 | r0: 200 99 | r1: == 100 | r2: == 101 | 102 | - name: "!index, writeback" 103 | setup: 104 | memory: 105 | 190: [0, 0, 0, 0] 106 | 194: [0, 0, 0, 0] 107 | registers: 108 | r0: 190 109 | r1: 10 110 | r2: 20 111 | execute: 112 | - "strd r1, r2, [r0], #8" 113 | expect: 114 | memory: 115 | 190: [10, 0, 0, 0] 116 | 194: [20, 0, 0, 0] 117 | registers: 118 | r0: 198 119 | r1: == 120 | r2: == 121 | 122 | - name: "!index, writeback, subtract" 123 | setup: 124 | memory: 125 | 190: [0, 0, 0, 0] 126 | 194: [0, 0, 0, 0] 127 | registers: 128 | r0: 190 129 | r1: 10 130 | r2: 20 131 | execute: 132 | - "strd r1, r2, [r0], #-8" 133 | expect: 134 | memory: 135 | 190: [10, 0, 0, 0] 136 | 194: [20, 0, 0, 0] 137 | registers: 138 | r0: 182 139 | r1: == 140 | r2: == 141 | -------------------------------------------------------------------------------- /test/suites/teq.yaml: -------------------------------------------------------------------------------- 1 | "TEQ (immediate)": 2 | - setup: 3 | registers: 4 | r0: 0b1111 5 | execute: 6 | - "teq r0, #0b1111" 7 | expect: 8 | registers: 9 | r0: == 10 | nzcv: "0100" 11 | 12 | - setup: 13 | registers: 14 | r0: 0b1111 15 | execute: 16 | - "teq r0, #0b1010" 17 | expect: 18 | registers: 19 | r0: == 20 | nzcv: "0000" 21 | 22 | "TEQ (register)": 23 | - setup: 24 | registers: 25 | r0: 0b1111 26 | r1: 0b1111 27 | execute: 28 | - "teq r0, r1" 29 | expect: 30 | registers: 31 | r0: == 32 | nzcv: "0100" 33 | 34 | - setup: 35 | registers: 36 | r0: 0b1111 37 | r1: 0b11110 38 | execute: 39 | - "teq r0, r1, LSR #1" 40 | expect: 41 | registers: 42 | r0: == 43 | r1: == 44 | nzcv: "0100" 45 | -------------------------------------------------------------------------------- /tools/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode -------------------------------------------------------------------------------- /tools/runlog-analyzer/.gitignore: -------------------------------------------------------------------------------- 1 | __debug_bin* -------------------------------------------------------------------------------- /tools/runlog-analyzer/asm.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // #cgo LDFLAGS: -lcapstone 4 | // #include 5 | import "C" 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | type Disassembler struct { 11 | cs C.ulong 12 | } 13 | 14 | type Instruction struct { 15 | Address uint32 16 | Mnemonic string 17 | Bytes []byte 18 | Size uint 19 | } 20 | 21 | func NewDisassembler() (*Disassembler, error) { 22 | var cs C.ulong 23 | if C.cs_open(C.CS_ARCH_ARM, C.CS_MODE_THUMB+C.CS_MODE_MCLASS, &cs) != C.CS_ERR_OK { 24 | return nil, fmt.Errorf("failed to initialize capstone") 25 | } 26 | 27 | return &Disassembler{cs}, nil 28 | } 29 | 30 | func (d *Disassembler) Close() { 31 | C.cs_close(&d.cs) 32 | } 33 | 34 | func (d *Disassembler) Disassemble(code []byte, addr uint32) (*Instruction, error) { 35 | var insn *C.cs_insn 36 | count := C.cs_disasm(d.cs, (*C.uchar)(&code[0]), C.size_t(len(code)), C.uint64_t(addr), 1, &insn) 37 | if count != 1 { 38 | err := C.cs_errno(d.cs) 39 | 40 | return nil, fmt.Errorf("disassemble instruction: %d", err) 41 | } 42 | defer C.cs_free(insn, 1) 43 | 44 | b := make([]byte, insn.size) 45 | for i := 0; i < int(insn.size); i++ { 46 | b[i] = byte(insn.bytes[i]) 47 | } 48 | 49 | return &Instruction{ 50 | Address: uint32(insn.address), 51 | Mnemonic: C.GoString(&insn.mnemonic[0]) + " " + C.GoString(&insn.op_str[0]), 52 | Bytes: b, 53 | Size: uint(insn.size), 54 | }, nil 55 | } 56 | -------------------------------------------------------------------------------- /tools/runlog-analyzer/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pipe01/InfiniEmu/tools/runlog-analyzer 2 | 3 | go 1.22.2 4 | 5 | require github.com/chzyer/readline v1.5.1 6 | 7 | require golang.org/x/sys v0.1.0 // indirect 8 | -------------------------------------------------------------------------------- /tools/runlog-analyzer/go.sum: -------------------------------------------------------------------------------- 1 | github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= 2 | github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= 3 | github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= 4 | github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= 5 | github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= 6 | github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= 7 | golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 8 | golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= 9 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 10 | -------------------------------------------------------------------------------- /tools/runlog-analyzer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/chzyer/readline" 13 | ) 14 | 15 | func parseInt(str string) (int, error) { 16 | n, err := strconv.ParseInt(str, 0, 32) 17 | return int(n), err 18 | } 19 | 20 | var frames Frames 21 | var frameIndex = 0 22 | var frameIndexStack = []int{} 23 | 24 | var bookmarks []int 25 | 26 | func main() { 27 | flag.Parse() 28 | 29 | if flag.NArg() != 1 { 30 | log.Fatalf("usage: %s ", os.Args[0]) 31 | } 32 | 33 | runlogPath := flag.Arg(0) 34 | 35 | f, err := os.Open(runlogPath) 36 | if err != nil { 37 | log.Fatalf("failed to open runlog: %v", err) 38 | } 39 | defer f.Close() 40 | 41 | fmt.Println("Loading frames...") 42 | 43 | frames, err = ReadFrames(f) 44 | if err != nil { 45 | log.Fatalf("failed to read frames: %v", err) 46 | } 47 | 48 | fmt.Printf("Loaded %d frames from %s\n", len(frames), runlogPath) 49 | 50 | pcItems := make([]readline.PrefixCompleterInterface, 0, len(frames)) 51 | for name := range Commands { 52 | pcItems = append(pcItems, readline.PcItem(name)) 53 | } 54 | 55 | rl, err := readline.NewEx(&readline.Config{ 56 | EOFPrompt: "exit", 57 | InterruptPrompt: "", 58 | AutoComplete: readline.NewPrefixCompleter(pcItems...), 59 | }) 60 | if err != nil { 61 | log.Fatalf("failed to create readline: %v", err) 62 | } 63 | defer rl.Close() 64 | 65 | // rl.CaptureExitSignal() 66 | 67 | lastCommand := "" 68 | 69 | for { 70 | if frameIndex < 0 || frameIndex >= len(frames) { 71 | frameIndex = 0 72 | fmt.Printf("frame index out of bounds (%d), resetting to 0. This is most likely a programming error\n", frameIndex) 73 | } 74 | 75 | currentFrame := frames[frameIndex] 76 | 77 | rl.SetPrompt(fmt.Sprintf("#%d (0x%x %s)> ", frameIndex, currentFrame.NextInstruction.Address, currentFrame.NextInstruction.Mnemonic)) 78 | 79 | line, err := rl.Readline() 80 | if err == readline.ErrInterrupt { 81 | if len(line) == 0 { 82 | break 83 | } 84 | continue 85 | } else if err == io.EOF { 86 | break 87 | } 88 | 89 | if line == "" { 90 | line = lastCommand 91 | } else { 92 | lastCommand = line 93 | } 94 | 95 | parts := strings.Split(line, ";") 96 | 97 | for _, part := range parts { 98 | err = executeLine(part) 99 | if err != nil { 100 | if err == ErrExit { 101 | break 102 | } 103 | 104 | fmt.Println(err) 105 | } 106 | } 107 | } 108 | } 109 | 110 | func doFrameJump(newIndex int) { 111 | if newIndex == frameIndex { 112 | return 113 | } 114 | 115 | dir := "forward" 116 | amount := newIndex - frameIndex 117 | if newIndex < frameIndex { 118 | dir = "backward" 119 | amount = frameIndex - newIndex 120 | } 121 | 122 | fmt.Printf("Jumping %d frames %s\n", amount, dir) 123 | 124 | frameIndex = newIndex 125 | } 126 | 127 | func executeLine(line string) error { 128 | defer func() { 129 | if r := recover(); r != nil { 130 | fmt.Println("panicked while executing command:", r) 131 | } 132 | }() 133 | 134 | line = strings.TrimSpace(line) 135 | 136 | if line == "" { 137 | return nil 138 | } 139 | 140 | cmdName, arg, hasArg := strings.Cut(line, " ") 141 | 142 | if hasArg { 143 | arg = strings.TrimSpace(arg) 144 | hasArg = arg != "" 145 | } 146 | 147 | modifier := "" 148 | if cmd, mod, ok := strings.Cut(cmdName, "/"); ok { 149 | cmdName = cmd 150 | modifier = mod 151 | } 152 | 153 | cmd, err := FindCommand(cmdName) 154 | if err != nil { 155 | return err 156 | } 157 | 158 | err = cmd(modifier, arg) 159 | if err != nil { 160 | return err 161 | } 162 | 163 | return nil 164 | } 165 | -------------------------------------------------------------------------------- /tools/simulrun/.gitignore: -------------------------------------------------------------------------------- 1 | toolchain 2 | __debug_bin* -------------------------------------------------------------------------------- /tools/simulrun/asm/assembler.go: -------------------------------------------------------------------------------- 1 | package asm 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "os/exec" 9 | ) 10 | 11 | type ToolPaths struct { 12 | As string 13 | Objcopy string 14 | } 15 | 16 | func Assemble(code string, tools ToolPaths) ([]byte, error) { 17 | asmOut, err := os.CreateTemp("", "") 18 | if err != nil { 19 | return nil, fmt.Errorf("create temp file: %w", err) 20 | } 21 | asmOut.Close() 22 | defer os.Remove(asmOut.Name()) 23 | 24 | var assembly bytes.Buffer 25 | assembly.WriteString(".syntax unified\n") 26 | assembly.WriteString(code) 27 | assembly.WriteByte('\n') 28 | 29 | as := exec.Command(tools.As, "-mcpu=cortex-m4", "-march=armv7-m", "-mthumb", "-o", asmOut.Name()) 30 | as.Stdin = &assembly 31 | as.Stderr = os.Stderr 32 | if err := as.Run(); err != nil { 33 | return nil, fmt.Errorf("run as: %w", err) 34 | } 35 | 36 | binOut, err := os.CreateTemp("", "") 37 | if err != nil { 38 | return nil, fmt.Errorf("create temp file: %w", err) 39 | } 40 | defer os.Remove(binOut.Name()) 41 | defer binOut.Close() 42 | 43 | objcopy := exec.Command(tools.Objcopy, "-Obinary", asmOut.Name(), binOut.Name()) 44 | objcopy.Stderr = os.Stderr 45 | if err := objcopy.Run(); err != nil { 46 | return nil, fmt.Errorf("run objcopy: %w", err) 47 | } 48 | 49 | bin, err := io.ReadAll(binOut) 50 | if err != nil { 51 | return nil, fmt.Errorf("read bin: %w", err) 52 | } 53 | 54 | return bin, nil 55 | } 56 | -------------------------------------------------------------------------------- /tools/simulrun/exchange.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "compress/gzip" 7 | "encoding/base64" 8 | "encoding/binary" 9 | "fmt" 10 | ) 11 | 12 | type ExchangeInstruction struct { 13 | BeforeRegisters [RegisterCount]uint32 14 | ExpectedRegisters [RegisterCount]uint32 15 | Instruction []byte 16 | Mnemonic string 17 | } 18 | 19 | func (i ExchangeInstruction) String() string { 20 | var data bytes.Buffer 21 | 22 | gw := gzip.NewWriter(&data) 23 | bw := bufio.NewWriter(gw) 24 | 25 | bw.WriteByte(byte(len(i.BeforeRegisters))) 26 | for _, reg := range i.BeforeRegisters { 27 | binary.Write(bw, binary.LittleEndian, reg) 28 | } 29 | 30 | bw.WriteByte(byte(len(i.ExpectedRegisters))) 31 | for _, reg := range i.ExpectedRegisters { 32 | binary.Write(bw, binary.LittleEndian, reg) 33 | } 34 | 35 | bw.WriteByte(byte(len(i.Instruction))) 36 | bw.Write(i.Instruction) 37 | 38 | bw.WriteByte(byte(len(i.Mnemonic))) 39 | bw.WriteString(i.Mnemonic) 40 | 41 | bw.Flush() 42 | gw.Close() 43 | 44 | return base64.StdEncoding.EncodeToString(data.Bytes()) 45 | } 46 | 47 | func ParseExchangeInstruction(s string) (ExchangeInstruction, error) { 48 | data, err := base64.StdEncoding.DecodeString(s) 49 | if err != nil { 50 | return ExchangeInstruction{}, err 51 | } 52 | 53 | var i ExchangeInstruction 54 | 55 | r, err := gzip.NewReader(bytes.NewReader(data)) 56 | if err != nil { 57 | return ExchangeInstruction{}, err 58 | } 59 | 60 | var n byte 61 | binary.Read(r, binary.LittleEndian, &n) 62 | if n != byte(len(i.BeforeRegisters)) { 63 | return ExchangeInstruction{}, fmt.Errorf("invalid register count: %d", n) 64 | } 65 | 66 | for j := range i.BeforeRegisters { 67 | binary.Read(r, binary.LittleEndian, &i.BeforeRegisters[j]) 68 | } 69 | 70 | binary.Read(r, binary.LittleEndian, &n) 71 | if n != byte(len(i.ExpectedRegisters)) { 72 | return ExchangeInstruction{}, fmt.Errorf("invalid register count: %d", n) 73 | } 74 | 75 | for j := range i.ExpectedRegisters { 76 | binary.Read(r, binary.LittleEndian, &i.ExpectedRegisters[j]) 77 | } 78 | 79 | binary.Read(r, binary.LittleEndian, &n) 80 | i.Instruction = make([]byte, n) 81 | r.Read(i.Instruction) 82 | 83 | binary.Read(r, binary.LittleEndian, &n) 84 | mnemonic := make([]byte, n) 85 | r.Read(mnemonic) 86 | i.Mnemonic = string(mnemonic) 87 | 88 | return i, nil 89 | } 90 | -------------------------------------------------------------------------------- /tools/simulrun/go.mod: -------------------------------------------------------------------------------- 1 | module simulrun 2 | 3 | go 1.22.2 4 | -------------------------------------------------------------------------------- /tools/web-previewer/cache.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "fmt" 7 | "io" 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | const previewCacheDir = "/tmp/preview-cache" 13 | 14 | func init() { 15 | if err := os.MkdirAll(previewCacheDir, 0755); err != nil { 16 | panic(err) 17 | } 18 | } 19 | 20 | func getCachedPath(job PreviewJob, commitSHA string) string { 21 | scriptSum := md5.Sum([]byte(job.Script)) 22 | 23 | key := fmt.Sprintf("%s-%s-%t", commitSHA, hex.EncodeToString(scriptSum[:]), job.ShowInfo) 24 | 25 | return filepath.Join(previewCacheDir, key) 26 | } 27 | 28 | func GetCachedPreview(job PreviewJob, commitSHA string) (io.ReadCloser, bool) { 29 | path := getCachedPath(job, commitSHA) 30 | 31 | if f, err := os.Open(path); err == nil { 32 | return f, true 33 | } 34 | 35 | return nil, false 36 | } 37 | 38 | func CachePreview(job PreviewJob, commitSHA string, preview io.Reader) error { 39 | path := getCachedPath(job, commitSHA) 40 | 41 | f, err := os.Create(path) 42 | if err != nil { 43 | return fmt.Errorf("create cache file: %w", err) 44 | } 45 | defer f.Close() 46 | 47 | if _, err := io.Copy(f, preview); err != nil { 48 | return fmt.Errorf("write cache file: %w", err) 49 | } 50 | 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /tools/web-previewer/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pipe01/tools/web-previewer 2 | 3 | go 1.23.0 4 | 5 | require github.com/google/go-github/v66 v66.0.0 6 | 7 | require ( 8 | github.com/jellydator/ttlcache/v3 v3.3.0 // indirect 9 | github.com/mattn/go-colorable v0.1.13 // indirect 10 | github.com/mattn/go-isatty v0.0.19 // indirect 11 | golang.org/x/sync v0.8.0 // indirect 12 | golang.org/x/sys v0.12.0 // indirect 13 | ) 14 | 15 | require ( 16 | github.com/fogleman/gg v1.3.0 // indirect 17 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect 18 | github.com/google/go-querystring v1.1.0 // indirect 19 | github.com/rs/zerolog v1.33.0 20 | golang.org/x/image v0.21.0 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /tools/web-previewer/go.sum: -------------------------------------------------------------------------------- 1 | github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 2 | github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= 3 | github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= 4 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 5 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= 6 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= 7 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 8 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 9 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 10 | github.com/google/go-github/v66 v66.0.0 h1:ADJsaXj9UotwdgK8/iFZtv7MLc8E8WBl62WLd/D/9+M= 11 | github.com/google/go-github/v66 v66.0.0/go.mod h1:+4SO9Zkuyf8ytMj0csN1NR/5OTR+MfqPp8P8dVlcvY4= 12 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 13 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 14 | github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc= 15 | github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw= 16 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 17 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 18 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 19 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 20 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 21 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 22 | github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= 23 | github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= 24 | github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= 25 | golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= 26 | golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= 27 | golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= 28 | golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 29 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 30 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 31 | golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= 32 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 33 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 34 | -------------------------------------------------------------------------------- /user_config.h: -------------------------------------------------------------------------------- 1 | // Check include/config.h for the full list of defines. 2 | --------------------------------------------------------------------------------