[
  {
    "path": ".github/workflows/debug.yml",
    "content": "name: Debug\n\non:\n  push:\n    paths-ignore:\n      - \"**/*.md\"\n      - \"docs/**\"\n  pull_request:\n    paths-ignore:\n      - \"**/*.md\"\n      - \"docs/**\"\n  merge_group:\n    paths-ignore:\n      - \"**/*.md\"\n      - \"docs/**\"\n  workflow_dispatch:\n\njobs:\n  build:\n    name: ${{ matrix.config.name }}\n    env:\n      QtLatestVersion: &QtLatest \"6.10.1\"\n      QtBrewVersion: &QtBrew \"6.9.3\"\n      QtMinimalVersion: &QtMinimal \"5.12.0\"\n    needs: [build-riscv-tests, build-stud-support-tests]\n    # Avoid duplicate builds on PR from the same repository\n    if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository\n    runs-on: ${{ matrix.config.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        config:\n          # Linux testing\n          - {\n              name: \"Ubuntu latest (GCC) + packaged Qt6\",\n              os: ubuntu-latest,\n              build_type: \"Debug\",\n              cc: \"gcc\",\n              cxx: \"g++\",\n              build_system: \"Unix Makefiles\",\n              qt_version: \"native\",\n              qt_install_command: \"sudo apt-get update && sudo apt-get install qt6-base-dev\",\n            }\n          - {\n              name: \"Ubuntu latest (GCC) + packaged Qt5\",\n              os: ubuntu-latest,\n              build_type: \"Debug\",\n              cc: \"gcc\",\n              cxx: \"g++\",\n              build_system: \"Unix Makefiles\",\n              qt_version: \"native\",\n              qt_install_command: \"sudo apt-get update && sudo apt-get install qtbase5-dev\",\n            }\n          - {\n              name: \"Ubuntu latest (GCC) + latest Qt6\",\n              os: ubuntu-latest,\n              build_type: \"Debug\",\n              cc: \"gcc\",\n              cxx: \"g++\",\n              build_system: \"Unix Makefiles\",\n              qt_version: *QtLatest,\n            }\n          - {\n              name: \"Ubuntu oldest (GCC) + minimal supported Qt\",\n              os: ubuntu-22.04,\n              build_type: \"Debug\",\n              cc: \"gcc\",\n              cxx: \"g++\",\n              build_system: \"Unix Makefiles\",\n              qt_version: *QtMinimal,\n            }\n          # macOS testing\n          - {\n              name: \"macOS latest ARM64 (Clang) + Qt6\",\n              os: macos-latest,\n              build_type: \"Debug\",\n              cc: \"clang\",\n              cxx: \"clang++\",\n              build_system: \"Unix Makefiles\",\n              qt_version: *QtBrew,\n              # Cached aqt is faster that brew.\n            }\n          - {\n              name: \"macOS latest AMD64 (Clang) + Qt6\",\n              os: macos-15-intel,\n              build_type: \"Debug\",\n              cc: \"clang\",\n              cxx: \"clang++\",\n              build_system: \"Unix Makefiles\",\n              qt_version: *QtBrew,\n              # Cached aqt is faster that brew.\n            }\n          - {\n              name: \"macOS oldest AMD64 (Clang) + minimal supported Qt\",\n              os: macos-15-intel,\n              build_type: \"Debug\",\n              cc: \"clang\",\n              cxx: \"clang++\",\n              build_system: \"Unix Makefiles\",\n              qt_version: *QtMinimal,\n              # Cached aqt is faster that brew.\n            }\n          # Windows testing\n          - {\n              name: \"Windows latest (Clang) + Qt6\",\n              os: windows-latest,\n              build_type: \"Debug\",\n              # on Windows, msbuild does not support Clang with GNU-like interface\n              #  and NMake does not support parallel builds\n              cc: \"clang\",\n              cxx: \"clang++\",\n              build_system: \"Ninja\",\n              qt_version: *QtLatest,\n              qt_arch: \"win64_msvc2022_64\",\n            }\n          - {\n              name: \"Windows oldest (Clang) + minimal supported Qt\",\n              os: windows-2022,\n              build_type: \"Debug\",\n              # on Windows, msbuild does not support Clang with GNU-like interface\n              #  and NMake does not support parallel builds\n              cc: \"clang\",\n              cxx: \"clang++\",\n              build_system: \"Ninja\",\n              qt_version: *QtMinimal,\n              qt_arch: \"win64_msvc2017_64\",\n            }\n          - {\n              name: \"Windows 2022 (MinGW) + Qt5\",\n              os: windows-2022,\n              build_type: \"Debug\",\n              cc: \"gcc\",\n              cxx: \"g++\",\n              build_system: \"Ninja\",\n              # Older Qt releases do not have 64bit mingw release\n              qt_version: \"5.12.9\",\n              qt_arch: \"win64_mingw73\",\n            }\n\n    steps:\n      - uses: actions/checkout@v4\n      - run: git submodule update --recursive --init\n\n      - name: Ccache\n        uses: hendrikmuhs/ccache-action@v1\n        with:\n          key: ${{ github.ref_name }}-${{ matrix.config.name }}\n\n      - name: Install specified Qt version\n        if: matrix.config.qt_version != 'native'\n        uses: jurplel/install-qt-action@v4\n        with:\n          version: ${{ matrix.config.qt_version }}\n          cache: true\n          cache-key-prefix: ${{ runner.os }}-${{ matrix.config.qt_version }}-Qt\n          arch: ${{ matrix.config.qt_arch }}\n          dir: ${{ github.workspace }}/Qt\n\n      - name: Install native Qt by package manager\n        if: matrix.config.qt_version == 'native'\n        run: ${{ matrix.config.qt_install_command }}\n\n      - name: Setup Ninja\n        if: matrix.config.build_system == 'Ninja'\n        uses: seanmiddleditch/gha-setup-ninja@v6\n\n      - name: Create Build Environment\n        run: cmake -E make_directory ${{ github.workspace }}/build\n\n      - name: Configure CMake\n        shell: bash\n        working-directory: ${{github.workspace}}/build\n        run: 'cmake $GITHUB_WORKSPACE\n          -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }}\n          -DCMAKE_C_COMPILER=${{ matrix.config.cc }}\n          -DCMAKE_CXX_COMPILER=${{ matrix.config.cxx }}\n          -DCMAKE_C_COMPILER_LAUNCHER=ccache\n          -DCMAKE_CXX_COMPILER_LAUNCHER=ccache\n          -DFORCE_COLORED_OUTPUT=true\n          -G \"${{ matrix.config.build_system }}\"'\n\n      - name: Build\n        working-directory: ${{ github.workspace }}/build\n        run: cmake --build . -j4\n\n      - name: Deploy on Windows\n        if: runner.os == 'Windows'\n        working-directory: ${{ github.workspace }}/build/target\n        shell: bash\n        run: 'windeployqt \"${{ github.workspace }}/build/target/qtrvsim_gui.exe\"'\n\n      - name: Test\n        working-directory: ${{ github.workspace }}/build\n        shell: bash\n        run: ctest --output-on-failure --verbose\n\n      - name: Get official RISC-V tests\n        if: ${{ steps.cache-tests.outputs.cache-hit != 'true' }}\n        uses: actions/download-artifact@v4\n        with:\n          name: riscv-official-tests\n          path: tests/riscv-official/isa\n\n      - name: Official RISC-V tests (single cycle)\n        # The testing python script does not support Ubuntu 18\n        if: matrix.config.os != 'ubuntu-18.04'\n        working-directory: ${{ github.workspace }}/tests/riscv-official\n        #run: python qtrvsim_tester.py --no-64  ${{ github.workspace }}/build/target/qtrvsim_cli\n        run: python qtrvsim_tester.py -M -A ${{ github.workspace }}/build/target/qtrvsim_cli\n\n      - name: Official RISC-V tests (pipelined)\n        # The testing python script does not support Ubuntu 18\n        if: matrix.config.os != 'ubuntu-18.04'\n        working-directory: ${{ github.workspace }}/tests/riscv-official\n        #run: python qtrvsim_tester.py --no-64  ${{ github.workspace }}/build/target/qtrvsim_cli\n        run: python qtrvsim_tester.py -M -A --pipeline ${{ github.workspace }}/build/target/qtrvsim_cli\n\n      - name: Official RISC-V tests (single cycle, cached)\n        # The testing python script does not support Ubuntu 18\n        if: matrix.config.os != 'ubuntu-18.04'\n        working-directory: ${{ github.workspace }}/tests/riscv-official\n        #run: python qtrvsim_tester.py --no-64  ${{ github.workspace }}/build/target/qtrvsim_cli\n        run: python qtrvsim_tester.py -M -A --cache ${{ github.workspace }}/build/target/qtrvsim_cli\n\n      - name: Official RISC-V tests (pipelined, cached)\n        # The testing python script does not support Ubuntu 18\n        if: matrix.config.os != 'ubuntu-18.04'\n        working-directory: ${{ github.workspace }}/tests/riscv-official\n        #run: python qtrvsim_tester.py --no-64  ${{ github.workspace }}/build/target/qtrvsim_cli\n        run: python qtrvsim_tester.py -M -A --pipeline --cache ${{ github.workspace }}/build/target/qtrvsim_cli\n\n      - name: Get stud-support tests\n        uses: actions/download-artifact@v4\n        with:\n          name: stud-support-tests\n          path: tests/stud-support/elfs\n\n      - name: Stud-support tests (single cycle)\n        if: matrix.config.os != 'ubuntu-18.04'\n        working-directory: ${{ github.workspace }}/tests/stud-support\n        run: python run_tests.py --qtrvsim-cli ${{ github.workspace }}/build/target/qtrvsim_cli --build-dir elfs --stud-support-path .\n\n      - name: Stud-support tests (pipelined)\n        if: matrix.config.os != 'ubuntu-18.04'\n        working-directory: ${{ github.workspace }}/tests/stud-support\n        run: python run_tests.py --qtrvsim-cli ${{ github.workspace }}/build/target/qtrvsim_cli --build-dir elfs --stud-support-path . --pipeline\n\n      - name: Stud-support tests (single cycle, cached)\n        if: matrix.config.os != 'ubuntu-18.04'\n        working-directory: ${{ github.workspace }}/tests/stud-support\n        run: python run_tests.py --qtrvsim-cli ${{ github.workspace }}/build/target/qtrvsim_cli --build-dir elfs --stud-support-path . --cache\n\n      - name: Stud-support tests (pipelined, cached)\n        if: matrix.config.os != 'ubuntu-18.04'\n        working-directory: ${{ github.workspace }}/tests/stud-support\n        run: python run_tests.py --qtrvsim-cli ${{ github.workspace }}/build/target/qtrvsim_cli --build-dir elfs --stud-support-path . --pipeline --cache\n\n      - name: Store created artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: target-${{ runner.name }}\n          path: ${{ github.workspace }}/build/target\n\n  build-emscripten:\n    name: ${{ matrix.config.name }}\n    # Avoid duplicate builds on PR from the same repository\n    if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository\n    runs-on: ${{ matrix.config.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        config:\n          - {\n              name: \"WASM Linux\",\n              os: ubuntu-latest,\n              build_type: Release,\n              qt_arch: wasm_32,\n              emsdk_version: 1.39.8,\n              qt_version: 5.15.2,\n            }\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: Ccache\n        uses: hendrikmuhs/ccache-action@v1.2.11 # https://github.com/hendrikmuhs/ccache-action/issues/181\n        with:\n          key: ${{ github.ref_name }}-${{ matrix.config.name }}\n\n      - name: Setup EMSDK cache\n        id: cache-system-libraries\n        uses: actions/cache@v4\n        with:\n          path: \"emsdk-cache\"\n          key: ${{ runner.os }}-${{ matrix.config.emsdk_version }}-${{ matrix.config.qt_version }}-emsdk\n\n      - name: Setup emsdk\n        uses: mymindstorm/setup-emsdk@v14\n        with:\n          version: ${{ matrix.config.emsdk_version }}\n          actions-cache-folder: \"emsdk-cache\"\n\n      - name: Install Qt\n        uses: jurplel/install-qt-action@v3\n        with:\n          version: ${{ matrix.config.qt_version }}\n          cache: true\n          cache-key-prefix: \"wasm-qt\"\n          arch: ${{ matrix.config.qt_arch }}\n          dir: ${{ github.workspace }}/Qt\n\n      - name: Create Build Environment\n        run: cmake -E make_directory ${{github.workspace}}/build\n\n      - name: Configure CMake\n        working-directory: ${{github.workspace}}/build\n        run: \"emcmake cmake $GITHUB_WORKSPACE\n          -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }}\n          -DCMAKE_PREFIX_PATH=$Qt5_DIR\n          -DCMAKE_FIND_ROOT_PATH=$Qt5_DIR\n          -DCMAKE_C_COMPILER_LAUNCHER=ccache\n          -DCMAKE_CXX_COMPILER_LAUNCHER=ccache\n          -DFORCE_COLORED_OUTPUT=true\n          -Wno-dev\"\n\n      - name: Build\n        working-directory: ${{ github.workspace }}/build\n        run: cmake --build . -j4\n\n      - name: Store created artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: target-wasm-${{ runner.os }}-qt${{ matrix.config.qt_version }}\n          path: ${{ github.workspace }}/build/target\n\n  build-riscv-tests:\n    name: Build official RISC-V tests\n    # Avoid duplicate builds on PR from the same repository\n    if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: actions/checkout@v4\n      - run: git submodule update --recursive --init\n\n      - name: Get cache key\n        uses: mathiasvr/command-output@v2.0.0\n        id: subtree-hash\n        with:\n          run: git rev-parse HEAD:tests/riscv-official/\n\n      - name: Cache\n        id: cache-tests\n        uses: actions/cache@v4\n        with:\n          path: ${{ github.workspace }}/tests/riscv-official/isa\n          key: riscv-tests-${{ steps.subtree-hash.outputs.stdout }}\n\n      - name: Toolchain for official RISC-V tests\n        if: ${{ steps.cache-tests.outputs.cache-hit != 'true' }}\n        working-directory: ${{ github.workspace }}/tests/riscv-official\n        # Version 14 is needed to correctly process newer parts of RV\n        run: |\n          ### Ubuntu 22 already provides those\n          # echo \"deb http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main\" | sudo tee -a /etc/apt/sources.list &&\n          # wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - &&\n          sudo apt-get update &&\n          sudo apt-get install clang-14 lldb-14 lld-14\n\n      - name: Build official RISC-V tests\n        if: ${{ steps.cache-tests.outputs.cache-hit != 'true' }}\n        working-directory: ${{ github.workspace }}/tests/riscv-official/isa\n        env:\n          RISCV_COMPILER: clang-14\n          USE_CLANG_OPTS: true\n          RISCV_OBJDUMP_CMD: llvm-objdump-14\n        run: make\n\n      - name: Store created artifacts\n        # Use of tar significantly improves performance as artifact upload has significant per file penalty\n        uses: actions/upload-artifact@v4\n        with:\n          name: riscv-official-tests\n          path: tests/riscv-official/isa\n\n  build-stud-support-tests:\n    name: Build stud-support tests\n    # Avoid duplicate builds on PR from the same repository\n    if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository\n    runs-on: ubuntu-22.04\n    env:\n      STUD_SUPPORT_REV: 53c684c05dc990e6043fcc3c9a346b2173610731\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: true\n\n      - name: Cache stud-support tests\n        id: cache-stud-support\n        uses: actions/cache@v4\n        with:\n          path: ${{ github.workspace }}/tests/stud-support/elfs\n          key: stud-support-tests-${{ env.STUD_SUPPORT_REV }}\n\n      - name: Checkout stud-support\n        if: ${{ steps.cache-stud-support.outputs.cache-hit != 'true' }}\n        run: |\n          git clone https://gitlab.fel.cvut.cz/b35apo/stud-support.git stud-support\n          cd stud-support\n          git checkout ${{ env.STUD_SUPPORT_REV }}\n\n      - name: Install RISC-V toolchain for stud-support\n        if: ${{ steps.cache-stud-support.outputs.cache-hit != 'true' }}\n        run: |\n          sudo apt-get update &&\n          sudo apt-get install -y gcc-riscv64-unknown-elf\n\n      - name: Build stud-support tests\n        if: ${{ steps.cache-stud-support.outputs.cache-hit != 'true' }}\n        working-directory: ${{ github.workspace }}/tests/stud-support\n        run: python build_tests.py --stud-support-path ../../stud-support\n\n      - name: Store stud-support artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: stud-support-tests\n          path: tests/stud-support/elfs\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "content": "name: Docs\n\non:\n  push:\n    branches:\n      - master\n    paths:\n      - \"docs/user/**\"\n      - \".github/workflows/docs.yml\"\n  workflow_dispatch:\n\npermissions:\n  contents: read\n  pages: write\n  id-token: write\n\nconcurrency:\n  group: \"pages\"\n  cancel-in-progress: true\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n\n      - name: Setup mdBook\n        uses: peaceiris/actions-mdbook@v2\n        with:\n          mdbook-version: \"latest\"\n\n      - name: Build the book\n        run: mdbook build docs/user\n\n      - name: Setup Pages\n        uses: actions/configure-pages@v5\n\n      - name: Prepare artifact with /manual subdirectory\n        run: |\n          rm -rf pages\n          mkdir -p pages/manual\n          cp -a docs/user/book/. pages/manual/\n\n      - name: Upload artifact\n        uses: actions/upload-pages-artifact@v3\n        with:\n          path: pages\n      - name: Create ZIP for Comparch deployment\n        run: |\n          cd docs/user/book\n          zip -r ../../../manual.zip .\n      - name: Deploy to Comparch\n        run: |\n          curl -X POST -H \"Authorization: Bearer ${{ secrets.COMPARCH_DOCS_TOKEN }}\" -F \"file=@manual.zip\" http://eval.comparch.edu.cvut.cz:1111/github-actions/update-manual\n\n  deploy:\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    runs-on: ubuntu-latest\n    needs: build\n    steps:\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non: workflow_dispatch\n\njobs:\n  build:\n    name: ${{ matrix.config.name }}\n    runs-on: ${{ matrix.config.os }}\n    strategy:\n      fail-fast: true\n      matrix:\n        config:\n          - {\n            name: \"OBS bundle (Ubuntu Latest GCC)\",\n            os: ubuntu-latest,\n            build_type: \"Release\",\n            cc: \"gcc\", cxx: \"g++\"\n          }\n\n    steps:\n      - uses: actions/checkout@v4\n\n      # CMake will not configure without Qt installed\n      - name: Install Qt\n        run: sudo apt-get update && sudo apt-get install qtbase5-dev\n\n      - name: Create Build Environment\n        run: cmake -E make_directory ${{ github.workspace }}/build\n\n      - name: Configure CMake\n        shell: bash\n        working-directory: ${{ github.workspace }}/build\n        env:\n          CC: ${{ matrix.config.cc }}\n          CXX: ${{ matrix.config.cxx }}\n        run: \"cmake $GITHUB_WORKSPACE\n                    -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }}\n                    -DDEV_MODE=true\"\n\n      - name: Create OBS bundle\n        working-directory: ${{ github.workspace }}/build\n        run: make open_build_service_bundle\n\n      - name: Store OBS bundle\n        uses: actions/upload-artifact@v4\n        with:\n          name: obs-package-bundle\n          path: ${{ github.workspace }}/build/target/pkg\n"
  },
  {
    "path": ".gitignore",
    "content": ".*\n!.gitignore\n!.gitmodules\n!.github\n\n# Qt stuff\n*.pro.user*\n*.qbs.user*\n\n# Common test directory\ntest_dir\n# Build directory\nbuild\n/CMakeFiles/\ncompile_commands.json\n/src/project_info.h\n# Local settings template\n.dev-config.mk\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"riscv-tests\"]\n\tpath = tests/riscv-official/riscv-tests\n\turl = https://github.com/riscv-software-src/riscv-tests.git\n[submodule \"external/libelfin\"]\n\tpath = external/libelfin\n\turl = https://github.com/mortbopet/libelfin\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\ncmake_policy(VERSION 3.10)\n\nproject(QtRVSim\n        LANGUAGES C CXX\n        VERSION 0.9.8\n        DESCRIPTION \"RISC-V CPU simulator for education purposes\")\n\nset(KAREL_KOCI \"Karel Koci <cynerd@email.cz>\")\nset(PAVEL_PISA \"Pavel Pisa <pisa@cmp.felk.cvut.cz>\")\nset(JAKUB_DUPAK \"Jakub Dupak <dev@jakubdupak.com>\")\nset(MAX_HOLLMANN \"Max Hollmann <hollmmax@fel.cvut.cz>\")\n\nset(PROJECT_HOMEPAGE_URL \"https://github.com/cvut/qtrvsim\")\nset(GENERIC_NAME \"RISC-V CPU simulator\")\nset(LICENCE \"GPL-3.0-or-later\")\nset(LONG_DESCRIPTION\n        \"RISC-V CPU simulator for education purposes with pipeline and cache visualization.\")\nstring(TIMESTAMP YEAR \"%Y\")\n\ninclude(cmake/CopyrightTools.cmake)\n\n\ncopyright(\n        \"Copyright (c) 2017-2019 ${KAREL_KOCI}\"\n        \"Copyright (c) 2019-${YEAR} ${PAVEL_PISA}\"\n        \"Copyright (c) 2020-${YEAR} ${JAKUB_DUPAK}\"\n        \"Copyright (c) 2020-2021 ${MAX_HOLLMANN}\")\n\ninclude(cmake/GPL-3.0-or-later.cmake)\n\n# =============================================================================\n# Configurable options\n# =============================================================================\n\nset(DEV_MODE false CACHE BOOL \"Enable developer options in this CMake, like packaging.\\\n    They should be ignored, when user just wants to build this project.\")\nset(FORCE_ELFLIB_STATIC false CACHE BOOL\n        \"Use included statically linked libelf even if system one is available.\")\nset(SANITIZERS \"address,undefined\" CACHE STRING\n        \"Runtime sanitizers to use in debug builds.\n    Column separated subset of {address, memory, undefined, thread} or none.\n    Memory and address cannot be used at the same time.\")\nset(EXECUTABLE_OUTPUT_PATH \"${CMAKE_BINARY_DIR}/target\"\n        CACHE STRING \"Absolute path to place executables to.\")\nset(PACKAGE_OUTPUT_PATH \"${EXECUTABLE_OUTPUT_PATH}/pkg\"\n        CACHE STRING \"Absolute path to place generated package files.\")\nset(FORCE_COLORED_OUTPUT false CACHE BOOL \"Always produce ANSI-colored output (GNU/Clang only).\")\nset(USE_ALTERNATE_LINKER \"\" CACHE STRING \"Use alternate linker. Leave empty for system default; alternatives are 'gold', 'lld', 'bfd', 'mold'\")\nset(QT_VERSION_MAJOR \"auto\" CACHE STRING \"Qt major version to use. 5|6|auto\")\n\n# =============================================================================\n# Generated variables\n# =============================================================================\n\nif (NOT \"${QT_VERSION_MAJOR}\" MATCHES \"5|6|auto\")\n    message(FATAL_ERROR \"Invalid value for QT_VERSION_MAJOR: ${QT_VERSION_MAJOR} (expected 5, 6 or auto)\")\nendif ()\n\nif (${CMAKE_SYSTEM_NAME} MATCHES \"Emscripten\")\n    set(WASM true)\nelse ()\n    set(WASM false)\nendif ()\nset(CXX_TEST_PATH ${EXECUTABLE_OUTPUT_PATH})\nset(C_TEST_PATH ${EXECUTABLE_OUTPUT_PATH})\n\n\nmacro(set_alternate_linker linker)\n    find_program(LINKER_EXECUTABLE ld.${USE_ALTERNATE_LINKER} ${USE_ALTERNATE_LINKER})\n    if (LINKER_EXECUTABLE)\n        if (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\" AND \"${CMAKE_CXX_COMPILER_VERSION}\" VERSION_LESS 12.0.0)\n            add_link_options(\"-ld-path=${USE_ALTERNATE_LINKER}\")\n        else ()\n            add_link_options(\"-fuse-ld=${USE_ALTERNATE_LINKER}\")\n        endif ()\n    else ()\n        set(USE_ALTERNATE_LINKER \"\" CACHE STRING \"Use alternate linker\" FORCE)\n    endif ()\nendmacro()\nif (NOT \"${USE_ALTERNATE_LINKER}\" STREQUAL \"\")\n    set_alternate_linker(${USE_ALTERNATE_LINKER})\nendif ()\n\n# I don't want to relly on the assumption, that this file is invoked as root\n# project. Therefore I propagate the information to all subprojects\n# MAIN_PROJECT_*. Lowercase and uppercase are used for executable names and\n# C defines, respectively.\nset(MAIN_PROJECT_NAME \"${PROJECT_NAME}\")\nset(MAIN_PROJECT_VERSION \"${PROJECT_VERSION}\")\nset(MAIN_PROJECT_APPID \"cz.cvut.edu.comparch.qtrvsim\")\nset(MAIN_PROJECT_ORGANIZATION \"FEE CTU\")\nset(MAIN_PROJECT_HOMEPAGE_URL \"${PROJECT_HOMEPAGE_URL}\")\nstring(TOLOWER \"${PROJECT_NAME}\" MAIN_PROJECT_NAME_LOWER)\nstring(TOUPPER \"${PROJECT_NAME}\" MAIN_PROJECT_NAME_UPPER)\n\n# =============================================================================\n# CMake config and tools\n# =============================================================================\n\nlist(APPEND CMAKE_MODULE_PATH \"${PROJECT_SOURCE_DIR}/cmake\")\n\nif (CMAKE_VERSION VERSION_LESS \"3.7.0\")\n    set(CMAKE_INCLUDE_CURRENT_DIR ON)\nendif ()\n\ninclude(cmake/BuildType.cmake)\nset(CMAKE_INCLUDE_CURRENT_DIR ON)\n\n# =============================================================================\n# Build options\n# - common to all subdirs\n# =============================================================================\n\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\n# MSVC is not supported, Clang+MSVC currently does not seem to support sanitizers in with debug CRT,\n#  MinGW does not support sanitizers at all; all together, just skip sanitizers on Windows and re-investigate\n#  in a year or two whether Clang sanitizer support has improved\nif (NOT \"${SANITIZERS}\" MATCHES \"none\" AND NOT \"${WASM}\" AND NOT \"${WIN32}\")\n    set(CMAKE_C_FLAGS_DEBUG\n            \"${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=${SANITIZERS} -g -g3 -ggdb\")\n    set(CMAKE_CXX_FLAGS_DEBUG\n            \"${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=${SANITIZERS} -g -g3 -ggdb\")\n    set(CMAKE_LINKER_FLAGS_DEBUG\n            \"${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=${SANITIZERS}\")\nendif ()\n\nif (NOT \"${BUILD_DEBUG}\")\n    message(STATUS \"Debug prints globally suppressed.\")\n    add_definitions(-DQT_NO_DEBUG_OUTPUT=1)\nendif ()\n\nadd_compile_definitions(QT_USE_QSTRINGBUILDER)\n\n# Profiling flags\nif (NOT \"${WASM}\" AND NOT \"${WIN32}\")\n    set(CMAKE_C_FLAGS_RELWITHDEBINFO \"${CMAKE_C_FLAGS_RELWITHDEBINFO} -fno-omit-frame-pointer\")\n    set(CMAKE_CXX_FLAGS_RELWITHDEBINFO \"${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fno-omit-frame-pointer\")\n    set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO \"${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} -fno-omit-frame-pointer\")\nendif ()\n\ninclude_directories(\"src\" \"src/machine\")\n\n\nif (${FORCE_COLORED_OUTPUT})\n    if (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"GNU\")\n        add_compile_options(-fdiagnostics-color=always)\n    elseif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\n        add_compile_options(-fcolor-diagnostics)\n    endif ()\nendif ()\n\n## ============================================================================\n## Warning level\n## ============================================================================\n\nif (WIN32)\n    # otherwise CRT complains that `strerror` is deprecated\n    add_compile_definitions(_CRT_SECURE_NO_WARNINGS)\nendif ()\n\nif (MSVC)\n    add_compile_options(/W4 /WX)\nelse ()\n    add_compile_options(-Wall -Wextra -Werror=switch)\n    if (CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\")\n        # This is currently a wont-fix and it will be OK in cpp20.\n        add_compile_options(-Wno-c99-designator)\n    endif ()\nendif ()\n\n# =============================================================================\n# Dependencies\n# =============================================================================\n\nif (\"${WASM}\")\n    message(STATUS \"WASM build detected\")\n\n    message(STATUS \"Enabled WASM exception handling\")\n    add_compile_options(\"-fexceptions\")\n    # Extra options for WASM linking\n    add_link_options(\"-fexceptions\")\n    add_link_options(\"-flto\")\n    # Activate Embind C/C++ bindings\n    # https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html\n    add_link_options(\"--bind\")\n    # Enable C++ exception catching\n    # https://emscripten.org/docs/optimizing/Optimizing-Code.html#c-exceptions\n    add_link_options(\"SHELL:-s DISABLE_EXCEPTION_CATCHING=0\")\n    # Enable Fetch API\n    # https://emscripten.org/docs/api_reference/fetch.html\n    add_link_options(\"SHELL:-s FETCH=1\")\n    add_link_options(\"SHELL:-s WASM=1\")\n    # Emulate missing OpenGL ES2/ES3 features\n    # https://emscripten.org/docs/porting/multimedia_and_graphics/OpenGL-support.html#opengl-es-2-0-3-0-emulation\n    add_link_options(\"SHELL:-s FULL_ES2=1\")\n    add_link_options(\"SHELL:-s FULL_ES3=1\")\n    # Activate WebGL 2 (in addition to WebGL 1)\n    # https://emscripten.org/docs/porting/multimedia_and_graphics/OpenGL-support.html#webgl-friendly-subset-of-opengl-es-2-0-3-0\n    add_link_options(\"SHELL:-s USE_WEBGL2=1\")\n    # Run static dtors at teardown\n    # https://emscripten.org/docs/getting_started/FAQ.html#what-does-exiting-the-runtime-mean-why-don-t-atexit-s-run\n    add_link_options(\"SHELL:-s ALLOW_MEMORY_GROWTH=1\")\n    # Export UTF16ToString,stringToUTF16\n    # Required by https://codereview.qt-project.org/c/qt/qtbase/+/286997 (since Qt 5.14)\n    add_link_options(\"SHELL:-s EXTRA_EXPORTED_RUNTIME_METHODS=[\\\"UTF16ToString\\\",\\\"stringToUTF16\\\"]\")\n    # Enable demangling of C++ stack traces\n    # https://emscripten.org/docs/porting/Debugging.html\n    add_link_options(\"SHELL:-s DEMANGLE_SUPPORT=1\")\n    # Run static dtors at teardown\n    # https://emscripten.org/docs/getting_started/FAQ.html#what-does-exiting-the-runtime-mean-why-don-t-atexit-s-run\n    add_link_options(\"SHELL:-s EXIT_RUNTIME=1\")\n\n    # Debug build\n    if (\"${BUILD_DEBUG}\")\n        message(STATUS \"Enabled WASM debug\")\n        add_link_options(\"SHELL:-s ERROR_ON_WASM_CHANGES_AFTER_LINK\")\n        add_link_options(\"SHELL:-s WASM_BIGINT\")\n        add_link_options(\"-O1\")\n    endif ()\n\n    add_definitions(-DQT_NO_DEBUG_OUTPUT=1)\n    message(STATUS \"Debug output disabled\")\nelse ()\n    # Not available for WASM\n    enable_testing()\nendif ()\n\nset(CMAKE_POLICY_VERSION_MINIMUM 3.5)\nset(CMAKE_PROJECT_libelfin_INCLUDE \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibElfinSettings.cmake\")\nadd_subdirectory(\"external/libelfin\")\n\n# Detect Qt used qt version\n# Based on article https://www.steinzone.de/wordpress/how-to-support-both-qt5-and-qt6-using-cmake/\n# Cannot use version-less approach due to Qt 5.9.5 support constraint.\n\nif (\"${QT_VERSION_MAJOR}\" STREQUAL \"auto\")\n    find_package(QT NAMES Qt5 Qt6 COMPONENTS Core REQUIRED)\nendif ()\n\n# Normally, we would use variable Qt5 or Qt6 to reference the Qt library. Here we do that through\n# this variable based on detected version major of Qt.\nset(QtLib \"Qt${QT_VERSION_MAJOR}\")\nfind_package(${QtLib}\n        REQUIRED COMPONENTS Core Widgets Gui Test\n        OPTIONAL_COMPONENTS PrintSupport)\n\nmessage(STATUS \"${QtLib} version: ${${QtLib}Core_VERSION}\")\nmessage(STATUS \"${QtLib} print support: ${${QtLib}PrintSupport_FOUND}\")\n\nif (\"${WASM}\")\n    string(TIMESTAMP DATE \"%Y%m%d\")\n    file(READ \"${${QtLib}Core_DIR}/../../../plugins/platforms/qtloader.js\" QTLOADER)\n    string(REPLACE \"applicationName + \\\".js\\\"\" \"applicationName + \\\".js?v=${DATE}\\\"\" QTLOADER \"${QTLOADER}\")\n    string(REPLACE \"applicationName + \\\".wasm\\\"\" \"applicationName + \\\".wasm?v=${DATE}\\\"\" QTLOADER \"${QTLOADER}\")\n    file(WRITE \"${CMAKE_CURRENT_BINARY_DIR}/target/qtloader.js\" \"${QTLOADER}\")\nendif ()\n\n\n# Qt 5.9.5 is the oldest supported\nadd_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050905)\n\nadd_subdirectory(\"external/svgscene\")\n\n# =============================================================================\n# Sources\n# =============================================================================\n\nadd_subdirectory(\"src/common\")\nadd_subdirectory(\"src/machine\")\nadd_subdirectory(\"src/assembler\")\nadd_subdirectory(\"src/os_emulation\")\nadd_subdirectory(\"src/gui\")\nif (NOT \"${WASM}\")\n    add_subdirectory(\"src/cli\")\n    add_custom_target(all_unit_tests\n            DEPENDS common_unit_tests machine_unit_tests)\nendif ()\n\n# =============================================================================\n# Installation\n# =============================================================================\n\n# Prior to CMake version 3.13, installation must be performed in the subdirectory,\n# there the target was created. Therefore executable installation is to be found\n# in corresponding CMakeLists.txt.\n\nif (NOT \"${WASM}\")\n    configure_file(data/gui.desktop.in\n            \"${EXECUTABLE_OUTPUT_PATH}/${MAIN_PROJECT_NAME_LOWER}.desktop\")\n    configure_file(\"data/${MAIN_PROJECT_APPID}.metainfo.xml.in\"\n            \"${EXECUTABLE_OUTPUT_PATH}/${MAIN_PROJECT_APPID}.metainfo.xml\")\n\n    install(FILES \"data/icons/gui.svg\"\n            DESTINATION \"share/icons/hicolor/scalable/apps\"\n            RENAME \"${MAIN_PROJECT_NAME_LOWER}_gui.svg\")\n    install(FILES \"data/icons/48x48/gui.png\"\n            DESTINATION \"share/icons/hicolor/48x48/apps\"\n            RENAME \"${MAIN_PROJECT_NAME_LOWER}_gui.png\")\n    install(FILES \"${EXECUTABLE_OUTPUT_PATH}/${MAIN_PROJECT_NAME_LOWER}.desktop\"\n            DESTINATION share/applications)\n\n    install(FILES \"${EXECUTABLE_OUTPUT_PATH}/${MAIN_PROJECT_APPID}.metainfo.xml\"\n            DESTINATION share/metainfo)\nendif ()\n\n# =============================================================================\n# Packages\n# =============================================================================\n\nif (\"${DEV_MODE}\")\n    # The condition prevents execution of this section during regular user installation.\n    # It created files useless to normal users and requires additional tools (git, xz).\n    message(STATUS \"Packaging tools enabled.\")\n\n    set(PACKAGE_NAME \"${MAIN_PROJECT_NAME_LOWER}\")\n    set(PACKAGE_VERSION \"${PROJECT_VERSION}\")\n    set(PACKAGE_RELEASE \"1\")\n    set(PACKAGE_SOURCE_ARCHIVE_FILE \"${PACKAGE_NAME}_${PACKAGE_VERSION}.orig.tar.xz\")\n    set(PACKAGE_SOURCE_ARCHIVE_PATH \"${PACKAGE_OUTPUT_PATH}/${PACKAGE_SOURCE_ARCHIVE_FILE}\")\n    set(PACKAGE_TOPLEVEL_DIR \"${PACKAGE_NAME}-${PACKAGE_VERSION}\")\n    set(PACKAGE_DESCRIPTION \"${PROJECT_DESCRIPTION}\")\n    set(PACKAGE_LONG_DESCRIPTION \"${LONG_DESCRIPTION}\")\n    set(PACKAGE_MAINTAINER \"${JAKUB_DUPAK}\")\n    set(PACKAGE_URL \"${PROJECT_HOMEPAGE_URL}\")\n    set(PACKAGE_GIT \"github.com:cvut/qtrvsim.git\")\n    set(PACKAGE_LICENCE \"${LICENCE}\")\n\n    include(cmake/PackageTools.cmake)\n\n    # Inject up-to-date information into package config files.\n    package_config_file(appimage appimage.yml extras/packaging/appimage/appimage.yml.in)\n    package_config_file(archlinux PKGBUILD extras/packaging/arch/PKGBUILD.in)\n    package_config_file(rpm ${PACKAGE_NAME}.spec extras/packaging/rpm/spec.in)\n    # Debian uses whole directory which has to be saved to archive and shipped.\n    package_debian_quilt(deb\n            ${PACKAGE_NAME}_${PACKAGE_VERSION}-${PACKAGE_RELEASE}.dsc\n            extras/packaging/deb/dsc.in\n            extras/packaging/deb/debian\n            ${PACKAGE_NAME}_${PACKAGE_VERSION}-${PACKAGE_RELEASE}.debian.tar.xz)\n    # Creates bunch of files in ${CMAKE_BINARY_DIR}/target/pkg that you can just pass to\n    # Open Build Service and it will build all packaging.\n    # TODO: Currently changelog is not handled automatically.\n    add_custom_target(open_build_service_bundle\n            DEPENDS ${PACKAGE_SOURCE_ARCHIVE_FILE} appimage archlinux deb rpm\n            WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}/pkg)\nendif ()\n\nconfigure_file(src/project_info.h.in ${CMAKE_CURRENT_LIST_DIR}/src/project_info.h @ONLY)\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    {one line to give the program's name and a brief idea of what it does.}\n    Copyright (C) {year}  {name of author}\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    {project}  Copyright (C) {year}  {fullname}\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n"
  },
  {
    "path": "Makefile",
    "content": "include .dev-config.mk\n\n.dev-config.mk:\n\t[ ! -f .dev-config.mk ] && cp .dev-config.mk.default .dev-config.mk\n\nrelease:\n\tmkdir -p build/Release\n\tcd build/Release && cmake -DCMAKE_BUILD_TYPE=Release ../..\n\tcd build/Release && cmake --build .\n\ndebug:\n\tmkdir -p build/Debug\n\tcd build/Debug && cmake -DCMAKE_BUILD_TYPE=Debug ../..\n\tcd build/Debug && cmake --build .\n\n########################################################################################################################\n# WASM\n########################################################################################################################\n\nwasm:\n\tbash extras/building/build-wasm.sh $(EMSCRIPTEN_VERSION) $(QT_WASM_PATH) $(EMSDK_PATH)\n\nwasm-clean:\n\trm -rf $(WASM_BUILD_DIR)\n\nwasm-clean-deep: wasm-clean\n\trm -rf $(EMSDK_PATH)/upstream/cache/*\n\trm -rf $(EMSDK_PATH)/upstream/emscripten/cache/*\n\nwasm-serve:\n\tcp data/wasm/* $(WASM_BUILD_DIR)/target\n\t# Server is necessary due to CORS\n\tcd $(WASM_BUILD_DIR)/target/ && python -m http.server\n\topen localhost:8000\n\nwasm-install-qt:\n\tpython -m aqt install $(QT_WASM_VERSION) linux desktop wasm_32 --outputdir $(QT_INSTALL_DIR)\n\n.PHONY: release debug wasm wasm-clean wasm-clean-deep wasm-serve wasm-install-qt"
  },
  {
    "path": "README.md",
    "content": "# QtRvSim–RISC-V CPU simulator for education\n\n![QtRvSim screenshot](data/screenshots/intro.webp)\n\nDeveloped by the [Computer Architectures Education](http://comparch.edu.cvut.cz) project\nat [Czech Technical University](http://www.cvut.cz/).\n\n**Are you using QtRvSim at your organization?** [Please, let us know in the discussion!](https://github.com/cvut/qtrvsim/discussions/136)\n\n## Table of contents\n\n<!-- TOC start -->\n\n- [Try it out! (WebAssembly)](#try-it-out-webassembly)\n- [Build and packages](#build-and-packages)\n  - [Build Dependencies](#build-dependencies)\n  - [General Compilation](#general-compilation)\n  - [Building from source on macOS](#building-from-source-on-macos)\n  - [Download Binary Packages](#download-binary-packages)\n  - [Nix package](#nix-package)\n  - [Tests](#tests)\n- [Documentation](#documentation)\n- [Accepted Binary Formats](#accepted-binary-formats)\n  - [LLVM toolchain usage](#llvm-toolchain-usage)\n  - [GNU toolchain usage](#gnu-toolchain-usage)\n  - [GNU 64-bit toolchain use for RV32I target](#gnu-64-bit-toolchain-use-for-rv32i-target)\n- [Integrated Assembler](#integrated-assembler)\n- [Support to call external make utility](#support-to-call-external-make-utility)\n- [Advanced functionalities](#advanced-functionalities)\n  - [Peripherals](#peripherals)\n  - [Interrupts and Control and Status Registers](#interrupts-and-control-and-status-registers)\n  - [System Calls Support](#system-calls-support)\n- [Limitations of the Implementation](#limitations-of-the-implementation)\n  - [QtRvSim limitations](#qtrvsim-limitations)\n  - [List of Currently Supported Instructions](#list-of-currently-supported-instructions)\n- [Links to Resources and Similar Projects](#links-to-resources-and-similar-projects)\n- [Copyright](#copyright)\n- [License](#license)\n\n<!-- TOC end -->\n\n## Try it out! (WebAssembly)\n\nQtRVSim is experimentally available for [WebAssembly](https://webassembly.org/) and it can be run in most browsers\nwithout installation. **[QtRVSim online](https://comparch.edu.cvut.cz/qtrvsim/app)**\n\n**Note, that WebAssembly version is experimental.**\nPlease, report any difficulties via [GitHub issues](https://github.com/cvut/qtrvsim/issues/new).\n\n## Build and packages\n\n[![Packaging status](https://repology.org/badge/vertical-allrepos/qtrvsim.svg)](https://repology.org/project/qtrvsim/versions)\n\n[![build result](https://build.opensuse.org/projects/home:jdupak/packages/qtrvsim/badge.svg?type=default)](https://build.opensuse.org/package/show/home:jdupak/qtrvsim)\n\n### Build Dependencies\n\n- Qt 5 (minimal tested version is 5.9.5), experimental support of Qt 6\n- elfutils (optional; libelf works too but there can be some problems)\n\n### Quick Compilation on Linux\n\nOn Linux, you can use a wrapper Makefile and run `make` in the project root directory. It will create a build directory\nand run CMake in it. Available targets are: `release` (default) and `debug`.\n\nNote for packagers: This Makefile is deleted by CMake when source archive is created to avoid any ambiguity. Packages\nshould invoke CMake directly.\n\n### General Compilation\n\n```shell\ncmake -DCMAKE_BUILD_TYPE=Release /path/to/qtrvsim\nmake\n```\n\nWhere `/path/to/qtrvsim` is path to this project root. The built binaries are to be found in the directory `target`in\nthe build directory (the one, where cmake was called).\n\n`-DCMAKE_BUILD_TYPE=Debug` builds development version including sanitizers.\n\nIf no build type is supplied, `Debug` is the default.\n\n### Building from source on macOS\n\nInstall the latest version of **Xcode** from the App Store. Then open a terminal and execute `xcode-select --install` to\ninstall Command Line Tools. Then open Xcode, accept the license agreement and wait for it to install any additional\ncomponents. After you finally see the \"Welcome to Xcode\" screen, from the top bar\nchoose `Xcode -> Preferences -> Locations -> Command Line Tools` and select an SDK version.\n\nInstall [Homebrew](https://brew.sh/) and use it to install Qt. (macOS builds must use the bundled libelf)\n\n```shell\nbrew install qt\n```\n\nNow build the project the same way as in general compilation ([above](#general-compilation)).\n\n### Download Binary Packages\n\n- [https://github.com/cvut/qtrvsim/releases](https://github.com/cvut/qtrvsim/releases)\n    - archives with Windows and generic GNU/Linux binaries\n- [https://build.opensuse.org/repositories/home:jdupak/qtrvsim](https://build.opensuse.org/repositories/home:jdupak/qtrvsim)\n- [https://software.opensuse.org/download.html?project=home%3Ajdupak&package=qtrvsim](https://software.opensuse.org/download.html?project=home%3Ajdupak&package=qtrvsim)\n    - Open Build Service binary packages\n- [https://launchpad.net/~qtrvsimteam/+archive/ubuntu/ppa](https://launchpad.net/~qtrvsimteam/+archive/ubuntu/ppa)\n    - Ubuntu PPA archive\n\n```bash\nsudo add-apt-repository ppa:qtrvsimteam/ppa\nsudo apt-get update\nsudo apt-get install qtrvsim\n```\n\n### Nix package\n\nQtRVSim provides a Nix package as a part of the repository. You can build and install it by a command bellow. Updates\nhave to be done manually by checking out the git. NIXPKGS package is in PR phase.\n\n```shell\nnix-env -if .\n```\n\n### Tests\n\nTests are managed by CTest (part of CMake). To build and run all tests, use this commands:\n\n```bash\ncmake -DCMAKE_BUILD_TYPE=Release /path/to/QtRVSim\nmake\nctest\n```\n\n## Documentation\n\nMain documentation is provided in this README and in subdirectories [`docs/user`](docs/user)\nand [`docs/developer`](docs/developer).\n\nThe project was developed and extended as theses of Karel Kočí, Jakub Dupak and Max Hollmann. See section [Resources and Publications](#resources-and-publications) for links and references.\n\n## Accepted Binary Formats\n\nThe simulator accepts statically linked little-endian ELF executables compiled for\nthe RISC-V target, either 32-bit (GCC options `-mabi=ilp32 -march=rv32i` to `rv32ima_zicsr`)\nor 64-bit (`-mabi=lp64 -march=rv64i` to `rv64ima_zicsr`).\nSimulation will execute them as XLEN=32 or XLEN=64 according to the ELF file header.\nThere is even initial support for endianness selection based on the ELF file header.\n\n- 64-bit RISC-V ISA RV64IM and 32-bit RV32IM ELF executables are supported.\n- Compressed instructions are not yet supported.\n\nYou can use compile the code for simulation using specialized RISC-V GCC/Binutils toolchain (`riscv32-elf`) or using\nunified Clang/LLVM toolchain with [LLD](https://lld.llvm.org/). If you have Clang installed, you don't need any\nadditional tools. Clang can be used on Linux, Windows, macOS and others...\n\n### LLVM toolchain usage\n\n```shell\nclang --target=riscv32 -march=rv32g -nostdlib -static -fuse-ld=lld test.S -o test\nllvm-objdump -S test\n```\n\n### GNU toolchain usage\n\n```shell\nriscv32-elf-as test.S -o test.o\nriscv32-elf-ld test.o -o test\nriscv32-elf-objdump -S test\n```\n\nor\n\n```shell\nriscv32-elf-gcc test.S -o test\nriscv32-elf-objdump -S test\n```\n\n### GNU 64-bit toolchain use for RV32I target\n\nMultilib supporting 64-bit embedded toolchain can be used for to build executable\n\n```shell\nriscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -o test test.c crt0local.S -lgcc\n```\n\nThe global pointer and stack has to be set to setup runtime C code conformant environment. When no other C library is\nused then next simple `crt0local.S` can be used.\n\n<details>\n  <summary>example code</summary>\n\n```asm\n/* minimal replacement of crt0.o which is else provided by C library */\n\n.globl main\n.globl _start\n.globl __start\n\n.option norelax\n\n.text\n\n__start:\n_start:\n        .option push\n        .option norelax\n        la gp, __global_pointer$\n        .option pop\n        la      sp, __stack_end\n        addi    a0, zero, 0\n        addi    a1, zero, 0\n        jal     main\nquit:\n        addi    a0, zero, 0\n        addi    a7, zero, 93  /* SYS_exit */\n        ecall\n\nloop:   ebreak\n        beq     zero, zero, loop\n\n.bss\n\n__stack_start:\n        .skip   4096\n__stack_end:\n\n.end _start\n```\n\n</details>\n\n## Integrated Assembler\n\nBasic integrated assembler is included in the simulator. Small subset of\n[GNU assembler](https://sourceware.org/binutils/docs/as/) directives is recognized as well. Next directives are\nrecognized: `.word`, `.orig`, `.set`\n/`.equ`, `.ascii` and `.asciz`. Some other directives are simply ignored: `.data`, `.text`, `.globl`, `.end` and `.ent`.\nThis allows to write code which can be compiled by both - integrated and full-featured assembler. Addresses are assigned\nto labels/symbols which are stored in symbol table. Addition, subtraction, multiplication, divide and bitwise and or are\nrecognized.\n\n## Support to call external make utility\n\nThe action \"Build executable by external make\" call \"make\" program. If the action is invoked, and some source editors\nselected in main windows tabs then the \"make\" is started in the corresponding directory. Else directory of last selected\neditor is chosen. If no editor is open then directory of last loaded ELF executable are used as \"make\" start path. If\neven that is not an option then default directory when the emulator has been started is used.\n\n## Advanced functionalities\n\n### Peripherals\n\n<details>\n  <summary>Emuated LCD, knobs, buttons, serial port, timer...</summary>\n\nThe simulator implements emulation of two peripherals for now.\n\nThe first is simple serial port (UART). It support transmission\n(Tx) and reception (Rx). Receiver status register (`SERP_RX_ST_REG`)\nimplements two bits. Read-only bit 0 (`SERP_RX_ST_REG_READY`)\nis set to one if there is unread character available in the receiver data register (`SERP_RX_DATA_REG`). The bit 1\n(`SERP_RX_ST_REG_IE`) can be written to 1 to enable interrupt request when unread character is available. The\ntransmitter status register (`SERP_TX_ST_REG`) bit 0\n(SERP_TX_ST_REG_READY) signals by value 1 that UART is ready and can accept next character to be sent. The bit 1\n(`SERP_TX_ST_REG_IE`) enables generation of interrupt. The register `SERP_TX_DATA_REG` is actual Tx buffer. The LSB byte\nof written word is transmitted to the terminal window. Definition of peripheral base address and registers\noffsets (`_o`) and individual fields masks (`_m`) follows\n\n```\n#define SERIAL_PORT_BASE   0xffffc000\n\n#define SERP_RX_ST_REG_o           0x00\n#define SERP_RX_ST_REG_READY_m      0x1\n#define SERP_RX_ST_REG_IE_m         0x2\n\n#define SERP_RX_DATA_REG_o         0x04\n\n#define SERP_TX_ST_REG_o           0x08\n#define SERP_TX_ST_REG_READY_m      0x1\n#define SERP_TX_ST_REG_IE_m         0x2\n\n#define SERP_TX_DATA_REG_o         0x0c\n```\n\nThe UART registers region is mirrored on the address 0xffff0000 to enable use of programs initially written\nfor [SPIM](http://spimsimulator.sourceforge.net/) or [MARS](http://courses.missouristate.edu/KenVollmar/MARS/)\nemulators.\n\nThe another peripheral allows to set three byte values concatenated to single word (read-only `KNOBS_8BIT` register)\nfrom user panel set by knobs and display one word in hexadecimal, decimal and binary format (`LED_LINE` register). There\nare two other words writable which control color of RGB LED 1 and 2\n(registers `LED_RGB1` and `LED_RGB2`).\n\n```\n#define SPILED_REG_BASE    0xffffc100\n\n#define SPILED_REG_LED_LINE_o           0x004\n#define SPILED_REG_LED_RGB1_o           0x010\n#define SPILED_REG_LED_RGB2_o           0x014\n#define SPILED_REG_LED_KBDWR_DIRECT_o   0x018\n\n#define SPILED_REG_KBDRD_KNOBS_DIRECT_o 0x020\n#define SPILED_REG_KNOBS_8BIT_o         0x024\n```\n\nThe simple 16-bit per pixel (RGB565) framebuffer and LCD are implemented. The framebuffer is mapped into range starting\nat `LCD_FB_START` address. The display size is 480 x 320 pixel. Pixel format RGB565 expect red component in bits 11..\n15, green component in bits 5..10 and blue component in bits 0..4.\n\n```\n#define LCD_FB_START       0xffe00000\n#define LCD_FB_END         0xffe4afff\n```\n\nThe basic implementation of RISC-V Advanced Core Local Interruptor\nis implemented with basic support for\n\n- Machine-level Timer Device (MTIMER)\n- Machine-level Software Interrupt Device (MSWI)\n\n```\n#define ACLINT_MSWI        0xfffd0000 // core 0 machine SW interrupt request\n#define ACLINT_MTIMECMP    0xfffd4000 // core 0 compare value\n#define ACLINT_MTIME       0xfffdbff8 // timer base 10 MHz\n#define ACLINT_SSWI        0xfffd0000 // core 0 system SW interrupt request\n```\n\nMore information about ACLINT can be found in [RISC-V Advanced Core Local Interruptor Specification](https://github.com/riscv/riscv-aclint/blob/main/riscv-aclint.adoc).\n\n</details>\n\n### Interrupts and Control and Status Registers\n\n<details>\n  <summary>Implemented CSR registers and their usage</summary>\n\nList of interrupt sources:\n\n| Irq number | mie / mip Bit    | Source                                       |\n|-----------:|-----------------:|:---------------------------------------------|\n| 3          | 3                | Machine software interrupt request           |\n| 7          | 7                | Machine timer interrupt                      |\n| 16         | 16               | There is received character ready to be read |\n| 17         | 17               | Serial port ready to accept character to Tx  |\n\nFollowing Control Status registers are recognized\n\n| Number | Name       | Description                                                         |\n|-------:|:-----------|:--------------------------------------------------------------------|\n|  0x300 | mstatus    | Machine status register. |\n|  0x304 | mie        | Machine interrupt-enable register. |\n|  0x305 | mtvec      | Machine trap-handler base address. |\n|  0x340 | mscratch   | Scratch register for machine trap handlers. |\n|  0x341 | mepc       | Machine exception program counter. |\n|  0x342 | mcause     | Machine trap cause. |\n|  0x343 | mtval      | Machine bad address or instruction. |\n|  0x344 | mip        | Machine interrupt pending. |\n|  0x34A | mtinsr     | Machine trap instruction (transformed). |\n|  0x34B | mtval2     | Machine bad guest physical address. |\n|  0xB00 | mcycle     | Machine cycle counter. |\n|  0xB02 | minstret   | Machine instructions-retired counter. |\n|  0xF11 | mvendorid  | Vendor ID. |\n|  0xF12 | marchid    | Architecture ID. |\n|  0xF13 | mimpid     | Implementation ID. |\n|  0xF14 | mhardid    | Hardware thread ID. |\n\n`csrr`, `csrw`, `csrrs` , `csrrs` and `csrrw` are used to copy and exchange value from/to RISC-V control status registers.\n\nSequence to enable serial port receive interrupt:\n\nDecide location of interrupt service routine the first. The address of the common trap handler is defined by `mtvec` register and then PC is set to this address when exception or interrupt is accepted.\n\nEnable bit 16 in the machine Interrupt-Enable register (`mie`). Ensure that bit 3 (`mstatus.mie` - machine global interrupt-enable) of Machine Status register is set to one.\n\nEnable interrupt in the receiver status register (bit 1 of `SERP_RX_ST_REG`).\n\nWrite character to the terminal. It should be immediately consumed by the serial port receiver if interrupt is enabled\nin `SERP_RX_ST_REG`. CPU should report interrupt exception and when it propagates to the execution phase `PC` is set to\nthe interrupt routine start address.\n\n</details>\n\n### System Calls Support\n\n<details>\n  <summary>Syscall table and documentation</summary>\n\nThe emulator includes support for a few Linux kernel system calls. The RV32G ilp32 ABI is used.\n\n| Register                           | use on input          | use on output   | Calling Convention             |\n|:-----------------------------------|:----------------------|:----------------|:-------------------------------|\n| zero (x0)                          | —                     | -               | Hard-wired zero                |\n| ra (x1)                            | —                     | (preserved)     | Return address                 |\n| sp (x2)                            | —                     | (callee saved)  | Stack pointer                  |\n| gp (x3)                            | —                     | (preserved)     | Global pointer                  |\n| tp (x4)                            | —                     | (preserved)     | Thread pointer                 |\n| t0 .. t2 (x5 .. x7)                | —                     | -               | Temporaries                    |\n| s0/fp (x8)                         | —                     | (callee saved)  | Saved register/frame pointer   |\n| s1 (x9)                            | —                     | (callee saved)  | Saved register                 |\n| a0 (x10)                           | 1st syscall argument  | return value    | Function argument/return value |\n| a1 (x11)                           | 2nd syscall argument  | -               | Function argument/return value |\n| a2 .. a5 (x12 .. x15)              | syscall arguments     | -               | Function arguments             |\n| a6 (x16)                           | -                     | -               | Function arguments             |\n| a7 (x17)                           | syscall number        | -               | Function arguments             |\n| s2 .. s11 (x18 .. x27)             | —                     | (callee saved)  | Saved registers                |\n| t3 .. t6 (x28 .. x31)              | —                     | -               | Temporaries                    |\n\nThe all system call input arguments are passed in register.\n\nSupported syscalls:\n\n#### void [exit](http://man7.org/linux/man-pages/man2/exit.2.html)(int status) __NR_exit (93)\n\nStop/end execution of the program. The argument is exit status code, zero means OK, other values informs about error.\n\n#### ssize_t [read](http://man7.org/linux/man-pages/man2/read.2.html)(int fd, void *buf, size_t count) __NR_read (63)\n\nRead `count` bytes from open file descriptor `fd`. The emulator maps file descriptors 0, 1 and 2 to the internal\nterminal/console emulator. They can be used without `open` call. If there are no more characters to read from the\nconsole, newline is appended. At most the count bytes read are stored to the memory location specified by `buf`\nargument. Actual number of read bytes is returned.\n\n#### ssize_t [write](http://man7.org/linux/man-pages/man2/write.2.html)(int fd, const void *buf, size_t count) __NR_write (64)\n\nWrite `count` bytes from memory location `buf` to the open file descriptor\n`fd`. The same about console for file handles 0, 1 and 2 is valid as for `read`.\n\n#### int [close](http://man7.org/linux/man-pages/man2/close.2.html)(int fd) __NR_close (57)\n\nClose file associated to descriptor `fd` and release descriptor.\n\n#### int [openat](http://man7.org/linux/man-pages/man2/open.2.html)(int dirfd, const char *pathname, int flags, mode_t mode) __NR_openat (56)\n\nOpen file and associate it with the first unused file descriptor number and return that number. If the\noption `OS Emulation`->`Filesystem root`\nis not empty then the file path `pathname` received from emulated environment is appended to the path specified\nby `Filesystem root`. The host filesystem is protected against attempt to traverse to random directory by use of `..`\npath elements. If the root is not specified then all open files are targetted to the emulated terminal. Only `TARGET_AT_FDCWD` (`dirfd` = -100) mode is supported.\n\n#### void * [brk](http://man7.org/linux/man-pages/man2/brk.2.html)(void *addr) __NR_brk (214)\n\nSet end of the area used by standard heap after end of the program data/bss. The syscall is emulated by dummy\nimplementation. Whole address space up to 0xffff0000 is backuped by automatically attached RAM.\n\n#### int [ftruncate](http://man7.org/linux/man-pages/man2/ftruncate.2.html)(int fd, off_t length) __NR_truncate (46)\n\nSet length of the open file specified by `fd` to the new `length`. The `length`\nargument is 64-bit even on 32-bit system and it is passed as the lower part and the higher part in the\nsecond and third argument.\n\n#### ssize_t [readv](http://man7.org/linux/man-pages/man2/readv.2.html)(int fd, const struct iovec *iov, int iovcnt) __NR_Linux (65)\n\nThe variant of `read` system call where data to read are would be stored to locations specified by `iovcnt` pairs of\nbase address, length pairs stored in memory at address pass in `iov`.\n\n#### ssize_t [writev](http://man7.org/linux/man-pages/man2/writev.2.html)(int fd, const struct iovec *iov, int iovcnt) __NR_Linux (66)\n\nThe variant of `write` system call where data to write are defined by `iovcnt`\npairs of base address, length pairs stored in memory at address pass in `iov`.\n\n</details>\n\n## Limitations of the Implementation\n\n- See list of currently supported instructions.\n\n### QtRvSim limitations\n\n* Only very minimal support for privileged instruction is implemented for now (mret).\n* Only machine mode and minimal subset of machine CSRs is implemented.\n* TLB and virtual memory are not implemented.\n* No floating point support\n* Memory access stall (stalling execution because of cache miss would be pretty annoying for users so difference between\n  cache and memory is just in collected statistics)\n* Only limited support for interrupts and exceptions. When `ecall`\n  instruction is recognized, small subset of the Linux kernel system calls\n  can be emulated or simulator can be configured to continue by trap handler\n  on `mtvec` address.\n\n### List of Currently Supported Instructions\n\n- **RV32I**:\n  - **LOAD**: `lw, lh, lb, lwu, lhu, lbu`\n  - **STORE**: `sw, sh, sb, swu, shu, sbu`\n  - **OP**: `add, sub, sll, slt, sltu, xor, srl, sra, or, and`\n  - **MISC-MEM**: `fence, fence.i`\n  - **OP-IMM**: `addi, sll, slti, sltiu, xori, srli, srai, ori, andi, auipc, lui`\n  - **BRANCH**: `beq, bne, btl, bge, bltu, bgtu`\n  - **JUMP**: `jal, jalr`\n  - **SYSTEM**: `ecall, mret, ebreak, csrrw, csrrs, csrrc, csrrwi, csrrsi, csrrci`\n- **RV64I**:\n  - **LOAD/STORE**: `lwu, ld, sd`\n  - **OP-32**: `addw, subw, sllw, srlw, sraw, or, and`\n  - **OP-IMM-32**: `addiw, sllw, srliw, sraiw`\n- **Pseudoinstructions**\n  - **BASIC**: `nop`\n  - **LOAD**: `la, li`,\n  - **OP**: `mv, not, neg, negw, sext.b, sext.h, sext.w, zext.b, zext.h, zext.w, seqz, snez, sltz, slgz`\n  - **BRANCH**: `beqz, bnez, blez, bgez, bltz, bgtz, bgt, ble, bgtu, bleu`\n  - **JUMP**: `j, jal, jr, jalr, ret, call, tail`\n- **Extensions**\n  - **RV32M/RV64M**: `mul, mulh, mulhsu, div, divu, rem, remu`\n  - **RV64M**: `mulw, divw, divuw, remw, remuw`\n  - **RV32A/RV64A**: `lr.w, sc.w, amoswap.w, amoadd.w, amoxor.w, amoand.w, amoor.w, amomin.w, amomax.w, amominu.w, amomaxu.w`\n  - **RV64A**: `lr.d, sc.d, amoswap.d, amoadd.d, amoxor.d, amoand.d, amoor.d, amomin.d, amomax.d, amominu.d, amomaxu.d`\n  - **Zicsr**: `csrrw, csrrs, csrrc, csrrwi, csrrsi, csrrci`\n\nFor details about RISC-V, refer to the ISA specification:\n[https://riscv.org/technical/specifications/](https://riscv.org/technical/specifications/).\n\n## Links to Resources and Similar Projects\n\n### Resources and Publications\n\n- Computer architectures pages at Czech Technical University in Prague [https://comparch.edu.cvut.cz/](https://comparch.edu.cvut.cz/)\n\n- Dupak, J.; Pisa, P.; Stepanovsky, M.; Koci, K. [QtRVSim – RISC-V Simulator for Computer Architectures Classes](https://comparch.edu.cvut.cz/publications/ewC2022-Dupak-Pisa-Stepanovsky-QtRvSim.pdf) In: [embedded world Conference 2022](https://events.weka-fachmedien.de/embedded-world-conference). Haar: WEKA FACHMEDIEN GmbH, 2022. p. 775-778. ISBN 978-3-645-50194-1. ([Slides](https://comparch.edu.cvut.cz/slides/ewc22-qtrvsim.pdf))\n\nPlease reference above article, if you use QtRvSim in education or research related materials and publications.\n\n- [FEE CTU - B35APO - Computer Architectures](https://cw.fel.cvut.cz/wiki/courses/b35apo)\n  - Undergraduate computer architecture class materials (\n    Czech) ([English](https://cw.fel.cvut.cz/wiki/courses/b35apo/en/start))\n- [FEE CTU - B4M35PAP - Advanced Computer Architectures](https://cw.fel.cvut.cz/wiki/courses/b4m35pap/start)\n  - Graduate computer architecture class materials (Czech/English)\n- [Graphical RISC-V Architecture Simulator - Memory Model and Project Management](https://dspace.cvut.cz/bitstream/handle/10467/94446/F3-BP-2021-Dupak-Jakub-thesis.pdf)\n  - Jakub Dupak's thesis\n  - Documents 2020-2021 QtMips and QtRvSim development\n- [Graphical CPU Simulator with Cache Visualization](https://dspace.cvut.cz/bitstream/handle/10467/76764/F3-DP-2018-Koci-Karel-diploma.pdf)\n  - Karel Koci's thesis\n  - Documents initial QtMips development\n\n### Projects\n\n- **QtMips** - MIPS predecessor of this simulator [https://github.com/cvut/QtMips/](https://github.com/cvut/QtMips/)\n\n- **RARS** - RISC-V Assembler and Runtime\n  Simulator [https://github.com/TheThirdOne/rars](https://github.com/TheThirdOne/rars)\n\n## Copyright\n\n- Copyright (c) 2017-2019 Karel Koci <cynerd@email.cz>\n- Copyright (c) 2019-2025 Pavel Pisa <pisa@cmp.felk.cvut.cz>\n- Copyright (c) 2020-2025 Jakub Dupak <dev@jakubdupak.com>\n- Copyright (c) 2020-2021 Max Hollmann <hollmmax@fel.cvut.cz>\n\n## License\n\nThis project is licensed under `GPL-3.0-or-later`. The full text of the license is in the [LICENSE](LICENSE) file. The\nlicense applies to all files except for directories named `external` and files in them. Files in external directories\nhave a separate license compatible with the projects license.\n\n> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n>\n> This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n>\n> You should have received a copy of the GNU General Public License along with this program. If not, see [https://www.gnu.org/licenses/](https://www.gnu.org/licenses/).\n\n![Faculty of Electrical Engineering](./data/ctu/logo-fee.svg) ![Faculty of Information Technology](./data/ctu/logo-fit.svg) ![Czech Technical University](./data/ctu/logo-ctu.svg)\n"
  },
  {
    "path": "cmake/AddFileHashes.cmake",
    "content": "# Add file hashes to placeholder location in a template file.\n# Used for packaging.\n#\n# Example:\n# cmake -DTEMPLATE=PKGBUILD.in -DOUTPUT=PKGBUILD -DFILE=qtrvsim_0.8.0.tar.xz -P AddFileHashes.cmake\n#\n# See extras/packaging/arch/PKGBUILD.in for template examples.\n# Note that most files are configured (injected with information) twice:\n# First, during configure phase, package information is added and FILE_*\n# placeholders are left intact. Second, after source packing, FILE_*\n# placeholders are resolved.\n\nfile(MD5 ${FILE} FILE_MD5)\nfile(SHA1 ${FILE} FILE_SHA1)\nfile(SHA256 ${FILE} FILE_SHA256)\nfile(SIZE ${FILE} FILE_SIZE)\nconfigure_file(${TEMPLATE} ${OUTPUT})"
  },
  {
    "path": "cmake/BuildType.cmake",
    "content": "# Source (BSD-3) https://github.com/openchemistry/tomviz/blob/master/cmake/BuildType.cmake\n\n# Set a default build type if none was specified\nset(default_build_type \"Release\")\n\nif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n\tmessage(STATUS \"Setting build type to '${default_build_type}' as none was specified.\")\n\tset(CMAKE_BUILD_TYPE \"${default_build_type}\" CACHE\n\t    STRING \"Choose the type of build.\" FORCE)\n\t# Set the possible values of build type for cmake-gui\n\tset_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS \"Debug\" \"Release\"\n\t             \"MinSizeRel\" \"RelWithDebInfo\")\nendif()\n\n# Set variable to detect given build type\nif(CMAKE_BUILD_TYPE)\n    string(TOUPPER \"${CMAKE_BUILD_TYPE}\" _upper_build_type)\n    set(BUILD_${_upper_build_type} 1)\nendif()"
  },
  {
    "path": "cmake/BundleMacOS.cmake",
    "content": "find_program(MACDEPLOYQT\n             NAMES macdeployqt)\n"
  },
  {
    "path": "cmake/CopyrightTools.cmake",
    "content": "function(copyright_to_html OUTPUT INPUT)\n\tstring(REPLACE \";\" \"<br>\" TMP1 \"${INPUT}\")\n\tstring(REGEX REPLACE \"<([^<>]+@[^<>]+)>\" \"<a href='mailto:\\\\1'>\\\\1</a>\" TMP2 ${TMP1})\n\tset(${OUTPUT} ${TMP2} PARENT_SCOPE)\nendfunction()\n\nfunction(copyright_to_comment OUTPUT INPUT)\n\tstring(REGEX REPLACE \"([^;]+)\" \"# \\\\1\" ${OUTPUT} \"${INPUT}\")\n\tstring(REPLACE \";\" \"\\n\" ${OUTPUT} \"${${OUTPUT}}\")\n\tset(${OUTPUT} ${${OUTPUT}} PARENT_SCOPE)\nendfunction()\n\nmacro(copyright)\n\tset(COPYRIGHT_LIST \"${ARGN}\")\n\tcopyright_to_html(COPYRIGHT_HTML \"${COPYRIGHT_LIST}\")\n\tcopyright_to_comment(COPYRIGHT_HASH_COMMENT \"${COPYRIGHT_LIST}\")\nendmacro()\n"
  },
  {
    "path": "cmake/FindElfUtils.cmake",
    "content": "# Source (GPLv2): https://github.com/SimonKagstrom/kcov/blob/master/cmake/FindElfutils.cmake\n\n# - Try to find libdwarf\n# Once done this will define\n#\n#  LIBDWARF_FOUND - system has libdwarf\n#  LIBDWARF_INCLUDE_DIRS - the libdwarf include directory\n#  LIBDWARF_LIBRARIES - Link these to use libdwarf\n#  LIBDWARF_DEFINITIONS - Compiler switches required for using libdwarf\n#\n\n# Locate libelf library at first\nif (NOT LIBELF_FOUND)\n    find_package(LibElf)\nendif (NOT LIBELF_FOUND)\n\nif (LIBDWARF_LIBRARIES AND LIBDWARF_INCLUDE_DIRS)\n    set(LibDwarf_FIND_QUIETLY ON)\nendif (LIBDWARF_LIBRARIES AND LIBDWARF_INCLUDE_DIRS)\n\nfind_package(PkgConfig QUIET)\n\nif (PKG_CONFIG_FOUND)\n    set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON)\n    pkg_check_modules(PC_LIBDW QUIET libdw)\nendif ()\n\nfind_path(DWARF_INCLUDE_DIR\n        NAMES\n        dwarf.h\n        HINTS\n        ${PC_LIBDW_INCLUDE_DIRS}\n        PATHS\n        /usr/include\n        /usr/local/include\n        /opt/local/include\n        /sw/include\n        ENV CPATH) # PATH and INCLUDE will also work\nfind_path(LIBDW_INCLUDE_DIR\n        NAMES\n        elfutils/libdw.h\n        HINTS\n        ${PC_LIBDW_INCLUDE_DIRS}\n        PATHS\n        /usr/include\n        /usr/local/include\n        /opt/local/include\n        /sw/include\n        ENV CPATH)\nif (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR)\n    set(LIBDWARF_INCLUDE_DIRS ${DWARF_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR})\nendif (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR)\n\nfind_library(LIBDW_LIBRARY\n        NAMES\n        dw\n        HINTS\n        ${PC_LIBDW_LIBRARY_DIRS}\n        PATHS\n        /usr/lib\n        /usr/local/lib\n        /opt/local/lib\n        /sw/lib\n        ENV LIBRARY_PATH   # PATH and LIB will also work\n        ENV LD_LIBRARY_PATH)\n\ninclude(FindPackageHandleStandardArgs)\n\n\n# handle the QUIETLY and REQUIRED arguments and set LIBDWARF_FOUND to TRUE\n# if all listed variables are TRUE\nfind_package_handle_standard_args(ElfUtils DEFAULT_MSG\n        LIBDW_LIBRARY\n        LIBDW_INCLUDE_DIR)\n\nmark_as_advanced(LIBDW_INCLUDE_DIR LIBDW_LIBRARY)\n\nset(LIBDW_LIBRARIES ${LIBDW_LIBRARY})\nset(LIBDW_INCLUDE_DIRS ${LIBDW_INCLUDE_DIR})"
  },
  {
    "path": "cmake/FindLibElf.cmake",
    "content": "# Source (GPLv2): https://github.com/SimonKagstrom/kcov/blob/master/cmake/FindLibElf.cmake\n\n# - Try to find libelf\n# Once done this will define\n#\n#  LIBELF_FOUND - system has libelf\n#  LIBELF_INCLUDE_DIRS - the libelf include directory\n#  LIBELF_LIBRARIES - Link these to use libelf\n#  LIBELF_DEFINITIONS - Compiler switches required for using libelf\n#\n#  Copyright (c) 2008 Bernhard Walle <bernhard.walle@gmx.de>\n#\n#  Redistribution and use is allowed according to the terms of the New\n#  BSD license.\n#  For details see the accompanying COPYING-CMAKE-SCRIPTS file.\n#\n\nif (LIBELF_LIBRARIES AND LIBELF_INCLUDE_DIRS)\n    set(LibElf_FIND_QUIETLY ON)\nendif (LIBELF_LIBRARIES AND LIBELF_INCLUDE_DIRS)\n\nfind_package(PkgConfig QUIET)\n\nif (PKG_CONFIG_FOUND)\n    set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON)\n    pkg_check_modules(PC_LIBELF QUIET libelf)\nendif ()\n\nfind_path(LIBELF_INCLUDE_DIR\n        NAMES\n        libelf.h\n        HINTS\n        ${PC_LIBELF_INCLUDE_DIRS}\n        PATHS\n        /usr/include\n        /usr/include/libelf\n        /usr/local/include\n        /usr/local/include/libelf\n        /opt/local/include\n        /opt/local/include/libelf\n        /sw/include\n        /sw/include/libelf\n        ENV CPATH)\n\nfind_library(LIBELF_LIBRARY\n        NAMES\n        elf\n        HINTS\n        ${PC_LIBELF_LIBRARY_DIRS}\n        PATHS\n        /usr/lib\n        /usr/local/lib\n        /opt/local/lib\n        /sw/lib\n        ENV LIBRARY_PATH\n        ENV LD_LIBRARY_PATH)\n\ninclude(FindPackageHandleStandardArgs)\n\n\n# handle the QUIETLY and REQUIRED arguments and set LIBELF_FOUND to TRUE if all listed variables are TRUE\nfind_package_handle_standard_args(LibElf DEFAULT_MSG\n        LIBELF_LIBRARY\n        LIBELF_INCLUDE_DIR)\n\n\nmark_as_advanced(LIBELF_INCLUDE_DIR LIBELF_LIBRARY)\n\nset(LIBELF_LIBRARIES ${LIBELF_LIBRARY})\nset(LIBELF_INCLUDE_DIRS ${LIBELF_INCLUDE_DIR})"
  },
  {
    "path": "cmake/FindPythonInterp.cmake",
    "content": "# Compatibility wrapper for CMake > 3.12 where FindPythonInterp is deprecated/removed\n# This allows external projects using the old module to work with newer CMake and Python3\n\nfind_package(Python3 COMPONENTS Interpreter QUIET)\n\nif(Python3_Interpreter_FOUND)\n    set(PYTHONINTERP_FOUND TRUE)\n    set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE})\n    set(PYTHON_VERSION_STRING ${Python3_VERSION})\n    set(PYTHON_VERSION_MAJOR ${Python3_VERSION_MAJOR})\n    set(PYTHON_VERSION_MINOR ${Python3_VERSION_MINOR})\n    set(PYTHON_VERSION_PATCH ${Python3_VERSION_PATCH})\n    \n    if(NOT PythonInterp_FIND_QUIETLY)\n        message(STATUS \"Found PythonInterp (via Python3): ${PYTHON_EXECUTABLE} (found version \\\"${PYTHON_VERSION_STRING}\\\")\")\n    endif()\nelse()\n    if(PythonInterp_FIND_REQUIRED)\n        message(FATAL_ERROR \"Could NOT find PythonInterp (missing: PYTHON_EXECUTABLE)\")\n    endif()\nendif()\n"
  },
  {
    "path": "cmake/GPL-3.0-or-later.cmake",
    "content": "# Licence related data to be included in project files\n\nset(LICENCE_IDENTIFIER \"GPL-3.0-or-later\")\nset(LICENCE_SHORT \"\\\nThis program is free software: you can redistribute it and/or modify it under the terms of the\nGNU General Public License as published by the Free Software Foundation, either version 3 of the\nLicense, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\neven the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\nGeneral Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not,\nsee <https://www.gnu.org/licenses/>.\")\nset(LICENCE_SHORT_HTML \"\\\n<p>This program is free software: you can redistribute it and/or modify it under the terms of the \\\nGNU General Public License as published by the Free Software Foundation, either version 3 of the \\\nLicense, or (at your option) any later version.</p> \\\n<p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; \\\nwithout even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the \\\nGNU General Public License for more details.</p> \\\n<p>You should have received a copy of the GNU General Public License along with this program. \\\nIf not, see <a href='https://www.gnu.org/licenses/'>https://www.gnu.org/licenses/</a>.</p>\")"
  },
  {
    "path": "cmake/LibElfinSettings.cmake",
    "content": "if(POLICY CMP0148)\n    cmake_policy(SET CMP0148 OLD)\nendif()\n\n# Libelfin requires Threads for dwarf++ library\nfind_package(Threads REQUIRED)\n\n"
  },
  {
    "path": "cmake/PackageTools.cmake",
    "content": "# Support code for package handling.\n# Mainly intended to generate inputs for Open Build Service.\n\n# Keep file properties placeholder in configure phase, they are replaced in build phase\nset(FILE_NAME \"\\@FILE_NAME\\@\")\nset(FILE_SIZE \"\\@FILE_SIZE\\@\")\nset(FILE_MD5 \"\\@FILE_MD5\\@\")\nset(FILE_SHA1 \"\\@FILE_SHA1\\@\")\nset(FILE_SHA256 \"\\@FILE_SHA256\\@\")\nset(FILE2_NAME \"\\@FILE2_NAME\\@\")\nset(FILE2_SIZE \"\\@FILE2_SIZE\\@\")\nset(FILE2_MD5 \"\\@FILE2_MD5\\@\")\nset(FILE2_SHA1 \"\\@FILE2_SHA1\\@\")\nset(FILE2_SHA256 \"\\@FILE2_SHA256\\@\")\n\n\n# Source file tar generation.\n\n# Make sure that destination path exists as it wont be created automatically.\nfile(MAKE_DIRECTORY ${PACKAGE_OUTPUT_PATH})\n\nfind_program(BASH bash REQUIRED)\nfind_program(GIT git REQUIRED)\nfind_program(XZ xz REQUIRED)\nfind_program(TAR tar REQUIRED)\n\n# Command to build source archive from git HEAD.\nadd_custom_command(OUTPUT ${PACKAGE_SOURCE_ARCHIVE_FILE}\n\t\tCOMMAND ${BASH} ${CMAKE_SOURCE_DIR}/extras/packaging/_tools/git-archive-submodules.sh\n\t\t${CMAKE_SOURCE_DIR} ${PACKAGE_OUTPUT_PATH} ${PACKAGE_SOURCE_ARCHIVE_FILE}\n\t\t${PACKAGE_NAME} ${PACKAGE_VERSION} ${GIT} ${TAR} ${XZ}\n\t\tWORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\")\nadd_custom_target(source_archive\n\t\tDEPENDS ${PACKAGE_SOURCE_ARCHIVE_FILE})\n\n# Procedure adding support for individual OS distributions.\n#\n# @param target_name            name of created target for simple reference (recommended: OS name)\n# @param config_file_name       name of resulting config file\n# @param template               path to cmake template file (relative from source dir)\n#\n# NOTE: ${PACKAGE_SOURCE_ARCHIVE_FILE} is assumed to exist in scope and be unique for project\n#\n\nmacro(package_config_file target_name config_file_name template)\n\t# The @ONLY option disable replacement of ${} which may be used by shell as well.\n\tconfigure_file(${template} ${config_file_name}.in @ONLY)\n\tadd_custom_command(OUTPUT ${config_file_name}\n\t\t\tCOMMAND ${CMAKE_COMMAND} -DFILE=\"${PACKAGE_SOURCE_ARCHIVE_PATH}\"\n\t\t\t-DTEMPLATE=\"${CMAKE_BINARY_DIR}/${config_file_name}.in\"\n\t\t\t-DOUTPUT=${PACKAGE_OUTPUT_PATH}/${config_file_name}\n\t\t\t-P \"${CMAKE_SOURCE_DIR}/cmake/AddFileHashes.cmake\"\n\t\t\tDEPENDS source_archive)\n\tadd_custom_target(${target_name}\n\t\t\tDEPENDS ${config_file_name})\nendmacro()\n\nmacro(package_debian_quilt target_name config_file_name template debian_dir output_archive)\n\tfile(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/debian)\n\t# The @ONLY option disable replacement of ${} which may be used by shell as well.\n\tconfigure_file(${debian_dir}/control.in ${CMAKE_BINARY_DIR}/debian/control @ONLY)\n\tfile(COPY ${CMAKE_BINARY_DIR}/debian/control DESTINATION ${CMAKE_BINARY_DIR}/debian\n\t\t\tFILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)\n\tfile(COPY ${debian_dir}/compat\n\t\t\tDESTINATION ${CMAKE_BINARY_DIR}/debian\n\t\t\tFILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)\n\tfile(COPY ${debian_dir}/rules\n\t\t\tDESTINATION ${CMAKE_BINARY_DIR}/debian\n\t\t\tFILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_EXECUTE GROUP_EXECUTE GROUP_EXECUTE)\n\tfile(COPY ${debian_dir}/docs\n\t\t\tDESTINATION ${CMAKE_BINARY_DIR}/debian\n\t\t\tFILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)\n\tfile(COPY ${debian_dir}/changelog\n\t\t\tDESTINATION ${CMAKE_BINARY_DIR}/debian\n\t\t\tFILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)\n\tfile(COPY ${debian_dir}/source/format\n\t\t\tDESTINATION ${CMAKE_BINARY_DIR}/debian/source\n\t\t\tFILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)\n\tfile(ARCHIVE_CREATE\n\t\t\tOUTPUT ${PACKAGE_OUTPUT_PATH}/${output_archive}\n\t\t\tPATHS ${CMAKE_BINARY_DIR}/debian\n\t\t\tFORMAT gnutar\n\t\t\tCOMPRESSION XZ)\n\tset(DEBIAN_ARCHIVE_FILE ${output_archive})\n\tfile(MD5 ${PACKAGE_OUTPUT_PATH}/${output_archive} DEBIAN_MD5)\n\tfile(SHA1 ${PACKAGE_OUTPUT_PATH}/${output_archive} DEBIAN_SHA1)\n\tfile(SHA256 ${PACKAGE_OUTPUT_PATH}/${output_archive} DEBIAN_SHA256)\n\tfile(SIZE ${PACKAGE_OUTPUT_PATH}/${output_archive} DEBIAN_SIZE)\n\tconfigure_file(${template} ${config_file_name}.in @ONLY)\n\tadd_custom_command(OUTPUT ${config_file_name}\n\t\t\tCOMMAND ${CMAKE_COMMAND} -DFILE=\"${PACKAGE_SOURCE_ARCHIVE_PATH}\"\n\t\t\t-DTEMPLATE=\"${CMAKE_BINARY_DIR}/${config_file_name}.in\"\n\t\t\t-DOUTPUT=${PACKAGE_OUTPUT_PATH}/${config_file_name}\n\t\t\t-P \"${CMAKE_SOURCE_DIR}/cmake/AddFileHashes.cmake\"\n\t\t\tDEPENDS source_archive)\n\tadd_custom_target(${target_name}\n\t\t\tDEPENDS ${config_file_name})\n\n\tmessage(STATUS \"Debian archive created\")\nendmacro()"
  },
  {
    "path": "cmake/TestingTools.cmake",
    "content": "# Helper functions for integration testing\n\n# Creates a new CLI test. The test consists of two parts: the first runs the command and checks nonzero return value,\n# the second compares the stdout with provided files. Currently there diff is not displayed as cmake does not provide a\n# portable way to do that.\n#\n# TODO: show diff whenever available.\n#\n# NOTE:\n#   If CLI was build in debug mode (which is recommended) the test will run with sanitizers and any memory errors\n#   including memory leaks will fail the test.\n#\n# Usage:\n#   add_cli_test(\n#\t\tNAME <name>\n#\t\tARGS\n#\t\t\t--asm \"${CMAKE_SOURCE_DIR}/tests/cli/<name>/program.S\"\n#\t\t\t<other CLI commands>\n#\t\tEXPECTED_OUTPUT \"tests/cli/<name>/stdout.txt\"\n#   )\n\nfunction(add_cli_test)\n\tcmake_parse_arguments(\n\t\t\tCLI_TEST\n\t\t\t\"\"\n\t\t\t\"NAME;EXPECTED_OUTPUT\"\n\t\t\t\"ARGS\"\n\t\t\t${ARGN}\n\t)\n\tadd_custom_target(\n\t\t\tcli_test_${CLI_TEST_NAME}\n\t\t\tCOMMAND ${CMAKE_COMMAND} -E make_directory \"Testing\"\n\t\t\tCOMMAND cli ${CLI_TEST_ARGS} > \"Testing/stall_test.out\"\n\t\t\tCOMMAND ${CMAKE_COMMAND} -E compare_files \"Testing/stall_test.out\"\n\t\t\t\"${CMAKE_SOURCE_DIR}/${CLI_TEST_EXPECTED_OUTPUT}\"\n\t\t\tWORKING_DIRECTORY \"${CMAKE_BINARY_DIR}\"\n\t\t\tDEPENDS cli\n\t)\n\n\tadd_test(\n\t\t\tNAME \"cli_${CLI_TEST_NAME}\"\n\t\t\tCOMMAND ${CMAKE_COMMAND} --build . --target \"cli_test_${CLI_TEST_NAME}\"\n\t\t\tWORKING_DIRECTORY \"${CMAKE_BINARY_DIR}\")\nendfunction()\n"
  },
  {
    "path": "data/cz.cvut.edu.comparch.qtrvsim.metainfo.xml.in",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Copyright 2022 David Heidelberg -->\n<component type=\"desktop-application\">\n  <id>@MAIN_PROJECT_APPID@</id>\n  <name>@GENERIC_NAME@</name>\n  <developer_name>Czech Technical University in Prague</developer_name>\n  <summary>RISC-V CPU simulator for education purposes</summary>\n  <metadata_license>CC0-1.0</metadata_license>\n  <project_license>GPL-3.0-or-later</project_license>\n  <launchable type=\"desktop-id\">@MAIN_PROJECT_NAME_LOWER@.desktop</launchable>\n  <content_rating type=\"oars-1.1\"/>\n\n  <description>\n    <p>\n      RISC-V processor architecture simulator for education purposes with pipeline and cache visualization.\n    </p>\n  </description>\n\n  <releases>\n    <release version=\"@MAIN_PROJECT_VERSION@\" date=\"2024-10-01\">\n      <description>\n        <p>Current project release.</p>\n      </description>\n    </release>\n    <release version=\"0.9.7\" date=\"2024-02-16\">\n      <description>\n        <p>GUI fix crash when no tab is selected, use bundled LibElf on macOS as Homebrew misses RISC-V</p>\n      </description>\n    </release>\n    <release version=\"0.9.6\" date=\"2023-12-11\">\n      <description>\n        <p>RISC-V A extension, ACLINT MTIMER, IRQ support, L2 cache, GUI editor tabs</p>\n      </description>\n    </release>\n    <release version=\"0.9.5\" date=\"2023-01-16\">\n      <description>\n        <p>More CSR updates, fix branch range, CLI OS emulation, coreview diagrams updates, RV64IM in CI</p>\n      </description>\n    </release>\n    <release version=\"0.9.4\" date=\"2022-10-26\">\n      <description>\n        <p>Support for 64-bit RV64IM target and official tests in CI</p>\n      </description>\n    </release>\n    <release version=\"0.9.3\" date=\"2022-04-22\">\n      <description>\n        <p>Corrected peripherals, LCD endianness and memory stalls.</p>\n      </description>\n    </release>\n    <release version=\"0.9.2\" date=\"2022-03-14\">\n      <description>\n        <p>Initial Flatpak release.</p>\n      </description>\n    </release>\n  </releases>\n\n  <screenshots>\n    <screenshot type=\"default\" width=\"1526\" height=\"888\">\n      <image>https://raw.githubusercontent.com/cvut/qtrvsim/master/data/screenshots/0.png</image>\n    </screenshot>\n    <screenshot type=\"default\" width=\"1526\" height=\"888\">\n      <image>https://raw.githubusercontent.com/cvut/qtrvsim/master/data/screenshots/1.png</image>\n    </screenshot>\n  </screenshots>\n\n  <url type=\"homepage\">https://github.com/cvut/qtrvsim</url>\n  <url type=\"bugtracker\">https://github.com/cvut/qtrvsim/issues</url>\n\n  <requires>\n    <display_length compare=\"ge\">768</display_length>\n  </requires>\n\n  <recommends>\n    <control>keyboard</control>\n    <control>pointing</control>\n  </recommends>\n\n  <kudos>\n    <kudo>ModernToolkit</kudo>\n  </kudos>\n\n  <update_contact>david_AT_ixit.cz</update_contact>\n\n  <custom>\n    <value key=\"Purism::form_factor\">workstation</value>\n  </custom>\n</component>\n"
  },
  {
    "path": "data/gui.desktop.in",
    "content": "[Desktop Entry]\nName=@PROJECT_NAME@\nGenericName=@GENERIC_NAME@\nExec=@MAIN_PROJECT_NAME_LOWER@_gui\nIcon=@MAIN_PROJECT_NAME_LOWER@_gui\nType=Application\nComment=@GENERIC_NAME@\nTerminal=false\nCategories=System;Emulator;\nKeywords=emulator;RISC-V;education;\n"
  },
  {
    "path": "data/wasm/browserconfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig>\n    <msapplication>\n        <tile>\n            <square150x150logo src=\"/mstile-150x150.png\"/>\n            <TileColor>#da532c</TileColor>\n        </tile>\n    </msapplication>\n</browserconfig>\n"
  },
  {
    "path": "data/wasm/index.html",
    "content": "<!doctype html>\n<!-- BASED ON: qt/5.15.2/wasm_32/plugins/platforms/wasm_shell.html -->\n<html lang=\"en-us\">\n<head>\n    <meta charset=\"utf-8\">\n    <meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n    <title>QtRVSim: RISV-V simulator for education</title>\n    <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"./apple-touch-icon.png\">\n    <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"./favicon-32x32.png\">\n    <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"./favicon-16x16.png\">\n    <link rel=\"manifest\" href=\"./manifest.json\">\n    <link rel=\"mask-icon\" href=\"./safari-pinned-tab.svg\" color=\"#d5875b\">\n    <meta name=\"apple-mobile-web-app-title\" content=\"QtRVSim\">\n    <meta name=\"application-name\" content=\"QtRVSim\">\n    <meta name=\"msapplication-TileColor\" content=\"#da532c\">\n    <meta name=\"theme-color\" content=\"#ffffff\">\n\n    <style>\n        html, body {\n            padding: 0;\n            margin: 0;\n            overflow: hidden;\n            height: 100%\n        }\n\n        /* the canvas *must not* have any border or padding, or mouse coords will be wrong */\n        canvas {\n            border: 0 none;\n            background-color: white;\n            height: 100%;\n            width: 100%;\n        }\n\n        /* The content editable property is set to true for the canvas in order to support\n           clipboard events. Hide the resulting focus frame and set the cursor back to\n           the default cursor. */\n        canvas {\n            outline: 0 solid transparent;\n            caret-color: transparent;\n            cursor: default\n        }\n    </style>\n</head>\n<body onload=\"init()\">\n<figure id=\"qtspinner\" style=\"overflow:visible;\">\n    <div style=\"margin-top:1.5em; line-height:150%; text-align: center;\">\n        <img height=200; src=\"logo.svg\" style=\"display:block; margin: auto;\" width=\"320\" alt=\"Qt\">\n        <strong>Qt for WebAssembly: qtrvsim_gui</strong>\n        <div id=\"qtstatus\"></div>\n        <noscript>JavaScript is disabled. Please enable JavaScript to use this application.</noscript>\n    </div>\n</figure>\n<canvas contenteditable=\"true\" id=\"qtcanvas\" oncontextmenu=\"event.preventDefault()\"></canvas>\n\n<script type='text/javascript'>\n    function init() {\n        var spinner = document.querySelector('#qtspinner');\n        var canvas = document.querySelector('#qtcanvas');\n        var status = document.querySelector('#qtstatus')\n\n        var qtLoader = QtLoader({\n            canvasElements: [canvas],\n            showLoader: function (loaderStatus) {\n                spinner.style.display = 'block';\n                canvas.style.display = 'none';\n                status.innerHTML = loaderStatus + \"...\";\n            },\n            showError: function (errorText) {\n                status.innerHTML = errorText;\n                spinner.style.display = 'block';\n                canvas.style.display = 'none';\n            },\n            showExit: function () {\n                status.innerHTML = \"Application exit\";\n                if (qtLoader.exitCode !== undefined)\n                    status.innerHTML += \" with code \" + qtLoader.exitCode;\n                if (qtLoader.exitText !== undefined)\n                    status.innerHTML += \" (\" + qtLoader.exitText + \")\";\n                spinner.style.display = 'block';\n                canvas.style.display = 'none';\n            },\n            showCanvas: function () {\n                spinner.style.display = 'none';\n                canvas.style.display = 'block';\n            },\n        });\n        qtLoader.loadEmscriptenModule(\"qtrvsim_gui\");\n    }\n</script>\n<script src=\"qtloader.js\" type=\"text/javascript\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "data/wasm/manifest.json",
    "content": "{\n    \"name\": \"QtRVSim: RISC-V simulator for education\",\n    \"short_name\": \"QtRVSim\",\n    \"icons\": [\n        {\n            \"src\": \"./android-chrome-192x192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"./android-chrome-512x512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\"\n        }\n    ],\n    \"theme_color\": \"#ffffff\",\n    \"background_color\": \"#ffffff\",\n    \"display\": \"standalone\",\n    \"start_url\": \"./index.hml\"\n}\n"
  },
  {
    "path": "default.nix",
    "content": "let\n  pkgs = import <nixpkgs> { };\nin\n{\n    qtrvsim = pkgs.libsForQt5.callPackage (import extras/packaging/nix/qtrvsim.nix) {};\n}\n"
  },
  {
    "path": "docs/developer/build&deploy/building-wasm.md",
    "content": "# Building and deploying WebAssembly release of the simulator\n\n## Jump start\n\nWASM release is automatically build by the GitHub actions. Resulting files are available in `Actions`\n/`<detail of build you want to use>`, section `Artifacts`, file `target-wasm-Linux-qtx.xx.x`. Download the files and\nskip to deploy. Details on the build process can be found in `.github/workflows/cmake.yml`.\n\n## Jump start 2\n\nInstall `emsdk` and `aqt` (details below).\n\nUse prepared helper make in the root of the project.\n\n```shell\nmake wasm # build\nmake wasm-clean # clean wasm build\nmake wasm-clean-deap # also clean cached external libraries - when changing compile settings, of when it just does not work ;)\nmake wasm-install-qt # install appropriate qt for wasm using aqt\n```\n\nBehavior of this commands is customized to local system in `.dev-config.local.mk`. The file is provided as a template\nand further changes are ignored.\n\n## Dependencies\n\n- WASM compiler (Emscripten/EMSDK)\n- WASM compiled Qt\n- bash compatible shell (not fish, unfortunately)\n\nSteps:\n\n- Install `emsdk` toolkit. Installing just the Emscripten compiler might work, but I would discourage it.\n- Choose version of the toolkit and run `emsdk activate <version>`. You can choose freely as long as all your components\n  are compiled with the compatible version. Qt5 distributed release is build with version `1.39.8`. Some distributions\n  provide packages like `qt5-wasm`. It is imperative to compile the project with the same toolchain, otherwise it might\n  not link.\n- Source the toolkit as suggested by the output of the activate command.\n- Download or compile qt to wasm. Common way is to store it in `/opt/`. I use an unofficial qt downloader\n  [`aqtinstall`](https://pypi.org/project/aqtinstall/).\n  - `python -m aqt install 5.12.2 linux desktop wasm_32 --outputdir \"/opt/qt\"`\n\n## Build\n\n- Move to build directory a build directory (I use`build/wasm`) and\n  run (replace the paths accordingly and use your favorite buildsystem):\n  ```shell\n  emcmake cmake ../..\n        -DCMAKE_BUILD_TYPE=Release\n        -DCMAKE_PREFIX_PATH=/opt/qt/5.13.1/wasm_32/ \n        -DCMAKE_FIND_ROOT_PATH=/opt/qt/5.13.1/wasm_32/ -Wno-dev -G Ninja\n  ```\n- Move the resulting `.js` and `.wasm` files to `target/wasm` along with all content of `data/wasm`. It contains\n  loader script, icons and other support files.\n\n## Deploy\n\n- Move the contents of `target/wasm` to a webserver. Done.\n- Note: the webserver must use https due to CORS.\n\n## Tricks\n\n### Fish and co.\n\nIf you wish to use bash incompatible shell. Run bash, source the file, and the run the shell with the environment of the\nbash. \n"
  },
  {
    "path": "docs/developer/build&deploy/release.md",
    "content": "# Release process\n\n- Check version in root `CMakeLists.txt`\n- Update debian changelog\n- Make sure, CMake was configured with flag `-DDEV_MODE=true` to enable packaging tools. Without this flag, CMake will\n  not try to do anything, that would require an external tool.\n- Run `make open_build_service_bundle`.\n- Deploy the contents of `<build_dir>/target/pkg` to Open Build Service (and Launchpad).\n\n## Debian changelog\n\nDebian changelog has very strict (and little weird) format. Bash script `extras/packaging/add-to-changelog.sh\n<version>` generates a new entry in the changelog and opens it in `$EDITOR`. Edit CAREFULLY, the format is fragile.\nAfter that, changes are committed and git tag is updated.\n\n### TODO\n\n- Decide, whether this should be replaced with CMake.\n- Get version from CMake\n\n## `open_build_service_bundle` target\n\nAt configure time, package related files are injected with up to date information from CMake (variables prefixed\n`PACKAGE_`) and copied to `<executable output directory>`/`pkg`. This target bundles sources to `tar.xz` and updates\nhashes in the package files.\n\n### Generate using GitHub Actions (CI)\n\nIn the repository page at GitHub go to actions tab and choose release in the left menu.\n\n![](./media/obs-ci-step-1.png)\n\nClick `run workflow` and select a branch.\n\n![](./media/obs-ci-step-2.png)\n\nOne the workflow has finished, the bundle is to be found in the artifacts section. Upload this bundle to OBS.\n\n![](./media/obs-ci-step-3.png)\n\n### TODO\n\n- Package signing\n\n## Open Build Service\n\nThe easiest way to deploy files to OBS is the `osc` cli tool.\n\n- Setup credentials. The first run of `osc` will lead you.\n- Download the repository:\n\n```shell\nosc co home:jdupak qtrvsim\n```\n\n- Copy files to `home:jdupak`/`qtrvsim`.\n- Add files to tracking (in the `qtrvsim` directory):\n\n```shell\nosc addremove *\n```\n\n- Commit changes and push:\n\n```shell\nosc commit\n```\n\n- If something went wrong, delete th directory and start from th beginning.\n\n"
  },
  {
    "path": "docs/developer/coreview-graphics/using-drawio-diagram.md",
    "content": "# Using .drawio diagram\n\nFor easier manipulation the coreview diagrams are created i a diagramming tool [diagrams.net](https://app.diagrams.net),\nformerly known as __draw.io__. It is fully sufficient to use it in browser, but installable version is available as\nwell.\n\n## Prepare your workspace\n\nBefore you begin and open the diagram, you have to enable some plugins in menu `Extra`/`Plugins...`. Tou will\nneed `tags`. Choose it and reload the app.\n\nYou will need to enable the tools `Tabs` and `Hidden Tabs` in the `Extras` menu. I also recommend enabling `Layers` tool\nin view.\n\n## Export\n\nYou have to export 3 SVG files.\n\nIn the `Tags` window, empty the field and click `Hide`. Everything will disappear. Then type `simple` and click show. It\nwill show only components tagged as simple. No hit `File`/`Export as...`/`SVG...`. Uncheck\nthe `include a copy of diagram` option and git export. Save as `simple.svg`\nto `device` and move it to project directory (`src/gui/core`). Hit hide and repeat for other versions (\ncurrently `simple`,`pipeline` and `forwarding`).\n\n## Editing\n\nYou can load the diagram from device or from GitHub. Device is preferred as it will not create many useless commits. All\nshapes I have used are on [my GitHub](https://github.com/jdupak/Diagrams.net-CPU-scheme-kit). You can open it in `File`\n/`Open library from`. If you need write access just open an issue.\n\nThe editor itself is quite intuitive, so here are only special functions and highlights:\n\n- When using texts, make sure, that formatted text option is disabled. It would use SVG foreign objects, which are not\n  supported in most simple renderers and parsers. The option is cca in the middle of the text tab on the right.\n- If you need to ensure that some elements are inside others in the resulting SVG, you have to use `Custom element`,\n  otherwise the SVG is flattened. Currently, it is how cache works. In the style tab, you can see inspect it by the `\n  edit shape` button. Make sure to select the element and not some group. You can create new one in \n  `Arrange`/`Insert`/`Shape...`. The language is \n  similar to SVG but not\n  the same, and it is documented here: [https://www.diagrams.net/doc/faq/shape-complex-create-edit](https://www.\n  diagrams.net/doc/faq/shape-complex-create-edit) with complete language reference\n  here: [https://jgraph.github. io/mxgraph/docs/stencils.xsd](https://jgraph.github.io/mxgraph/docs/stencils.xsd). This\n  is the code of the cache:\n  ```xml\n    <shape name=\"cache\" h=\"50\" w=\"60\" aspect=\"fixed\" strokewidth=\"inherit\">\n      <background>\n        <roundrect x=\"0\" y=\"0\" w=\"60\" h=\"50\" arcsize=\"2\" />\n      </background>\n      <foreground>\n        <fillstroke />\n        <fontsize size=\"6\" />\n        <fontstyle style=\"1\" />\n        <fontfamily family=\"sans-serif\" />\n        <text str=\"Cache\" x=\"30\" y=\"10\" align=\"center\" />\n        <text str=\"Hit:\" x=\"5\" y=\"25\" align=\"left\" />\n        <text str=\"Miss:\" x=\"5\" y=\"34\" align=\"left\" />\n        <text str=\"0\" x=\"20\" y=\"25\" align=\"left\" />\n        <text str=\"0\" x=\"25\" y=\"34\" align=\"left\" />\n      </foreground>\n    </shape>\n  ```\n- Components with dynamic values are created in `Edit data` in context menu (`CTRL+M`). Example (written here as a\n  json):\n  ```json\n    {\n      \"component\": \"instruction-value\",\n      \"source\": \"decode\"\n    }\n  ```\n  Components are handled in `src/gui/coreview/scene.cpp`, defined in `src/gui/coreview/domponents` and data bindings\n  (source attribute) are controlled in `src/gui/coreview/data.h`.\n  ![](./media/data.png).\n- In context menu - `Edit link...` you can create hyperlinks, what will open some part of gui on doubleclick. The\n  connect to `CoreViewScene` signals using table in `src/gui/coreview/data.h`.\n- To control in which variant the given element appears, you can use `tags` in `Edit data` of gui tool, which can be\n  displayed in `Extras`/`Hidden tags...`\n  ![](./media/tags.png).\n"
  },
  {
    "path": "docs/developer/debuging/logging.md",
    "content": "# Logging\n\nThe project uses the Qt logging framework. For quick introduction\nsee [this KDAB presentation](https://www.kdab.com/wp-content/uploads/stories/slides/Day2/KaiKoehne_Qt%20Logging%20Framework%2016_9_0.pdf)\n.\n\nTo enable logging in a `cpp` file, add the following snippet.\n\n```c++\n#include \"common/logging.h\"\n\nLOG_CATEGORY(\"<project>.<file/class>\");\n```\n\nThis creates a category object in the scope and enables seamless use of logging macros `DEBUG` `LOG` `WARN` and `INFO`.\nIf you need to use logging in a header file, call the `LOG_CATEGORY` macro only in a local scope to avoid interfering\nwith log categories in source files.\n\nDebug logs are intended to provide fine level of information and should be hidden by default and enabled only for\ncurrently debugged category. Debug logs are removed from all `release` builds.\n\n## Using the log\n\nQt supports both C++ streams and `printf`-like interface. **In this project, only `printf` style logs should be used.**\nLogs are considered to be part of the documentation and stream are too hard to read. The only exception is compatibility\nwith used libraries (curretly *svgscene*).\n\nTo print any Qt type, just wrap it with `qPrintable` macro.\n\n**Example:**\n\n```\nQT_LOGGING_CONF=/data/Dev/school/QtRvSim/qtlogging.ini;\n```\n\n```cpp\nDEBUG(\"Link triggered (href: %s).\", qPrintable(getTargetName()));\n```\n\n## Configuring the log\n\nTo filter shown logs, modify `qtlogging.ini` file in the root of the project. If this config is not found automatically,\nuse `QT_LOGGING_CONF`environment variable.\n\n**Example:**\n\n```shell\nQT_LOGGING_CONF=/data/Dev/school/QtRvSim/qtlogging.ini\n```\n\n**Configuration example** (shows debug logs from `instruction.cpp` only):\n\n```ini\n[Rules]\n*.debug=false\nmachine.Instruction.debug=true\n```"
  },
  {
    "path": "docs/developer/debuging/sanitizers.md",
    "content": "# Runtime sanitizers\n\n## Using sanitizers in this project\n\nAll debug builds are by default built with address and undefined runtime\nsanitizers. To disable them (which is strongly discouraged) run cmake with\noptions none.\n\n```shell\ncmake -DSANITIZERS=none\n```\n\nTo run other selection of sanitiser use, pass colon separated list to the\ncommand.\n\n```shell\ncmake -DSANITIZERS=memory,undefined\n```\n\nNOTE: Some sanitizer cannot be used together, like address and memory.\n\n### Sanitizer debug info and Clang\n\nIf you are using sanitizers with clang and you don't files and line numbers,\nmake sure that `llvm-symbolizer` is installed and if that does not help, add\nenv `ASAN_SYMBOLIZER_PATH=` with path to your symbolizer executable. Most of the\ntime, having it in `PATH` should be enough. \n\n"
  },
  {
    "path": "docs/developer/need-to-know.md",
    "content": "# Need To Know\n\n**Critical information for every developer.**\n\n## Modal Windows, Dialogues, Message Boxes\n\nIn typical, native, Qt setting, modal windows allocated on stack and block upon opening. To manage the modal window, Qt\nspins up a second event loop. However, that is not technically possible in Web-Assembly environment. *(This might be\nsolved in new Qt with “asyncify” build flag, but it does not work with currently used Qt 5.15.2)*\n\n[Read more](http://qtandeverything.blogspot.com/2019/05/exec-on-qt-webassembly.html)\n\n### Broken API usage\n\n```cpp\nQMessageBox msg;\nmsg.exec();\n```\n\n```cpp\nQMessageBox::critical(\"Hello\");\n```\n\nThe `.exec()` method and single line call are typical warning signs.\n\nSome parts of code employ special code for Web-Assembly (like HTML file selector) and the native part may use blocking\nsolution. However, in performance non-critical parts (which most dialogs are), unified asynchronous solution is\npreferred.\n\n### Solution\n\nThe modal window object has to be allocated dynamically and started using asynchronous method `open`. Answer has to be\nobtained via `connect` callback.\n\nFor ease of use, wrapper functions are prepared in `gui/helper/async_modal.h`.\n\nFree can be accomplished using special call `msg->setAttribute(Qt::WA_DeleteOnClose)` before opening. This method is\nautomatically employed in `async_modal.h`.\n\nDocs: [WA_DeleteOnClose](https://doc.qt.io/qt-5/qt.html#WidgetAttribute-enum)\n, [QCloseEvent](https://doc.qt.io/qt-5/qcloseevent.html)"
  },
  {
    "path": "docs/user/SUMMARY.md",
    "content": "# Summary\n\n[Introduction](introduction.md)\n\n# The basics\n\n- [Getting Started](basics/getting_started.md)\n- [First Launch](basics/first_launch.md)\n- [Basics of the User Interface](basics/basics_of_user_interface.md)\n  - [Menus and the Toolbar](basics/menus_and_the_toolbar.md)\n- [Writing Programs](basics/writing_programs.md)\n\n# Reference\n\n- [External Toolchains](reference/external_toolchains.md)\n- [Advanced Configuration](reference/advanced_configuration.md)\n  - [Environment Variables](reference/advanced_configuration/environment_variables.md)\n"
  },
  {
    "path": "docs/user/basics/basics_of_user_interface.md",
    "content": "# User Interface Overview\n\nAfter selecting a configuration preset (or loading an example) in the initial dialog, the main **QtRvSim window** appears.\nThis chapter introduces the primary functional areas of the interface necessary for basic operation.\n![QtRvSim main window (empty workspace)](media/gui_elements/user_interface.webp)\nThe interface can be divided into five main areas:\n![The QtRvSim interface, divided into main sections](media/gui_elements/user_interface_annotated.webp)\n\n## 1. Menu Bar & Toolbar (Blue Area)\n\n- Located at the **top of the window**.\n- The **Menu Bar** provides full access to all commands (`File`, `Machine`, `Windows`, etc.).\n- The **Toolbar** below offers quick access to the most commonly used commands via icons.\n\n**Key Toolbar Buttons (basic simulation):**\n- <img src=\"../media/icons/compfile-256.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Compile Source** – Assemble your code.\n- <img src=\"../media/icons/play.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Run** – Execute the program continuously.\n- <img src=\"../media/icons/next.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Step** – Advance execution one instruction (or one cycle) at a time.\n- <img src=\"../media/icons/reload.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Reset** – Reset the simulation state to the beginning.\n\n## 2. Program Panel (Green Area)\n\n- Toggles via **`Windows -> Program`** or **Ctrl+P**.\n- Displays the **sequence of assembly instructions** for the currently loaded program.\n- During execution, the **currently processed instruction** is highlighted (blue).\n- A dropdown menu allows you to follow a particular **pipeline stage** (fetch, decode, execute, etc.):\n  - Default: **Follow Fetch** – ideal to get started.\n\nThis panel makes it easy to trace the flow of instructions and observe where execution currently is.\n\n## 3. Datapath Visualization / Core View (Red Area)\n\n- The central and most distinctive panel of QtRvSim.\n- Visually represents the processor’s **datapath** based on the selected configuration (single-cycle/pipelined).\n- Shows how instructions flow through components such as the **registers, ALU and memory**.\n\nThis view makes abstract computer architecture concepts concrete by showing the processor \"at work.\"\n\n## 4. Simulation Counters (Purple Area)\n\n- Located in the **top-right corner** of the window.\n- Displays important **statistics** about the simulation:\n  - **Cycles** – Total number of clock cycles since the last reset.\n  - **Stalls** – Number of pipeline stalls encountered (in pipelined configurations).\n\nValues update continuously during execution (`Run`) or after each `Step`.\nCounters reset when you **Reset** or **Reload** the simulation.\n\n## 5. Additional Information Windows\n\nQtRvSim offers several **dockable panels** to inspect system state in detail.\nThese can be opened from the **`Windows`** menu:\n\n![QtRvSim with the windows menu open](media/gui_elements/windows_menu.webp)\n\nYou can rearrange, dock, tab, or float these windows to customize your workspace. Use **`Windows -> Reset Windows`** to restore the default layout.\n\n**Essential Panels for Beginners:**\n- **Registers** (`Windows -> Registers` or **Ctrl+D**) – Displays general-purpose registers **x0–x31** and the Program Counter.\n- **Memory** (`Windows -> Memory` or **Ctrl+M**) – Displays the contents of simulated memory.\n- **Terminal** (`Windows -> Terminal`) – For text I/O when programs interact with the console.\n\n## Next Steps\n\nIn the following chapter, we will look more closely at the available menus and toolbar options.\n*(If you are just starting out, you may skip ahead to chapter _____ and return later as needed.)*\n"
  },
  {
    "path": "docs/user/basics/first_launch.md",
    "content": "# First Launch\n\nWhen you start QtRvSim, or when you select **`File > New Simulation`**, the **Pre-sets and ELF File** dialog will appear.\nThis dialog allows you to select a hardware configuration (pre-set) and, optionally, load a program to run.\n\n![QtRvSim launch window](media/gui_elements/launch_window.webp)\n\n## Dialog Options\n\n- **Config Pages (Left Pane)**\n  Displays the different categories of hardware settings (ISA, caches, etc.).\n  For now, focus on the **Pre-sets and ELF File** page.\n  The other pages will be covered in later chapters.\n\n- **Pre-set (Radio Buttons)**\n  Choose a predefined hardware configuration:\n  - **No pipeline, no cache**: Simplest single-cycle processor model without cache memory. Ideal for learning basic instruction behavior.\n  - **No pipeline, with cache**: Single-cycle processor with cache enabled.\n  - **Pipelined, no hazard unit, no cache**: Classic 5-stage pipeline without automatic hazard handling. Useful to demonstrate data/control hazards. No cache.\n  - **Pipelined, hazard unit, with cache**: More realistic 5-stage pipeline with hazard detection/forwarding enabled, plus caches.\n  - **Custom**: Advanced mode – configure settings manually through the other Config pages.\n\n- **Reset at compile time (reload after make)**\n  If enabled, the simulator automatically reapplies the selected configuration each time you assemble a program.\n  *Recommended: Keep this option checked.*\n\n- **ELF Executable**\n  Use the **Browse** button to load a precompiled RISC-V ELF program (`.elf`).\n  *Tip: For most workflows, you’ll write and assemble assembly (`.s`) code directly in the editor instead.*\n\n- **Action Buttons**\n  - **Example**: Loads a default example assembly program into the editor with the selected pre-set. *This is the easiest way to get started.*\n  - **Start empty**: Opens the simulator with the selected pre-set and an empty editor, ready for you to write or load code.\n  - **Load machine**: Applies the selected pre-set and loads the chosen ELF executable.\n  - **Cancel**: Closes the dialog without applying changes.\n\n---\n\nFor your first run, we recommend selecting either:\n- **`No pipeline, no cache`** (simplest model), or\n- **`Pipelined with hazard unit and cache`** (closer to a realistic processor).\n\nThen click **`Example`** (to load a sample program) or **`Start empty`** (to write your own).\n\nFor this manual, we’ll begin with **`Start empty`** and the **`No pipeline, no cache`** configuration.\n"
  },
  {
    "path": "docs/user/basics/getting_started.md",
    "content": "# Getting started\n\n## System Requirements\n\nQtRvSim is built using the Qt framework and supports multiple platforms. Please ensure your system meets the following minimum requirements before installation.\n\n### Supported Operating Systems\n\n- **Linux** (64-bit distributions with Qt 5.12+)\n- **macOS** (10.10 (Yosemite) or later)\n- **Windows** (Windows 7 or later)\n- **WebAssembly (Browser-based)**\n  - Modern versions of Firefox, Chromium-based browsers or Safari\n\n## Installation\n\nDownload the appropriate installation files from the official QtRvSim releases page: <https://github.com/cvut/qtrvsim/releases>\n\n### WebAssembly\n\nJust open [comparch.edu.cvut.cz/qtrvsim/app](https://comparch.edu.cvut.cz/qtrvsim/app) in your web browser to run QtRvSim without any installation.\n\n\n### Linux Installation\n\nChoose the installation method that best suits your distribution:\n\n[![Packaging status](https://repology.org/badge/vertical-allrepos/qtrvsim.svg)](https://repology.org/project/qtrvsim/versions)\n\n#### Ubuntu 18.04+ and Derivatives\n\n```bash\nsudo add-apt-repository ppa:qtrvsimteam/ppa\nsudo apt-get update\nsudo apt-get install qtrvsim\n```\n\n#### Arch, CentOS, Debian, Fedora, openSUSE, Raspbian/Raspberry Pi OS\n\n1. Visit the [Open Build Service download page](https://software.opensuse.org/download.html?project=home%3Ajdupak&package=qtrvsim)\n2. Select your specific distribution and version\n3. Follow the provided instructions to add the repository and install it\n\n#### Arch Linux (AUR)\n\nYou can install QtRvSim from the Arch User Repository (AUR) using an AUR helper like `yay`:\n\n```bash\nyay -S qtrvsim\n# or\nyay -S qtrvsim-git  # for the latest development version\n```\n\n\n#### Flatpak (Universal Package)\n```bash\nflatpak install flathub cz.cvut.edu.comparch.qtrvsim\n```\nLaunch with:\n```bash\nflatpak run cz.cvut.edu.comparch.qtrvsim\n```\n\n#### NixOS/Nix\n\nInstall the package directly from the official `nixpkgs` [repository](https://search.nixos.org/packages?channel=unstable&show=qtrvsim&query=qtrvsim).\n\n<details>\n<summary><strong>Permanent installation</strong></summary>\n\nAdd `qtrvsim` to your `configuration.nix` file:\n\n```nix\nenvironment.systemPackages = [\n    pkgs.qtrvsim\n];\n```\n\n</details>\n\n<details>\n<summary><strong>Temporary usage</strong></summary>\n\n```bash\nnix-shell -p qtrvsim --run qtrvsim_gui\n```\n\n</details>\n\n### Windows Installation\n\n1. Download the Windows archive: `qtrvsim-mingw32-[version].zip`\n2. Extract the contents to your preferred location (e.g., `C:\\QtRvSim`)\n3. Launch `qtrvsim.exe` from the extracted folder\n4. No additional installation steps required\n\n### macOS Installation\n\n#### Method 1: Disk Image (Recommended)\n1. Download the macOS disk image: `qtrvsim-macos-[version].dmg`\n2. Open the .dmg file\n3. Drag the QtRvSim application to your Applications folder\n4. **First launch**: Right-click the application in Applications, select \"Open,\" and confirm if prompted by Gatekeeper\n\n#### Method 2: Archive File\n1. Download the macOS archive: `qtrvsim-macos-[version].zip`\n2. Extract the QtRvSim.app bundle\n3. Move QtRvSim.app to your Applications folder\n4. Follow step 4 from Method 1 for first launch\n"
  },
  {
    "path": "docs/user/basics/menus_and_the_toolbar.md",
    "content": "### Menu Bar\n\nThe menu bar provides access to all features of QtRvSim. It is divided into standard menus: **File**, **Machine**, **Windows**, **Options**, and **Help**.\n\n---\n\n#### File Menu\nHandles operations related to simulation setup, source files, and the application itself.\n\n- <img src=\"../media/icons/document-import.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **New simulation...** (Ctrl+N)\n  Opens the *Pre-sets and ELF File* configuration dialog to start a new simulation.\n- <img src=\"../media/icons/reload.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Reload simulation** (Ctrl+Shift+R)\n  Reassembles the source code currently in the editor and resets the simulation state (registers, memory, cycle count).\n- **Print** (Ctrl+Alt+P)\n  Prints the contents of the active panel or editor.\n- <img src=\"../media/icons/new.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **New source** (Ctrl+F)\n  Opens a new editor tab to write a fresh assembly program.\n- <img src=\"../media/icons/open.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Open source** (Ctrl+O)\n  Loads an existing RISC-V assembly source file (`.s`) into the editor.\n- <img src=\"../media/icons/save.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Save source** (Ctrl+S)\n  Saves the current editor contents. If previously unsaved, behaves like \"Save source as.\"\n- **Save source as**\n  Prompts for a filename and saves the editor contents to that path.\n- <img src=\"../media/icons/closetab.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Close source** (Ctrl+W)\n  Closes the current editor tab (prompts to save unsaved work).\n- **Examples**\n  Opens a submenu of example RISC-V programs bundled with QtRvSim. Selecting one loads it into the editor.\n- <img src=\"../media/icons/application-exit.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Exit** (Ctrl+Q)\n  Exits the QtRvSim application.\n\n---\n\n#### Machine Menu\nControls simulation execution and machine-specific options.\n\n- <img src=\"../media/icons/play.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Run** (Ctrl+R)\n  Starts continuous execution at the configured speed.\n- <img src=\"../media/icons/pause.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Pause** (Ctrl+P)\n  Halts execution if running.\n- <img src=\"../media/icons/next.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Step** (Ctrl+T)\n  Executes only one instruction.\n- **Speed Controls**\n  Set the execution rate when using **Run**:\n  - 1 instruction/s (Ctrl+1)\n  - 2 instructions/s (Ctrl+2)\n  - 5 instructions/s (Ctrl+5)\n  - 10 instructions/s (Ctrl+0)\n  - 25 instructions/s (Ctrl+F5)\n  - Unlimited (Ctrl+U) – fastest possible speed\n  - Max (Ctrl+A) – fastest speed, with reduced GUI updates (higher performance)\n- **Restart**\n  Resets registers, memory, peripherals, and counters to state after the last assembly.\n- **Mnemonics Registers** (checkbox)\n  Toggle register view to show mnemonic names (`ra`, `sp`) instead of raw (`x1`, `x2`).\n- **Show Symbol**\n  ? to be added ?\n- <img src=\"../media/icons/compfile-256.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Compile Source** (Ctrl+E)\n  Assembles source code in the editor using the built-in assembler.\n- <img src=\"../media/icons/build-256.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Build Executable** (Ctrl+B)\n  Builds a standalone ELF file using an external RISC-V toolchain (e.g., GCC/Clang).\n\n#### Windows Menu\nManages visibility of all simulation panels.\n\n- **Registers** (Ctrl+D) – Register state viewer.\n![alt text](media/gui_elements/registers.webp)\n- **Program** (Ctrl+P) – Code view with current instruction highlighting.\n![alt text](media/gui_elements/program.webp)\n- **Memory** (Ctrl+M) – Memory contents.\n![alt text](media/gui_elements/memory.webp)\n- **Program Cache** (Ctrl+Shift+P) – Instruction cache visualization (if enabled).\n![alt text](media/gui_elements/program_cache.webp)\n- **Data Cache** (Ctrl+Shift+M) – Data cache visualization (if enabled).\n![alt text](media/gui_elements/data_cache.webp)\n- **L2 Cache** – Second-level cache view (if enabled).\n![alt text](media/gui_elements/l2_cache.webp)\n- **Branch Predictor** – Displays prediction tables and data (if configured).\n![alt text](media/gui_elements/branch_predictor.webp)\n- **Peripherals** – Container for simulated I/O devices.\n![alt text](media/gui_elements/peripherals.webp)\n- **Terminal** – Simulated serial terminal.\n![alt text](media/gui_elements/terminal.webp)\n- **LCD Display** – Simulated LCD.\n![alt text](media/gui_elements/lcd_display.webp)\n- **Control and Status Registers (CSR)** (Ctrl+I) – CSR panel.\n![alt text](media/gui_elements/control_and_status_registers.webp)\n- **Core View** – Datapath visualization (CPU core).\n![alt text](media/gui_elements/core.webp)\n- **Messages** – Log and feedback panel.\n![alt text](media/gui_elements/messages.webp)\n- **Reset Windows** – Restores all panels to their default positions.\n\n---\n\n#### Options Menu\n- **Show Line Numbers** (checkbox) – Toggle line numbers in the code editor.\n\n---\n\n#### Help Menu\nProvides information about QtRvSim, version details, and licensing.\n\n---\n\n### Toolbar\n![Toolbar screenshot](media/gui_elements/toolbar.webp)\n\nThe toolbar provides quick access to frequently used actions:\n\n- <img src=\"../media/icons/document-import.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **New Simulation** – Equivalent to *File → New simulation...* (Ctrl+N).\n- <img src=\"../media/icons/reload.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Reload** – Resets simulator state (equivalent to *Machine → Restart*).\n- <img src=\"../media/icons/play.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Run** – Start program execution (Ctrl+R).\n- <img src=\"../media/icons/pause.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Pause** – Pause execution (Ctrl+P).\n- <img src=\"../media/icons/next.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Step** – Execute one instruction (Ctrl+T).\n- **Speed Controls** – Select execution speed (same as *Machine → Speed Controls*).\n- <img src=\"../media/icons/new.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **New Source** – Open a new editor tab (Ctrl+F).\n- <img src=\"../media/icons/open.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Open Source** – Load an existing `.s` or `.elf` file (Ctrl+O).\n- <img src=\"../media/icons/save.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Save Source** – Save current file (Ctrl+S).\n- <img src=\"../media/icons/closetab.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Close Source** – Close active code editor tab (Ctrl+W).\n- <img src=\"../media/icons/compfile-256.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Compile Source** – Assemble current RISC-V program with the built-in assembler (Ctrl+E).\n- <img src=\"../media/icons/build-256.png\" style=\"vertical-align: middle; width: 20px; height: 20px\"> **Build Executable** – Build ELF with external RISC-V toolchain (Ctrl+B).\n"
  },
  {
    "path": "docs/user/basics/writing_programs.md",
    "content": "# Writing Programs\n\nThis chapter covers how to write and run RISC-V assembly programs in QtRvSim.\n\n## Using the Built-in Assembler\n\nQtRvSim includes a built-in assembler that can compile RISC-V assembly code directly within the application:\n\n1. Write your assembly code in the editor\n2. Click **Compile Source** (<kbd>Ctrl+E</kbd>) to assemble the code\n3. The program is loaded into simulated memory and ready to execute\n\nThis is the recommended workflow for learning and experimenting with RISC-V assembly.\n\n## RISC-V Register Reference\n\nRISC-V has 32 general-purpose registers. The simulator supports both numeric names (`x0`–`x31`) and ABI mnemonic names:\n\n| Register | ABI Name | Description |\n|----------|----------|-------------|\n| x0 | zero | Hard-wired zero |\n| x1 | ra | Return address |\n| x2 | sp | Stack pointer |\n| x3 | gp | Global pointer |\n| x4 | tp | Thread pointer |\n| x5–x7 | t0–t2 | Temporary registers |\n| x8 | s0/fp | Saved register / Frame pointer |\n| x9 | s1 | Saved register |\n| x10–x11 | a0–a1 | Function arguments / Return values |\n| x12–x17 | a2–a7 | Function arguments |\n| x18–x27 | s2–s11 | Saved registers |\n| x28–x31 | t3–t6 | Temporary registers |\n\nToggle between numeric and mnemonic names via **Machine → Mnemonic Registers**.\n\n## Simple Assembly Program Template\n\nHere is a minimal RISC-V assembly template for use with the built-in assembler:\n\n```asm\n.globl _start\n.text\n\n_start:\n    # Your code here\n    \n    # Example: Load immediate values\n    li a0, 42          # Load 42 into a0\n    li a1, 10          # Load 10 into a1\n    add a2, a0, a1     # a2 = a0 + a1\n\nend:\n    ebreak             # Stop execution\n    j end              # Loop forever if continued\n```\n\nThe `ebreak` instruction triggers a breakpoint exception, which causes the simulator to pause execution.\n\n## Breakpoints and Debugging\n\n### Software Breakpoints\n\nThe `ebreak` instruction causes the simulator to pause execution. This is useful for stopping at specific points in your program:\n\n```asm\n    # ... some code ...\n    ebreak           # Pause here\n    # ... more code ...\n```\n\nWhen `ebreak` is encountered, the simulator stops. You can then inspect registers and memory, and continue execution with **Step** or **Run**.\n\n### Hardware Breakpoints\n\nYou can also set breakpoints directly in the QtRvSim interface:\n1. In the **Program** panel, click on the address or instruction where you want to break\n2. The simulator will pause when that instruction is fetched\n\n## Loading Programs\n\nThere are several ways to load programs into QtRvSim:\n\n1. **Built-in editor**: Write assembly in the editor and compile with <kbd>Ctrl+E</kbd>\n2. **Open source file**: Use **File → Open Source** to load a `.s` file, then compile\n3. **Load ELF**: In the launch dialog, use **Browse** to select a precompiled `.elf` file\n4. **Build Executable**: Use **Machine → Build Executable** (<kbd>Ctrl+B</kbd>) to compile using an external toolchain\n\n## Next Steps\n\nOnce you're comfortable writing basic assembly programs, you may want to explore:\n- Using an [external RISC-V toolchain](external_toolchains.md) for compiling C programs or more complex projects"
  },
  {
    "path": "docs/user/book.toml",
    "content": "[book]\ntitle = \"QtRVSim User Manual\"\nauthors = [\"Filip Kraus\"]\nlanguage = \"en\"\nsrc = \".\"\n"
  },
  {
    "path": "docs/user/introduction.md",
    "content": "# Introduction\n\nWelcome to the **QtRvSim User Manual**.\n\nQtRvSim is a graphical simulator designed to support learning the fundamentals of computer architecture using the **RISC-V instruction set**. It provides an interactive environment for writing, assembling, and simulating simple RISC-V assembly programs. By visualizing instruction execution on different processor models, QtRvSim helps make abstract computer architecture concepts clear and tangible.\n\nThe project is developed by the [Computer Architectures Education](http://comparch.edu.cvut.cz) project at [Czech Technical University](http://www.cvut.cz/) and souce code is available on [GitHub](https://github.com/cvut/qtrvsim).\n\n## Key Features\n\n- **Visual Datapaths** – Observe execution on both single-cycle and 5-stage pipelined processor models.\n- **Integrated Tools** – Write code with the built-in text editor and assemble directly within the simulator.\n- **State Inspection** – Inspect register contents, memory, and cache status in real time.\n- **Peripheral Emulation** – Experiment with simulated devices, including a terminal, LCD, and LEDs/buttons.\n- **Cross-Platform Support** – Available on Linux, Windows, macOS, and via WebAssembly (browser-based).\n\n## About This Manual\n\nThis manual will guide you through:\n- Installing QtRvSim on your preferred platform\n- Understanding the user interface\n- ...\n\nWhether you are a student learning the basics of computer architecture or an instructor looking for an accessible teaching tool, QtRvSim provides an intuitive and hands-on way to experiment with RISC-V systems.\n"
  },
  {
    "path": "docs/user/reference/advanced_configuration/environment_variables.md",
    "content": "# Environment Variables\n\nQtRvSim supports several environment variables to customize its behavior. These are primarily useful for advanced users, portable deployments, and development purposes.\n\n---\n\n## GUI Configuration\n\n### Portable Application Mode\n\nBy default, QtRvSim GUI stores application state (window positions, last selected configuration, etc.) using the system's standard configuration location. To make the simulator fully portable (e.g., running from a USB drive), you can specify a custom configuration file path.\n\n**`QTRVSIM_CONFIG_FILE`**\n\nSet this variable to the path of a `.ini` file where QtRvSim should store its GUI state.\n\n**Example (Linux/macOS):**\n```bash\nexport QTRVSIM_CONFIG_FILE=/path/to/portable/qtrvsim_config.ini\n./qtrvsim_gui\n```\n\n**Example (Windows):**\n```cmd\nset QTRVSIM_CONFIG_FILE=D:\\QtRvSim\\config.ini\nqtrvsim_gui.exe\n```\n\n---\n\n### Scale GUi Fonts for Classroom Projector\n\nThe fonts can be to small for students to see them from the more distant places or projector resolution can be interpolated or scaled that text in editors and menus is hard to read. The next option allows to scale fonts for the whole application.\n\n```bash\nQT_SCALE_FACTOR=1.4 ./qtrvsim_gui\n```\n\n---\n\n## Logging\n\nLogging environment variables are primarily intended for development and debugging purposes. For detailed information about available logging options, please refer to the developer documentation.\n"
  },
  {
    "path": "docs/user/reference/advanced_configuration.md",
    "content": "# Advanced Configuration\n\nThis section covers advanced configuration options for QtRvSim, including environment variables and other settings for power users.\n\n- [Environment Variables](advanced_configuration/environment_variables.md) – Customize simulator behavior through environment variables, including portable application mode.\n"
  },
  {
    "path": "docs/user/reference/external_toolchains.md",
    "content": "# External Toolchains\n\nFor more complex programs or C code, you can use an external RISC-V cross-compiler toolchain instead of the built-in assembler.\n\n## Installing the Toolchain\n\n**Ubuntu/Debian:**\n```bash\nsudo apt install gcc-riscv64-unknown-elf\n```\n\n**Fedora:**\n```bash\nsudo dnf install riscv64-elf-gcc\n```\n\n**Arch Linux:**\n```bash\nsudo pacman -S riscv64-elf-gcc\n```\n\n**macOS (Homebrew):**\n```bash\nbrew install riscv64-elf-gcc\n```\n\n## Compiling Assembly Programs\n\nTo compile a simple assembly program:\n\n```bash\nriscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -nostartfiles -o program.elf program.s\n```\n\nOr for RV64:\n```bash\nriscv64-unknown-elf-gcc -march=rv64i -mabi=lp64 -nostdlib -nostartfiles -o program.elf program.s\n```\n\nThe above choice is for basic RISC-V integer ISA with 32 registers.\n\nQtRvSim supports even following extensions:\n\n- M (`-march=rv32im`/`-march=rv64im`) - hardware multiply and divide instructions\n- A (with M `-march=rv32ima`/`-march=rv64ima`) - atomic operations\n- Zicsr (with A and M `-march=rv32ima_zicsr`/`-march=rv64ima_zicsr`) support for control registers\n\nThe A, M and XLEN should match setting in the `Core ISA and Hazards` setup dialog tab.\n\n## Compiling C Programs\n\nFor C programs, you need a minimal startup file and appropriate compiler flags.\n\n**Startup code (`crt0.s`):**\n```asm\n/* minimal replacement of crt0.o which is else provided by C library */\n\n.globl main\n.globl _start\n.globl _heap_stack_start\n.globl _heap_stack_end\n\n.text\n\n_start:\n        .option push\n        .option norelax\n        /* set a global pointer to allow access to C global variables */\n        la gp, __global_pointer$\n        /* it has to be done without \"relax\", because else it is\n         * optimized to a gp register relative operation by linker\n         */\n        .option pop\n        la      sp, _heap_stack_end\n        addi    a0, zero, 0\n        addi    a1, zero, 0\n        jal     main\n_exit:\n        addi    a0, zero, 0\n        addi    a7, zero, 93  /* SYS_exit */\n        ecall\n        /* catch case when syscalls are disabled */\n        ebreak\n        j       _exit\n\n.bss\n\n.align 4\n\n/* the area which can be used for a heap from the bootom\n * and stack from the top\n */\n_heap_stack_start:\n        .skip   16384\n_heap_stack_end:\n\n.end\n```\n\n**Example C program (`hello.c`):**\n```c\n#define SERIAL_PORT_BASE   0xffffc000\n#define SERP_TX_ST_REG     (*(volatile unsigned *)(SERIAL_PORT_BASE + 0x00))\n#define SERP_TX_DATA_REG   (*(volatile unsigned *)(SERIAL_PORT_BASE + 0x04))\n\nvoid print_char(char c) {\n    while (SERP_TX_ST_REG & 0x1);  // Wait while busy\n    SERP_TX_DATA_REG = c;\n}\n\nvoid print_string(const char *s) {\n    while (*s) {\n        print_char(*s++);\n    }\n}\n\nint main(void) {\n    print_string(\"Hello from QtRvSim!\\n\");\n    return 0;\n}\n```\n\n**Compilation:**\n```bash\nriscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -c crt0.s -o crt0.o\nriscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -O2 -c hello.c -o hello.o\nriscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -static -T linker.ld crt0.o hello.o -o hello.elf\n```\n\n## Linker Script\n\nA simple linker script (`linker.ld`) for QtRvSim:\n\n```ld\nENTRY(_start)\n\nMEMORY {\n    RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 64K\n}\n\nSECTIONS {\n    .text : {\n        *(.text)\n        *(.text.*)\n    } > RAM\n\n    .rodata : {\n        *(.rodata)\n        *(.rodata.*)\n    } > RAM\n\n    .data : {\n        *(.data)\n        *(.data.*)\n    } > RAM\n\n    .bss : {\n        *(.bss)\n        *(.bss.*)\n        *(COMMON)\n    } > RAM\n}\n```\n\n## Using Make for Build Automation\n\nFor larger projects, a `Makefile` automates the build process:\n\n```makefile\nCROSS = riscv64-unknown-elf-\nCC = $(CROSS)gcc\nAS = $(CROSS)as\nLD = $(CROSS)ld\nOBJCOPY = $(CROSS)objcopy\nOBJDUMP = $(CROSS)objdump\n\nARCH_FLAGS = -march=rv32i -mabi=ilp32\nCFLAGS = $(ARCH_FLAGS) -O2 -Wall -nostdlib -ffreestanding\nLDFLAGS = $(ARCH_FLAGS) -nostdlib -static -T linker.ld\n\nSOURCES = crt0.s main.c\nTARGET = program.elf\n\nOBJECTS = $(SOURCES:.s=.o)\nOBJECTS := $(OBJECTS:.c=.o)\n\nall: $(TARGET)\n\n%.o: %.s\n\t$(CC) $(ARCH_FLAGS) -c $< -o $@\n\n%.o: %.c\n\t$(CC) $(CFLAGS) -c $< -o $@\n\n$(TARGET): $(OBJECTS)\n\t$(CC) $(LDFLAGS) $^ -o $@\n\ndisasm: $(TARGET)\n\t$(OBJDUMP) -d $(TARGET)\n\nclean:\n\trm -f $(OBJECTS) $(TARGET)\n\n.PHONY: all clean disasm\n```\n\n## Loading ELF Files\n\nTo load a compiled ELF file into QtRvSim:\n\n1. In the launch dialog, use **Browse** to select the `.elf` file, then click **Load machine**\n2. Or use **Machine → Build Executable** (<kbd>Ctrl+B</kbd>) to compile and load using an external toolchain directly from the editor"
  },
  {
    "path": "external/compiler/compile.sh",
    "content": "#!/usr/bin/env bash\n# This script compiles compilation tools for mips to be used with qtmips\nset -e\n\nINST_PREFIX=\"$(pwd)\"\n\nINST_CT_PREFIX=\"$INST_PREFIX/ct-ng\"\nmkdir -p \"$INST_CT_PREFIX\"\n\n# First update git submodule\npushd \"$(dirname \"$0\")\" >/dev/null\ngit submodule update crosstool-ng\n\npushd crosstool-ng >/dev/null\n\n# Now compile it\n# TODO don't compile it in place?\n./bootstrap\n./configure --prefix=\"$INST_CT_PREFIX\"\nmake\nmake install\n# TODO do cleanups?\n\npopd >/dev/null\n\npopd >/dev/null\n\n# Copy configuration\ncp \"$(dirname \"$0\")/config\" ct-ng/.config\n\npushd ct-ng >/dev/null\n\n# And compile\n./bin/ct-ng oldconfig\nCT_PREFIX=\"$INST_PREFIX\" ./bin/ct-ng build\n\npopd >/dev/null\n\n# Cleanup installed crosstool-ng\nrm -rf ct-ng\n"
  },
  {
    "path": "external/compiler/config",
    "content": "#\n# Automatically generated file; DO NOT EDIT.\n# Crosstool-NG Configuration\n#\nCT_CONFIGURE_has_static_link=y\nCT_CONFIGURE_has_wget=y\nCT_CONFIGURE_has_curl=y\nCT_CONFIGURE_has_stat_flavor_GNU=y\nCT_CONFIGURE_has_make_3_81_or_newer=y\nCT_CONFIGURE_has_libtool_2_4_or_newer=y\nCT_CONFIGURE_has_libtoolize_2_4_or_newer=y\nCT_CONFIGURE_has_autoconf_2_63_or_newer=y\nCT_CONFIGURE_has_autoreconf_2_63_or_newer=y\nCT_CONFIGURE_has_automake_1_15_or_newer=y\nCT_CONFIGURE_has_gnu_m4_1_4_12_or_newer=y\nCT_CONFIGURE_has_svn=y\nCT_CONFIGURE_has_git=y\nCT_MODULES=y\n\n#\n# Paths and misc options\n#\n\n#\n# crosstool-NG behavior\n#\n# CT_OBSOLETE is not set\n# CT_EXPERIMENTAL is not set\n# CT_DEBUG_CT is not set\n\n#\n# Paths\n#\nCT_LOCAL_TARBALLS_DIR=\"${HOME}/src\"\nCT_SAVE_TARBALLS=y\nCT_WORK_DIR=\"${CT_TOP_DIR}/.build\"\nCT_BUILD_TOP_DIR=\"${CT_WORK_DIR}/${CT_HOST:+HOST-${CT_HOST}/}${CT_TARGET}\"\nCT_PREFIX_DIR=\"${CT_PREFIX:-${HOME}/x-tools}/${CT_HOST:+HOST-${CT_HOST}/}${CT_TARGET}\"\nCT_RM_RF_PREFIX_DIR=y\nCT_REMOVE_DOCS=y\nCT_PREFIX_DIR_RO=y\nCT_STRIP_HOST_TOOLCHAIN_EXECUTABLES=y\n# CT_STRIP_TARGET_TOOLCHAIN_EXECUTABLES is not set\n\n#\n# Downloading\n#\nCT_DOWNLOAD_AGENT_WGET=y\n# CT_DOWNLOAD_AGENT_CURL is not set\n# CT_DOWNLOAD_AGENT_NONE is not set\n# CT_FORBID_DOWNLOAD is not set\n# CT_FORCE_DOWNLOAD is not set\nCT_CONNECT_TIMEOUT=10\nCT_DOWNLOAD_WGET_OPTIONS=\"--passive-ftp --tries=3 -nc --progress=dot:binary\"\n# CT_ONLY_DOWNLOAD is not set\n# CT_USE_MIRROR is not set\n\n#\n# Extracting\n#\n# CT_FORCE_EXTRACT is not set\nCT_OVERRIDE_CONFIG_GUESS_SUB=y\n# CT_ONLY_EXTRACT is not set\nCT_PATCH_BUNDLED=y\n# CT_PATCH_LOCAL is not set\n# CT_PATCH_BUNDLED_LOCAL is not set\n# CT_PATCH_LOCAL_BUNDLED is not set\n# CT_PATCH_BUNDLED_FALLBACK_LOCAL is not set\n# CT_PATCH_LOCAL_FALLBACK_BUNDLED is not set\n# CT_PATCH_NONE is not set\nCT_PATCH_ORDER=\"bundled\"\n\n#\n# Build behavior\n#\nCT_PARALLEL_JOBS=0\nCT_LOAD=\"\"\nCT_USE_PIPES=y\nCT_EXTRA_CFLAGS_FOR_BUILD=\"\"\nCT_EXTRA_LDFLAGS_FOR_BUILD=\"\"\nCT_EXTRA_CFLAGS_FOR_HOST=\"\"\nCT_EXTRA_LDFLAGS_FOR_HOST=\"\"\n# CT_CONFIG_SHELL_SH is not set\n# CT_CONFIG_SHELL_ASH is not set\nCT_CONFIG_SHELL_BASH=y\n# CT_CONFIG_SHELL_CUSTOM is not set\nCT_CONFIG_SHELL=\"${bash}\"\n\n#\n# Logging\n#\n# CT_LOG_ERROR is not set\n# CT_LOG_WARN is not set\n# CT_LOG_INFO is not set\nCT_LOG_EXTRA=y\n# CT_LOG_ALL is not set\n# CT_LOG_DEBUG is not set\nCT_LOG_LEVEL_MAX=\"EXTRA\"\n# CT_LOG_SEE_TOOLS_WARN is not set\nCT_LOG_PROGRESS_BAR=y\nCT_LOG_TO_FILE=y\nCT_LOG_FILE_COMPRESS=y\n\n#\n# Target options\n#\nCT_ARCH=\"mips\"\n# CT_ARCH_alpha is not set\n# CT_ARCH_arm is not set\n# CT_ARCH_avr is not set\n# CT_ARCH_m68k is not set\nCT_ARCH_mips=y\n# CT_ARCH_nios2 is not set\n# CT_ARCH_powerpc is not set\n# CT_ARCH_s390 is not set\n# CT_ARCH_sh is not set\n# CT_ARCH_sparc is not set\n# CT_ARCH_x86 is not set\n# CT_ARCH_xtensa is not set\nCT_ARCH_alpha_AVAILABLE=y\nCT_ARCH_arm_AVAILABLE=y\nCT_ARCH_avr_AVAILABLE=y\nCT_ARCH_m68k_AVAILABLE=y\nCT_ARCH_microblaze_AVAILABLE=y\nCT_ARCH_mips_AVAILABLE=y\nCT_ARCH_nios2_AVAILABLE=y\nCT_ARCH_powerpc_AVAILABLE=y\nCT_ARCH_s390_AVAILABLE=y\nCT_ARCH_sh_AVAILABLE=y\nCT_ARCH_sparc_AVAILABLE=y\nCT_ARCH_x86_AVAILABLE=y\nCT_ARCH_xtensa_AVAILABLE=y\nCT_ARCH_SUFFIX=\"\"\n\n#\n# Generic target options\n#\n# CT_MULTILIB is not set\nCT_DEMULTILIB=y\nCT_ARCH_USE_MMU=y\nCT_ARCH_SUPPORTS_BOTH_ENDIAN=y\nCT_ARCH_DEFAULT_BE=y\nCT_ARCH_BE=y\n# CT_ARCH_LE is not set\nCT_ARCH_ENDIAN=\"big\"\nCT_ARCH_SUPPORTS_32=y\nCT_ARCH_SUPPORTS_64=y\nCT_ARCH_DEFAULT_32=y\nCT_ARCH_BITNESS=32\nCT_ARCH_32=y\n# CT_ARCH_64 is not set\n\n#\n# Target optimisations\n#\nCT_ARCH_SUPPORTS_WITH_ARCH=y\nCT_ARCH_SUPPORTS_WITH_TUNE=y\nCT_ARCH_SUPPORTS_WITH_FLOAT=y\nCT_ARCH_ARCH=\"\"\nCT_ARCH_TUNE=\"\"\n# CT_ARCH_FLOAT_AUTO is not set\n# CT_ARCH_FLOAT_HW is not set\nCT_ARCH_FLOAT_SW=y\nCT_TARGET_CFLAGS=\"\"\nCT_TARGET_LDFLAGS=\"\"\nCT_ARCH_FLOAT=\"soft\"\n\n#\n# mips other options\n#\nCT_ARCH_mips_o32=y\nCT_ARCH_mips_ABI=\"32\"\n\n#\n# Toolchain options\n#\n\n#\n# General toolchain options\n#\nCT_WANTS_STATIC_LINK=y\nCT_WANTS_STATIC_LINK_CXX=y\nCT_STATIC_TOOLCHAIN=y\nCT_TOOLCHAIN_PKGVERSION=\"\"\nCT_TOOLCHAIN_BUGURL=\"\"\n\n#\n# Tuple completion and aliasing\n#\nCT_TARGET_VENDOR=\"qtmips\"\nCT_TARGET_ALIAS_SED_EXPR=\"\"\nCT_TARGET_ALIAS=\"\"\n\n#\n# Toolchain type\n#\nCT_CROSS=y\n# CT_CANADIAN is not set\nCT_TOOLCHAIN_TYPE=\"cross\"\n\n#\n# Build system\n#\nCT_BUILD=\"\"\nCT_BUILD_PREFIX=\"\"\nCT_BUILD_SUFFIX=\"\"\n\n#\n# Misc options\n#\n# CT_TOOLCHAIN_ENABLE_NLS is not set\n\n#\n# Operating System\n#\nCT_BARE_METAL=y\nCT_KERNEL=\"bare-metal\"\nCT_KERNEL_bare_metal=y\n# CT_KERNEL_linux is not set\nCT_KERNEL_bare_metal_AVAILABLE=y\nCT_KERNEL_linux_AVAILABLE=y\nCT_KERNEL_windows_AVAILABLE=y\n\n#\n# Common kernel options\n#\n\n#\n# Binary utilities\n#\nCT_ARCH_BINFMT_ELF=y\nCT_BINUTILS=\"binutils\"\nCT_BINUTILS_binutils=y\n\n#\n# GNU binutils\n#\nCT_BINUTILS_VERSION=\"2.28\"\n# CT_BINUTILS_SHOW_LINARO is not set\nCT_BINUTILS_V_2_28=y\n# CT_BINUTILS_V_2_27 is not set\n# CT_BINUTILS_V_2_26 is not set\nCT_BINUTILS_2_27_or_later=y\nCT_BINUTILS_2_26_or_later=y\nCT_BINUTILS_2_25_1_or_later=y\nCT_BINUTILS_2_25_or_later=y\nCT_BINUTILS_2_24_or_later=y\nCT_BINUTILS_2_23_2_or_later=y\nCT_BINUTILS_HAS_HASH_STYLE=y\nCT_BINUTILS_HAS_GOLD=y\nCT_BINUTILS_HAS_PLUGINS=y\nCT_BINUTILS_HAS_PKGVERSION_BUGURL=y\nCT_BINUTILS_LINKER_LD=y\nCT_BINUTILS_LINKERS_LIST=\"ld\"\nCT_BINUTILS_LINKER_DEFAULT=\"bfd\"\nCT_BINUTILS_EXTRA_CONFIG_ARRAY=\"\"\n\n#\n# binutils other options\n#\n\n#\n# C-library\n#\nCT_LIBC=\"newlib\"\nCT_LIBC_VERSION=\"2.5.0.20170323\"\nCT_LIBC_newlib=y\n# CT_LIBC_none is not set\nCT_LIBC_avr_libc_AVAILABLE=y\nCT_LIBC_glibc_AVAILABLE=y\nCT_THREADS=\"none\"\nCT_LIBC_mingw_AVAILABLE=y\nCT_LIBC_musl_AVAILABLE=y\nCT_LIBC_newlib_AVAILABLE=y\n# CT_CC_NEWLIB_SHOW_LINARO is not set\nCT_LIBC_NEWLIB_V_2_5_0=y\n# CT_LIBC_NEWLIB_V_2_4_0 is not set\n# CT_LIBC_NEWLIB_V_2_3_0 is not set\n# CT_LIBC_NEWLIB_V_2_2_0 is not set\n# CT_LIBC_NEWLIB_V_2_1_0 is not set\n# CT_LIBC_NEWLIB_V_2_0_0 is not set\n# CT_LIBC_NEWLIB_V_1_20_0 is not set\n# CT_LIBC_NEWLIB_V_1_19_0 is not set\n# CT_LIBC_NEWLIB_V_1_18_0 is not set\n# CT_LIBC_NEWLIB_V_1_17_0 is not set\nCT_LIBC_NEWLIB_2_5=y\nCT_LIBC_NEWLIB_2_5_or_later=y\nCT_LIBC_NEWLIB_2_4_or_later=y\nCT_LIBC_NEWLIB_2_3_or_later=y\nCT_LIBC_NEWLIB_2_2_or_later=y\nCT_LIBC_NEWLIB_2_1_or_later=y\nCT_LIBC_NEWLIB_2_0_or_later=y\nCT_LIBC_NEWLIB_TARGET_CFLAGS=\"\"\nCT_LIBC_none_AVAILABLE=y\nCT_LIBC_uClibc_AVAILABLE=y\nCT_LIBC_SUPPORT_THREADS_NONE=y\nCT_LIBC_PROVIDES_CXA_ATEXIT=y\n\n#\n# Common C library options\n#\nCT_THREADS_NONE=y\n\n#\n# newlib other options\n#\n# CT_LIBC_NEWLIB_IO_C99FMT is not set\n# CT_LIBC_NEWLIB_IO_LL is not set\n# CT_LIBC_NEWLIB_IO_FLOAT is not set\n# CT_LIBC_NEWLIB_IO_POS_ARGS is not set\nCT_LIBC_NEWLIB_FVWRITE_IN_STREAMIO=y\nCT_LIBC_NEWLIB_UNBUF_STREAM_OPT=y\nCT_LIBC_NEWLIB_FSEEK_OPTIMIZATION=y\n# CT_LIBC_NEWLIB_DISABLE_SUPPLIED_SYSCALLS is not set\n# CT_LIBC_NEWLIB_REGISTER_FINI is not set\nCT_LIBC_NEWLIB_ATEXIT_DYNAMIC_ALLOC=y\n# CT_LIBC_NEWLIB_GLOBAL_ATEXIT is not set\n# CT_LIBC_NEWLIB_LITE_EXIT is not set\n# CT_LIBC_NEWLIB_REENT_SMALL is not set\nCT_LIBC_NEWLIB_MULTITHREAD=y\n# CT_LIBC_NEWLIB_EXTRA_SECTIONS is not set\nCT_LIBC_NEWLIB_WIDE_ORIENT=y\nCT_LIBC_NEWLIB_ENABLE_TARGET_OPTSPACE=y\n# CT_LIBC_NEWLIB_NANO_MALLOC is not set\n# CT_LIBC_NEWLIB_NANO_FORMATTED_IO is not set\nCT_LIBC_NEWLIB_EXTRA_CONFIG_ARRAY=\"\"\n\n#\n# C compiler\n#\nCT_CC=\"gcc\"\nCT_CC_CORE_PASS_2_NEEDED=y\nCT_CC_gcc=y\nCT_CC_GCC_VERSION=\"5.4.0\"\n# CT_CC_GCC_SHOW_LINARO is not set\n# CT_CC_GCC_V_6_3_0 is not set\nCT_CC_GCC_V_5_4_0=y\n# CT_CC_GCC_V_4_9_4 is not set\nCT_CC_GCC_4_8_or_later=y\nCT_CC_GCC_4_9_or_later=y\nCT_CC_GCC_5=y\nCT_CC_GCC_5_or_later=y\nCT_CC_GCC_HAS_LIBMPX=y\nCT_CC_GCC_ENABLE_CXX_FLAGS=\"\"\nCT_CC_GCC_CORE_EXTRA_CONFIG_ARRAY=\"\"\nCT_CC_GCC_EXTRA_CONFIG_ARRAY=\"\"\n# CT_CC_GCC_TARGET_FINAL is not set\nCT_CC_GCC_STATIC_LIBSTDCXX=y\n# CT_CC_GCC_SYSTEM_ZLIB is not set\nCT_CC_GCC_CONFIG_TLS=m\n\n#\n# Optimisation features\n#\nCT_CC_GCC_USE_GRAPHITE=y\n\n#\n# Settings for libraries running on target\n#\nCT_CC_GCC_ENABLE_TARGET_OPTSPACE=y\n# CT_CC_GCC_LIBMUDFLAP is not set\n# CT_CC_GCC_LIBSSP is not set\n# CT_CC_GCC_LIBQUADMATH is not set\n\n#\n# Misc. obscure options.\n#\nCT_CC_CXA_ATEXIT=y\n# CT_CC_GCC_DISABLE_PCH is not set\nCT_CC_GCC_LDBL_128=m\n# CT_CC_GCC_BUILD_ID is not set\nCT_CC_GCC_LNK_HASH_STYLE_DEFAULT=y\n# CT_CC_GCC_LNK_HASH_STYLE_SYSV is not set\n# CT_CC_GCC_LNK_HASH_STYLE_GNU is not set\n# CT_CC_GCC_LNK_HASH_STYLE_BOTH is not set\nCT_CC_GCC_LNK_HASH_STYLE=\"\"\nCT_CC_GCC_DEC_FLOAT_AUTO=y\n# CT_CC_GCC_DEC_FLOAT_BID is not set\n# CT_CC_GCC_DEC_FLOAT_DPD is not set\n# CT_CC_GCC_DEC_FLOATS_NO is not set\nCT_CC_GCC_HAS_ARCH_OPTIONS=y\n\n#\n# archictecture-specific options\n#\nCT_CC_GCC_mips_llsc=m\nCT_CC_GCC_mips_synci=m\n# CT_CC_GCC_mips_plt is not set\nCT_CC_SUPPORT_CXX=y\nCT_CC_SUPPORT_FORTRAN=y\nCT_CC_SUPPORT_JAVA=y\nCT_CC_SUPPORT_ADA=y\nCT_CC_SUPPORT_OBJC=y\nCT_CC_SUPPORT_OBJCXX=y\nCT_CC_SUPPORT_GOLANG=y\n\n#\n# Additional supported languages:\n#\n# CT_CC_LANG_CXX is not set\n# CT_CC_LANG_FORTRAN is not set\n\n#\n# Debug facilities\n#\n# CT_DEBUG_gdb is not set\n# CT_DEBUG_ltrace is not set\n# CT_DEBUG_strace is not set\n\n#\n# Companion libraries\n#\nCT_COMPLIBS_NEEDED=y\nCT_GMP_NEEDED=y\nCT_MPFR_NEEDED=y\nCT_ISL_NEEDED=y\nCT_MPC_NEEDED=y\nCT_COMPLIBS=y\n# CT_LIBICONV is not set\n# CT_GETTEXT is not set\nCT_GMP=y\nCT_MPFR=y\nCT_ISL=y\nCT_MPC=y\n# CT_ZLIB is not set\nCT_GMP_V_6_1_2=y\nCT_GMP_5_0_2_or_later=y\nCT_GMP_VERSION=\"6.1.2\"\nCT_MPFR_V_3_1_5=y\nCT_MPFR_VERSION=\"3.1.5\"\nCT_ISL_V_0_16_1=y\n# CT_ISL_V_0_15 is not set\nCT_ISL_V_0_16_or_later=y\nCT_ISL_V_0_15_or_later=y\nCT_ISL_V_0_14_or_later=y\nCT_ISL_V_0_12_or_later=y\nCT_ISL_VERSION=\"0.16.1\"\nCT_MPC_V_1_0_3=y\nCT_MPC_VERSION=\"1.0.3\"\n\n#\n# Companion libraries common options\n#\n# CT_COMPLIBS_CHECK is not set\n\n#\n# Companion tools\n#\n# CT_COMP_TOOLS_FOR_HOST is not set\n# CT_COMP_TOOLS_autoconf is not set\n# CT_COMP_TOOLS_automake is not set\n# CT_COMP_TOOLS_libtool is not set\n# CT_COMP_TOOLS_m4 is not set\n# CT_COMP_TOOLS_make is not set\n"
  },
  {
    "path": "external/svgscene/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\nproject(svgscene)\n\nset(QT_VERSION_MAJOR \"auto\" CACHE STRING \"Qt major version to use. 5|6|auto\")\n\nif (NOT \"${QT_VERSION_MAJOR}\" MATCHES \"5|6|auto\")\n    message(FATAL_ERROR \"Invalid value for QT_VERSION_MAJOR: ${QT_VERSION_MAJOR} (expected 5, 6 or auto)\")\nendif ()\n\nif (\"${QT_VERSION_MAJOR}\" STREQUAL \"auto\")\n    find_package(QT NAMES Qt5 Qt6 COMPONENTS Core REQUIRED)\nendif ()\n\n# Normally, we would use variable Qt5 or Qt6 to reference the Qt library. Here we do that through\n# this variable based on detected version major of Qt.\nset(QtLib \"Qt${QT_VERSION_MAJOR}\")\nfind_package(${QtLib}\n\t\tREQUIRED COMPONENTS Core Widgets Gui)\n\nset(CMAKE_AUTOMOC ON)\nset(CMAKE_AUTORCC ON)\nset(CMAKE_AUTOUIC ON)\n\nadd_library(svgscene STATIC\n\t\tsrc/svgscene/components/groupitem.cpp\n\t\tsrc/svgscene/components/groupitem.h\n\t\tsrc/svgscene/components/hyperlinkitem.cpp\n\t\tsrc/svgscene/components/hyperlinkitem.h\n\t\tsrc/svgscene/components/simpletextitem.cpp\n\t\tsrc/svgscene/components/simpletextitem.h\n\t\tsrc/svgscene/graphicsview/svggraphicsview.cpp\n\t\tsrc/svgscene/graphicsview/svggraphicsview.h\n\t\tsrc/svgscene/svgdocument.cpp\n\t\tsrc/svgscene/svgdocument.h\n\t\tsrc/svgscene/svggraphicsscene.cpp\n\t\tsrc/svgscene/svggraphicsscene.h\n\t\tsrc/svgscene/svghandler.cpp\n\t\tsrc/svgscene/svghandler.h\n\t\tsrc/svgscene/svgmetadata.cpp\n\t\tsrc/svgscene/svgmetadata.h\n\t\tsrc/svgscene/svgspec.h\n\t\tsrc/svgscene/polyfills/qt5/qwheelevent.h\n\t\tsrc/svgscene/polyfills/qt5/qstringview.h\n\t\tsrc/svgscene/utils/logging.h\n\t\tsrc/svgscene/utils/memory_ownership.h\n\t\t)\ntarget_link_libraries(svgscene\n\t\tPRIVATE ${QtLib}::Core ${QtLib}::Gui ${QtLib}::Widgets)\ntarget_include_directories(svgscene PUBLIC src PRIVATE src/svgscene)\n\nadd_executable(svgscene-example EXCLUDE_FROM_ALL\n\t\tsrc/example/main.cpp\n\t\tsrc/example/mainwindow.cpp\n\t\tsrc/example/mainwindow.h\n\t\tsrc/example/mainwindow.ui\n\t\t)\ntarget_link_libraries(svgscene-example\n\t\tPRIVATE ${QtLib}::Core ${QtLib}::Gui ${QtLib}::Widgets svgscene)"
  },
  {
    "path": "external/svgscene/LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    {one line to give the program's name and a brief idea of what it does.}\n    Copyright (C) {year}  {name of author}\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    {project}  Copyright (C) {year}  {fullname}\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n"
  },
  {
    "path": "external/svgscene/README.md",
    "content": "# Qt SVG DOM\n\nThis tool parses an SVG file into Qt graphics objects and provides tools to lookup elements based on xml attributes in\nthe SVG file.\n\n**WARNING** SVG support is extremely limited, but it is enough to render most static SVGs.\n\n## Based on\n\n- [svgscene](https://github.com/fvacek/svgscene)\n- [libshv](https://github.com/silicon-heaven/libshv/)\n- [Qt SVG](https://github.com/qt/qtsvg)\n\n## Roadmap\n\n- Parametrize xml attributes to save\n- Add default style attributes.\n- Store style in struct, not hashtable. Maybe parse to enums."
  },
  {
    "path": "external/svgscene/src/example/main.cpp",
    "content": "#include \"mainwindow.h\"\n#include \"svgscene/utils/logging.h\"\n\n#include <QApplication>\n\nint main(int argc, char *argv[]) {\n    QApplication a(argc, argv);\n    MainWindow w;\n    w.show();\n\n    w.openFile(QApplication::arguments().value(1));\n\n    return QApplication::exec();\n}\n"
  },
  {
    "path": "external/svgscene/src/example/mainwindow.cpp",
    "content": "#include \"mainwindow.h\"\n\n#include \"svgscene/components/simpletextitem.h\"\n#include \"svgscene/graphicsview/svggraphicsview.h\"\n#include \"svgscene/svgdocument.h\"\n#include \"svgscene/svghandler.h\"\n#include \"ui_mainwindow.h\"\n\n#include <QFileDialog>\n#include <QGraphicsScene>\n#include <QTimer>\n#include <QXmlStreamReader>\n\nusing namespace svgscene;\n\nMainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {\n    ui->setupUi(this);\n\n    m_scene = new QGraphicsScene(this);\n    ui->graphicsView->setScene(m_scene);\n}\n\nMainWindow::~MainWindow() {\n    delete ui;\n}\n\nvoid MainWindow::openFile(const QString &fn) {\n    QString file_name = fn;\n    if (file_name.isEmpty())\n        file_name = QFileDialog::getOpenFileName(\n            this, tr(\"Open Image\"), \"\", tr(\"SVG Image Files (*.svg)\"));\n    if (file_name.isEmpty()) return;\n    QFile f(file_name);\n    if (f.open(QFile::ReadOnly)) {\n        m_scene->clear();\n        QXmlStreamReader rd(&f);\n        SvgHandler h(m_scene);\n    }\n}\n\nvoid MainWindow::on_action_Open_triggered() {\n    openFile();\n}\n\nvoid MainWindow::on_actionZoom_to_fit_triggered() {\n    ui->graphicsView->zoomToFit();\n}\nvoid MainWindow::inc() {}\n"
  },
  {
    "path": "external/svgscene/src/example/mainwindow.h",
    "content": "#pragma once\n\n#include \"svgscene/components/simpletextitem.h\"\n\n#include <QMainWindow>\n#include <QTimer>\n\nclass QGraphicsScene;\n\nnamespace Ui {\nclass MainWindow;\n}\n\nclass MainWindow : public QMainWindow {\n    Q_OBJECT\n\npublic:\n    explicit MainWindow(QWidget *parent = nullptr);\n    ~MainWindow() override;\n\n    void openFile(const QString &fn = QString());\n\nprivate:\n    Q_SLOT void on_action_Open_triggered();\n    Q_SLOT void on_actionZoom_to_fit_triggered();\n    Q_SLOT void inc();\n\nprivate:\n    Ui::MainWindow *ui;\n    QGraphicsScene *m_scene;\n    QTimer test;\n    svgscene::SimpleTextItem *label;\n    int counter = 0;\n};"
  },
  {
    "path": "external/svgscene/src/example/mainwindow.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MainWindow</class>\n <widget class=\"QMainWindow\" name=\"MainWindow\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>1000</width>\n    <height>693</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>MainWindow</string>\n  </property>\n  <widget class=\"QWidget\" name=\"centralWidget\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <item>\n     <widget class=\"SvgGraphicsView\" name=\"graphicsView\"/>\n    </item>\n   </layout>\n  </widget>\n  <widget class=\"QMenuBar\" name=\"menuBar\">\n   <property name=\"geometry\">\n    <rect>\n     <x>0</x>\n     <y>0</y>\n     <width>1000</width>\n     <height>32</height>\n    </rect>\n   </property>\n   <widget class=\"QMenu\" name=\"menu_File\">\n    <property name=\"title\">\n     <string>&amp;File</string>\n    </property>\n    <addaction name=\"action_Open\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menu_View\">\n    <property name=\"title\">\n     <string>&amp;View</string>\n    </property>\n    <addaction name=\"actionZoom_to_fit\"/>\n   </widget>\n   <addaction name=\"menu_File\"/>\n   <addaction name=\"menu_View\"/>\n  </widget>\n  <widget class=\"QToolBar\" name=\"mainToolBar\">\n   <attribute name=\"toolBarArea\">\n    <enum>TopToolBarArea</enum>\n   </attribute>\n   <attribute name=\"toolBarBreak\">\n    <bool>false</bool>\n   </attribute>\n  </widget>\n  <widget class=\"QStatusBar\" name=\"statusBar\"/>\n  <action name=\"action_Open\">\n   <property name=\"text\">\n    <string>&amp;Open</string>\n   </property>\n  </action>\n  <action name=\"actionZoom_to_fit\">\n   <property name=\"text\">\n    <string>Z&amp;oom to fit</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+I</string>\n   </property>\n   <property name=\"shortcutVisibleInContextMenu\">\n    <bool>true</bool>\n   </property>\n  </action>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <customwidgets>\n  <customwidget>\n   <class>SvgGraphicsView</class>\n   <extends>QGraphicsView</extends>\n   <header>svgscene/graphicsview/svggraphicsview.h</header>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "external/svgscene/src/svgscene/components/groupitem.cpp",
    "content": "#include \"groupitem.h\"\n\n#include <QPainter>\n\nnamespace svgscene {\n\nGroupItem::GroupItem(QGraphicsItem *parent) : Super(parent) {}\n\n} // namespace svgscene\n"
  },
  {
    "path": "external/svgscene/src/svgscene/components/groupitem.h",
    "content": "#pragma once\n\n#include <QGraphicsItem>\n\nnamespace svgscene {\n\nclass GroupItem : public QGraphicsRectItem {\n    using Super = QGraphicsRectItem;\n\npublic:\n    explicit GroupItem(QGraphicsItem *parent = nullptr);\n};\n\n} // namespace svgscene"
  },
  {
    "path": "external/svgscene/src/svgscene/components/hyperlinkitem.cpp",
    "content": "#include \"hyperlinkitem.h\"\n\n#include \"svgmetadata.h\"\n#include \"utils/logging.h\"\n\nLOG_CATEGORY(\"svgscene.hyperlink\");\n\nnamespace svgscene {\n\nHyperlinkItem::HyperlinkItem() = default;\n\nQString svgscene::HyperlinkItem::getTargetName() const {\n    // href attribute is mandatory, therefore using default value\n    return getXmlAttributeOr(this, \"href\", \"\");\n}\n\nvoid HyperlinkItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {\n    Q_UNUSED(event);\n    DEBUG(\"Link triggered (href: %s).\", qPrintable(getTargetName()));\n    emit triggered();\n}\n\n} // namespace svgscene\n"
  },
  {
    "path": "external/svgscene/src/svgscene/components/hyperlinkitem.h",
    "content": "#pragma once\n\n#include \"groupitem.h\"\nnamespace svgscene {\n\n/**\n * Represents SVG element <a>.\n *\n * Works exactly as the group item but it adds emits event on doubleclick.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/a\n * @see https://www.w3.org/TR/SVG11/linking.html#Links\n */\nclass HyperlinkItem : public QObject, public GroupItem {\n    Q_OBJECT\npublic:\n    explicit HyperlinkItem();\n    QString getTargetName() const;\n\n    void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;\n\nsignals:\n    void triggered();\n};\n\n} // namespace svgscene\n"
  },
  {
    "path": "external/svgscene/src/svgscene/components/simpletextitem.cpp",
    "content": "#include \"simpletextitem.h\"\n\n#include <QPainter>\n\nnamespace svgscene {\n\nSimpleTextItem::SimpleTextItem(const CssAttributes &css, QGraphicsItem *parent) : Super(parent) {\n    const QString anchor = css.value(QStringLiteral(\"text-anchor\"));\n    if (anchor == QLatin1String(\"middle\"))\n        m_alignment = Qt::AlignHCenter;\n    else if (anchor == QLatin1String(\"end\"))\n        m_alignment = Qt::AlignRight;\n    else\n        m_alignment = Qt::AlignLeft;\n}\n\nvoid SimpleTextItem::setText(const QString &text) {\n    if (!m_origTransformLoaded) {\n        m_origTransformLoaded = true;\n        m_origTransform = transform();\n    }\n    Super::setText(text);\n    if (m_alignment != Qt::AlignLeft) {\n        qreal w = boundingRect().width();\n        QTransform t = m_origTransform;\n        if (m_alignment == Qt::AlignHCenter)\n            t.translate(-w / 2, 0);\n        else if (m_alignment == Qt::AlignRight)\n            t.translate(-w, 0);\n        setTransform(t);\n    }\n}\n\nvoid SimpleTextItem::paint(\n    QPainter *painter,\n    const QStyleOptionGraphicsItem *option,\n    QWidget *widget) {\n    Super::paint(painter, option, widget);\n    // painter->setPen(Qt::green);\n    // painter->drawRect(boundingRect());\n}\n\n} // namespace svgscene\n"
  },
  {
    "path": "external/svgscene/src/svgscene/components/simpletextitem.h",
    "content": "#pragma once\n\n#include \"svgscene/svghandler.h\"\n\n#include <QGraphicsItem>\n\nnamespace svgscene {\n\nclass SimpleTextItem : public QGraphicsSimpleTextItem {\n    using Super = QGraphicsSimpleTextItem;\n\npublic:\n    explicit SimpleTextItem(const CssAttributes &css, QGraphicsItem *parent = nullptr);\n\n    void setText(const QString &text);\n    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;\n\nprivate:\n    int m_alignment = Qt::AlignLeft;\n    QTransform m_origTransform;\n    bool m_origTransformLoaded = false;\n};\n\n} // namespace svgscene"
  },
  {
    "path": "external/svgscene/src/svgscene/graphicsview/svggraphicsview.cpp",
    "content": "#include \"svggraphicsview.h\"\n\n#include \"polyfills/qt5/qwheelevent.h\"\n#include \"utils/logging.h\"\n\n#include <QWheelEvent>\n\nLOG_CATEGORY(\"svgscene.view\");\n\nSvgGraphicsView::SvgGraphicsView(QWidget *parent) : Super(parent) {}\n\nvoid SvgGraphicsView::zoomToFit() {\n    if (scene()) {\n        QRectF sr = scene()->sceneRect();\n        fitInView(sr, Qt::KeepAspectRatio);\n    }\n}\n\nvoid SvgGraphicsView::zoom(double delta, const QPointF &mouse_pos) {\n    LOG() << \"delta:\" << delta << \"center_pos:\" << mouse_pos.x() << mouse_pos.y();\n    double factor = delta / 100;\n    factor = 1 + factor;\n    if (factor < 0) factor = 0.1;\n    scale(factor, factor);\n\n    QRect view_rect = QRect(viewport()->pos(), viewport()->size());\n    QPoint view_center = view_rect.center();\n    QSize view_d(view_center.x() - mouse_pos.x(), view_center.y() - mouse_pos.y());\n    view_d /= factor;\n    view_center = QPoint(mouse_pos.x() + view_d.width(), mouse_pos.y() + view_d.height());\n    QPointF new_scene_center = mapToScene(view_center);\n    centerOn(new_scene_center);\n}\n\nvoid SvgGraphicsView::paintEvent(QPaintEvent *event) {\n    Super::paintEvent(event);\n}\n\nvoid SvgGraphicsView::wheelEvent(QWheelEvent *ev) {\n    if (ev->angleDelta().y() != 0) { // vertical orientation\n        if (ev->modifiers() == Qt::ControlModifier) {\n            double delta = ev->angleDelta().y();\n            zoom(delta / 10, QWheelEvent_poly(ev).position());\n            ev->accept();\n            return;\n        }\n    }\n    Super::wheelEvent(ev);\n}\n\nvoid SvgGraphicsView::mousePressEvent(QMouseEvent *ev) {\n    if (ev->button() == Qt::LeftButton && ev->modifiers() == Qt::ControlModifier) {\n        m_dragMouseStartPos = ev->pos();\n        setCursor(QCursor(Qt::ClosedHandCursor));\n        ev->accept();\n        return;\n    }\n    Super::mousePressEvent(ev);\n}\n\nvoid SvgGraphicsView::mouseReleaseEvent(QMouseEvent *ev) {\n    if (ev->button() == Qt::LeftButton && ev->modifiers() == Qt::ControlModifier) {\n        setCursor(QCursor());\n    }\n    Super::mouseReleaseEvent(ev);\n}\n\nvoid SvgGraphicsView::mouseMoveEvent(QMouseEvent *ev) {\n    if (ev->buttons() == Qt::LeftButton && ev->modifiers() == Qt::ControlModifier) {\n        QPoint pos = ev->pos();\n        QRect view_rect = QRect(viewport()->pos(), viewport()->size());\n        QPoint view_center = view_rect.center();\n        QPoint d(pos.x() - m_dragMouseStartPos.x(), pos.y() - m_dragMouseStartPos.y());\n        view_center -= d;\n        QPointF new_scene_center = mapToScene(view_center);\n        centerOn(new_scene_center);\n        m_dragMouseStartPos = pos;\n        ev->accept();\n        return;\n    }\n    Super::mouseMoveEvent(ev);\n}\n"
  },
  {
    "path": "external/svgscene/src/svgscene/graphicsview/svggraphicsview.h",
    "content": "#pragma once\n\n#include <QGraphicsView>\n\nclass SvgGraphicsView : public QGraphicsView {\n    Q_OBJECT\n\n    using Super = QGraphicsView;\n\npublic:\n    explicit SvgGraphicsView(QWidget *parent = nullptr);\n\n    void zoomToFit();\n\nprotected:\n    void zoom(double delta, const QPointF &mouse_pos);\n\n    void paintEvent(QPaintEvent *event) override;\n\n    void wheelEvent(QWheelEvent *ev) Q_DECL_OVERRIDE;\n    void mousePressEvent(QMouseEvent *ev) Q_DECL_OVERRIDE;\n    void mouseReleaseEvent(QMouseEvent *ev) Q_DECL_OVERRIDE;\n    void mouseMoveEvent(QMouseEvent *ev) Q_DECL_OVERRIDE;\n\nprivate:\n    QPoint m_dragMouseStartPos;\n};"
  },
  {
    "path": "external/svgscene/src/svgscene/polyfills/qt5/qstringview.h",
    "content": "#pragma once\n\n#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)\n    using QStringView = QString;\n#endif\n"
  },
  {
    "path": "external/svgscene/src/svgscene/polyfills/qt5/qwheelevent.h",
    "content": "#pragma once\n\n#include <QWheelEvent>\n\nclass QWheelEvent_poly final : public QWheelEvent {\npublic:\n    explicit QWheelEvent_poly(QWheelEvent *event) : QWheelEvent(*event) {}\n\n#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)\n    QPointF position() const {\n        return this->pos();\n    }\n#endif\n};"
  },
  {
    "path": "external/svgscene/src/svgscene/svgdocument.cpp",
    "content": "#include \"svgdocument.h\"\n\nnamespace svgscene {\n\nSvgDomTree<QGraphicsItem> SvgDocument::getRoot() const {\n    return root;\n}\n\nSvgDocument::SvgDocument(QGraphicsItem *root) : root(root) {}\n\n} // namespace svgscene\n"
  },
  {
    "path": "external/svgscene/src/svgscene/svgdocument.h",
    "content": "#pragma once\n\n#include \"svgmetadata.h\"\n\n#include <QGraphicsItem>\n\nnamespace svgscene {\n\ntemplate<typename TT>\nclass SvgDomTree {\npublic:\n    explicit SvgDomTree(QGraphicsItem *root);\n\n    TT *getElement() const;\n\n    QString getAttrValueOr(const QString &attr_name, const QString &default_value = QString());\n    QString getCssValueOr(const QString &attr_name, const QString &default_value = QString());\n\n    template<typename T>\n    static SvgDomTree<T> findFromParent(\n        const QGraphicsItem *parent,\n        const QString &attr_name = QString(),\n        const QString &attr_value = QString());\n\n    template<typename T>\n    static QList<SvgDomTree<T>> findAllFromParent(\n        const QGraphicsItem *parent,\n        const QString &attr_name = QString(),\n        const QString &attr_value = QString());\n\n    template<typename T = QGraphicsItem>\n    SvgDomTree<T> find(const QString &attr_name = QString(), const QString &attr_value = QString());\n\n    template<typename T = QGraphicsItem>\n    QList<SvgDomTree<T>>\n    findAll(const QString &attr_name = QString(), const QString &attr_value = QString());\n\nprotected:\n    /**\n     * WASM does not allows exception catching so we have to handle\n     * recoverable errors in a different way - using nullptr.\n     */\n    template<typename T>\n    static T *findFromParentRaw(\n        const QGraphicsItem *parent,\n        const QString &attr_name = QString(),\n        const QString &attr_value = QString());\n\nprotected:\n    TT *root;\n};\n\nclass SvgDocument {\npublic:\n    explicit SvgDocument(QGraphicsItem *root);\n    SvgDomTree<QGraphicsItem> getRoot() const;\n\nprotected:\n    SvgDomTree<QGraphicsItem> root;\n};\n\ntemplate<typename T>\nbool itemMatchesSelector(\n    const QGraphicsItem *item,\n    const QString &attr_name,\n    const QString &attr_value) {\n    if (item == nullptr) { throw std::out_of_range(\"Supplied item is nullptr.\"); }\n    if (attr_name.isEmpty()) { return true; }\n\n    auto attrs = getXmlAttributes(item);\n    if (!attrs.contains(attr_name)) { return false; }\n    return attr_value.isEmpty() || attrs.value(attr_name) == attr_value;\n}\n\ntemplate<typename T>\nSvgDomTree<T>::SvgDomTree(QGraphicsItem *root) : root(dynamic_cast<T *>(root)) {\n    if (this->root == nullptr) {\n        throw std::out_of_range(\"Cannot build dom tree with nullptr item.\");\n    }\n}\n\ntemplate<typename TT>\nTT *SvgDomTree<TT>::getElement() const {\n    return root;\n}\n\ntemplate<typename T>\nQString SvgDomTree<T>::getAttrValueOr(const QString &attr_name, const QString &default_value) {\n    svgscene::XmlAttributes attrs = getXmlAttributes(root);\n    return attrs.value(attr_name, default_value);\n}\n\ntemplate<typename T>\nQString SvgDomTree<T>::getCssValueOr(const QString &attr_name, const QString &default_value) {\n    return getCssAttributeOr(root, attr_name, default_value);\n}\n\ntemplate<typename TT>\ntemplate<typename T>\nSvgDomTree<T> SvgDomTree<TT>::findFromParent(\n    const QGraphicsItem *parent,\n    const QString &attr_name,\n    const QString &attr_value) {\n    if (!parent) { throw std::out_of_range(\"Current element is nullptr.\"); }\n\n    for (QGraphicsItem *_child : parent->childItems()) {\n        if (T *child = dynamic_cast<T *>(_child)) {\n            if (itemMatchesSelector<T>(child, attr_name, attr_value)) {\n                return SvgDomTree<T>(child);\n            }\n        }\n\n        T *found = findFromParentRaw<T>(_child, attr_name, attr_value);\n        if (found != nullptr) { return SvgDomTree<T>(found); }\n    }\n    throw std::out_of_range(\"Not found.\");\n}\n\ntemplate<typename TT>\ntemplate<typename T>\nT *SvgDomTree<TT>::findFromParentRaw(\n    const QGraphicsItem *parent,\n    const QString &attr_name,\n    const QString &attr_value) {\n    if (!parent) { return nullptr; }\n\n    for (QGraphicsItem *_child : parent->childItems()) {\n        if (T *child = dynamic_cast<T *>(_child)) {\n            if (itemMatchesSelector<T>(child, attr_name, attr_value)) { return child; }\n        }\n        T *found = findFromParentRaw<T>(_child, attr_name, attr_value);\n        if (found != nullptr) { return found; }\n    }\n    return nullptr;\n}\ntemplate<typename TT>\ntemplate<typename T>\nQList<SvgDomTree<T>> SvgDomTree<TT>::findAllFromParent(\n    const QGraphicsItem *parent,\n    const QString &attr_name,\n    const QString &attr_value) {\n    QList<SvgDomTree<T>> ret;\n\n    if (!parent) { return ret; }\n\n    for (QGraphicsItem *_child : parent->childItems()) {\n        if (T *child = dynamic_cast<T *>(_child)) {\n            if (itemMatchesSelector<T>(child, attr_name, attr_value)) {\n                ret.append(SvgDomTree<T>(child));\n            }\n        }\n        ret.append(findAllFromParent<T>(_child, attr_name, attr_value));\n    }\n    return ret;\n}\n\ntemplate<typename TT>\ntemplate<typename T>\nSvgDomTree<T> SvgDomTree<TT>::find(const QString &attr_name, const QString &attr_value) {\n    return findFromParent<T>(root, attr_name, attr_value);\n}\n\ntemplate<typename TT>\ntemplate<typename T>\nQList<SvgDomTree<T>> SvgDomTree<TT>::findAll(const QString &attr_name, const QString &attr_value) {\n    return findAllFromParent<T>(root, attr_name, attr_value);\n}\n\n} // namespace svgscene\n"
  },
  {
    "path": "external/svgscene/src/svgscene/svggraphicsscene.cpp",
    "content": "#include \"svggraphicsscene.h\"\n\n#include \"components/hyperlinkitem.h\"\n\n#include <QGraphicsItem>\n#include <QGraphicsSceneMouseEvent>\n\nnamespace svgscene {\n\nvoid SvgGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {\n    // Do not prevent default behavior.\n    QGraphicsScene::mouseDoubleClickEvent(event);\n\n    // Hyperlink will usually be obscured, but we need to propagate the click.\n    QGraphicsItem *item = this->itemAt(event->pos(), {});\n    while (item != nullptr) {\n        if (auto hyperlink = dynamic_cast<HyperlinkItem *>(item)) {\n            hyperlink->mouseDoubleClickEvent(event);\n            break;\n        }\n        item = item->parentItem();\n    }\n}\n\n} // namespace svgscene\n"
  },
  {
    "path": "external/svgscene/src/svgscene/svggraphicsscene.h",
    "content": "#pragma once\n\n#include <QGraphicsScene>\n\nnamespace svgscene {\n\n/**\n * Graphics scene with extended support for SVG.\n *\n * Current support:\n * - hyperlinks (doubleclick)\n *    Links in svg are parsed as groups and therefore they are always\n *    obscured in qt scene. Therefore we have to travers the while tree up\n *    and find the closest hyperlink element (if one exists).\n */\nclass SvgGraphicsScene : public QGraphicsScene {\nprotected:\n    virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;\n};\n\n} // namespace svgscene\n"
  },
  {
    "path": "external/svgscene/src/svgscene/svghandler.cpp",
    "content": "﻿#include \"svghandler.h\"\n\n#include \"components/groupitem.h\"\n#include \"components/hyperlinkitem.h\"\n#include \"components/simpletextitem.h\"\n#include \"polyfills/qt5/qstringview.h\"\n#include \"svgmetadata.h\"\n#include \"svgspec.h\"\n#include \"utils/logging.h\"\n\n#include <QFontMetrics>\n#include <QGraphicsItem>\n#include <QGraphicsScene>\n#include <QSet>\n#include <QStack>\n#include <QXmlStreamReader>\n#include <QtMath>\n\nLOG_CATEGORY(\"svgscene.parsing\");\n\nnamespace svgscene {\n\nSvgDocument parseFromFileName(QGraphicsScene *scene, const QString &filename) {\n    QFile file(filename);\n    file.open(QIODevice::ReadOnly);\n    return parseFromFile(scene, &file);\n}\n\nSvgDocument parseFromFile(QGraphicsScene *scene, QFile *file) {\n    QXmlStreamReader xml(file);\n    SvgHandler handler(scene);\n    handler.load(&xml);\n    return handler.getDocument();\n}\n\n// see: https://www.w3.org/TR/SVG11/\n\n// '0' is 0x30 and '9' is 0x39\nstatic inline bool isDigit(ushort ch) {\n    static quint16 magic = 0x3ff;\n    return ((ch >> 4) == 3) && (magic >> (ch & 15));\n}\n\nstatic qreal toDouble(const QChar *&str) {\n    const int maxLen = 255; // technically doubles can go til 308+ but whatever\n    char temp[maxLen + 1];\n    int pos = 0;\n    if (*str == QLatin1Char('-')) {\n        temp[pos++] = '-';\n        ++str;\n    } else if (*str == QLatin1Char('+')) {\n        ++str;\n    }\n    while (isDigit(str->unicode()) && pos < maxLen) {\n        temp[pos++] = str->toLatin1();\n        ++str;\n    }\n    if (*str == QLatin1Char('.') && pos < maxLen) {\n        temp[pos++] = '.';\n        ++str;\n    }\n    while (isDigit(str->unicode()) && pos < maxLen) {\n        temp[pos++] = str->toLatin1();\n        ++str;\n    }\n    bool exponent = false;\n    if ((*str == QLatin1Char('e') || *str == QLatin1Char('E')) && pos < maxLen) {\n        exponent = true;\n        temp[pos++] = 'e';\n        ++str;\n        if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {\n            temp[pos++] = str->toLatin1();\n            ++str;\n        }\n        while (isDigit(str->unicode()) && pos < maxLen) {\n            temp[pos++] = str->toLatin1();\n            ++str;\n        }\n    }\n    temp[pos] = '\\0';\n    qreal val;\n    if (!exponent && pos < 10) {\n        int ival = 0;\n        const char *t = temp;\n        bool neg = false;\n        if (*t == '-') {\n            neg = true;\n            ++t;\n        }\n        while (*t && *t != '.') {\n            ival *= 10;\n            ival += (*t) - '0';\n            ++t;\n        }\n        if (*t == '.') {\n            ++t;\n            int div = 1;\n            while (*t) {\n                ival *= 10;\n                ival += (*t) - '0';\n                div *= 10;\n                ++t;\n            }\n            val = ((qreal)ival) / ((qreal)div);\n        } else {\n            val = ival;\n        }\n        if (neg) val = -val;\n    } else {\n        val = QByteArray::fromRawData(temp, pos).toDouble();\n    }\n    return val;\n}\n\nstatic qreal toDouble(const QString &str, bool *ok = nullptr) {\n    const QChar *c = str.constData();\n    qreal res = toDouble(c);\n    if (ok) { *ok = ((*c) == QLatin1Char('\\0')); }\n    return res;\n}\n\n/*\nstatic qreal toDouble(const QStringView &str, bool *ok = nullptr)\n{\n    const QChar *c = str.constData();\n    qreal res = toDouble(c);\n    if (ok) {\n        *ok = (c == (str.constData() + str.length()));\n    }\n    return res;\n}\n*/\nstatic inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points) {\n    while (str->isSpace())\n        ++str;\n    while (isDigit(str->unicode()) || *str == QLatin1Char('-') || *str == QLatin1Char('+')\n           || *str == QLatin1Char('.')) {\n        points.append(toDouble(str));\n        while (str->isSpace())\n            ++str;\n        if (*str == QLatin1Char(',')) ++str;\n        // eat the rest of space\n        while (str->isSpace())\n            ++str;\n    }\n}\n\nstatic QVector<qreal> parseNumbersList(const QChar *&str) {\n    QVector<qreal> points;\n    if (!str) return points;\n    points.reserve(32);\n    while (str->isSpace())\n        ++str;\n    while (isDigit(str->unicode()) || *str == QLatin1Char('-') || *str == QLatin1Char('+')\n           || *str == QLatin1Char('.')) {\n        points.append(toDouble(str));\n        while (str->isSpace())\n            ++str;\n        if (*str == QLatin1Char(',')) ++str;\n        // eat the rest of space\n        while (str->isSpace())\n            ++str;\n    }\n    return points;\n}\n\nstatic QVector<qreal> parsePercentageList(const QChar *&str) {\n    QVector<qreal> points;\n    if (!str) return points;\n    while (str->isSpace())\n        ++str;\n    while ((*str >= QLatin1Char('0') && *str <= QLatin1Char('9')) || *str == QLatin1Char('-')\n           || *str == QLatin1Char('+') || *str == QLatin1Char('.')) {\n        points.append(toDouble(str));\n        while (str->isSpace())\n            ++str;\n        if (*str == QLatin1Char('%')) ++str;\n        while (str->isSpace())\n            ++str;\n        if (*str == QLatin1Char(',')) ++str;\n        // eat the rest of space\n        while (str->isSpace())\n            ++str;\n    }\n    return points;\n}\n\nstatic inline int qsvg_h2i(char hex) {\n    if (hex >= '0' && hex <= '9') return hex - '0';\n    if (hex >= 'a' && hex <= 'f') return hex - 'a' + 10;\n    if (hex >= 'A' && hex <= 'F') return hex - 'A' + 10;\n    return -1;\n}\nstatic inline int qsvg_hex2int(const char *s) {\n    return (qsvg_h2i(s[0]) << 4) | qsvg_h2i(s[1]);\n}\nstatic inline int qsvg_hex2int(char s) {\n    int h = qsvg_h2i(s);\n    return (h << 4) | h;\n}\n\nbool qsvg_get_hex_rgb(const char *name, QRgb *rgb) {\n    if (name[0] != '#') return false;\n    name++;\n    int len = static_cast<int>(qstrlen(name));\n    int r, g, b;\n    if (len == 12) {\n        r = qsvg_hex2int(name);\n        g = qsvg_hex2int(name + 4);\n        b = qsvg_hex2int(name + 8);\n    } else if (len == 9) {\n        r = qsvg_hex2int(name);\n        g = qsvg_hex2int(name + 3);\n        b = qsvg_hex2int(name + 6);\n    } else if (len == 6) {\n        r = qsvg_hex2int(name);\n        g = qsvg_hex2int(name + 2);\n        b = qsvg_hex2int(name + 4);\n    } else if (len == 3) {\n        r = qsvg_hex2int(name[0]);\n        g = qsvg_hex2int(name[1]);\n        b = qsvg_hex2int(name[2]);\n    } else {\n        r = g = b = -1;\n    }\n    if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255) {\n        *rgb = 0;\n        return false;\n    }\n    *rgb = qRgb(r, g, b);\n    return true;\n}\n\nbool qsvg_get_hex_rgb(const QByteArray &str, int len, QRgb *rgb) {\n    if (len > 13) return false;\n    char tmp[16];\n    for (int i = 0; i < len; ++i)\n        tmp[i] = str[i];\n    tmp[len] = 0;\n    return qsvg_get_hex_rgb(tmp, rgb);\n}\n\nstatic QColor parseColor(const QString &color, const QString &opacity) {\n    QColor ret;\n    {\n        QStringView color_str = QStringView(color).trimmed();\n        if (color_str.isEmpty()) return ret;\n        switch (color_str.at(0).unicode()) {\n        case '#': {\n            // #rrggbb is very very common, so let's tackle it here\n            // rather than falling back to QColor\n            QRgb rgb;\n            bool ok = qsvg_get_hex_rgb(color_str.toUtf8(), color_str.length(), &rgb);\n            if (ok) ret.setRgb(rgb);\n            break;\n        }\n        case 'r': {\n            // starts with \"rgb(\", ends with \")\" and consists of at least 7\n            // characters \"rgb(,,)\"\n            if (color_str.length() >= 7 && color_str.at(color_str.length() - 1) == QLatin1Char(')')\n                && QStringView(color_str.cbegin(), 4) == QLatin1String(\"rgb(\")) {\n                const QChar *s = color_str.cbegin() + 4;\n                QVector<qreal> compo = parseNumbersList(s);\n                // 1 means that it failed after reaching non-parsable\n                // character which is going to be \"%\"\n                if (compo.size() == 1) {\n                    s = color_str.cbegin() + 4;\n                    compo = parsePercentageList(s);\n                    for (double &i : compo)\n                        i *= (qreal)2.55;\n                }\n                if (compo.size() == 3) {\n                    ret = QColor(int(compo[0]), int(compo[1]), int(compo[2]));\n                }\n            }\n            break;\n        }\n        case 'c':\n            if (color_str == QLatin1String(\"currentColor\")) {\n                // color = handler->currentColor();\n                return ret;\n            }\n            break;\n        case 'i':\n            if (color_str == QLatin1String(\"inherit\")) return ret;\n            break;\n        default: ret = QColor(QString(color_str.cbegin(), color_str.length())); break;\n        }\n    }\n    if (!opacity.isEmpty() && ret.isValid()) {\n        bool ok = true;\n        qreal op = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity, &ok)));\n        if (!ok) op = 1.0;\n        ret.setAlphaF(op);\n    }\n    return ret;\n}\n\nstatic QTransform parseTransformationMatrix(const QStringView &value) {\n    if (value.isEmpty()) return {};\n    QTransform matrix;\n    const QChar *str = value.cbegin();\n    const QChar *end = str + value.length();\n    while (str < end) {\n        if (str->isSpace() || *str == QLatin1Char(',')) {\n            ++str;\n            continue;\n        }\n        enum State { Matrix, Translate, Rotate, Scale, SkewX, SkewY };\n        State state = Matrix;\n        if (*str == QLatin1Char('m')) { // matrix\n            const char *ident = \"atrix\";\n            for (int i = 0; i < 5; ++i)\n                if (*(++str) != QLatin1Char(ident[i])) goto error;\n            ++str;\n            state = Matrix;\n        } else if (*str == QLatin1Char('t')) { // translate\n            const char *ident = \"ranslate\";\n            for (int i = 0; i < 8; ++i)\n                if (*(++str) != QLatin1Char(ident[i])) goto error;\n            ++str;\n            state = Translate;\n        } else if (*str == QLatin1Char('r')) { // rotate\n            const char *ident = \"otate\";\n            for (int i = 0; i < 5; ++i)\n                if (*(++str) != QLatin1Char(ident[i])) goto error;\n            ++str;\n            state = Rotate;\n        } else if (*str == QLatin1Char('s')) { // scale, skewX, skewY\n            ++str;\n            if (*str == QLatin1Char('c')) {\n                const char *ident = \"ale\";\n                for (int i = 0; i < 3; ++i)\n                    if (*(++str) != QLatin1Char(ident[i])) goto error;\n                ++str;\n                state = Scale;\n            } else if (*str == QLatin1Char('k')) {\n                if (*(++str) != QLatin1Char('e')) goto error;\n                if (*(++str) != QLatin1Char('w')) goto error;\n                ++str;\n                if (*str == QLatin1Char('X'))\n                    state = SkewX;\n                else if (*str == QLatin1Char('Y'))\n                    state = SkewY;\n                else\n                    goto error;\n                ++str;\n            } else {\n                goto error;\n            }\n        } else {\n            goto error;\n        }\n        while (str < end && str->isSpace())\n            ++str;\n        if (*str != QLatin1Char('(')) goto error;\n        ++str;\n        QVarLengthArray<qreal, 8> points;\n        parseNumbersArray(str, points);\n        if (*str != QLatin1Char(')')) goto error;\n        ++str;\n        if (state == Matrix) {\n            if (points.count() != 6) goto error;\n            matrix = QTransform(points[0], points[1], points[2], points[3], points[4], points[5])\n                     * matrix;\n        } else if (state == Translate) {\n            if (points.count() == 1)\n                matrix.translate(points[0], 0);\n            else if (points.count() == 2)\n                matrix.translate(points[0], points[1]);\n            else\n                goto error;\n        } else if (state == Rotate) {\n            if (points.count() == 1) {\n                matrix.rotate(points[0]);\n            } else if (points.count() == 3) {\n                matrix.translate(points[1], points[2]);\n                matrix.rotate(points[0]);\n                matrix.translate(-points[1], -points[2]);\n            } else {\n                goto error;\n            }\n        } else if (state == Scale) {\n            if (points.count() < 1 || points.count() > 2) goto error;\n            qreal sx = points[0];\n            qreal sy = sx;\n            if (points.count() == 2) sy = points[1];\n            matrix.scale(sx, sy);\n        } else if (state == SkewX) {\n            if (points.count() != 1) goto error;\n            const auto deg2rad = qreal(0.017453292519943295769);\n            matrix.shear(qTan(points[0] * deg2rad), 0);\n        } else if (state == SkewY) {\n            if (points.count() != 1) goto error;\n            const auto deg2rad = qreal(0.017453292519943295769);\n            matrix.shear(0, qTan(points[0] * deg2rad));\n        }\n    }\nerror:\n    return matrix;\n}\n\n// the arc handling code underneath is from XSVG (BSD license)\n/*\n * Copyright  2002 USC/Information Sciences Institute\n *\n * Permission to use, copy, modify, distribute, and sell this software\n * and its documentation for any purpose is hereby granted without\n * fee, provided that the above copyright notice appear in all copies\n * and that both that copyright notice and this permission notice\n * appear in supporting documentation, and that the name of\n * Information Sciences Institute not be used in advertising or\n * publicity pertaining to distribution of the software without\n * specific, written prior permission.  Information Sciences Institute\n * makes no representations about the suitability of this software for\n * any purpose.  It is provided \"as is\" without express or implied\n * warranty.\n *\n * INFORMATION SCIENCES INSTITUTE DISCLAIMS ALL WARRANTIES WITH REGARD\n * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INFORMATION SCIENCES\n * INSTITUTE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL\n * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA\n * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\n * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n * PERFORMANCE OF THIS SOFTWARE.\n *\n */\nstatic void pathArcSegment(\n    QPainterPath &path,\n    qreal xc,\n    qreal yc,\n    qreal th0,\n    qreal th1,\n    qreal rx,\n    qreal ry,\n    qreal xAxisRotation) {\n    qreal sinTh, cosTh;\n    qreal a00, a01, a10, a11;\n    qreal x1, y1, x2, y2, x3, y3;\n    qreal t;\n    qreal thHalf;\n    sinTh = qSin(xAxisRotation * (M_PI / 180.0));\n    cosTh = qCos(xAxisRotation * (M_PI / 180.0));\n    a00 = cosTh * rx;\n    a01 = -sinTh * ry;\n    a10 = sinTh * rx;\n    a11 = cosTh * ry;\n    thHalf = 0.5 * (th1 - th0);\n    t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);\n    x1 = xc + qCos(th0) - t * qSin(th0);\n    y1 = yc + qSin(th0) + t * qCos(th0);\n    x3 = xc + qCos(th1);\n    y3 = yc + qSin(th1);\n    x2 = x3 + t * qSin(th1);\n    y2 = y3 - t * qCos(th1);\n    path.cubicTo(\n        a00 * x1 + a01 * y1, a10 * x1 + a11 * y1, a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,\n        a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);\n}\n\nstatic void pathArc(\n    QPainterPath &path,\n    qreal rx,\n    qreal ry,\n    qreal x_axis_rotation,\n    int large_arc_flag,\n    int sweep_flag,\n    qreal x,\n    qreal y,\n    qreal curx,\n    qreal cury) {\n    qreal sin_th, cos_th;\n    qreal a00, a01, a10, a11;\n    qreal x0, y0, x1, y1, xc, yc;\n    qreal d, sfactor, sfactor_sq;\n    qreal th0, th1, th_arc;\n    int i, n_segs;\n    qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;\n    rx = qAbs(rx);\n    ry = qAbs(ry);\n    sin_th = qSin(x_axis_rotation * (M_PI / 180.0));\n    cos_th = qCos(x_axis_rotation * (M_PI / 180.0));\n    dx = (curx - x) / 2.0;\n    dy = (cury - y) / 2.0;\n    dx1 = cos_th * dx + sin_th * dy;\n    dy1 = -sin_th * dx + cos_th * dy;\n    Pr1 = rx * rx;\n    Pr2 = ry * ry;\n    Px = dx1 * dx1;\n    Py = dy1 * dy1;\n    /* Spec : check if radii are large enough */\n    check = Px / Pr1 + Py / Pr2;\n    if (check > 1) {\n        rx = rx * qSqrt(check);\n        ry = ry * qSqrt(check);\n    }\n    a00 = cos_th / rx;\n    a01 = sin_th / rx;\n    a10 = -sin_th / ry;\n    a11 = cos_th / ry;\n    x0 = a00 * curx + a01 * cury;\n    y0 = a10 * curx + a11 * cury;\n    x1 = a00 * x + a01 * y;\n    y1 = a10 * x + a11 * y;\n    /* (x0, y0) is current point in transformed coordinate space.\n       (x1, y1) is new point in transformed coordinate space.\n       The arc fits a unit-radius circle in this space.\n    */\n    d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);\n    sfactor_sq = 1.0 / d - 0.25;\n    if (sfactor_sq < 0) sfactor_sq = 0;\n    sfactor = qSqrt(sfactor_sq);\n    if (sweep_flag == large_arc_flag) sfactor = -sfactor;\n    xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);\n    yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);\n    /* (xc, yc) is center of the circle. */\n    th0 = qAtan2(y0 - yc, x0 - xc);\n    th1 = qAtan2(y1 - yc, x1 - xc);\n    th_arc = th1 - th0;\n    if (th_arc < 0 && sweep_flag)\n        th_arc += 2 * M_PI;\n    else if (th_arc > 0 && !sweep_flag)\n        th_arc -= 2 * M_PI;\n    n_segs = qCeil(qAbs(th_arc / (M_PI * 0.5 + 0.001)));\n    for (i = 0; i < n_segs; i++) {\n        pathArcSegment(\n            path, xc, yc, th0 + i * th_arc / n_segs, th0 + (i + 1) * th_arc / n_segs, rx, ry,\n            x_axis_rotation);\n    }\n}\n\nstatic bool parsePathDataFast(const QStringView &dataStr, QPainterPath &path) {\n    qreal x0 = 0, y0 = 0; // starting point\n    qreal x = 0, y = 0;   // current point\n    char lastMode = 0;\n    QPointF ctrlPt;\n    const QChar *str = dataStr.cbegin();\n    const QChar *end = str + dataStr.size();\n    while (str != end) {\n        while (str->isSpace())\n            ++str;\n        QChar pathElem = *str;\n        ++str;\n        QChar endc = *end;\n        *const_cast<QChar *>(end) = QChar('\\0'); // parseNumbersArray requires\n                                                 // 0-termination that QStringView cannot\n                                                 // guarantee\n        QVarLengthArray<qreal, 8> arg;\n        parseNumbersArray(str, arg);\n        *const_cast<QChar *>(end) = endc;\n        if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z')) arg.append(0); // dummy\n        const qreal *num = arg.constData();\n        int count = arg.count();\n        while (count > 0) {\n            qreal offsetX = x; // correction offsets\n            qreal offsetY = y; // for relative commands\n            switch (pathElem.unicode()) {\n            case 'm': {\n                if (count < 2) {\n                    num++;\n                    count--;\n                    break;\n                }\n                x = x0 = num[0] + offsetX;\n                y = y0 = num[1] + offsetY;\n                num += 2;\n                count -= 2;\n                path.moveTo(x0, y0);\n                // As per 1.2  spec 8.3.2 The \"moveto\" commands\n                // If a 'moveto' is followed by multiple pairs of coordinates\n                // without explicit commands, the subsequent pairs shall be\n                // treated as implicit 'lineto' commands.\n                pathElem = QLatin1Char('l');\n            } break;\n            case 'M': {\n                if (count < 2) {\n                    num++;\n                    count--;\n                    break;\n                }\n                x = x0 = num[0];\n                y = y0 = num[1];\n                num += 2;\n                count -= 2;\n                path.moveTo(x0, y0);\n                // As per 1.2  spec 8.3.2 The \"moveto\" commands\n                // If a 'moveto' is followed by multiple pairs of coordinates\n                // without explicit commands, the subsequent pairs shall be\n                // treated as implicit 'lineto' commands.\n                pathElem = QLatin1Char('L');\n            } break;\n            case 'z':\n            case 'Z': {\n                x = x0;\n                y = y0;\n                count--; // skip dummy\n                num++;\n                path.closeSubpath();\n            } break;\n            case 'l': {\n                if (count < 2) {\n                    num++;\n                    count--;\n                    break;\n                }\n                x = num[0] + offsetX;\n                y = num[1] + offsetY;\n                num += 2;\n                count -= 2;\n                path.lineTo(x, y);\n            } break;\n            case 'L': {\n                if (count < 2) {\n                    num++;\n                    count--;\n                    break;\n                }\n                x = num[0];\n                y = num[1];\n                num += 2;\n                count -= 2;\n                path.lineTo(x, y);\n            } break;\n            case 'h': {\n                x = num[0] + offsetX;\n                num++;\n                count--;\n                path.lineTo(x, y);\n            } break;\n            case 'H': {\n                x = num[0];\n                num++;\n                count--;\n                path.lineTo(x, y);\n            } break;\n            case 'v': {\n                y = num[0] + offsetY;\n                num++;\n                count--;\n                path.lineTo(x, y);\n            } break;\n            case 'V': {\n                y = num[0];\n                num++;\n                count--;\n                path.lineTo(x, y);\n            } break;\n            case 'c': {\n                if (count < 6) {\n                    num += count;\n                    count = 0;\n                    break;\n                }\n                QPointF c1(num[0] + offsetX, num[1] + offsetY);\n                QPointF c2(num[2] + offsetX, num[3] + offsetY);\n                QPointF e(num[4] + offsetX, num[5] + offsetY);\n                num += 6;\n                count -= 6;\n                path.cubicTo(c1, c2, e);\n                ctrlPt = c2;\n                x = e.x();\n                y = e.y();\n                break;\n            }\n            case 'C': {\n                if (count < 6) {\n                    num += count;\n                    count = 0;\n                    break;\n                }\n                QPointF c1(num[0], num[1]);\n                QPointF c2(num[2], num[3]);\n                QPointF e(num[4], num[5]);\n                num += 6;\n                count -= 6;\n                path.cubicTo(c1, c2, e);\n                ctrlPt = c2;\n                x = e.x();\n                y = e.y();\n                break;\n            }\n            case 's': {\n                if (count < 4) {\n                    num += count;\n                    count = 0;\n                    break;\n                }\n                QPointF c1;\n                if (lastMode == 'c' || lastMode == 'C' || lastMode == 's' || lastMode == 'S')\n                    c1 = QPointF(2 * x - ctrlPt.x(), 2 * y - ctrlPt.y());\n                else\n                    c1 = QPointF(x, y);\n                QPointF c2(num[0] + offsetX, num[1] + offsetY);\n                QPointF e(num[2] + offsetX, num[3] + offsetY);\n                num += 4;\n                count -= 4;\n                path.cubicTo(c1, c2, e);\n                ctrlPt = c2;\n                x = e.x();\n                y = e.y();\n                break;\n            }\n            case 'S': {\n                if (count < 4) {\n                    num += count;\n                    count = 0;\n                    break;\n                }\n                QPointF c1;\n                if (lastMode == 'c' || lastMode == 'C' || lastMode == 's' || lastMode == 'S')\n                    c1 = QPointF(2 * x - ctrlPt.x(), 2 * y - ctrlPt.y());\n                else\n                    c1 = QPointF(x, y);\n                QPointF c2(num[0], num[1]);\n                QPointF e(num[2], num[3]);\n                num += 4;\n                count -= 4;\n                path.cubicTo(c1, c2, e);\n                ctrlPt = c2;\n                x = e.x();\n                y = e.y();\n                break;\n            }\n            case 'q': {\n                if (count < 4) {\n                    num += count;\n                    count = 0;\n                    break;\n                }\n                QPointF c(num[0] + offsetX, num[1] + offsetY);\n                QPointF e(num[2] + offsetX, num[3] + offsetY);\n                num += 4;\n                count -= 4;\n                path.quadTo(c, e);\n                ctrlPt = c;\n                x = e.x();\n                y = e.y();\n                break;\n            }\n            case 'Q': {\n                if (count < 4) {\n                    num += count;\n                    count = 0;\n                    break;\n                }\n                QPointF c(num[0], num[1]);\n                QPointF e(num[2], num[3]);\n                num += 4;\n                count -= 4;\n                path.quadTo(c, e);\n                ctrlPt = c;\n                x = e.x();\n                y = e.y();\n                break;\n            }\n            case 't': {\n                if (count < 2) {\n                    num += count;\n                    count = 0;\n                    break;\n                }\n                QPointF e(num[0] + offsetX, num[1] + offsetY);\n                num += 2;\n                count -= 2;\n                QPointF c;\n                if (lastMode == 'q' || lastMode == 'Q' || lastMode == 't' || lastMode == 'T')\n                    c = QPointF(2 * x - ctrlPt.x(), 2 * y - ctrlPt.y());\n                else\n                    c = QPointF(x, y);\n                path.quadTo(c, e);\n                ctrlPt = c;\n                x = e.x();\n                y = e.y();\n                break;\n            }\n            case 'T': {\n                if (count < 2) {\n                    num += count;\n                    count = 0;\n                    break;\n                }\n                QPointF e(num[0], num[1]);\n                num += 2;\n                count -= 2;\n                QPointF c;\n                if (lastMode == 'q' || lastMode == 'Q' || lastMode == 't' || lastMode == 'T')\n                    c = QPointF(2 * x - ctrlPt.x(), 2 * y - ctrlPt.y());\n                else\n                    c = QPointF(x, y);\n                path.quadTo(c, e);\n                ctrlPt = c;\n                x = e.x();\n                y = e.y();\n                break;\n            }\n            case 'a': {\n                if (count < 7) {\n                    num += count;\n                    count = 0;\n                    break;\n                }\n                qreal rx = (*num++);\n                qreal ry = (*num++);\n                qreal xAxisRotation = (*num++);\n                qreal largeArcFlag = (*num++);\n                qreal sweepFlag = (*num++);\n                qreal ex = (*num++) + offsetX;\n                qreal ey = (*num++) + offsetY;\n                count -= 7;\n                qreal curx = x;\n                qreal cury = y;\n                pathArc(\n                    path, rx, ry, xAxisRotation, int(largeArcFlag), int(sweepFlag), ex, ey, curx,\n                    cury);\n                x = ex;\n                y = ey;\n            } break;\n            case 'A': {\n                if (count < 7) {\n                    num += count;\n                    count = 0;\n                    break;\n                }\n                qreal rx = (*num++);\n                qreal ry = (*num++);\n                qreal xAxisRotation = (*num++);\n                qreal largeArcFlag = (*num++);\n                qreal sweepFlag = (*num++);\n                qreal ex = (*num++);\n                qreal ey = (*num++);\n                count -= 7;\n                qreal curx = x;\n                qreal cury = y;\n                pathArc(\n                    path, rx, ry, xAxisRotation, int(largeArcFlag), int(sweepFlag), ex, ey, curx,\n                    cury);\n                x = ex;\n                y = ey;\n            } break;\n            default: return false;\n            }\n            lastMode = pathElem.toLatin1();\n        }\n    }\n    return true;\n}\n\nSvgHandler::SvgHandler(QGraphicsScene *scene) : m_scene(scene) {}\n\nSvgHandler::~SvgHandler() = default;\n\nvoid SvgHandler::load(QXmlStreamReader *data, bool skip_definitions) {\n    m_skipDefinitions = skip_definitions;\n    m_xml = data;\n    m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);\n    m_defaultPen.setMiterLimit(4);\n    parse();\n    /*\n    QGraphicsRectItem *it = new QGraphicsRectItem();\n    it->setRect(m_scene->sceneRect());\n    it->setPen(QPen(Qt::blue));\n    m_scene->addItem(it);\n    */\n}\n\nvoid SvgHandler::parse() {\n    m_xml->setNamespaceProcessing(false);\n    bool done = false;\n\n    m_elementStack.push(SvgElement::initial_element());\n\n    while (!m_xml->atEnd() && !done) {\n        switch (m_xml->readNext()) {\n        case QXmlStreamReader::StartElement: {\n            SvgElement el(m_xml->name().toString());\n            if (el.name == QLatin1String(\"defs\")) {\n                if (m_skipDefinitions) { m_xml->skipCurrentElement(); }\n            }\n            el.styleAttributes = m_elementStack.last().styleAttributes;\n            el.xmlAttributes = parseXmlAttributes(m_xml->attributes(), el.styleAttributes);\n            DEBUG() << QString(m_elementStack.count(), '-') << \">\"\n                    << \"+ start element:\" << el.name << \"id:\" << el.xmlAttributes.value(\"id\");\n            mergeCSSAttributes(el.styleAttributes, QStringLiteral(\"style\"), el.xmlAttributes);\n            m_elementStack.push(el);\n            bool is_item_created = startElement();\n            m_elementStack.last().itemCreated = is_item_created;\n            break;\n        }\n        case QXmlStreamReader::EndElement: {\n            SvgElement svg_element = m_elementStack.pop();\n            DEBUG() << QString(m_elementStack.count(), '-') << \">\"\n                    << \"- end element:\" << m_xml->name()\n                    << \"item created:\" << svg_element.itemCreated;\n            if (svg_element.itemCreated && m_topLevelItem) {\n                // logSvgI() << \"m_topLevelItem:\" << m_topLevelItem << typeid\n                // (*m_topLevelItem).name() << svg_element.name;\n                installVisuController(m_topLevelItem, svg_element);\n                m_topLevelItem = m_topLevelItem->parentItem();\n            }\n            break;\n        }\n        case QXmlStreamReader::Characters: {\n            DEBUG() << \"characters element:\" << m_xml->text(); // << typeid\n                                                               // (*m_topLevelItem).name();\n            if (auto *text_item = dynamic_cast<SimpleTextItem *>(m_topLevelItem)) {\n                QString text = text_item->text();\n                if (!text.isEmpty()) text += '\\n';\n                DEBUG() << text_item->text() << \"+\" << m_xml->text().toString();\n                text_item->setText(text + m_xml->text().toString());\n            } else if (auto *text_item = dynamic_cast<QGraphicsTextItem *>(m_topLevelItem)) {\n                QString text = text_item->toPlainText();\n                if (!text.isEmpty()) text += '\\n';\n                text += m_xml->text();\n                text_item->setPlainText(text);\n                // nInfo() << text_item->toPlainText();\n            } else {\n                DEBUG() << \"characters are not part of text item, will be ignored\";\n                // nWarning() << \"top:\" << m_topLevelItem << (m_topLevelItem?\n                // typeid (*m_topLevelItem).name(): \"NULL\");\n            }\n            break;\n        }\n        case QXmlStreamReader::ProcessingInstruction:\n            DEBUG() << \"ProcessingInstruction:\" << m_xml->processingInstructionTarget()\n                    << m_xml->processingInstructionData();\n            // processingInstruction(xml->processingInstructionTarget().toString(),\n            // xml->processingInstructionData().toString());\n            break;\n        default: break;\n        }\n    }\n}\n\nbool SvgHandler::startElement() {\n    const SvgElement &el = m_elementStack.last();\n    if (!m_topLevelItem) {\n        if (el.name == QLatin1String(\"svg\")) {\n            m_topLevelItem = new QGraphicsRectItem();\n            m_scene->addItem(m_topLevelItem);\n            root = m_topLevelItem;\n            return true;\n        } else {\n            WARN() << \"unsupported root element:\" << el.name;\n        }\n        return false;\n    } else {\n        if (el.name == QLatin1String(\"g\")) {\n            QGraphicsItem *item = createGroupItem(el);\n            if (item) {\n                setXmlAttributes(item, el);\n                if (auto *rect_item = dynamic_cast<QGraphicsRectItem *>(item)) {\n                    setStyle(rect_item, el.xmlAttributes);\n                }\n                setTransform(item, el.xmlAttributes.value(QStringLiteral(\"transform\")));\n                addItem(item);\n                return true;\n            }\n            return false;\n        } else if (el.name == QLatin1String(\"a\")) {\n            QGraphicsItem *item = createHyperlinkItem(el);\n            if (item) {\n                setXmlAttributes(item, el);\n                if (auto *rect_item = dynamic_cast<QGraphicsRectItem *>(item)) {\n                    setStyle(rect_item, el.xmlAttributes);\n                }\n                setTransform(item, el.xmlAttributes.value(QStringLiteral(\"transform\")));\n                addItem(item);\n                return true;\n            }\n            return false;\n        } else if (el.name == QLatin1String(\"rect\")) {\n            qreal x = el.xmlAttributes.value(QStringLiteral(\"x\")).toDouble();\n            qreal y = el.xmlAttributes.value(QStringLiteral(\"y\")).toDouble();\n            qreal w = el.xmlAttributes.value(QStringLiteral(\"width\")).toDouble();\n            qreal h = el.xmlAttributes.value(QStringLiteral(\"height\")).toDouble();\n            if (auto *text_item = dynamic_cast<QGraphicsTextItem *>(m_topLevelItem)) {\n                QTransform t;\n                t.translate(x, y);\n                text_item->setTransform(t, true);\n                text_item->setTextWidth(w);\n                return false;\n            } else {\n                auto *item = new QGraphicsRectItem();\n                setXmlAttributes(item, el);\n                item->setRect(QRectF(x, y, w, h));\n                setStyle(item, el.styleAttributes);\n                setTransform(item, el.xmlAttributes.value(QStringLiteral(\"transform\")));\n                addItem(item);\n                return true;\n            }\n        } else if (el.name == QLatin1String(\"circle\")) {\n            auto *item = new QGraphicsEllipseItem();\n            setXmlAttributes(item, el);\n            qreal cx = toDouble(el.xmlAttributes.value(QStringLiteral(\"cx\")));\n            qreal cy = toDouble(el.xmlAttributes.value(QStringLiteral(\"cy\")));\n            qreal rx = toDouble(el.xmlAttributes.value(QStringLiteral(\"r\")));\n            QRectF r(0, 0, 2 * rx, 2 * rx);\n            r.translate(cx - rx, cy - rx);\n            item->setRect(r);\n            setStyle(item, el.styleAttributes);\n            setTransform(item, el.xmlAttributes.value(QStringLiteral(\"transform\")));\n            addItem(item);\n            return true;\n        } else if (el.name == QLatin1String(\"ellipse\")) {\n            auto *item = new QGraphicsEllipseItem();\n            setXmlAttributes(item, el);\n            qreal cx = toDouble(el.xmlAttributes.value(QStringLiteral(\"cx\")));\n            qreal cy = toDouble(el.xmlAttributes.value(QStringLiteral(\"cy\")));\n            qreal rx = toDouble(el.xmlAttributes.value(QStringLiteral(\"rx\")));\n            qreal ry = toDouble(el.xmlAttributes.value(QStringLiteral(\"ry\")));\n            QRectF r(0, 0, 2 * rx, 2 * ry);\n            r.translate(cx - rx, cy - ry);\n            item->setRect(r);\n            setStyle(item, el.styleAttributes);\n            setTransform(item, el.xmlAttributes.value(QStringLiteral(\"transform\")));\n            addItem(item);\n            return true;\n        } else if (el.name == QLatin1String(\"path\")) {\n            auto *item = new QGraphicsPathItem();\n            setXmlAttributes(item, el);\n            QString data = el.xmlAttributes.value(QStringLiteral(\"d\"));\n            QPainterPath p;\n            parsePathDataFast(QStringView(data), p);\n            setStyle(item, el.styleAttributes);\n            static auto FILL_RULE = QStringLiteral(\"fill-rule\");\n            if (el.styleAttributes.value(FILL_RULE) == QLatin1String(\"evenodd\"))\n                p.setFillRule(Qt::OddEvenFill);\n            else\n                p.setFillRule(Qt::WindingFill);\n            item->setPath(p);\n            setTransform(item, el.xmlAttributes.value(QStringLiteral(\"transform\")));\n            addItem(item);\n            return true;\n        } else if (el.name == QLatin1String(\"text\")) {\n            auto *item = new SimpleTextItem(el.styleAttributes);\n            setXmlAttributes(item, el);\n            qreal x = toDouble(el.xmlAttributes.value(QStringLiteral(\"x\")));\n            qreal y = toDouble(el.xmlAttributes.value(QStringLiteral(\"y\")));\n            //            item->setPos(x, y);\n            setStyle(item, el.styleAttributes);\n            setTextStyle(item, el.styleAttributes);\n            setTransform(item, el.xmlAttributes.value(QStringLiteral(\"transform\")));\n            QFontMetricsF fm(item->font());\n            QTransform t;\n            t.translate(x, y - fm.ascent());\n            item->setTransform(t, true);\n            addItem(item);\n            return true;\n        } else if (el.name == QLatin1String(\"tspan\")) {\n            auto *item = new SimpleTextItem(el.styleAttributes);\n            setXmlAttributes(item, el);\n            qreal x = toDouble(el.xmlAttributes.value(QStringLiteral(\"x\")));\n            qreal y = toDouble(el.xmlAttributes.value(QStringLiteral(\"y\")));\n            // text->setPos(x, y);\n            setStyle(item, el.styleAttributes);\n            setTextStyle(item, el.styleAttributes);\n            setTransform(item, el.xmlAttributes.value(QStringLiteral(\"transform\")));\n            QFontMetricsF fm(item->font());\n            QTransform t;\n            t.translate(x, y - fm.ascent());\n            item->setTransform(t, true);\n            addItem(item);\n            return true;\n        } else if (el.name == QLatin1String(\"flowRoot\")) {\n            auto *item = new QGraphicsTextItem();\n            setXmlAttributes(item, el);\n            // nWarning() << \"FlowRoot:\" << (QGraphicsItem*)item;\n            setTextStyle(item, el.styleAttributes);\n            setTransform(item, el.xmlAttributes.value(QStringLiteral(\"transform\")));\n            addItem(item);\n            return true;\n        } else {\n            LOG() << \"unsupported element:\" << el.name;\n        }\n        return false;\n    }\n}\n\nQGraphicsItem *SvgHandler::createGroupItem(const SvgHandler::SvgElement &el) {\n    Q_UNUSED(el)\n    QGraphicsItem *item = new GroupItem();\n    return item;\n}\n\nQGraphicsItem *SvgHandler::createHyperlinkItem(const SvgHandler::SvgElement &el) {\n    Q_UNUSED(el)\n    QGraphicsItem *item = new HyperlinkItem();\n    return item;\n}\n\nvoid SvgHandler::installVisuController(QGraphicsItem *it, const SvgHandler::SvgElement &el) {\n    Q_UNUSED(it)\n    Q_UNUSED(el)\n}\n\nvoid SvgHandler::setXmlAttributes(QGraphicsItem *git, const SvgHandler::SvgElement &el) {\n    git->setData(\n        static_cast<int>(MetadataType::XmlAttributes), QVariant::fromValue(el.xmlAttributes));\n    git->setData(\n        static_cast<int>(MetadataType::CssAttributes), QVariant::fromValue(el.styleAttributes));\n}\n\nvoid SvgHandler::setTransform(QGraphicsItem *it, const QString &str_val) {\n    QStringView transform(str_val);\n    QTransform mx = parseTransformationMatrix(transform.trimmed());\n    if (!mx.isIdentity()) {\n        QTransform t(mx);\n        // logSvgI() << typeid (*it).name() << \"setting matrix:\" << t.dx() <<\n        // t.dy();\n        it->setTransform(t);\n    }\n}\n\nCssAttributes\nSvgHandler::parseXmlAttributes(const QXmlStreamAttributes &attributes, CssAttributes &css) {\n    XmlAttributes xml;\n    for (const QXmlStreamAttribute &attr : attributes) {\n        const auto name = attr.name().toString();\n        const auto value = attr.value().toString();\n        xml[name] = value;\n\n        /*\n         * \"\"\"\n         * The presentation attributes thus will participate in the CSS2 cascade\n         * as if they were replaced by corresponding CSS style rules placed at\n         * the start of the author style sheet with a specificity of zero. In\n         * general, this means that the presentation attributes have lower\n         * priority than other CSS style rules specified in author style sheets\n         * or ‘style’ attributes.\n         * \"\"\"\n         * https://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes\n         */\n        if (svgspec::presentation_attributes.contains(name)) { css[name] = value; }\n    }\n    return xml;\n}\n\nvoid SvgHandler::mergeCSSAttributes(\n    CssAttributes &css_attributes,\n    const QString &attr_name,\n    const XmlAttributes &xml_attributes) {\n#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)\n    QStringList css = xml_attributes.value(attr_name).split(';', QString::SkipEmptyParts);\n#else\n    QStringList css = xml_attributes.value(attr_name).split(';', Qt::SkipEmptyParts);\n#endif\n    for (const QString &ss : css) {\n        int ix = ss.indexOf(':');\n        if (ix > 0) { css_attributes[ss.mid(0, ix).trimmed()] = ss.mid(ix + 1).trimmed(); }\n    }\n}\n\nvoid SvgHandler::setStyle(QAbstractGraphicsShapeItem *it, const CssAttributes &attributes) {\n    QString fill = attributes.value(QStringLiteral(\"fill\"));\n    if (fill.isEmpty()) {\n        // default fill\n    } else if (fill == QLatin1String(\"none\")) {\n        it->setBrush(Qt::NoBrush);\n    } else {\n        QString opacity = attributes.value(QStringLiteral(\"fill-opacity\"));\n        it->setBrush(parseColor(fill, opacity));\n    }\n    QString stroke = attributes.value(QStringLiteral(\"stroke\"));\n    if (stroke.isEmpty() || stroke == QLatin1String(\"none\")) {\n        it->setPen(Qt::NoPen);\n    } else {\n        QString opacity = attributes.value(QStringLiteral(\"stroke-opacity\"));\n        QPen pen(parseColor(stroke, opacity));\n        pen.setWidthF(toDouble(attributes.value(QStringLiteral(\"stroke-width\"))));\n        QString linecap = attributes.value(QStringLiteral(\"stroke-linecap\"));\n        if (linecap == QLatin1String(\"round\"))\n            pen.setCapStyle(Qt::RoundCap);\n        else if (linecap == QLatin1String(\"square\"))\n            pen.setCapStyle(Qt::SquareCap);\n        else // if(linecap == QLatin1String(\"butt\"))\n            pen.setCapStyle(Qt::FlatCap);\n        QString join = attributes.value(QStringLiteral(\"stroke-linejoin\"));\n        if (join == QLatin1String(\"round\"))\n            pen.setJoinStyle(Qt::RoundJoin);\n        else if (join == QLatin1String(\"bevel\"))\n            pen.setJoinStyle(Qt::BevelJoin);\n        else // if( join == QLatin1String(\"miter\"))\n            pen.setJoinStyle(Qt::MiterJoin);\n        QString dash_pattern = attributes.value(QStringLiteral(\"stroke-dasharray\"));\n        if (!(dash_pattern.isEmpty() || dash_pattern == QLatin1String(\"none\"))) {\n            QStringList array = dash_pattern.split(',');\n            QVector<qreal> arr;\n            for (const auto &s : array) {\n                bool ok;\n                double d = s.toDouble(&ok);\n                if (!ok) {\n                    LOG() << \"Invalid stroke dash definition:\" << dash_pattern;\n                    arr.clear();\n                    break;\n                }\n                arr << d;\n            }\n            if (!arr.isEmpty()) { pen.setDashPattern(arr); }\n        }\n        QString dash_offset = attributes.value(QStringLiteral(\"stroke-dashoffset\"));\n        if (!(dash_offset.isEmpty() || dash_offset == QLatin1String(\"none\"))) {\n            bool ok;\n            double d = dash_offset.toDouble(&ok);\n            if (ok) {\n                pen.setDashOffset(d);\n            } else {\n                LOG() << \"Invalid stroke dash offset:\" << dash_offset;\n            }\n        }\n        it->setPen(pen);\n    }\n}\n\nvoid SvgHandler::setTextStyle(QFont &font, const CssAttributes &attributes) {\n    DEBUG() << \"orig font\" << font.toString();\n    // font.setStyleName(QString());\n    font.setStyleName(QStringLiteral(\"Normal\"));\n    QString font_size = attributes.value(QStringLiteral(\"font-size\"));\n    if (!font_size.isEmpty()) {\n        DEBUG() << \"font_size:\" << font_size;\n        if (font_size.endsWith(QLatin1String(\"px\")))\n            font.setPixelSize((int)toDouble(font_size.mid(0, font_size.size() - 2)));\n        else if (font_size.endsWith(QLatin1String(\"pt\")))\n            font.setPointSizeF(toDouble(font_size.mid(0, font_size.size() - 2)));\n    }\n    QString font_family = attributes.value(QStringLiteral(\"font-family\"));\n    if (!font_family.isEmpty()) {\n        DEBUG() << \"font_family:\" << font_family;\n        font.setFamily(font_family);\n    }\n    font.setWeight(QFont::Normal);\n    QString font_weight = attributes.value(QStringLiteral(\"font-weight\"));\n    if (!font_weight.isEmpty()) {\n        DEBUG() << \"font_weight:\" << font_weight;\n        if (font_weight == QLatin1String(\"thin\"))\n            font.setWeight(QFont::Thin);\n        else if (font_weight == QLatin1String(\"light\"))\n            font.setWeight(QFont::Light);\n        else if (font_weight == QLatin1String(\"normal\"))\n            font.setWeight(QFont::Normal);\n        else if (font_weight == QLatin1String(\"medium\"))\n            font.setWeight(QFont::Medium);\n        else if (font_weight == QLatin1String(\"bold\"))\n            font.setWeight(QFont::Bold);\n        else if (font_weight == QLatin1String(\"black\"))\n            font.setWeight(QFont::Black);\n    }\n    font.setStretch(QFont::Unstretched);\n    QString font_stretch = attributes.value(QStringLiteral(\"font-stretch\"));\n    if (!font_stretch.isEmpty()) {\n        DEBUG() << \"font_stretch:\" << font_stretch;\n        if (font_stretch == QLatin1String(\"ultra-condensed\"))\n            font.setStretch(QFont::UltraCondensed);\n        else if (font_stretch == QLatin1String(\"extra-condensed\"))\n            font.setStretch(QFont::ExtraCondensed);\n        else if (font_stretch == QLatin1String(\"condensed\"))\n            font.setStretch(QFont::Condensed);\n        else if (font_stretch == QLatin1String(\"semi-condensed\"))\n            font.setStretch(QFont::SemiCondensed);\n        else if (font_stretch == QLatin1String(\"semi-expanded\"))\n            font.setStretch(QFont::SemiExpanded);\n        else if (font_stretch == QLatin1String(\"expanded\"))\n            font.setStretch(QFont::Expanded);\n        else if (font_stretch == QLatin1String(\"extra-expanded\"))\n            font.setStretch(QFont::ExtraExpanded);\n        else if (font_stretch == QLatin1String(\"ultra-expanded\"))\n            font.setStretch(QFont::UltraExpanded);\n        else // if(font_stretch == QLatin1String(\"normal\"))\n            font.setStretch(QFont::Unstretched);\n    }\n    font.setStyle(QFont::StyleNormal);\n    QString font_style = attributes.value(QStringLiteral(\"font-style\"));\n    if (!font_style.isEmpty()) {\n        if (font_style == QLatin1String(\"normal\"))\n            font.setStyle(QFont::StyleNormal);\n        else if (font_style == QLatin1String(\"italic\"))\n            font.setStyle(QFont::StyleItalic);\n        else if (font_style == QLatin1String(\"oblique\"))\n            font.setStyle(QFont::StyleOblique);\n    }\n    DEBUG() << \"font\"\n            << \"px size:\" << font.pixelSize() << \"pt size:\" << font.pointSize()\n            << \"stretch:\" << font.stretch() << \"weight:\" << font.weight();\n    DEBUG() << \"new font\" << font.toString();\n}\n\nvoid SvgHandler::setTextStyle(QGraphicsSimpleTextItem *text, const CssAttributes &attributes) {\n    QFont f = text->font();\n    setTextStyle(f, attributes);\n    text->setFont(f);\n}\n\nvoid SvgHandler::setTextStyle(QGraphicsTextItem *text, const CssAttributes &attributes) {\n    QFont f = text->font();\n    setTextStyle(f, attributes);\n    text->setFont(f);\n    static auto FILL = QStringLiteral(\"fill\");\n    QString fill = attributes.value(FILL);\n    if (fill.isEmpty() || fill == QLatin1String(\"none\")) {\n        // it->setBrush(Qt::NoBrush);\n    } else {\n        static auto FILL_OPACITY = QStringLiteral(\"fill-opacity\");\n        QString opacity = attributes.value(FILL_OPACITY);\n        text->setDefaultTextColor(parseColor(fill, opacity));\n    }\n}\n\nQString SvgHandler::point2str(QPointF r) {\n    auto ret = QStringLiteral(\"Point(%1, %2)\");\n    return ret.arg(r.x()).arg(r.y());\n}\n\nQString SvgHandler::rect2str(QRectF r) {\n    auto ret = QStringLiteral(\"Rect(%1, %2 %3 x %4)\");\n    return ret.arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height());\n}\n\nvoid SvgHandler::addItem(QGraphicsItem *it) {\n    if (!m_topLevelItem) return;\n    DEBUG() << \"adding item:\" << typeid(*it).name() << \"pos:\" << point2str(it->pos())\n            << \"bounding rect:\" << rect2str(it->boundingRect());\n    if (auto *grp = dynamic_cast<QGraphicsItemGroup *>(m_topLevelItem)) {\n        grp->addToGroup(it);\n    } else {\n        it->setParentItem(m_topLevelItem);\n    }\n    m_topLevelItem = it;\n}\n\nSvgDocument SvgHandler::getDocument() const {\n    return SvgDocument(root);\n}\n\nSvgHandler::SvgElement SvgHandler::SvgElement::initial_element() {\n    auto el = SvgHandler::SvgElement();\n    el.styleAttributes = {\n        // TODO Choose some reasonable set.\n        { \"stroke-width\", \"1\" },\n    };\n    return el;\n}\n} // namespace svgscene"
  },
  {
    "path": "external/svgscene/src/svgscene/svghandler.h",
    "content": "﻿#pragma once\n\n#include \"svgdocument.h\"\n#include \"svgmetadata.h\"\n\n#include <QFile>\n#include <QMap>\n#include <QPen>\n#include <QStack>\n#include <utility>\n\nclass QXmlStreamReader;\nclass QXmlStreamAttributes;\nclass QGraphicsScene;\nclass QGraphicsItem;\nclass QGraphicsSimpleTextItem;\nclass QGraphicsTextItem;\nclass QAbstractGraphicsShapeItem;\n\nnamespace svgscene {\n\nSvgDocument parseFromFileName(QGraphicsScene *scene, const QString &filename);\nSvgDocument parseFromFile(QGraphicsScene *scene, QFile *file);\n\nclass SvgHandler {\npublic:\n    struct SvgElement {\n        QString name;\n        XmlAttributes xmlAttributes;\n        CssAttributes styleAttributes;\n        bool itemCreated = false;\n\n        SvgElement() = default;\n        explicit SvgElement(QString n, bool created = false)\n            : name(std::move(n))\n            , itemCreated(created) {}\n\n        static SvgElement initial_element();\n    };\n\npublic:\n    explicit SvgHandler(QGraphicsScene *scene);\n    virtual ~SvgHandler();\n\n    void load(QXmlStreamReader *data, bool is_skip_definitions = false);\n\n    static QString point2str(QPointF r);\n    static QString rect2str(QRectF r);\n\n    SvgDocument getDocument() const;\n\nprotected:\n    virtual QGraphicsItem *createGroupItem(const SvgElement &el);\n    QGraphicsItem *createHyperlinkItem(const SvgElement &el);\n    virtual void installVisuController(QGraphicsItem *it, const SvgElement &el);\n    virtual void setXmlAttributes(QGraphicsItem *git, const SvgElement &el);\n\n    QGraphicsScene *m_scene;\n\nprivate:\n    void parse();\n    static CssAttributes\n    parseXmlAttributes(const QXmlStreamAttributes &attributes, CssAttributes &css);\n    static void mergeCSSAttributes(\n        CssAttributes &css_attributes,\n        const QString &attr_name,\n        const XmlAttributes &xml_attributes);\n\n    static void setTransform(QGraphicsItem *it, const QString &str_val);\n    static void setStyle(QAbstractGraphicsShapeItem *it, const CssAttributes &attributes);\n    static void setTextStyle(QFont &font, const CssAttributes &attributes);\n    static void setTextStyle(QGraphicsSimpleTextItem *text, const CssAttributes &attributes);\n    static void setTextStyle(QGraphicsTextItem *text, const CssAttributes &attributes);\n\n    bool startElement();\n    void addItem(QGraphicsItem *it);\n\nprivate:\n    QGraphicsItem *root = nullptr;\n    QStack<SvgElement> m_elementStack;\n    QGraphicsItem *m_topLevelItem = nullptr;\n    QXmlStreamReader *m_xml = nullptr;\n    QPen m_defaultPen;\n    bool m_skipDefinitions = false;\n};\n\n} // namespace svgscene\n\nQ_DECLARE_METATYPE(svgscene::XmlAttributes)\n"
  },
  {
    "path": "external/svgscene/src/svgscene/svgmetadata.cpp",
    "content": "#include \"svgmetadata.h\"\n\nnamespace svgscene {\n\nXmlAttributes getXmlAttributes(const QGraphicsItem *element) {\n    QVariant raw = element->data(static_cast<int>(MetadataType::XmlAttributes));\n    if (!raw.isValid() || !raw.canConvert<XmlAttributes>()) {\n        throw std::out_of_range(\"XmlAttributes not present in the object.\\n\"\n                                \"Check whether the object was created by the svgscene parser.\");\n    }\n    return qvariant_cast<XmlAttributes>(raw);\n}\nQString getXmlAttribute(const QGraphicsItem *element, const QString &name) {\n    XmlAttributes attrs = getXmlAttributes(element);\n    if (!attrs.contains(name)) {\n        throw std::out_of_range(\"Element does not contain requested XML attribute.\");\n    }\n    return attrs.value(name);\n}\nQString getXmlAttributeOr(\n    const QGraphicsItem *element,\n    const QString &name,\n    const QString &defaultValue) noexcept {\n    XmlAttributes attrs = getXmlAttributes(element);\n    return attrs.value(name, defaultValue);\n}\n\nCssAttributes getCssAttributes(const QGraphicsItem *element) {\n    QVariant raw = element->data(static_cast<int>(MetadataType::CssAttributes));\n    if (!raw.isValid() || !raw.canConvert<CssAttributes>()) {\n        throw std::out_of_range(\"CssAttributes not present in the object.\\n\"\n                                \"Check whether the object was created by the svgscene parser.\");\n    }\n    return qvariant_cast<CssAttributes>(raw);\n}\nQString getCssAttribute(const QGraphicsItem *element, const QString &name) {\n    CssAttributes attrs = getCssAttributes(element);\n    if (!attrs.contains(name)) {\n        throw std::out_of_range(\"Element does not contain requested XML attribute.\");\n    }\n    return attrs.value(name);\n}\nQString getCssAttributeOr(\n    const QGraphicsItem *element,\n    const QString &name,\n    const QString &defaultValue) noexcept {\n    CssAttributes attrs = getCssAttributes(element);\n    return attrs.value(name, defaultValue);\n}\n\n} // namespace svgscene\n"
  },
  {
    "path": "external/svgscene/src/svgscene/svgmetadata.h",
    "content": "#pragma once\n\n#include <QGraphicsItem>\n#include <QMap>\n#include <QString>\n\nnamespace svgscene {\n\n/*\n * Fields that can be found on SVG related `QGraphicsItem`s using the `data`\n * method.\n * Data are stored using QVariant.\n */\nenum class MetadataType {\n    XmlAttributes = 1,\n    CssAttributes,\n};\n\nconstexpr MetadataType LAST_VALUE = MetadataType::CssAttributes;\n\nusing XmlAttributes = QMap<QString, QString>;\n\nXmlAttributes getXmlAttributes(const QGraphicsItem *element);\nQString getXmlAttribute(const QGraphicsItem *element, const QString &name);\nQString getXmlAttributeOr(\n    const QGraphicsItem *element,\n    const QString &name,\n    const QString &defaultValue) noexcept;\n\nusing CssAttributes = QMap<QString, QString>;\n\nCssAttributes getCssAttributes(const QGraphicsItem *element);\nQString getCssAttribute(const QGraphicsItem *element, const QString &name);\nQString getCssAttributeOr(\n    const QGraphicsItem *element,\n    const QString &name,\n    const QString &defaultValue) noexcept;\n\n} // namespace svgscene\n"
  },
  {
    "path": "external/svgscene/src/svgscene/svgspec.h",
    "content": "\n#ifndef SVGSCENE_SVG_SPEC_H\n#define SVGSCENE_SVG_SPEC_H\n\n#include <QSet>\n\nnamespace svgspec {\n\n/**\n * https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute\n */\nstatic const QSet<QString> presentation_attributes {\n    \"alignment-baseline\",\n    \"baseline-shift\",\n    \"clip\",\n    \"clip-path\",\n    \"clip-rule\",\n    \"color\",\n    \"color-interpolation\",\n    \"color-interpolation-filters\",\n    \"color-profile\",\n    \"color-rendering\",\n    \"cursor\",\n    \"direction\",\n    \"display\",\n    \"dominant-baseline\",\n    \"enable-background\",\n    \"fill\",\n    \"fill-opacity\",\n    \"fill-rule\",\n    \"filter\",\n    \"flood-color\",\n    \"flood-opacity\",\n    \"font-family\",\n    \"font-size\",\n    \"font-size-adjust\",\n    \"font-stretch\",\n    \"font-style\",\n    \"font-variant\",\n    \"font-weight\",\n    \"glyph-orientation-horizontal\",\n    \"glyph-orientation-vertical\",\n    \"image-rendering\",\n    \"kerning\",\n    \"letter-spacing\",\n    \"lighting-color\",\n    \"marker-end\",\n    \"marker-mid\",\n    \"marker-start\",\n    \"mask\",\n    \"opacity\",\n    \"overflow\",\n    \"pointer-events\",\n    \"shape-rendering\",\n    \"stop-color\",\n    \"stop-opacity\",\n    \"stroke\",\n    \"stroke-dasharray\",\n    \"stroke-dashoffset\",\n    \"stroke-linecap\",\n    \"stroke-linejoin\",\n    \"stroke-miterlimit\",\n    \"stroke-opacity\",\n    \"stroke-width\",\n    \"text-anchor\",\n    \"text-decoration\",\n    \"text-rendering\",\n    \"transform\",\n    \"transform-origin\",\n    \"unicode-bidi\",\n    \"vector-effect\",\n    \"visibility\",\n    \"word-spacing\",\n    \"writing-mode\",\n};\n} // namespace svgspec\n\n#endif // SVGSCENE_SVG_SPEC_H\n"
  },
  {
    "path": "external/svgscene/src/svgscene/utils/logging.h",
    "content": "#pragma once\n\n/**\n * Wrapper for QT logging library.\n *\n * Each source file is expected to declare a log category name what is\n * implicitly used for all logging macros. When logging in header files take\n * precaution not to pollute global scope. Either log manually or declare the\n * log within class scope.\n * Log categories can be structured using dots in name: `machine.core\n * .decode`.\n *\n * @see\n * https://www.kdab.com/wp-content/uploads/stories/slides/Day2/KaiKoehne_Qt%20Logging%20Framework%2016_9_0.pdf\n */\n\n#include <QLoggingCategory>\n\n#define LOG_CATEGORY(NAME) static QLoggingCategory _loging_category_(NAME)\n\n#if !defined(QT_NO_QDEBUG_MACRO)\n    #define QT_NO_QDEBUG_MACRO                                                                     \\\n        while (false)                                                                              \\\n        QMessageLogger().noDebug\n#endif\n\n#if defined(QT_NO_DEBUG_OUTPUT)\n    #define DEBUG QT_NO_QDEBUG_MACRO\n#else\n    #define DEBUG(...) qCDebug(_loging_category_, __VA_ARGS__)\n#endif\n#define LOG(...) qCInfo(_loging_category_, __VA_ARGS__)\n#define WARN(...) qCWarning(_loging_category_, __VA_ARGS__)\n#define ERROR(...) qCCritical(_loging_category_, __VA_ARGS__)\n"
  },
  {
    "path": "external/svgscene/src/svgscene/utils/memory_ownership.h",
    "content": "/**\n * Manual memory ownership toolkit.\n */\n#pragma once\n\n#include <QScopedPointer>\n\n/**\n * Tag for pointer owned by someone else. It is recommended to mention owner\n * in comment to make lifetimes manually verifiable.\n */\n#define BORROWED\n\n/**\n * Pointer with static lifetime.\n */\n#define STATIC\n\n/**\n * Owned pointer deallocated by automatic destructor.\n */\ntemplate<typename T>\nusing Box = QScopedPointer<T>;"
  },
  {
    "path": "extras/building/build-wasm.sh",
    "content": "#!/usr/bin/env bash\n\nset -eu\n\nEMSCRIPTEN_VERSION=$1\nQT_WASM=$2\nEMSDK_PATH=$3\n\n# If you know how to do this better, please, let me know.\n[ \"$(stat -c '%a' \"$EMSDK_PATH\")\" -ne 777 ] && sudo chmod 777 -R $EMSDK_PATH\nemsdk install $EMSCRIPTEN_VERSION\nemsdk activate $EMSCRIPTEN_VERSION\nsource $EMSDK_PATH/emsdk_env.sh\nmkdir -p build/wasm\npushd build/wasm\nemcmake cmake ../.. \\\n  -DCMAKE_BUILD_TYPE=Release \\\n  -DCMAKE_PREFIX_PATH=\"$QT_WASM\" \\\n  -DCMAKE_FIND_ROOT_PATH=\"$QT_WASM\" \\\n  -Wno-dev -G Ninja\nninja\npopd"
  },
  {
    "path": "extras/core_graphics/diagram.drawio",
    "content": "<mxfile host=\"app.diagrams.net\" modified=\"2023-06-29T14:37:58.075Z\" agent=\"Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/114.0\" etag=\"lMzkTVf1HWM6wJNUWKv4\" compressed=\"false\" version=\"21.5.2\" type=\"device\">\n  <diagram id=\"6v_UKsHeDzqBDUNiW_st\" name=\"RISC-V\">\n    <mxGraphModel dx=\"864\" dy=\"511\" grid=\"1\" gridSize=\"1\" guides=\"1\" tooltips=\"1\" connect=\"1\" arrows=\"1\" fold=\"1\" page=\"1\" pageScale=\"1\" pageWidth=\"700\" pageHeight=\"540\" background=\"#ffffff\" math=\"0\" shadow=\"0\">\n      <root>\n        <mxCell id=\"0jYIva3T7vnUbckOAgtF-0\" />\n        <mxCell id=\"0jYIva3T7vnUbckOAgtF-1\" style=\"locked=1;\" parent=\"0jYIva3T7vnUbckOAgtF-0\" />\n        <UserObject label=\"\" tags=\"simple forwarding pipeline\" id=\"Haz_8EwUBin59iLyk_8Q-227\">\n          <mxCell style=\"rounded=1;gradientColor=none;perimeterSpacing=0;arcSize=1;strokeColor=none;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;connectable=1;\" parent=\"0jYIva3T7vnUbckOAgtF-1\" vertex=\"1\">\n            <mxGeometry width=\"824\" height=\"463\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <mxCell id=\"Haz_8EwUBin59iLyk_8Q-26\" value=\"CPU\" parent=\"0jYIva3T7vnUbckOAgtF-0\" />\n        <UserObject label=\"\" tags=\"simple pipeline\" id=\"1xC1WIjRaXcGuhJV2cBO-2\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.35;exitDx=0;exitDy=0;exitPerimeter=0;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#000000;fontColor=#000000;endFill=0;shadow=0;strokeWidth=2;sketch=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"i2To6XwWsyNyerFe5G8K-12\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"232\" y=\"258\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"301\" y=\"266\" />\n                <mxPoint x=\"301\" y=\"258\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple\" id=\"1xC1WIjRaXcGuhJV2cBO-5\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=0.83;entryY=0.009;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#3333FF;fontColor=default;endFill=0;shadow=0;strokeWidth=1;sketch=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-85\" target=\"Haz_8EwUBin59iLyk_8Q-40\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"214\" y=\"203\" />\n                <mxPoint x=\"232\" y=\"203\" />\n                <mxPoint x=\"232\" y=\"248\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline\" id=\"1xC1WIjRaXcGuhJV2cBO-3\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.998;entryY=0.84;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#000000;fontColor=#000000;endFill=0;shadow=0;strokeWidth=2;sketch=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-13\" target=\"Haz_8EwUBin59iLyk_8Q-40\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"301\" y=\"321\" />\n                <mxPoint x=\"301\" y=\"313\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"q-6THzIC0rTZull3DPy9-49\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;fontColor=default;entryX=0.836;entryY=0.024;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#3333FF;sketch=0;shadow=0;endFill=0;strokeWidth=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-85\" target=\"uWEq7-W_z53UrtFael4O-37\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"599\" y=\"274\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"579\" y=\"128\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"q-6THzIC0rTZull3DPy9-39\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;exitX=1.017;exitY=0.942;exitDx=0;exitDy=0;entryX=0.693;entryY=0.246;entryDx=0;entryDy=0;entryPerimeter=0;fontColor=default;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#3333FF;sketch=0;shadow=0;endFill=0;strokeWidth=1;exitPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-85\" target=\"Haz_8EwUBin59iLyk_8Q-180\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"206\" y=\"191\" />\n                <mxPoint x=\"206\" y=\"176\" />\n                <mxPoint x=\"425\" y=\"176\" />\n                <mxPoint x=\"425\" y=\"273\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-60\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=1;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-3\" target=\"Haz_8EwUBin59iLyk_8Q-40\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"165\" y=\"397\" />\n                <mxPoint x=\"164\" y=\"397\" />\n                <mxPoint x=\"164\" y=\"398\" />\n                <mxPoint x=\"623\" y=\"398\" />\n                <mxPoint x=\"623\" y=\"421\" />\n                <mxPoint x=\"173\" y=\"421\" />\n                <mxPoint x=\"173\" y=\"306\" />\n              </Array>\n              <mxPoint x=\"141\" y=\"406\" as=\"sourcePoint\" />\n              <mxPoint x=\"149\" y=\"512\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"hCaf9_x39W0CY-DPdxUN-24\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.35;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.993;entryY=0.136;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=2;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#000000;shadow=0;endFill=0;sketch=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"i2To6XwWsyNyerFe5G8K-12\" target=\"Haz_8EwUBin59iLyk_8Q-40\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"343\" y=\"266\" />\n                <mxPoint x=\"343\" y=\"258\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"hCaf9_x39W0CY-DPdxUN-17\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.35;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=2;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#000000;sketch=0;shadow=0;endFill=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"i2To6XwWsyNyerFe5G8K-12\" target=\"Haz_8EwUBin59iLyk_8Q-116\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple forwarding pipeline\" id=\"dX68U42Ih_FQ-UEPESkK-9\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=arc;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.35;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;fontFamily=sans-serif;fontSize=6;strokeWidth=2;labelBackgroundColor=none;endFill=0;endArrow=none;sketch=0;startArrow=none;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"dX68U42Ih_FQ-UEPESkK-6\" target=\"Haz_8EwUBin59iLyk_8Q-112\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"581\" y=\"381\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding pipeline simple\" id=\"dX68U42Ih_FQ-UEPESkK-3\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;shadow=0;fontFamily=sans-serif;fontSize=6;strokeWidth=2;labelBackgroundColor=none;endFill=0;endArrow=none;sketch=0;jumpStyle=none;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"dX68U42Ih_FQ-UEPESkK-11\" target=\"hCaf9_x39W0CY-DPdxUN-22\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"426\" y=\"381\" as=\"targetPoint\" />\n              <mxPoint x=\"112\" y=\"386\" as=\"sourcePoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"351\" y=\"384\" />\n                <mxPoint x=\"351\" y=\"384\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"Zvq364hImIOJoiC3617Z-14\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=3;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;jumpSize=4;entryX=0;entryY=0.35;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-222\" target=\"nrxDrlzc4rpCp8ehld49-22\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"466.66666666666663\" y=\"365.5\" as=\"sourcePoint\" />\n              <mxPoint x=\"506.1388888888889\" y=\"368.25\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"448\" y=\"368\" />\n                <mxPoint x=\"448\" y=\"368\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"HRoSpS79B9qHWH9jaoA8-7\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=none;endFill=0;shadow=0;fontFamily=sans-serif;fontSize=6;strokeWidth=2;labelBackgroundColor=none;sketch=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-13\" target=\"Haz_8EwUBin59iLyk_8Q-211\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"426\" y=\"333\" as=\"sourcePoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"366\" y=\"343\" />\n                <mxPoint x=\"521\" y=\"343\" />\n                <mxPoint x=\"521\" y=\"316\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <object label=\"\" tags=\"forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-31\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;shadow=0;labelBackgroundColor=none;endArrow=none;endFill=0;strokeWidth=2;fontFamily=sans-serif;fontSize=6;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-12\" target=\"q-6THzIC0rTZull3DPy9-3\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"536\" y=\"381\" as=\"targetPoint\" />\n              <mxPoint x=\"383\" y=\"335\" as=\"sourcePoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"329\" y=\"412\" />\n                <mxPoint x=\"525\" y=\"412\" />\n                <mxPoint x=\"525\" y=\"381\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </object>\n        <object label=\"\" tags=\"forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-32\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;html=1;shadow=0;labelBackgroundColor=none;endArrow=none;endFill=0;strokeWidth=2;fontFamily=sans-serif;fontSize=6;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"q-6THzIC0rTZull3DPy9-4\" target=\"hCaf9_x39W0CY-DPdxUN-10\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"376\" y=\"327\" as=\"targetPoint\" />\n              <mxPoint x=\"322\" y=\"531\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </object>\n        <UserObject label=\"\" tags=\"pipeline simple forwarding\" id=\"nrxDrlzc4rpCp8ehld49-7\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#3333FF;strokeWidth=1;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=0.5;exitY=0.065;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"i2To6XwWsyNyerFe5G8K-12\" target=\"Haz_8EwUBin59iLyk_8Q-85\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"216.19999999999993\" y=\"178.15000000000003\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"375\" y=\"192\" />\n              </Array>\n              <mxPoint x=\"367\" y=\"244\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"NONE\" tags=\"pipeline forwarding\" component=\"multi-text-value\" source=\"memory-exception\" id=\"kbNWJ5ZPAqXhiAnqFs6d-6\">\n          <mxCell style=\"text;strokeColor=none;align=center;verticalAlign=middle;rounded=1;fontSize=6;fontFamily=sans-serif;spacing=3;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=0;fillColor=#CCCCCC;spacingTop=7;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"466\" y=\"34\" width=\"135\" height=\"26\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"NONE\" tags=\"pipeline forwarding\" component=\"multi-text-value\" source=\"execute-exception\" id=\"dQApTKY7MUPNvbngPjsP-8\">\n          <mxCell style=\"text;strokeColor=none;align=center;verticalAlign=middle;rounded=1;fontSize=6;fontFamily=sans-serif;spacing=3;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=0;fillColor=#CCCCCC;spacingTop=7;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"309\" y=\"34\" width=\"135\" height=\"26\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"NONE\" tags=\"pipeline forwarding\" component=\"multi-text-value\" source=\"decode-exception\" id=\"kbNWJ5ZPAqXhiAnqFs6d-3\">\n          <mxCell style=\"text;strokeColor=none;align=center;verticalAlign=middle;rounded=1;fontSize=6;fontFamily=sans-serif;spacing=3;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=0;fillColor=#CCCCCC;spacingTop=7;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"157\" y=\"36\" width=\"135\" height=\"26\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"NONE\" tags=\"simple pipeline forwarding\" component=\"multi-text-value\" source=\"fetch-exception\" id=\"uWEq7-W_z53UrtFael4O-88\">\n          <mxCell style=\"text;strokeColor=none;align=center;verticalAlign=middle;rounded=1;fontSize=6;fontFamily=sans-serif;spacing=3;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=0;fillColor=#CCCCCC;spacingTop=7;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"13\" y=\"36\" width=\"135\" height=\"26\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-30\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=arc;jumpSize=4;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#3333FF;strokeWidth=1;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=0.5;exitY=0.17;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1.002;entryY=0.733;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-180\" target=\"Haz_8EwUBin59iLyk_8Q-85\" edge=\"1\">\n            <mxGeometry x=\"451\" y=\"255\" as=\"geometry\">\n              <mxPoint x=\"216\" y=\"145.5\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"424\" y=\"277\" />\n                <mxPoint x=\"434\" y=\"277\" />\n                <mxPoint x=\"434\" y=\"168\" />\n                <mxPoint x=\"224\" y=\"168\" />\n              </Array>\n              <mxPoint x=\"463\" y=\"269.62\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-36\">\n          <mxCell style=\"group;rounded=0;container=0;fontFamily=sans-serif;fontSize=6;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"1\" width=\"752\" height=\"540\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-37\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;jumpStyle=arc;jumpSize=4;orthogonalLoop=1;jettySize=auto;entryX=-0.003;entryY=0.503;entryDx=0;entryDy=0;endArrow=none;endFill=0;fontSize=6;strokeWidth=2;sketch=0;shadow=0;rounded=0;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"dX68U42Ih_FQ-UEPESkK-13\" target=\"uWEq7-W_z53UrtFael4O-36\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"43\" y=\"298\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-38\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"dQApTKY7MUPNvbngPjsP-5\" target=\"Haz_8EwUBin59iLyk_8Q-177\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\" />\n              <mxPoint x=\"47\" y=\"276\" as=\"targetPoint\" />\n              <mxPoint x=\"42\" y=\"300\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-42\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeWidth=1;fontSize=6;strokeColor=#3333FF;entryX=0.837;entryY=0.004;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=0.999;exitY=0.086;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-85\" target=\"Haz_8EwUBin59iLyk_8Q-40\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"246\" y=\"232\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"216\" y=\"107\" />\n                <mxPoint x=\"216\" y=\"104\" />\n                <mxPoint x=\"640\" y=\"104\" />\n                <mxPoint x=\"640\" y=\"68\" />\n                <mxPoint x=\"232\" y=\"68\" />\n                <mxPoint x=\"232\" y=\"247\" />\n              </Array>\n              <mxPoint x=\"256\" y=\"107\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-44\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.2;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-45\" target=\"Haz_8EwUBin59iLyk_8Q-222\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"232\" y=\"360\" as=\"sourcePoint\" />\n              <mxPoint x=\"253.00000000000642\" y=\"361.00000000000006\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"430\" y=\"356\" />\n                <mxPoint x=\"430\" y=\"359\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Immediate&#xa;decode\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-45\">\n          <mxCell style=\"rounded=1;arcSize=8;fontSize=6;fontFamily=sans-serif;fontStyle=1;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"204\" y=\"344\" width=\"47\" height=\"24\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-46\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=sharp;orthogonalLoop=1;jettySize=auto;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.65;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;endArrow=none;endFill=0;strokeColor=default;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-211\" target=\"Haz_8EwUBin59iLyk_8Q-112\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"622\" y=\"296\" />\n                <mxPoint x=\"622\" y=\"373\" />\n                <mxPoint x=\"636\" y=\"373\" />\n              </Array>\n              <mxPoint x=\"634.0958219216135\" y=\"288.0596813421849\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-47\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;entryX=0.999;entryY=0.362;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;endArrow=none;endFill=0;strokeColor=#3333FF;strokeWidth=1;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"q-6THzIC0rTZull3DPy9-11\" target=\"Haz_8EwUBin59iLyk_8Q-85\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"527.25\" y=\"136\" as=\"sourcePoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"216\" y=\"136\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-48\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;exitX=0.934;exitY=0.001;exitDx=0;exitDy=0;shadow=0;endArrow=none;endFill=0;strokeColor=#3333FF;strokeWidth=1;fontSize=6;exitPerimeter=0;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"uWEq7-W_z53UrtFael4O-37\" target=\"Haz_8EwUBin59iLyk_8Q-85\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"585\" y=\"120\" />\n              </Array>\n              <mxPoint x=\"607.04\" y=\"276.08000000000004\" as=\"sourcePoint\" />\n              <mxPoint x=\"223.20000000000005\" y=\"125.46000000000004\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-49\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;exitX=0.5;exitY=0.065;exitDx=0;exitDy=0;entryX=0.999;entryY=0.175;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;endArrow=none;endFill=0;strokeColor=#3333FF;strokeWidth=1;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-112\" target=\"Haz_8EwUBin59iLyk_8Q-85\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"640\" y=\"131\" />\n                <mxPoint x=\"640\" y=\"112\" />\n                <mxPoint x=\"216\" y=\"112\" />\n                <mxPoint x=\"216\" y=\"116\" />\n              </Array>\n              <mxPoint x=\"660.1366906474822\" y=\"273.28720035554574\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-51\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-180\" target=\"Haz_8EwUBin59iLyk_8Q-56\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"474\" y=\"296\" as=\"sourcePoint\" />\n              <mxPoint x=\"480\" y=\"307\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-52\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=sharp;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#3333FF;strokeWidth=1;fontFamily=sans-serif;fontSize=6;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=1;exitY=0.4;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-180\" target=\"nrxDrlzc4rpCp8ehld49-40\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"438\" y=\"284\" />\n                <mxPoint x=\"468\" y=\"284\" />\n                <mxPoint x=\"468\" y=\"169\" />\n              </Array>\n              <mxPoint x=\"474\" y=\"284\" as=\"sourcePoint\" />\n              <mxPoint x=\"507\" y=\"209\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-55\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.009;entryY=0.371;entryDx=0;entryDy=0;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;entryPerimeter=0;startArrow=none;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"kLYEyaSkJVZjgnE96DWi-3\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"502\" y=\"295\" />\n                <mxPoint x=\"502\" y=\"295\" />\n              </Array>\n              <mxPoint x=\"483.9999999999918\" y=\"294.49999999999994\" as=\"sourcePoint\" />\n              <mxPoint x=\"535.5400000000001\" y=\"294.54999999999995\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"MQ-_pNAwyqKnrvp0fqe7-3\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.65;entryDx=0;entryDy=0;entryPerimeter=0;fontColor=#0000FF;endArrow=none;endFill=0;strokeWidth=2;exitX=0.5;exitY=1;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-56\" target=\"nrxDrlzc4rpCp8ehld49-22\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"495\" y=\"291.9999999999999\" as=\"sourcePoint\" />\n              <mxPoint x=\"499.1388888888889\" y=\"356.75\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"469\" y=\"360\" />\n                <mxPoint x=\"476\" y=\"360\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-56\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;aspect=fixed;rounded=0;fontSize=12;align=center;fillColor=#0;fontFamily=Helvetica;verticalAlign=middle;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"468\" y=\"294\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-57\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;startArrow=none;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"406\" y=\"357\" as=\"targetPoint\" />\n              <mxPoint x=\"415\" y=\"355.5\" as=\"sourcePoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"406\" y=\"356\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-62\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;entryX=0;entryY=0.5;entryDx=0;entryDy=0;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-5\" target=\"Haz_8EwUBin59iLyk_8Q-45\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"161\" y=\"361\" as=\"sourcePoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"172\" y=\"356\" />\n                <mxPoint x=\"172\" y=\"356\" />\n              </Array>\n              <mxPoint x=\"204\" y=\"360\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-65\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" target=\"hCaf9_x39W0CY-DPdxUN-5\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"165\" y=\"292.00000000000006\" as=\"sourcePoint\" />\n              <mxPoint x=\"165\" y=\"372.8512396694215\" as=\"targetPoint\" />\n              <Array as=\"points\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-66\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=sharp;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=1;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.012;entryY=0.414;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-1\" target=\"Haz_8EwUBin59iLyk_8Q-40\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"176\" y=\"288\" as=\"sourcePoint\" />\n              <mxPoint x=\"203\" y=\"290\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"204\" y=\"291\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-70\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.995;entryY=0.401;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-0\" target=\"uWEq7-W_z53UrtFael4O-36\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"154\" y=\"276\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-72\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=sharp;orthogonalLoop=1;jettySize=auto;entryX=-0.002;entryY=0.202;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=1;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-0\" target=\"Haz_8EwUBin59iLyk_8Q-40\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"176\" y=\"268\" as=\"sourcePoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"204\" y=\"276\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-77\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;entryX=0;entryY=0.35;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"dX68U42Ih_FQ-UEPESkK-11\" target=\"dQApTKY7MUPNvbngPjsP-5\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"46\" y=\"384\" />\n                <mxPoint x=\"46\" y=\"284\" />\n              </Array>\n              <mxPoint x=\"6.5488888888888965\" y=\"426.75000000000006\" as=\"targetPoint\" />\n              <mxPoint x=\"115.00000000000344\" y=\"410\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-78\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;html=1;shadow=0;labelBackgroundColor=none;endArrow=none;endFill=0;strokeWidth=2;fontFamily=sans-serif;fontSize=6;entryX=0;entryY=0.8;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;fontStyle=2\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"dX68U42Ih_FQ-UEPESkK-10\" target=\"Haz_8EwUBin59iLyk_8Q-222\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"303\" y=\"474\" as=\"targetPoint\" />\n              <mxPoint x=\"36\" y=\"371\" as=\"sourcePoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"85\" y=\"376\" />\n                <mxPoint x=\"87\" y=\"376\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"nrxDrlzc4rpCp8ehld49-4\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.2;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;fontFamily=sans-serif;fontSize=6;strokeWidth=2;labelBackgroundColor=none;endFill=0;endArrow=none;sketch=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"dX68U42Ih_FQ-UEPESkK-10\" target=\"Haz_8EwUBin59iLyk_8Q-219\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"94\" y=\"367\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00\" tags=\"simple pipeline forwarding\" component=\"reg-id-value\" source=\"rs1\" id=\"Haz_8EwUBin59iLyk_8Q-101\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;spacing=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"189\" y=\"272\" width=\"11\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00\" tags=\"simple pipeline forwarding\" component=\"reg-id-value\" source=\"rs2\" id=\"Haz_8EwUBin59iLyk_8Q-102\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;spacing=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"189\" y=\"287\" width=\"11\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"rs1\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-103\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"174\" y=\"272\" width=\"12\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"simple pipeline forwarding\" component=\"reg-value\" source=\"mem-write-addr\" id=\"Haz_8EwUBin59iLyk_8Q-106\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;direction=south;spacing=0;horizontal=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"516\" y=\"273\" width=\"8\" height=\"34\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-107\">\n          <mxCell style=\"edgeStyle=none;rounded=0;sketch=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;shadow=0;labelBackgroundColor=none;endArrow=none;endFill=0;strokeWidth=2;fontFamily=sans-serif;fontSize=6;entryX=0;entryY=0.5;entryDx=0;entryDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-199\" target=\"hCaf9_x39W0CY-DPdxUN-13\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"424\" y=\"321\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-109\">\n          <mxCell style=\"rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.012;entryY=0.841;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=sans-serif;fontSize=6;fontColor=#000000;shadow=0;strokeWidth=3;labelBackgroundColor=none;endFill=0;endArrow=none;sketch=0;elbow=vertical;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-113\" target=\"Haz_8EwUBin59iLyk_8Q-180\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"417.3611111111111\" y=\"323.5\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" component=\"mux2\" source=\"wb-MemToReg\" id=\"Haz_8EwUBin59iLyk_8Q-112\">\n          <mxCell style=\"shape=stencil(3VXBUoMwEP2aHHUgEVuODtpbj854TWErsTRhANv6924SaBuRtBM9OM4whH2bfdlsXhbCsrbkNRAaSb4Fwh4JpcvnF7QpfuFQWiyNrLm3Jkusydsa8s5ia3GAwsJt16gN7EXR9eFCltCITnvZE4kecI5+WJYrKZFBKNk6njM/knEhMTY6WLI+lY/euh2SIXQRzHEfwBF/4QhJYwg6kkQhmYxp0qmisAyRqaqzbMXzzWuj3mXx7eI11yc6cgzuSkh4U0KiZQaTzAp2UPn2NERv1Q4mT+gaAr38iYC5NYm9db2KYhZA4W4jDdiHy5D8OAf/dTnKrFItXJy1FlVlL7tHsYiPdGPQCbEhrWrAo8Lz9oIt6dRm5v6ro6uS8xoN/TYRdhFv1AXNu6q9oW6tY39XmJDdzCG5+wsH9hvVSOb/pRoGHanUoPZ/aoBP);rounded=1;fontSize=7;align=right;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;arcSize=19;flipV=1;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"631\" y=\"364.5\" width=\"19\" height=\"25\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" source=\"exec-AluSrc\" component=\"mux2\" id=\"Haz_8EwUBin59iLyk_8Q-113\">\n          <mxCell style=\"shape=stencil(3VXBUoMwEP2aHHUgEVuODtpbj854TWErsTRhANv6924SaBuRtBM9OM4whH2bfdlsXhbCsrbkNRAaSb4Fwh4JpcvnF7QpfuFQWiyNrLm3Jkusydsa8s5ia3GAwsJt16gN7EXR9eFCltCITnvZE4kecI5+WJYrKZFBKNk6njM/knEhMTY6WLI+lY/euh2SIXQRzHEfwBF/4QhJYwg6kkQhmYxp0qmisAyRqaqzbMXzzWuj3mXx7eI11yc6cgzuSkh4U0KiZQaTzAp2UPn2NERv1Q4mT+gaAr38iYC5NYm9db2KYhZA4W4jDdiHy5D8OAf/dTnKrFItXJy1FlVlL7tHsYiPdGPQCbEhrWrAo8Lz9oIt6dRm5v6ro6uS8xoN/TYRdhFv1AXNu6q9oW6tY39XmJDdzCG5+wsH9hvVSOb/pRoGHanUoPZ/aoBP);rounded=1;fontSize=7;align=right;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;arcSize=19;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"376\" y=\"312\" width=\"19\" height=\"25\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-144\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;labelBackgroundColor=none;endArrow=none;endFill=0;strokeWidth=2;fontFamily=sans-serif;fontSize=6;exitX=0.5;exitY=0;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-10\" target=\"Haz_8EwUBin59iLyk_8Q-116\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"376\" y=\"318\" as=\"sourcePoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"322\" y=\"266\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-146\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.75;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;labelBackgroundColor=none;endArrow=none;endFill=0;strokeWidth=2;fontFamily=sans-serif;fontSize=6;exitX=1;exitY=0.5;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-12\" target=\"Haz_8EwUBin59iLyk_8Q-199\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"390\" y=\"329\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-147\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.75;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;labelBackgroundColor=none;endArrow=none;endFill=0;strokeWidth=2;fontFamily=sans-serif;fontSize=6;exitX=0.5;exitY=0;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-12\" target=\"Haz_8EwUBin59iLyk_8Q-116\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"329\" y=\"275\" />\n              </Array>\n              <mxPoint x=\"361\" y=\"328\" as=\"sourcePoint\" />\n              <mxPoint x=\"402.2777777777777\" y=\"274.5\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-149\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;labelBackgroundColor=none;endArrow=none;endFill=0;strokeWidth=2;fontFamily=sans-serif;fontSize=6;exitX=0.002;exitY=0.891;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-40\" target=\"Haz_8EwUBin59iLyk_8Q-112\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"378\" y=\"527\" as=\"sourcePoint\" />\n              <mxPoint x=\"666.3611111111111\" y=\"374\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"189\" y=\"316\" />\n                <mxPoint x=\"189\" y=\"430\" />\n                <mxPoint x=\"655\" y=\"430\" />\n                <mxPoint x=\"655\" y=\"377\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-151\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;shadow=0;labelBackgroundColor=none;endArrow=none;endFill=0;strokeWidth=2;fontFamily=sans-serif;fontSize=6;jumpSize=4;entryX=1.017;entryY=0.856;entryDx=0;entryDy=0;entryPerimeter=0;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-199\" target=\"Haz_8EwUBin59iLyk_8Q-40\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"344\" y=\"314\" />\n              </Array>\n              <mxPoint x=\"234\" y=\"315\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-152\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;labelBackgroundColor=none;endArrow=none;endFill=0;strokeWidth=2;fontFamily=sans-serif;fontSize=6;jumpSize=4;exitX=1;exitY=0.5;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-10\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"324.9999999999943\" y=\"321\" as=\"sourcePoint\" />\n              <mxPoint x=\"369.2777777777778\" y=\"321\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"334\" y=\"321\" />\n                <mxPoint x=\"334\" y=\"321\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"simple pipeline forwarding\" component=\"reg-value\" source=\"wb\" id=\"Haz_8EwUBin59iLyk_8Q-157\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;direction=east;spacing=0;horizontal=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"616\" y=\"426\" width=\"34.5\" height=\"7.5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-176\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"73\" y=\"271\" width=\"6\" height=\"20\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-177\">\n          <mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fontFamily=sans-serif;fontSize=6;align=left;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-176\" vertex=\"1\">\n            <mxGeometry y=\"-1\" width=\"6\" height=\"20\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-178\">\n          <mxCell style=\"triangle;whiteSpace=wrap;html=1;rounded=0;fontFamily=sans-serif;fontSize=6;align=left;direction=south;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-176\" vertex=\"1\">\n            <mxGeometry x=\"0.5\" y=\"-0.736842105263158\" width=\"5\" height=\"4.736842105263156\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-179\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"410\" y=\"252\" width=\"34\" height=\"86\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-180\">\n          <mxCell style=\"shape=stencil(rZTdcoIwEIWfJrcOZLXVS4fqVW/7ABHWkhoThlC1b++SQFvkpxacYWDOOdkvCdkJg8imIkPGAy2OyOCFcb5+faM3WanXq8DLs5dQSWEzjAvv7eUFE2/bIjcHPMukqMqlTjGXRZnChgVrGlM+EMVGayJIo20j+ZUTTEhNtcHFw6q5vyo1414zvh2NWP4fEd4gFiNWURd9Q8LnR1CWMH07824EROT0HRpEOxEf3nPzqZPOiTNRNkQrqGMlNX4YqUm5j1vKDk+ohvZTVx/NCXtP+B5AOf0PAJoEmI54GoFoElaTCYvBNu0mhE3EfATi5l/eRYiVsfjnqL1Uyl83Aw1Pfqv1nNvTr4Q1OXYGVNTKnOvvUGdcAQ==);rounded=1;fontSize=7;align=right;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;fillColor=#D5E8D4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-179\" vertex=\"1\">\n            <mxGeometry x=\"-3\" width=\"34\" height=\"86\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"ALU\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-181\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=8;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-179\" vertex=\"1\">\n            <mxGeometry x=\"10\" y=\"39\" width=\"18\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"zero\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-182\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=right;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;fontColor=#0000CC;\" parent=\"Haz_8EwUBin59iLyk_8Q-179\" vertex=\"1\">\n            <mxGeometry x=\"10\" y=\"31\" width=\"16\" height=\"5.56\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Peripherals\" tags=\"simple pipeline forwarding\" link=\"#peripherals\" id=\"Haz_8EwUBin59iLyk_8Q-183\">\n          <mxCell style=\"rounded=1;arcSize=17;fontSize=6;fontFamily=sans-serif;fontStyle=1;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;fillColor=#DEDEDE;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"529\" y=\"238\" width=\"45\" height=\"16\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"MemToReg\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-191\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"235\" y=\"108\" width=\"31.5\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"MemWrite\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-192\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"235\" y=\"116\" width=\"32\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"BranchBxx\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-193\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"235\" y=\"132\" width=\"33\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"AluControl\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-194\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"235\" y=\"166\" width=\"32\" height=\"4\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" link=\"#data_memory\" id=\"Haz_8EwUBin59iLyk_8Q-210\">\n          <mxCell style=\"group;fontSize=12;fillColor=#d5e8d4;strokeColor=#82b366;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"529\" y=\"276\" width=\"60\" height=\"80\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" link=\"#data_memory\" id=\"Haz_8EwUBin59iLyk_8Q-211\">\n          <mxCell style=\"rounded=1;arcSize=2;fontSize=6;fontStyle=1;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;fillColor=#EAFFE9;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-210\" vertex=\"1\">\n            <mxGeometry width=\"60\" height=\"80\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Data&#xa;Memory\" tags=\"simple pipeline forwarding\" link=\"#data_memory\" id=\"Haz_8EwUBin59iLyk_8Q-212\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-210\" vertex=\"1\">\n            <mxGeometry x=\"6\" y=\"53\" width=\"48\" height=\"17\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" link=\"#cache_data\" component=\"data-cache\" tags=\"simple hazardunit pipeline forwarding\" id=\"uWEq7-W_z53UrtFael4O-37\">\n          <mxCell style=\"shape=stencil(rVTRbsMgDPwaHidRWPteZZv20o9gxDSolFTA1GRfP4LJOliXqVqlSInPOd8ZWxDe+E6cgDBqxREIfyKMSSG7iLAIdoisKYZnDDc5FP4EMiCm9AAtwj64/gBn3YZM17YDp8OU5c+EbuM/08ObNyEPe9e/27ZIzOmUcZMEowOWyspjEVW2KtPCSa8/cm8MMcJeKisR+cUNb1TvYMGm0sZgz1cqX2rYkGzE8/lys7nupuCE0SRSeifW6m+WEkdtxhjnj8TzwvoHHyehlgsEGAKOEXnNZR/yFHg5htV80kbvbd4hsAHcbTqvcUX4thBaFzpz+E3HgAq3qey098sy/PEOMrQQqPb2Lo1UCv/oIW3/jyVPKN4OCfgE);fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-210\" vertex=\"1\">\n            <mxGeometry width=\"60\" height=\"50\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"Haz_8EwUBin59iLyk_8Q-213\">\n          <mxCell style=\"group;direction=east;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"92\" y=\"331.5\" width=\"29\" height=\"39\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"4\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-214\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-213\" vertex=\"1\">\n            <mxGeometry x=\"1\" y=\"4.5\" width=\"8\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"Haz_8EwUBin59iLyk_8Q-215\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-213\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"9\" width=\"20\" height=\"39\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-217\">\n          <mxCell style=\"group;fontFamily=sans-serif;fontSize=6;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-215\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"1\" width=\"20\" height=\"39\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"Haz_8EwUBin59iLyk_8Q-218\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-217\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry width=\"20\" height=\"39\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-219\">\n          <mxCell style=\"shape=stencil(rZTdcoIwEIWfJrcOJOropUPbq972ASKssjUmDKH+vL1LAlrkpxadYWDOOdkvIdkJE5FNZQaMB1rugYk3xvnq84veZKVeLwMvj16KSkqbQVx4b4MnSLxti9zs4IhJUZWjTiHHokzFOwtWNKZ8RBQbrYmARttG8isnmERNtcHJw6q5z5WacK8Z/xiNWPwfEd4hZiNWURddIeH0FZTFvJsiInL6dlxEaxnvtrn50Unn3JksT7MV1LFCDd8GNSn3cWtZwwHU0C/V1XtzgN7jeQRQTn8DiCZBPI+Yj0A0CcunCbPBHusmhE3EdATibi8fIsTKWPhz1AaV8nfFQM+T32o95/b0K2FNDp0BFbUy5/oL0BkX);rounded=1;fontSize=6;align=center;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;fontFamily=sans-serif;verticalAlign=middle;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;flipV=1;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-218\" vertex=\"1\">\n            <mxGeometry width=\"20\" height=\"39\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-220\">\n          <mxCell style=\"shape=stencil(xVPtDoIgFH0a/iPMB2hW78ESk0JggGlvHx9qqdM1q7Wx3d1z7j0c2L0AZ6YkigIEBakowHuAkOK1ccFhZQQSCGPeTHJiFD3ZCBaspXmEjdXyShuW206AiZJqZj2LDwDuXI0/OCukpmcta5GPiJ5WxCvMiJ7mTNCLZMJlIYS7olrwAdBxpfvVpXva022SvtNeyZv/tjb2pN2P3MfpuoT3vyjxucIWE19/xhaJYb7+a2MiMbj6uY2CcR6nc7EUZw6frUdAZzsV0LjmAXgA);rounded=1;fontSize=6;align=center;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;fontFamily=sans-serif;verticalAlign=middle;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-218\" vertex=\"1\">\n            <mxGeometry x=\"10\" y=\"10.75\" width=\"4\" height=\"17.5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-216\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;exitX=0;exitY=0.8;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;endArrow=none;endFill=0;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-213\" source=\"Haz_8EwUBin59iLyk_8Q-219\" target=\"Haz_8EwUBin59iLyk_8Q-214\" edge=\"1\">\n            <mxGeometry x=\"97\" y=\"330\" as=\"geometry\">\n              <mxPoint x=\"9\" y=\"32\" as=\"targetPoint\" />\n              <mxPoint x=\"13.944444444444457\" y=\"32\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"RegWrite\" tags=\"pipeline forwarding\" id=\"4UeL6ZtvB5kMY3OpyeXH-3\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"235\" y=\"99.5\" width=\"30\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline simple forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-50\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;exitX=0.5;exitY=0.065;exitDx=0;exitDy=0;shadow=0;endArrow=none;endFill=0;strokeColor=#3333FF;strokeWidth=1;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitPerimeter=0;entryX=1.002;entryY=0.829;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-113\" target=\"Haz_8EwUBin59iLyk_8Q-85\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"227\" y=\"169\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"386\" y=\"184\" />\n                <mxPoint x=\"216\" y=\"184\" />\n              </Array>\n              <mxPoint x=\"441.30500000000006\" y=\"313.1659983142981\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple forwarding pipeline\" id=\"Haz_8EwUBin59iLyk_8Q-83\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#3333FF;strokeWidth=1;fontFamily=sans-serif;fontSize=6;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"8Br1XOdIxK8N6TFEatcO-5\" target=\"Haz_8EwUBin59iLyk_8Q-85\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"223\" y=\"152\" />\n                <mxPoint x=\"223\" y=\"152\" />\n              </Array>\n              <mxPoint x=\"485\" y=\"152\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"BranchJalr\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-196\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"235\" y=\"150\" width=\"33\" height=\"4\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"RegWrite\" tags=\"simple forwarding pipeline\" id=\"5soJhyaSc5fLxI4RMxeW-4\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;horizontal=1;fontColor=#0000FF;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"220.5\" y=\"220\" width=\"24.5\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"uWEq7-W_z53UrtFael4O-39\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.65;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-8\" target=\"Haz_8EwUBin59iLyk_8Q-113\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"231\" y=\"361\" as=\"sourcePoint\" />\n              <mxPoint x=\"235.5\" y=\"434\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"374\" y=\"355\" />\n                <mxPoint x=\"374\" y=\"328\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"uWEq7-W_z53UrtFael4O-44\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;entryX=0;entryY=0.5;entryDx=0;entryDy=0;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;jumpSize=4;exitX=0.5;exitY=0;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-0\" target=\"Haz_8EwUBin59iLyk_8Q-85\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"141\" y=\"276\" as=\"sourcePoint\" />\n              <mxPoint x=\"155\" y=\"143\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"165\" y=\"148\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"rd\" tags=\"simple pipeline forwarding\" id=\"uWEq7-W_z53UrtFael4O-45\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"177\" y=\"302\" width=\"8\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"simple pipeline forwarding\" component=\"reg-value\" source=\"decode-inst-bus\" id=\"uWEq7-W_z53UrtFael4O-42\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;fontStyle=0\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"160\" y=\"230\" width=\"34\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" component=\"mux3\" source=\"exec-ForwardA\" id=\"Haz_8EwUBin59iLyk_8Q-116\">\n          <mxCell style=\"shape=stencil(3ZbBboMwDIafJsdNIWnLOE5su+04adcU0pEVEgS0ZW8/k8AKo6QIOHSTENS/8RfHMVYR9fOIpRwRLFnCEX1ChLy+vYNN4Rc8IqN52JgnY5K1MVme8qAw2k6UPDRyXmRqz08iLOpwISOeiaLy0meEH+Gd6qJ+oKQEglAy73hafoAxISEWlwZWp/JVW/dNMoi8TGYsgHAnMJwF0miCfiB4swTFW12mUB+UoWOj/pYF+49MHWR4ce2UVS3RczTuWEj+qYQESz90Llt+5LFtS010oo588HzGAKrlzwDSLYkzH/EwAdHdhjebsLb22BgCHUUIYpXzq2/tRBybaWFpWNB7faPVgWYDrMq4pQvb8wlm2nlOXflyqqoELAWjuusIs4g1SufeHrDJocS/R6Gtk+9Ip/6E2OfEQCu6HcjqFg7RViFnRoXGbe5v14fMqM/G/S8dpNXe165V88dGC98=);rounded=1;fontSize=7;align=right;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;arcSize=19;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"333\" y=\"249\" width=\"20\" height=\"34\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding simple\" component=\"bool-value\" source=\"exec-AluZero\" id=\"uWEq7-W_z53UrtFael4O-55\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=0;recursiveResize=0;editable=0;expand=0;resizable=0;rotatable=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"444\" y=\"279\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding simple\" component=\"bool-value\" source=\"exec-AluSrc\" id=\"uWEq7-W_z53UrtFael4O-56\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=0;recursiveResize=0;editable=0;expand=0;resizable=0;rotatable=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"382\" y=\"300\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"exec-MemToReg\" id=\"uWEq7-W_z53UrtFael4O-57\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"321.5\" y=\"108\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"wb-MemToReg\" id=\"uWEq7-W_z53UrtFael4O-58\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"637\" y=\"255\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"decode-AluSrc\" id=\"uWEq7-W_z53UrtFael4O-60\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"274\" y=\"180\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"exec-BranchJalr\" id=\"uWEq7-W_z53UrtFael4O-61\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"312\" y=\"148\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"exec-MemWrite\" id=\"uWEq7-W_z53UrtFael4O-62\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"312\" y=\"116\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"exec-RegWrite\" id=\"uWEq7-W_z53UrtFael4O-63\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"312\" y=\"100\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple forwarding pipeline\" component=\"bool-value\" source=\"wb-RegWrite\" id=\"uWEq7-W_z53UrtFael4O-67\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=0;recursiveResize=0;editable=0;expand=0;resizable=0;rotatable=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"228\" y=\"233\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"decode-MemToReg\" id=\"uWEq7-W_z53UrtFael4O-68\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=0;recursiveResize=0;editable=0;expand=0;resizable=0;rotatable=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"287\" y=\"108\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"decode-MemWrite\" id=\"uWEq7-W_z53UrtFael4O-69\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=0;recursiveResize=0;editable=0;expand=0;resizable=0;rotatable=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"274.25\" y=\"116\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"decode-BranchJalr\" id=\"uWEq7-W_z53UrtFael4O-70\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"274\" y=\"148\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"wb-RegWrite\" id=\"uWEq7-W_z53UrtFael4O-71\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"442\" y=\"64\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"decode-RegWrite\" id=\"uWEq7-W_z53UrtFael4O-72\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"274.25\" y=\"100.5\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"mem-MemToReg\" id=\"uWEq7-W_z53UrtFael4O-73\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"480\" y=\"107.5\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding simple\" component=\"bool-value\" source=\"mem-MemWrite\" id=\"uWEq7-W_z53UrtFael4O-75\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"583\" y=\"216\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"mem-RegWrite\" id=\"uWEq7-W_z53UrtFael4O-76\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"469.5\" y=\"100.5\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"exec-AuiPC\" id=\"uWEq7-W_z53UrtFael4O-80\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;resizeWidth=1;movable=1;deletable=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"371\" y=\"244\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"forwarding\" component=\"debug-value\" source=\"exec-ForwardB\" id=\"uWEq7-W_z53UrtFael4O-82\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=0;recursiveResize=0;editable=0;expand=0;resizable=1;rotatable=0;resizeWidth=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"334.25\" y=\"292\" width=\"15.5\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"debug-value\" source=\"decode-AluControl\" id=\"uWEq7-W_z53UrtFael4O-83\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=0;recursiveResize=0;editable=0;expand=0;resizable=1;rotatable=0;resizeWidth=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"274\" y=\"164\" width=\"11.25\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Hazard Unit:\" tags=\"hazardunit\" id=\"uWEq7-W_z53UrtFael4O-84\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=right;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"648\" y=\"105\" width=\"41\" height=\"15\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"NORMAL\" tags=\"hazardunit\" component=\"multi-text-value\" source=\"hazard\" id=\"uWEq7-W_z53UrtFael4O-89\">\n          <mxCell style=\"text;strokeColor=none;align=center;verticalAlign=middle;rounded=1;fontSize=6;fontFamily=sans-serif;spacing=3;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=0;fillColor=#CCCCCC;spacingTop=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"651\" y=\"120\" width=\"102\" height=\"15\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"ADD $0, $1, $3\" tags=\"simple pipeline forwarding\" component=\"instruction-value\" source=\"fetch\" id=\"uWEq7-W_z53UrtFael4O-90\">\n          <mxCell style=\"rounded=1;comic=0;fontFamily=sans-serif;fontSize=9;fontColor=#000000;align=center;spacing=4;horizontal=1;fontStyle=0;fillColor=#FFADAD;strokeColor=none;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"13\" y=\"14.5\" width=\"135\" height=\"26.5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"ADD $0, $1, $3\" tags=\"pipeline forwarding\" component=\"instruction-value\" source=\"decode\" id=\"uWEq7-W_z53UrtFael4O-91\">\n          <mxCell style=\"rounded=1;comic=0;fontFamily=sans-serif;fontSize=9;fontColor=#000000;align=center;spacing=4;horizontal=1;fontStyle=0;strokeColor=none;fillColor=#FFD4AD;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"157\" y=\"14.5\" width=\"135\" height=\"26.5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"ADD $0, $1, $3\" tags=\"pipeline forwarding\" component=\"instruction-value\" source=\"exec\" id=\"uWEq7-W_z53UrtFael4O-92\">\n          <mxCell style=\"rounded=1;comic=0;fontFamily=sans-serif;fontSize=10;fontColor=#000000;align=center;spacing=4;horizontal=1;fontStyle=0;strokeColor=none;fillColor=#C1FFAD;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"309\" y=\"14.5\" width=\"135\" height=\"26.5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"ADD $0, $1, $3\" tags=\"pipeline forwarding\" component=\"instruction-value\" source=\"mem\" id=\"uWEq7-W_z53UrtFael4O-93\">\n          <mxCell style=\"rounded=1;comic=0;fontFamily=sans-serif;fontSize=9;fontColor=#000000;align=center;spacing=4;horizontal=1;fontStyle=0;strokeColor=none;fillColor=#ADFFE5;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"466\" y=\"14.5\" width=\"135\" height=\"26.5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"ADD $0, $1, $3\" tags=\"pipeline forwarding\" component=\"instruction-value\" source=\"wb\" id=\"uWEq7-W_z53UrtFael4O-94\">\n          <mxCell style=\"rounded=1;comic=0;fontFamily=sans-serif;fontSize=9;fontColor=#000000;align=center;spacing=4;horizontal=1;fontStyle=0;strokeColor=none;fillColor=#FFADE6;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"614\" y=\"14.5\" width=\"135\" height=\"26.5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"uWEq7-W_z53UrtFael4O-40\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;shadow=0;fontFamily=sans-serif;fontSize=6;strokeWidth=2;labelBackgroundColor=none;endFill=0;endArrow=none;sketch=0;jumpStyle=none;jumpSize=4;strokeColor=#000000;entryX=1.005;entryY=0.856;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-199\" target=\"Haz_8EwUBin59iLyk_8Q-40\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"344\" y=\"314\" />\n              </Array>\n              <mxPoint x=\"435.6388888888888\" y=\"301.75\" as=\"sourcePoint\" />\n              <mxPoint x=\"242\" y=\"305\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00\" tags=\"pipeline forwarding\" component=\"reg-id-value\" source=\"mem-rd\" id=\"Haz_8EwUBin59iLyk_8Q-98\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;spacing=0;labelBackgroundColor=none;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"558\" y=\"393\" width=\"14\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Registers\" tags=\"simple pipeline forwarding\" link=\"#registers\" id=\"Haz_8EwUBin59iLyk_8Q-40\">\n          <mxCell style=\"rounded=1;arcSize=2;fontSize=6;fontStyle=1;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"204\" y=\"247\" width=\"47\" height=\"78\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"simple pipeline forwarding\" component=\"reg-value\" source=\"decode-rs2\" id=\"Haz_8EwUBin59iLyk_8Q-153\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"256\" y=\"309\" width=\"34\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00\" tags=\"simple pipeline forwarding\" component=\"reg-id-value\" source=\"wb-rd\" id=\"Haz_8EwUBin59iLyk_8Q-154\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"189\" y=\"301\" width=\"11\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"forwarding simple pipeline\" component=\"reg-value\" source=\"alu-src2\" id=\"Haz_8EwUBin59iLyk_8Q-200\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;direction=south;spacing=0;horizontal=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"397\" y=\"304\" width=\"8\" height=\"34\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" component=\"mux3\" source=\"exec-ForwardB\" id=\"Haz_8EwUBin59iLyk_8Q-199\">\n          <mxCell style=\"shape=stencil(3ZbBboMwDIafJsdNIWnLOE5su+04adcU0pEVEgS0ZW8/k8AKo6QIOHSTENS/8RfHMVYR9fOIpRwRLFnCEX1ChLy+vYNN4Rc8IqN52JgnY5K1MVme8qAw2k6UPDRyXmRqz08iLOpwISOeiaLy0meEH+Gd6qJ+oKQEglAy73hafoAxISEWlwZWp/JVW/dNMoi8TGYsgHAnMJwF0miCfiB4swTFW12mUB+UoWOj/pYF+49MHWR4ce2UVS3RczTuWEj+qYQESz90Llt+5LFtS010oo588HzGAKrlzwDSLYkzH/EwAdHdhjebsLb22BgCHUUIYpXzq2/tRBybaWFpWNB7faPVgWYDrMq4pQvb8wlm2nlOXflyqqoELAWjuusIs4g1SufeHrDJocS/R6Gtk+9Ip/6E2OfEQCu6HcjqFg7RViFnRoXGbe5v14fMqM/G/S8dpNXe165V88dGC98=);rounded=1;fontSize=7;align=right;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;arcSize=19;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"332\" y=\"304\" width=\"20\" height=\"34\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Terminal\" link=\"#terminal\" tags=\"simple pipeline forwarding\" id=\"HRoSpS79B9qHWH9jaoA8-25\">\n          <mxCell style=\"rounded=1;arcSize=17;fontSize=6;fontFamily=sans-serif;fontStyle=1;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;fillColor=#DEDEDE;strokeColor=#000000;fontColor=#333333;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"529\" y=\"257\" width=\"45\" height=\"16\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" component=\"mux2\" source=\"mem-BranchOutcome\" id=\"dQApTKY7MUPNvbngPjsP-5\">\n          <mxCell style=\"shape=stencil(3VXBUoMwEP2aHHUgEVuODtpbj854TWErsTRhANv6924SaBuRtBM9OM4whH2bfdlsXhbCsrbkNRAaSb4Fwh4JpcvnF7QpfuFQWiyNrLm3Jkusydsa8s5ia3GAwsJt16gN7EXR9eFCltCITnvZE4kecI5+WJYrKZFBKNk6njM/knEhMTY6WLI+lY/euh2SIXQRzHEfwBF/4QhJYwg6kkQhmYxp0qmisAyRqaqzbMXzzWuj3mXx7eI11yc6cgzuSkh4U0KiZQaTzAp2UPn2NERv1Q4mT+gaAr38iYC5NYm9db2KYhZA4W4jDdiHy5D8OAf/dTnKrFItXJy1FlVlL7tHsYiPdGPQCbEhrWrAo8Lz9oIt6dRm5v6ro6uS8xoN/TYRdhFv1AXNu6q9oW6tY39XmJDdzCG5+wsH9hvVSOb/pRoGHanUoPZ/aoBP);rounded=1;fontSize=7;align=right;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;arcSize=19;flipV=1;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"52\" y=\"266.50000000000006\" width=\"20\" height=\"27\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"i2To6XwWsyNyerFe5G8K-6\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"642\" y=\"69\" width=\"77\" height=\"28\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Cycles:\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-187\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"i2To6XwWsyNyerFe5G8K-6\" vertex=\"1\">\n            <mxGeometry x=\"2\" y=\"1\" width=\"41\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Stalls:\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-188\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"i2To6XwWsyNyerFe5G8K-6\" vertex=\"1\">\n            <mxGeometry y=\"16\" width=\"41\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <object label=\"0\" component=\"debug-value\" source=\"CycleCount\" tags=\"simple hazardunit pipeline forwarding\" id=\"uWEq7-W_z53UrtFael4O-78\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=9;fontColor=#000000;fontStyle=1;spacing=0;spacingBottom=1;\" parent=\"i2To6XwWsyNyerFe5G8K-6\" vertex=\"1\">\n            <mxGeometry x=\"37\" width=\"40\" height=\"10\" as=\"geometry\" />\n          </mxCell>\n        </object>\n        <UserObject label=\"Privilege:\" tags=\"simple pipeline forwarding\" id=\"Haz_PrivilegeLabel\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"i2To6XwWsyNyerFe5G8K-6\" vertex=\"1\">\n            <mxGeometry x=\"2\" y=\"30\" width=\"41\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <object label=\"MACHINE\" component=\"multi-text-value\" source=\"Privilege\" tags=\"simple hazardunit pipeline forwarding\" id=\"uWEq7-PrivilegeValue\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=9;fontColor=#000000;fontStyle=1;spacing=0;spacingBottom=1;\" parent=\"i2To6XwWsyNyerFe5G8K-6\" vertex=\"1\">\n            <mxGeometry x=\"37\" y=\"27\" width=\"40\" height=\"10\" as=\"geometry\" />\n          </mxCell>\n        </object>\n        <object label=\"0\" component=\"debug-value\" source=\"StallCount\" tags=\"simple hazardunit pipeline forwarding\" id=\"uWEq7-W_z53UrtFael4O-79\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=9;fontColor=#000000;fontStyle=1;spacing=0;spacingBottom=1;\" parent=\"i2To6XwWsyNyerFe5G8K-6\" vertex=\"1\">\n            <mxGeometry x=\"37\" y=\"14\" width=\"40\" height=\"10\" as=\"geometry\" />\n          </mxCell>\n        </object>\n        <UserObject label=\"00000000\" tags=\"simple pipeline forwarding\" component=\"reg-value\" source=\"decode-rs1\" id=\"Haz_8EwUBin59iLyk_8Q-118\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"256\" y=\"254\" width=\"34\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"AuiPC\" tags=\"simple pipeline forwarding\" id=\"nrxDrlzc4rpCp8ehld49-5\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"235\" y=\"190.5\" width=\"20\" height=\"4\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding hazardunit pipeline simple\" id=\"hCaf9_x39W0CY-DPdxUN-16\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.2;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=2;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#000000;sketch=0;shadow=0;endFill=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"i2To6XwWsyNyerFe5G8K-12\" target=\"Haz_8EwUBin59iLyk_8Q-180\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" source=\"exec-AuiPC\" component=\"mux2\" id=\"i2To6XwWsyNyerFe5G8K-12\">\n          <mxCell style=\"shape=stencil(3VXBUoMwEP2aHHUgEVuODtpbj854TWErsTRhANv6924SaBuRtBM9OM4whH2bfdlsXhbCsrbkNRAaSb4Fwh4JpcvnF7QpfuFQWiyNrLm3Jkusydsa8s5ia3GAwsJt16gN7EXR9eFCltCITnvZE4kecI5+WJYrKZFBKNk6njM/knEhMTY6WLI+lY/euh2SIXQRzHEfwBF/4QhJYwg6kkQhmYxp0qmisAyRqaqzbMXzzWuj3mXx7eI11yc6cgzuSkh4U0KiZQaTzAp2UPn2NERv1Q4mT+gaAr38iYC5NYm9db2KYhZA4W4jDdiHy5D8OAf/dTnKrFItXJy1FlVlL7tHsYiPdGPQCbEhrWrAo8Lz9oIt6dRm5v6ro6uS8xoN/TYRdhFv1AXNu6q9oW6tY39XmJDdzCG5+wsH9hvVSOb/pRoGHanUoPZ/aoBP);rounded=1;fontSize=7;align=right;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;arcSize=19;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"365\" y=\"257\" width=\"19\" height=\"25\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"forwarding\" component=\"debug-value\" source=\"exec-ForwardA\" id=\"i2To6XwWsyNyerFe5G8K-16\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=0;recursiveResize=0;editable=0;expand=0;resizable=1;rotatable=0;resizeWidth=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"335.25\" y=\"236\" width=\"15.5\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"nrxDrlzc4rpCp8ehld49-12\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeWidth=1;fontFamily=sans-serif;fontSize=6;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;strokeColor=#3333FF;entryX=1.005;entryY=0.644;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0;exitY=0.25;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"nrxDrlzc4rpCp8ehld49-40\" target=\"Haz_8EwUBin59iLyk_8Q-85\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"442\" y=\"165\" />\n                <mxPoint x=\"442\" y=\"160\" />\n                <mxPoint x=\"216\" y=\"160\" />\n                <mxPoint x=\"216\" y=\"162\" />\n              </Array>\n              <mxPoint x=\"444\" y=\"165\" as=\"sourcePoint\" />\n              <mxPoint x=\"224\" y=\"159\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"exec-BranchVal\" id=\"nrxDrlzc4rpCp8ehld49-20\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"322\" y=\"156\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"BranchVal\" tags=\"simple pipeline forwarding\" id=\"nrxDrlzc4rpCp8ehld49-11\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"235\" y=\"158\" width=\"31\" height=\"4.5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"decode-BranchBxx\" id=\"uWEq7-W_z53UrtFael4O-77\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"274\" y=\"132\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"decode-AuiPC\" id=\"nrxDrlzc4rpCp8ehld49-31\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"287.25\" y=\"188\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"nrxDrlzc4rpCp8ehld49-25\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#3333FF;strokeWidth=1;fontFamily=sans-serif;fontSize=6;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;exitX=0;exitY=0.25;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"8Br1XOdIxK8N6TFEatcO-5\" target=\"Haz_8EwUBin59iLyk_8Q-85\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"229\" y=\"144\" />\n                <mxPoint x=\"229\" y=\"144\" />\n              </Array>\n              <mxPoint x=\"563\" y=\"427\" as=\"sourcePoint\" />\n              <mxPoint x=\"211\" y=\"147\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"exec-BranchJal\" id=\"nrxDrlzc4rpCp8ehld49-33\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"322\" y=\"140\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"decode-BranchJal\" id=\"nrxDrlzc4rpCp8ehld49-29\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"287.25\" y=\"140\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"debug-value\" source=\"exec-AluControl\" id=\"nrxDrlzc4rpCp8ehld49-34\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;resizeWidth=1;movable=1;deletable=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"428\" y=\"245\" width=\"15.5\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"jkDunDTEjqUDAv3C6Bv7-5\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#3333FF;sketch=0;shadow=0;endFill=0;strokeWidth=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"nrxDrlzc4rpCp8ehld49-40\" target=\"Haz_8EwUBin59iLyk_8Q-84\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"490\" y=\"162\" as=\"sourcePoint\" />\n              <mxPoint x=\"498\" y=\"163.25\" as=\"targetPoint\" />\n              <Array as=\"points\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"nrxDrlzc4rpCp8ehld49-40\">\n          <mxCell style=\"verticalLabelPosition=bottom;shadow=0;dashed=0;align=center;html=1;verticalAlign=top;shape=mxgraph.electrical.logic_gates.logic_gate;operation=xor;fontColor=#3333FF;strokeColor=#3333FF;strokeWidth=1;perimeterSpacing=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"470\" y=\"163\" width=\"9\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding pipeline\" id=\"nrxDrlzc4rpCp8ehld49-42\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"127\" y=\"85\" width=\"42\" height=\"320\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"IF/ID\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-202\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"nrxDrlzc4rpCp8ehld49-42\" vertex=\"1\">\n            <mxGeometry width=\"42\" height=\"6.228710462287104\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-203\">\n          <mxCell style=\"group;fillColor=#d5e8d4;strokeColor=#82b366;\" parent=\"nrxDrlzc4rpCp8ehld49-42\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"23\" y=\"9.732360097323598\" width=\"6\" height=\"310.26763990267636\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-204\">\n          <mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fontFamily=sans-serif;fontSize=6;align=left;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-203\" vertex=\"1\">\n            <mxGeometry width=\"6\" height=\"310.26763990267636\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-205\">\n          <mxCell style=\"triangle;whiteSpace=wrap;html=1;rounded=0;fontFamily=sans-serif;fontSize=6;align=left;direction=south;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-203\" vertex=\"1\">\n            <mxGeometry x=\"0.5\" width=\"5\" height=\"3.503649635036495\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" component=\"mux2\" source=\"mem-BranchJalx\" id=\"dX68U42Ih_FQ-UEPESkK-6\">\n          <mxCell style=\"shape=stencil(3VXBUoMwEP2aHHUgEVuODtpbj854TWErsTRhANv6924SaBuRtBM9OM4whH2bfdlsXhbCsrbkNRAaSb4Fwh4JpcvnF7QpfuFQWiyNrLm3Jkusydsa8s5ia3GAwsJt16gN7EXR9eFCltCITnvZE4kecI5+WJYrKZFBKNk6njM/knEhMTY6WLI+lY/euh2SIXQRzHEfwBF/4QhJYwg6kkQhmYxp0qmisAyRqaqzbMXzzWuj3mXx7eI11yc6cgzuSkh4U0KiZQaTzAp2UPn2NERv1Q4mT+gaAr38iYC5NYm9db2KYhZA4W4jDdiHy5D8OAf/dTnKrFItXJy1FlVlL7tHsYiPdGPQCbEhrWrAo8Lz9oIt6dRm5v6ro6uS8xoN/TYRdhFv1AXNu6q9oW6tY39XmJDdzCG5+wsH9hvVSOb/pRoGHanUoPZ/aoBP);rounded=1;fontSize=7;align=right;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;arcSize=19;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"488\" y=\"367\" width=\"38\" height=\"27\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"dX68U42Ih_FQ-UEPESkK-10\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;labelBackgroundColor=default;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"84\" y=\"362\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"dX68U42Ih_FQ-UEPESkK-12\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=2;shadow=0;fontFamily=sans-serif;fontSize=6;labelBackgroundColor=none;endFill=0;endArrow=none;sketch=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"dX68U42Ih_FQ-UEPESkK-11\" target=\"Haz_8EwUBin59iLyk_8Q-219\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"125\" y=\"351\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"dX68U42Ih_FQ-UEPESkK-11\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"124\" y=\"383\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"dX68U42Ih_FQ-UEPESkK-14\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;strokeWidth=2;shadow=0;fontFamily=sans-serif;fontSize=6;labelBackgroundColor=none;endFill=0;endArrow=none;sketch=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"dX68U42Ih_FQ-UEPESkK-13\" target=\"dX68U42Ih_FQ-UEPESkK-10\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"86.00671140939596\" y=\"289.1156623351651\" as=\"sourcePoint\" />\n              <mxPoint x=\"84.5\" y=\"371\" as=\"targetPoint\" />\n              <Array as=\"points\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"dX68U42Ih_FQ-UEPESkK-13\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"84\" y=\"279\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"dX68U42Ih_FQ-UEPESkK-15\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=arc;jumpSize=4;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;shadow=0;labelBackgroundColor=none;fontFamily=sans-serif;fontSize=6;endArrow=none;endFill=0;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-177\" target=\"dX68U42Ih_FQ-UEPESkK-13\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"hCaf9_x39W0CY-DPdxUN-0\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"164\" y=\"275\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"hCaf9_x39W0CY-DPdxUN-2\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeWidth=2;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#000000;sketch=0;shadow=0;endFill=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-1\" target=\"hCaf9_x39W0CY-DPdxUN-0\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"hCaf9_x39W0CY-DPdxUN-1\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"164\" y=\"290\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"rs2\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-104\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"174\" y=\"287\" width=\"12\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"hCaf9_x39W0CY-DPdxUN-3\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"164\" y=\"397\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"hCaf9_x39W0CY-DPdxUN-6\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeWidth=2;entryX=0.5;entryY=1;entryDx=0;entryDy=0;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#000000;sketch=0;shadow=0;endFill=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-5\" target=\"hCaf9_x39W0CY-DPdxUN-3\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"167\" y=\"453.4876033057851\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"165\" y=\"400\" />\n                <mxPoint x=\"165\" y=\"400\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"hCaf9_x39W0CY-DPdxUN-5\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"164\" y=\"355\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"hCaf9_x39W0CY-DPdxUN-8\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"373\" y=\"355\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"simple pipeline forwarding\" component=\"reg-value\" source=\"decode-imm\" id=\"uWEq7-W_z53UrtFael4O-38\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"256\" y=\"352\" width=\"34\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"hCaf9_x39W0CY-DPdxUN-10\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"321\" y=\"320\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"hCaf9_x39W0CY-DPdxUN-12\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"328\" y=\"328\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"hCaf9_x39W0CY-DPdxUN-15\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.35;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=2;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#000000;sketch=0;shadow=0;endFill=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-13\" target=\"Haz_8EwUBin59iLyk_8Q-113\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"hCaf9_x39W0CY-DPdxUN-13\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"365\" y=\"320\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"forwarding pipeline simple\" component=\"reg-value\" source=\"alu-src1\" id=\"HRoSpS79B9qHWH9jaoA8-23\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;direction=south;spacing=0;horizontal=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"396\" y=\"252\" width=\"8\" height=\"33.5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"decode-BranchVal\" id=\"nrxDrlzc4rpCp8ehld49-19\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"287.25\" y=\"156\" width=\"7\" height=\"7.861183012175247\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding pipeline simple\" id=\"Haz_8EwUBin59iLyk_8Q-221\">\n          <mxCell style=\"group;html=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"347\" y=\"351.5\" width=\"20\" height=\"28.000000000000004\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"hCaf9_x39W0CY-DPdxUN-19\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-221\" connectable=\"0\" vertex=\"1\">\n            <mxGeometry x=\"76\" y=\"1\" width=\"20\" height=\"30\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-222\">\n          <mxCell style=\"shape=stencil(rZTdcoIwEIWfJrcOJOropUPbq972ASKssjUmDKH+vL1LAlrkpxadYWDOOdkvIdkJE5FNZQaMB1rugYk3xvnq84veZKVeLwMvj16KSkqbQVx4b4MnSLxti9zs4IhJUZWjTiHHokzFOwtWNKZ8RBQbrYmARttG8isnmERNtcHJw6q5z5WacK8Z/xiNWPwfEd4hZiNWURddIeH0FZTFvJsiInL6dlxEaxnvtrn50Unn3JksT7MV1LFCDd8GNSn3cWtZwwHU0C/V1XtzgN7jeQRQTn8DiCZBPI+Yj0A0CcunCbPBHusmhE3EdATibi8fIsTKWPhz1AaV8nfFQM+T32o95/b0K2FNDp0BFbUy5/oL0BkX);rounded=1;fontSize=6;align=center;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;fontFamily=sans-serif;verticalAlign=middle;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"hCaf9_x39W0CY-DPdxUN-19\" vertex=\"1\">\n            <mxGeometry width=\"20\" height=\"30.000000000000007\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-223\">\n          <mxCell style=\"shape=stencil(xVPtDoIgFH0a/iPMB2hW78ESk0JggGlvHx9qqdM1q7Wx3d1z7j0c2L0AZ6YkigIEBakowHuAkOK1ccFhZQQSCGPeTHJiFD3ZCBaspXmEjdXyShuW206AiZJqZj2LDwDuXI0/OCukpmcta5GPiJ5WxCvMiJ7mTNCLZMJlIYS7olrwAdBxpfvVpXva022SvtNeyZv/tjb2pN2P3MfpuoT3vyjxucIWE19/xhaJYb7+a2MiMbj6uY2CcR6nc7EUZw6frUdAZzsV0LjmAXgA);rounded=1;fontSize=6;align=center;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;fontFamily=sans-serif;verticalAlign=middle;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;\" parent=\"hCaf9_x39W0CY-DPdxUN-19\" vertex=\"1\">\n            <mxGeometry x=\"10\" y=\"8.26923076923077\" width=\"4\" height=\"13.46153846153846\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding pipeline simple\" id=\"hCaf9_x39W0CY-DPdxUN-23\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.65;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=2;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#000000;sketch=0;shadow=0;endFill=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"q-6THzIC0rTZull3DPy9-47\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"358\" y=\"272\" />\n              </Array>\n              <mxPoint x=\"358\" y=\"382\" as=\"sourcePoint\" />\n              <mxPoint x=\"369.6388888888889\" y=\"272.25\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding pipeline simple\" id=\"8Br1XOdIxK8N6TFEatcO-2\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;endFill=0;shadow=0;strokeWidth=2;sketch=0;entryX=0;entryY=0.65;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-22\" target=\"dX68U42Ih_FQ-UEPESkK-6\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"512\" y=\"382\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"438\" y=\"385\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"hCaf9_x39W0CY-DPdxUN-22\">\n          <mxCell style=\"square;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;strokeColor=none;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"357\" y=\"383\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"AluSrc\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-195\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"235\" y=\"182\" width=\"22\" height=\"4\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"BranchJal\" tags=\"simple pipeline forwarding\" id=\"nrxDrlzc4rpCp8ehld49-24\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"235\" y=\"141\" width=\"31\" height=\"5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding pipeline\" id=\"MQ-_pNAwyqKnrvp0fqe7-0\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" connectable=\"0\" vertex=\"1\">\n            <mxGeometry x=\"280\" y=\"83\" width=\"42\" height=\"321\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-158\">\n          <mxCell style=\"group;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"MQ-_pNAwyqKnrvp0fqe7-0\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"18\" y=\"10.779901289462089\" width=\"6\" height=\"310.2200987105379\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-160\">\n          <mxCell style=\"triangle;whiteSpace=wrap;html=1;rounded=0;fontFamily=sans-serif;fontSize=6;fontColor=#0000CC;align=left;direction=south;\" parent=\"Haz_8EwUBin59iLyk_8Q-158\" vertex=\"1\">\n            <mxGeometry x=\"0.5\" width=\"5\" height=\"3.494344040544233\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-161\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-158\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry width=\"6\" height=\"309.4435778126393\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-163\">\n          <mxCell style=\"triangle;whiteSpace=wrap;html=1;rounded=0;fontFamily=sans-serif;fontSize=6;align=left;direction=south;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-161\" vertex=\"1\">\n            <mxGeometry x=\"0.5\" width=\"5\" height=\"3.494344040544233\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"ID/EX\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-133\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"MQ-_pNAwyqKnrvp0fqe7-0\" vertex=\"1\">\n            <mxGeometry width=\"42\" height=\"8.70467379316206\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"pipeline forwarding\" component=\"reg-value\" source=\"exec-rs1\" id=\"Haz_8EwUBin59iLyk_8Q-117\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;direction=south;spacing=0;horizontal=0;\" parent=\"MQ-_pNAwyqKnrvp0fqe7-0\" vertex=\"1\">\n            <mxGeometry x=\"30\" y=\"158.0357142857143\" width=\"7\" height=\"32.48214285714286\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"pipeline forwarding\" component=\"reg-value\" source=\"exec-rs2\" id=\"HRoSpS79B9qHWH9jaoA8-24\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;direction=south;spacing=0;horizontal=0;\" parent=\"MQ-_pNAwyqKnrvp0fqe7-0\" vertex=\"1\">\n            <mxGeometry x=\"29.5\" y=\"213.5357142857143\" width=\"7\" height=\"32.48214285714286\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"simple pipeline forwarding\" component=\"reg-value\" source=\"mem-write-val\" id=\"CJ-m8BARnU2k4Bpp9hul-5\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;direction=south;spacing=0;horizontal=0;fillColor=#FFFFFF;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"516\" y=\"312\" width=\"8\" height=\"34\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" component=\"mux2\" source=\"mem-BranchJalr\" id=\"nrxDrlzc4rpCp8ehld49-22\">\n          <mxCell style=\"shape=stencil(3VXBUoMwEP2aHHUgEVuODtpbj854TWErsTRhANv6924SaBuRtBM9OM4whH2bfdlsXhbCsrbkNRAaSb4Fwh4JpcvnF7QpfuFQWiyNrLm3Jkusydsa8s5ia3GAwsJt16gN7EXR9eFCltCITnvZE4kecI5+WJYrKZFBKNk6njM/knEhMTY6WLI+lY/euh2SIXQRzHEfwBF/4QhJYwg6kkQhmYxp0qmisAyRqaqzbMXzzWuj3mXx7eI11yc6cgzuSkh4U0KiZQaTzAp2UPn2NERv1Q4mT+gaAr38iYC5NYm9db2KYhZA4W4jDdiHy5D8OAf/dTnKrFItXJy1FlVlL7tHsYiPdGPQCbEhrWrAo8Lz9oIt6dRm5v6ro6uS8xoN/TYRdhFv1AXNu6q9oW6tY39XmJDdzCG5+wsH9hvVSOb/pRoGHanUoPZ/aoBP);rounded=1;fontSize=7;align=right;spacingRight=0;spacingBottom=0;fontStyle=1;spacing=0;arcSize=19;flipV=1;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"471.5\" y=\"350.00000000000006\" width=\"19\" height=\"27\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"8Br1XOdIxK8N6TFEatcO-1\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.35;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;endFill=0;shadow=0;strokeWidth=2;sketch=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"kLYEyaSkJVZjgnE96DWi-3\" target=\"dX68U42Ih_FQ-UEPESkK-6\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"497\" y=\"377\" />\n                <mxPoint x=\"502\" y=\"377\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"kLYEyaSkJVZjgnE96DWi-3\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;aspect=fixed;rounded=0;fontSize=12;align=center;fillColor=#0;fontFamily=Helvetica;verticalAlign=middle;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"496\" y=\"293.5\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding pipeline simple\" id=\"kLYEyaSkJVZjgnE96DWi-4\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=-0.047;entryY=0.672;entryDx=0;entryDy=0;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"Haz_8EwUBin59iLyk_8Q-56\" target=\"kLYEyaSkJVZjgnE96DWi-3\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"473\" y=\"295\" />\n              </Array>\n              <mxPoint x=\"464.00000000000216\" y=\"294.99999999999994\" as=\"sourcePoint\" />\n              <mxPoint x=\"536.54\" y=\"294.55\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Control&#xa;Unit\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-85\">\n          <mxCell style=\"rounded=1;arcSize=8;fontSize=6;fontStyle=1;spacing=0;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"184\" y=\"99\" width=\"40\" height=\"98\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"exec-BranchBxx\" id=\"uWEq7-W_z53UrtFael4O-64\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"312\" y=\"132\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline simple forwarding\" id=\"8Br1XOdIxK8N6TFEatcO-5\">\n          <mxCell style=\"verticalLabelPosition=bottom;shadow=0;dashed=0;align=center;html=1;verticalAlign=top;shape=mxgraph.electrical.logic_gates.logic_gate;operation=or;fontColor=#3333FF;strokeColor=#3333FF;strokeWidth=1;rotation=90;direction=north;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"491.83000000000004\" y=\"141.55\" width=\"15\" height=\"12.91\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"jkDunDTEjqUDAv3C6Bv7-9\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.25;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#3333FF;sketch=0;shadow=0;endFill=0;strokeWidth=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"8Br1XOdIxK8N6TFEatcO-7\" target=\"jkDunDTEjqUDAv3C6Bv7-8\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"514\" y=\"148\" />\n                <mxPoint x=\"514\" y=\"158\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"q-6THzIC0rTZull3DPy9-9\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;fontColor=default;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#3333FF;sketch=0;shadow=0;endFill=0;strokeWidth=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"8Br1XOdIxK8N6TFEatcO-7\" target=\"8Br1XOdIxK8N6TFEatcO-5\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"q-6THzIC0rTZull3DPy9-10\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;fontColor=default;entryX=0.5;entryY=0.065;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#3333FF;sketch=0;shadow=0;endFill=0;strokeWidth=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"8Br1XOdIxK8N6TFEatcO-7\" target=\"dX68U42Ih_FQ-UEPESkK-6\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"520.7307692307692\" y=\"231.26923076923077\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"509\" y=\"149\" />\n                <mxPoint x=\"509\" y=\"369\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"8Br1XOdIxK8N6TFEatcO-7\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;aspect=fixed;rounded=0;fontSize=12;align=center;fillColor=#0000FF;fontFamily=Helvetica;verticalAlign=middle;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;strokeWidth=1;strokeColor=#0000FF;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"507\" y=\"147\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding pipeline\" id=\"jkDunDTEjqUDAv3C6Bv7-1\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.75;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#3333FF;sketch=0;shadow=0;endFill=0;strokeWidth=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"jkDunDTEjqUDAv3C6Bv7-0\" target=\"8Br1XOdIxK8N6TFEatcO-5\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"482\" y=\"151\" as=\"sourcePoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"481\" y=\"152\" />\n                <mxPoint x=\"487\" y=\"152\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-84\">\n          <mxCell style=\"shape=or;whiteSpace=wrap;rounded=1;fontFamily=sans-serif;fontSize=6;align=center;verticalAlign=middle;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;strokeColor=#3333FF;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"491\" y=\"160.75\" width=\"5.5\" height=\"10\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"jkDunDTEjqUDAv3C6Bv7-4\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#3333FF;sketch=0;shadow=0;endFill=0;strokeWidth=1;entryX=0;entryY=0.25;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"q-6THzIC0rTZull3DPy9-11\" target=\"Haz_8EwUBin59iLyk_8Q-84\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"498\" y=\"163.25\" as=\"targetPoint\" />\n              <Array as=\"points\">\n                <mxPoint x=\"487\" y=\"137\" />\n                <mxPoint x=\"487\" y=\"163\" />\n              </Array>\n              <mxPoint x=\"482\" y=\"137\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"jkDunDTEjqUDAv3C6Bv7-7\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#3333FF;sketch=0;shadow=0;endFill=0;strokeWidth=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;exitPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"jkDunDTEjqUDAv3C6Bv7-8\" target=\"Haz_8EwUBin59iLyk_8Q-84\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"503\" y=\"163.70469798657717\" as=\"targetPoint\" />\n              <mxPoint x=\"508\" y=\"165.75\" as=\"sourcePoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"q-6THzIC0rTZull3DPy9-7\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0.935;entryDx=0;entryDy=0;entryPerimeter=0;fontColor=default;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#3333FF;sketch=0;shadow=0;endFill=0;strokeWidth=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"jkDunDTEjqUDAv3C6Bv7-0\" target=\"nrxDrlzc4rpCp8ehld49-22\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"481\" y=\"153\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"q-6THzIC0rTZull3DPy9-19\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;fontColor=default;labelBackgroundColor=none;endArrow=none;fontSize=6;fontFamily=sans-serif;strokeColor=#3333FF;sketch=0;shadow=0;endFill=0;strokeWidth=1;entryX=0.5;entryY=0.935;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"jkDunDTEjqUDAv3C6Bv7-8\" target=\"dQApTKY7MUPNvbngPjsP-5\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"537\" y=\"162\" />\n                <mxPoint x=\"537\" y=\"77\" />\n                <mxPoint x=\"62\" y=\"77\" />\n              </Array>\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline simple forwarding\" id=\"jkDunDTEjqUDAv3C6Bv7-8\">\n          <mxCell style=\"verticalLabelPosition=bottom;shadow=0;dashed=0;align=center;html=1;verticalAlign=top;shape=mxgraph.electrical.logic_gates.logic_gate;operation=or;fontColor=#3333FF;strokeColor=#3333FF;strokeWidth=1;rotation=90;direction=north;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"514.88\" y=\"154.38\" width=\"15.75\" height=\"15\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\" PC+4 \" tags=\"simple forwarding pipeline\" id=\"q-6THzIC0rTZull3DPy9-0\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;horizontal=1;fontColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"216\" y=\"380\" width=\"21\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\" PC \" tags=\"simple forwarding pipeline\" id=\"q-6THzIC0rTZull3DPy9-2\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;horizontal=1;fontColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"214\" y=\"372\" width=\"24.5\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"q-6THzIC0rTZull3DPy9-4\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;aspect=fixed;rounded=0;fontSize=12;align=center;fillColor=#0;fontFamily=Helvetica;verticalAlign=middle;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"321\" y=\"429\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"q-6THzIC0rTZull3DPy9-5\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=none;jumpSize=4;orthogonalLoop=1;jettySize=auto;html=1;fontColor=#000000;endArrow=none;endFill=0;strokeColor=#000000;shadow=0;fontFamily=sans-serif;fontSize=6;strokeWidth=2;labelBackgroundColor=none;sketch=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.65;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"nrxDrlzc4rpCp8ehld49-22\" target=\"dQApTKY7MUPNvbngPjsP-5\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <Array as=\"points\">\n                <mxPoint x=\"491\" y=\"364\" />\n                <mxPoint x=\"491\" y=\"440\" />\n                <mxPoint x=\"32\" y=\"440\" />\n                <mxPoint x=\"32\" y=\"276\" />\n              </Array>\n              <mxPoint x=\"499.861111111111\" y=\"361.5\" as=\"sourcePoint\" />\n              <mxPoint x=\"321\" y=\"532\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00\" tags=\"simple pipeline forwarding\" component=\"reg-id-value\" source=\"decode-rd\" id=\"Haz_8EwUBin59iLyk_8Q-93\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;spacing=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"243\" y=\"394\" width=\"14\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"rd\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-95\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;spacingBottom=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"229\" y=\"395\" width=\"8\" height=\"5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00\" tags=\"pipeline forwarding\" component=\"reg-id-value\" source=\"exec-rd\" id=\"Haz_8EwUBin59iLyk_8Q-94\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;spacing=0;labelBackgroundColor=none;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"398\" y=\"393\" width=\"14\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding pipeline\" id=\"i2To6XwWsyNyerFe5G8K-2\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"598\" y=\"86\" width=\"42\" height=\"319\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"MEM/WB\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-132\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"i2To6XwWsyNyerFe5G8K-2\" vertex=\"1\">\n            <mxGeometry x=\"-10\" width=\"42\" height=\"6.247246022031823\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-170\">\n          <mxCell style=\"group;fillColor=#d5e8d4;strokeColor=#82b366;\" parent=\"i2To6XwWsyNyerFe5G8K-2\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"9\" y=\"7.80905752753978\" width=\"6\" height=\"311.1909424724602\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-171\">\n          <mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fontFamily=sans-serif;fontSize=6;fontColor=#0000CC;align=left;\" parent=\"Haz_8EwUBin59iLyk_8Q-170\" vertex=\"1\">\n            <mxGeometry width=\"6\" height=\"311.1909424724602\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-172\">\n          <mxCell style=\"triangle;whiteSpace=wrap;html=1;rounded=0;fontFamily=sans-serif;fontSize=6;fontColor=#0000CC;align=left;direction=south;\" parent=\"Haz_8EwUBin59iLyk_8Q-170\" vertex=\"1\">\n            <mxGeometry x=\"0.5\" width=\"5\" height=\"3.514075887392901\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline simple forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-173\">\n          <mxCell style=\"group;fillColor=#d5e8d4;strokeColor=#82b366;\" parent=\"Haz_8EwUBin59iLyk_8Q-170\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry width=\"6\" height=\"311.1909424724602\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-174\">\n          <mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fontFamily=sans-serif;fontSize=6;align=left;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-173\" vertex=\"1\">\n            <mxGeometry width=\"6\" height=\"311.1909424724602\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-175\">\n          <mxCell style=\"triangle;whiteSpace=wrap;html=1;rounded=0;fontFamily=sans-serif;fontSize=6;align=left;direction=south;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-173\" vertex=\"1\">\n            <mxGeometry x=\"0.5\" width=\"5\" height=\"3.514075887392901\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"simple pipeline forwarding\" component=\"reg-value\" source=\"mem-read-val\" id=\"Haz_8EwUBin59iLyk_8Q-111\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;direction=south;spacing=0;horizontal=0;\" parent=\"i2To6XwWsyNyerFe5G8K-2\" vertex=\"1\">\n            <mxGeometry x=\"-4\" y=\"194.56666666666666\" width=\"8\" height=\"32.86666666666667\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"q-6THzIC0rTZull3DPy9-11\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;aspect=fixed;rounded=0;fontSize=12;align=left;fillColor=#0000FF;fontFamily=Helvetica;verticalAlign=middle;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;html=1;strokeWidth=1;strokeColor=none;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"486\" y=\"135\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\" WriteData \" tags=\"pipeline simple forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-96\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"415\" y=\"340\" width=\"33\" height=\"7\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"xor\" tags=\"simple pipeline forwarding\" id=\"q-6THzIC0rTZull3DPy9-12\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"469\" y=\"157\" width=\"9\" height=\"4\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Branch&#xa;Jalx\" tags=\"simple pipeline forwarding\" id=\"q-6THzIC0rTZull3DPy9-17\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=center;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"498\" y=\"176.5\" width=\"21\" height=\"11.5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding pipeline simple\" id=\"q-6THzIC0rTZull3DPy9-22\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"444\" y=\"84\" width=\"42\" height=\"320\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"EX/MEM\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-131\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"q-6THzIC0rTZull3DPy9-22\" vertex=\"1\">\n            <mxGeometry width=\"42\" height=\"6.251526251526251\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-164\">\n          <mxCell style=\"group\" parent=\"q-6THzIC0rTZull3DPy9-22\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"12\" y=\"8.595848595848596\" width=\"6\" height=\"311.4041514041514\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-165\">\n          <mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fontFamily=sans-serif;fontSize=6;fontColor=#0000CC;align=left;\" parent=\"Haz_8EwUBin59iLyk_8Q-164\" vertex=\"1\">\n            <mxGeometry width=\"6\" height=\"311.4041514041514\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-166\">\n          <mxCell style=\"triangle;whiteSpace=wrap;html=1;rounded=0;fontFamily=sans-serif;fontSize=6;fontColor=#0000CC;align=left;direction=south;\" parent=\"Haz_8EwUBin59iLyk_8Q-164\" vertex=\"1\">\n            <mxGeometry x=\"0.5\" width=\"5\" height=\"3.5164835164835164\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline simple forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-167\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-164\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry width=\"6\" height=\"311.4041514041514\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-168\">\n          <mxCell style=\"rounded=0;whiteSpace=wrap;html=1;fontFamily=sans-serif;fontSize=6;align=left;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-167\" vertex=\"1\">\n            <mxGeometry width=\"6\" height=\"311.4041514041514\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-169\">\n          <mxCell style=\"triangle;whiteSpace=wrap;html=1;rounded=0;fontFamily=sans-serif;fontSize=6;align=left;direction=south;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-167\" vertex=\"1\">\n            <mxGeometry x=\"0.5\" width=\"5\" height=\"3.5164835164835164\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"pipeline forwarding simple\" component=\"reg-value\" source=\"alu-res\" id=\"Haz_8EwUBin59iLyk_8Q-105\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;direction=south;horizontal=0;spacing=0;\" parent=\"q-6THzIC0rTZull3DPy9-22\" vertex=\"1\">\n            <mxGeometry y=\"206.31378299120232\" width=\"7\" height=\"32.47859237536657\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" id=\"jkDunDTEjqUDAv3C6Bv7-0\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;aspect=fixed;rounded=0;fontSize=12;align=center;fillColor=#0000FF;fontFamily=Helvetica;verticalAlign=middle;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;strokeWidth=1;strokeColor=#0000FF;\" parent=\"q-6THzIC0rTZull3DPy9-22\" vertex=\"1\">\n            <mxGeometry x=\"36.06\" y=\"66.99548387096775\" width=\"1.8768328445747802\" height=\"1.8768328445747802\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"BranchOutcome\" tags=\"simple pipeline forwarding\" id=\"q-6THzIC0rTZull3DPy9-23\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"479\" y=\"71.25\" width=\"49\" height=\"11.5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"mem-BranchOutcome\" id=\"uWEq7-W_z53UrtFael4O-66\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"59\" y=\"254\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"q-6THzIC0rTZull3DPy9-24\">\n          <mxCell style=\"group\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\" connectable=\"0\">\n            <mxGeometry x=\"92\" y=\"250\" width=\"50\" height=\"78\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"simple pipeline forwarding\" link=\"#program_memory\" id=\"Haz_8EwUBin59iLyk_8Q-208\">\n          <mxCell style=\"rounded=1;arcSize=2;fontSize=6;fontStyle=1;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;fillColor=#EAFFE9;strokeColor=#000000;\" parent=\"q-6THzIC0rTZull3DPy9-24\" vertex=\"1\">\n            <mxGeometry y=\"10\" width=\"50\" height=\"68\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Program&#xa;Memory\" tags=\"simple pipeline forwarding\" link=\"#program_memory\" id=\"Haz_8EwUBin59iLyk_8Q-209\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=1;\" parent=\"q-6THzIC0rTZull3DPy9-24\" vertex=\"1\">\n            <mxGeometry y=\"55\" width=\"50\" height=\"18\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" component=\"program-cache\" tags=\"simple pipeline forwarding\" link=\"#cache_program\" id=\"uWEq7-W_z53UrtFael4O-36\">\n          <mxCell style=\"shape=stencil(rVTRbsMgDPwaHidRWPteZZv20o9gxDSolFTA1GRfP4LJOliXqVqlSInPOd8ZWxDe+E6cgDBqxREIfyKMSSG7iLAIdoisKYZnDDc5FP4EMiCm9AAtwj64/gBn3YZM17YDp8OU5c+EbuM/08ObNyEPe9e/27ZIzOmUcZMEowOWyspjEVW2KtPCSa8/cm8MMcJeKisR+cUNb1TvYMGm0sZgz1cqX2rYkGzE8/lys7nupuCE0SRSeifW6m+WEkdtxhjnj8TzwvoHHyehlgsEGAKOEXnNZR/yFHg5htV80kbvbd4hsAHcbTqvcUX4thBaFzpz+E3HgAq3qey098sy/PEOMrQQqPb2Lo1UCv/oIW3/jyVPKN4OCfgE);collapsible=0;dropTarget=0;autosize=0;container=0;metaEdit=0;fillColor=#d5e8d4;strokeColor=#000000;\" parent=\"q-6THzIC0rTZull3DPy9-24\" vertex=\"1\">\n            <mxGeometry width=\"50\" height=\"60\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\" PC+4 \" tags=\"simple forwarding pipeline\" id=\"q-6THzIC0rTZull3DPy9-25\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;horizontal=1;fontColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"54.5\" y=\"380\" width=\"19.5\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"rd\" tags=\"pipeline forwarding\" id=\"q-6THzIC0rTZull3DPy9-27\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;spacingBottom=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"383\" y=\"395\" width=\"8\" height=\"5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"rd\" tags=\"pipeline forwarding\" id=\"q-6THzIC0rTZull3DPy9-28\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;spacingBottom=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"546\" y=\"395\" width=\"8\" height=\"5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\" BranchTarget \" tags=\"simple forwarding pipeline\" id=\"q-6THzIC0rTZull3DPy9-30\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;horizontal=1;fontColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"47.5\" y=\"436\" width=\"38.5\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Instruction\" tags=\"simple forwarding pipeline\" id=\"q-6THzIC0rTZull3DPy9-31\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;horizontal=1;fontColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"161\" y=\"219\" width=\"34\" height=\"7\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\" RegWriteData \" tags=\"simple forwarding pipeline\" id=\"q-6THzIC0rTZull3DPy9-34\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;horizontal=1;fontColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"201\" y=\"426\" width=\"39\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\" PC+4 \" tags=\"simple forwarding pipeline\" id=\"q-6THzIC0rTZull3DPy9-36\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;horizontal=1;fontColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"392\" y=\"380\" width=\"21\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\" PC \" tags=\"simple forwarding pipeline\" id=\"q-6THzIC0rTZull3DPy9-37\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;horizontal=1;fontColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"390\" y=\"372\" width=\"24.5\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"AluMul\" tags=\"simple pipeline forwarding\" id=\"q-6THzIC0rTZull3DPy9-40\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"235\" y=\"174\" width=\"22\" height=\"4\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"decode-AluMul\" id=\"q-6THzIC0rTZull3DPy9-41\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"287.25\" y=\"173\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"exec-AluMul\" id=\"q-6THzIC0rTZull3DPy9-42\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"421\" y=\"245\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"mem-BranchOutcome\" id=\"q-6THzIC0rTZull3DPy9-44\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"533\" y=\"149\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\" AluOut\" tags=\"simple forwarding pipeline\" id=\"q-6THzIC0rTZull3DPy9-45\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;horizontal=1;fontColor=#000000;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"482.83000000000004\" y=\"284\" width=\"23.17\" height=\"6\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding simple pipeline\" id=\"q-6THzIC0rTZull3DPy9-47\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;rounded=0;fillColor=#0;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"357\" y=\"375\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding simple\" component=\"bool-value\" source=\"mem-MemRead\" id=\"q-6THzIC0rTZull3DPy9-50\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"576\" y=\"216\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"MemRead\" tags=\"simple pipeline forwarding\" id=\"q-6THzIC0rTZull3DPy9-51\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"235\" y=\"124\" width=\"31\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"decode-MemRead\" id=\"q-6THzIC0rTZull3DPy9-52\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=0;recursiveResize=0;editable=0;expand=0;resizable=0;rotatable=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"287.25\" y=\"124\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"pipeline forwarding\" component=\"bool-value\" source=\"exec-MemRead\" id=\"q-6THzIC0rTZull3DPy9-53\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"322\" y=\"124\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"MemWrite\" tags=\"simple pipeline forwarding\" id=\"q-6THzIC0rTZull3DPy9-54\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"545\" y=\"116\" width=\"28\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"MemRead\" tags=\"simple pipeline forwarding\" id=\"q-6THzIC0rTZull3DPy9-55\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=left;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;horizontal=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"545\" y=\"124\" width=\"28\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"mem-BranchJalr\" id=\"1xC1WIjRaXcGuhJV2cBO-9\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"478\" y=\"329\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0\" tags=\"simple pipeline forwarding\" component=\"bool-value\" source=\"mem-BranchJalx\" id=\"1xC1WIjRaXcGuhJV2cBO-10\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;align=center;spacing=0;comic=0;allowArrows=0;connectable=1;recursiveResize=0;editable=1;expand=0;resizable=1;rotatable=1;movable=1;deletable=1;spacingLeft=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"504\" y=\"355\" width=\"7\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"Branch&#xa;Jalr\" tags=\"simple pipeline forwarding\" id=\"1xC1WIjRaXcGuhJV2cBO-11\">\n          <mxCell style=\"text;strokeColor=none;fillColor=default;align=center;verticalAlign=middle;rounded=0;fontFamily=sans-serif;fontSize=6;fontStyle=1;spacing=0;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;fontColor=#0000FA;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"473\" y=\"176.5\" width=\"21\" height=\"11.5\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"0x00000000\" tags=\"simple pipeline forwarding\" component=\"pc-value\" source=\"fetch-pc\" link=\"#focus_pc\" id=\"Haz_8EwUBin59iLyk_8Q-39\">\n          <mxCell style=\"rounded=1;gradientColor=none;perimeterSpacing=0;arcSize=1;fontSize=8;fontStyle=0;verticalAlign=middle;spacing=0;fontFamily=sans-serif;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingTop=6;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;connectable=1;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"70\" y=\"225\" width=\"72\" height=\"25\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"PC\" tags=\"simple pipeline forwarding\" id=\"Haz_8EwUBin59iLyk_8Q-206\">\n          <mxCell style=\"text;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=6;fontFamily=sans-serif;spacing=0;fontStyle=1;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;spacingBottom=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"99\" y=\"227\" width=\"13.999999999999998\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"00000000\" tags=\"pipeline forwarding\" component=\"reg-value\" source=\"exec-imm\" id=\"qpQ6hB7pEMShyfFWmkmH-1\">\n          <mxCell style=\"rounded=1;fontFamily=sans-serif;fontSize=6;arcSize=1;spacing=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"388\" y=\"351.5\" width=\"34\" height=\"8\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <mxCell id=\"eH55jN6bCJ1mTE-G3Nd7-0\" value=\"\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;sketch=0;jumpStyle=none;orthogonalLoop=1;jettySize=auto;shadow=0;endArrow=none;endFill=0;strokeColor=#000000;strokeWidth=2;fontSize=6;fontFamily=sans-serif;verticalAlign=middle;align=center;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=none;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"hCaf9_x39W0CY-DPdxUN-8\" target=\"qpQ6hB7pEMShyfFWmkmH-1\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"413\" y=\"357\" as=\"targetPoint\" />\n            <mxPoint x=\"375\" y=\"356\" as=\"sourcePoint\" />\n            <Array as=\"points\" />\n          </mxGeometry>\n        </mxCell>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"q-6THzIC0rTZull3DPy9-3\">\n          <mxCell style=\"ellipse;whiteSpace=wrap;aspect=fixed;rounded=0;fontSize=12;align=center;fillColor=#0;fontFamily=Helvetica;verticalAlign=middle;labelPosition=center;verticalLabelPosition=middle;labelBackgroundColor=default;html=1;strokeWidth=2;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" vertex=\"1\">\n            <mxGeometry x=\"524\" y=\"379.5\" width=\"2\" height=\"2\" as=\"geometry\" />\n          </mxCell>\n        </UserObject>\n        <UserObject label=\"\" tags=\"forwarding\" id=\"eH55jN6bCJ1mTE-G3Nd7-11\">\n          <mxCell style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=arc;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.35;entryDx=0;entryDy=0;entryPerimeter=0;shadow=0;fontFamily=sans-serif;fontSize=6;strokeWidth=2;labelBackgroundColor=none;endFill=0;endArrow=none;sketch=0;\" parent=\"Haz_8EwUBin59iLyk_8Q-26\" source=\"dX68U42Ih_FQ-UEPESkK-6\" target=\"q-6THzIC0rTZull3DPy9-3\" edge=\"1\">\n            <mxGeometry relative=\"1\" as=\"geometry\">\n              <mxPoint x=\"512.2500000000001\" y=\"380.5\" as=\"sourcePoint\" />\n              <mxPoint x=\"635.6388888888888\" y=\"380.75\" as=\"targetPoint\" />\n            </mxGeometry>\n          </mxCell>\n        </UserObject>\n      </root>\n    </mxGraphModel>\n  </diagram>\n</mxfile>\n"
  },
  {
    "path": "extras/crosscompiling/shell-mips-elf.nix",
    "content": "# Provides shell with a crosscompiler for code to simulate\nwith import <nixpkgs> {\n  crossSystem = {\n    config = \"mips-elf\";\n  };\n};\n\nmkShell {\n  buildInputs = [ ]; # your dependencies here\n}"
  },
  {
    "path": "extras/crosscompiling/shell-riscv-elf-with-newlib.nix",
    "content": "# Provides shell with a crosscompiler for code to simulate\nwith import <nixpkgs> {\n  crossSystem = {\n    config = \"riscv32-none-elf\";\n    libc = \"newlib\";\n    gcc.arch = \"rv32g\";\n  };\n};\n\nmkShell {\n  buildInputs = [ ]; # your dependencies here\n}\n"
  },
  {
    "path": "extras/crosscompiling/shell-riscv-elf.nix",
    "content": "# Provides shell with a crosscompiler for code to simulate\nwith import <nixpkgs> {};\npkgsCross.riscv64-embedded.mkShell {}\n"
  },
  {
    "path": "extras/packaging/_tools/add-to-changelog.sh",
    "content": "#!/usr/bin/env bash\n\nCHANGELOG_FILE=\"extras/packaging/deb/debian/changelog\"\n\nif [ $# -lt 1 ]; then\n  echo \"Version has to be specified\"\n  exit 1\nfi\n\nV_TXT=\"$1\"\n\ncd \"$(dirname \"$0\")/../..\"\n\nV_DATE_MDY=\"$(date '+%m/%d/%Y')\"\nV_DATE_YMD=\"$(date '+%Y-%m-%d')\"\nV_DATE_RFC=\"$(date -R)\"\n\nV_USER_NAME=\"$(git config user.name)\"\nV_USER_EMAIL=\"$(git config user.email)\"\n\nif grep -q \"qtrvsim ($V_TXT)\" $CHANGELOG_FILE; then\n  sed --in-place \\\n    -e '1,/^ -- .*$/s/^ -- .*$/'\" -- $V_USER_NAME <$V_USER_EMAIL>  $V_DATE_RFC/\" \\\n    $CHANGELOG_FILE\nelse\n  cat >$CHANGELOG_FILE.tmp <<EOF\nqtrvsim ($V_TXT) unstable; urgency=medium\n\n  * Debian package updated to version $V_TXT.\n\n -- $V_USER_NAME <$V_USER_EMAIL>  $V_DATE_RFC\n\nEOF\n  cat $CHANGELOG_FILE >> $CHANGELOG_FILE.tmp\n  mv $CHANGELOG_FILE.tmp $CHANGELOG_FILE\nfi\n\n$EDITOR $CHANGELOG_FILE\n\necho Press enter to continue\nread x\n\ngit add $CHANGELOG_FILE\n\necho >.git/GITGUI_MSG \"Version updated to $V_TXT\"\n\ngit gui\n\ngit tag -d v$V_TXT\n\ngit tag -s v$V_TXT\n\n# TODO\n#if [ -x /usr/lib/obs-build/changelog2spec ]; then\n#  /usr/lib/obs-build/changelog2spec debian/changelog >../qtrvsim.changes\n#elif [ -x /usr/lib/build/changelog2spec ]; then\n#  /usr/lib/build/changelog2spec debian/changelog >../qtrvsim.changes\n#fi\n"
  },
  {
    "path": "extras/packaging/_tools/git-archive-submodules.sh",
    "content": "#!/usr/bin/env bash\n\nset -eu\n\n# Tool to crate source archive including submodules\n# based on (MIT): https://github.com/nzanepro/git-archive-submodules\n\n# Parameters: project-root destination file_name module-prefix version [git-command] [tar-command] [xz-command]\n\nexport TMPDIR=`realpath ${2}`/pack/\n\nexport SOURCE=`realpath ${1}`\nexport DESTINATION=`realpath ${2}/${3}`\nexport TARMODULE=${4}\nexport TARVERSION=${5}\nexport TARPREFIX=\"${TARMODULE}-${TARVERSION}\"\nexport GIT=${6:-\"git\"}\nexport TAR=${7:-\"tar\"}\nexport XZ=${8:-\"xz\"}\n\nmkdir -p ${TMPDIR}\n\npushd ${SOURCE} || exit\n\n# create module archive\n${GIT} archive --prefix=${TARPREFIX}/ -o ${TMPDIR}${TARPREFIX}.tar HEAD\nif [[ ! -f \"${TMPDIR}${TARPREFIX}.tar\" ]]; then\n  echo \"ERROR: base sourcecode archive was not created. check git output in log above.\"\n  exit 1\nfi\n\n# Makefile is only helper for development and should be excluded in release.\n# The command is allowed to fail if Makefile is not found.\n${TAR} -f \"${TMPDIR}${TARPREFIX}.tar\" --delete ${TARPREFIX}/Makefile 2>/dev/null || true\n\n# force init submodules\n${GIT} submodule update --init --recursive\n\n# tar each submodule recursively\n${GIT} submodule foreach --recursive 'git archive --prefix=${TARPREFIX}/${displaypath}/ HEAD > ${TMPDIR}tmp.tar &&\n${TAR} --concatenate --file=${TMPDIR}${TARPREFIX}.tar ${TMPDIR}tmp.tar'\n\npopd || exit\n\n# compress tar file\n${XZ} ${TMPDIR}${TARPREFIX}.tar\nif [[ ! -f \"${TMPDIR}${TARPREFIX}.tar.xz\" ]]; then\n  echo \"ERROR: xzipped archive was not created. check git output in log above.\"\n  exit 1\nfi\n\n\ncp ${TMPDIR}${TARPREFIX}.tar.xz ${DESTINATION}\nif [[ -f \"${TMPDIR}${TARPREFIX}.tar.xz\" ]]; then\n  rm ${TMPDIR}${TARPREFIX}.tar.xz\n  echo \"created ${DESTINATION}\"\nelse\n  echo \"ERROR copying ${TMPDIR}${TARPREFIX}.tar.xz to ${DESTINATION}\"\n  usage\n  exit 1\nfi\n\n# cleanup\nrm -r ${TMPDIR}\n"
  },
  {
    "path": "extras/packaging/add-to-changelog.sh",
    "content": "#!/usr/bin/env bash\n\nCHANGELOG_FILE=\"extras/packaging/deb/debian/changelog\"\nMAIN_PROJECT_NAME_LOWER=\"qtrvsim\"\n\nif false ; then\n  CMAKELISTS_DIR=\"$( old_pwd=\"\" ;  while [ ! -e CMakeLists.txt ] ; do if [ \"$old_pwd\" = `pwd`  ] ; then exit 1 ; else old_pwd=\"$(pwd)\" ; cd -L .. 2>/dev/null ; fi ; done ; pwd )\"\n  if [ $? -ne 0 ] ; then\n    echo 'Can not found top level project directory with \"CMakeLists.txt\" file'\n    exit 1\n  fi\n  cd \"$CMAKELISTS_DIR\"\nelse\n  cd \"$(dirname \"$0\")/../..\"\n  CMAKELISTS_DIR=\"$(pwd)\"\nfi\n\nif [ $# -ge 1 ]; then\n  V_TXT=\"$1\"\nelse\n  V_TXT=\"$(sed -n '/^[ \\t]*project *(/{s///; :1; /) *$/!{N; b1;}; s///; s/^.*VERSION[\\t ]\\+\\([^ ]*\\)\\b.*$/\\1/p};' \"$CMAKELISTS_DIR/CMakeLists.txt\")\"\nfi\n\nif [ -z \"$V_TXT\" ] ; then\n  echo 'Version is not specified as argument neither determined from \"CMakeLists.txt\" file'\n  exit 1\nfi\n\nV_TXT_W_SUF=\"$( echo \"$V_TXT\" | sed -n 's/\\(.*-.*\\)/\\1/p')\"\nif [ -z \"$V_TXT_W_SUF\" ] ; then\n  V_TXT_W_SUF=\"$V_TXT-1\"\nfi\n\nV_DATE_MDY=\"$(date '+%m/%d/%Y')\"\nV_DATE_YMD=\"$(date '+%Y-%m-%d')\"\nV_DATE_RFC=\"$(date -R)\"\n\nV_USER_NAME=\"$(git config user.name)\"\nV_USER_EMAIL=\"$(git config user.email)\"\n\nif grep -q \"$MAIN_PROJECT_NAME_LOWER ($V_TXT_W_SUF)\" $CHANGELOG_FILE; then\n  sed --in-place \\\n    -e '1,/^ -- .*$/s/^ -- .*$/'\" -- $V_USER_NAME <$V_USER_EMAIL>  $V_DATE_RFC/\" \\\n    $CHANGELOG_FILE\nelse\n  cat >$CHANGELOG_FILE.tmp <<EOF\n$MAIN_PROJECT_NAME_LOWER ($V_TXT_W_SUF) unstable; urgency=medium\n\n  * Debian package updated to version $V_TXT.\n\n -- $V_USER_NAME <$V_USER_EMAIL>  $V_DATE_RFC\n\nEOF\n  cat $CHANGELOG_FILE >> $CHANGELOG_FILE.tmp\n  mv $CHANGELOG_FILE.tmp $CHANGELOG_FILE\nfi\n\nif [ -z \"$EDITOR\" ] ; then\n  EDITOR=nano\nfi\n$EDITOR $CHANGELOG_FILE\n\necho Press enter to continue\nread x\n\ngit add $CHANGELOG_FILE\n\necho >.git/GITGUI_MSG \"Version updated to $V_TXT\"\n\ngit gui\n\ngit tag -d v$V_TXT\n\ngit tag -s v$V_TXT\n\n# TODO\n#if [ -x /usr/lib/obs-build/changelog2spec ]; then\n#  /usr/lib/obs-build/changelog2spec debian/changelog >../$MAIN_PROJECT_NAME_LOWER.changes\n#elif [ -x /usr/lib/build/changelog2spec ]; then\n#  /usr/lib/build/changelog2spec debian/changelog >../$MAIN_PROJECT_NAME_LOWER.changes\n#fi\n"
  },
  {
    "path": "extras/packaging/appimage/appimage.yml.in",
    "content": "app: @PACKAGE_NAME@\n\nbuild:\n  packages:\n    - linuxdeployqt\n    - pkgconfig(Qt5Core)\n    - pkgconfig(Qt5Widgets)\n    - pkgconfig(Qt5Test)\n    - pkgconfig(Qt5PrintSupport)\n    - libelf-devel\n\nscript:\n  - cd $BUILD_SOURCE_DIR\n  - tar xf @PACKAGE_SOURCE_ARCHIVE_FILE@\n  - cd @PACKAGE_TOPLEVEL_DIR@\n  - mkdir build\n  - cd build\n  - export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc)\n  - cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr\n  - cmake --build .\n  - DESTDIR=\"$BUILD_APPDIR\" cmake --install .\n  - unset QTDIR; unset QT_PLUGIN_PATH; unset LD_LIBRARY_PATH\n  - linuxdeployqt $BUILD_APPDIR/usr/share/applications/*.desktop -bundle-non-qt-libs -verbose=2 -no-strip"
  },
  {
    "path": "extras/packaging/arch/PKGBUILD.in",
    "content": "# Maintainer: @PACKAGE_MAINTAINER@\npkgname=@PACKAGE_NAME@\npkgver=@PROJECT_VERSION@\npkgrel=1\npkgdesc=\"@PACKAGE_DESCRIPTION@\"\narch=(\"any\")\nurl=\"@PACKAGE_URL@\"\nlicense=('@PACKAGE_LICENCE@')\ndepends=(\"qt5-base\")\nmakedepends=(\"cmake\" \"elfutils\")\nsource=(\"@PACKAGE_SOURCE_ARCHIVE_FILE@\")\nmd5sums=(\"@FILE_MD5@\")\n\nprepare() {\n    export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc)\n\tcd \"$srcdir/@PACKAGE_TOPLEVEL_DIR@\"\n\tmkdir build\n\tcd build\n\tcmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr\n}\n\nbuild() {\n\tcd \"$srcdir/@PACKAGE_TOPLEVEL_DIR@/build\"\n    cmake --build .\n}\n\npackage() {\n\tcd \"$srcdir/@PACKAGE_TOPLEVEL_DIR@/build\"\n\tDESTDIR=\"$pkgdir\" cmake --install .\n}"
  },
  {
    "path": "extras/packaging/deb/debian/changelog",
    "content": "qtrvsim (0.9.8-1) unstable; urgency=medium\n\n  * Machine: aclintmtimer fix count type\n  * GUI: fix a crash on nonexistent include\n  * Use win32 config of libelf when compiling natively for Windows\n  * CI: Add Windows Clang debug build and macos ARM\n  * CLI: reporter dump to json\n  * Machine: instruction parsing refactor\n  * GUI: make printer settings persistent and scale to fit PDF page size\n  * Assembler: fix immediate parsing\n  * Assembler: implement GAS modifiers - PC rel still basic only\n  * Machine: fix zext.w/h inst parse and fix tokenized for inst.xxx\n  * Machine: fix parse_csr_address and CSR::RegisterMapByName key type\n  * Machine and GUI: Pseudo LRU cache policy\n  * Add 25x speed for teaching convenience\n  * Machien and GUI: Include Jiri Stefan's work on branch predictor\n  * Machien and GUI: BTB, BHT and BHR are implemented\n  * Project: Explicit cmake qt major version option\n  * Packaging: add Keywords entry into desktop file\n  * Machine: add peripherals high/top address aliases for XLEN=64\n  * GUI: switch \"New\" dialog page selection to tree widget, polishing required\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Tue, 01 Oct 2024 20:45:22 +0200\n\nqtrvsim (0.9.7-1) unstable; urgency=medium\n\n  * GUI: fix examples button crashes on Windows and sometimes other platforms\n  * GUI: fix crash when no tab is selected\n  * CI: add windows libs to artifacts and use more cores available\n  * CI: Make pack QT with Win artifact\n  * Project: Use the include directory for LibElf on macOS, Homebrew misses RISC-V\n  * Machine: set SXL and UXL to 64-bit XLEN for RV64\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Thu, 15 Feb 2024 20:57:12 +0100\n\nqtrvsim (0.9.6-1) unstable; urgency=medium\n\n  * GUI: add reset widows menu entry to restore default windows layout\n  * Machine: extend CSR support to pass rv32mi-p-mcsr and rv64mi-p-mcsr official test\n  * Machine: serial port interrupts reworked for RISC-V as platform irq 16 and 17\n  * GUI: RISC-V ACLINT MTIMER mapping added into resources/samples/template.S\n  * Machine: implemented RISC-V A extension for RV32IMA/RV64IMA support\n  * GUI: the XLEN, atomic and multiply options available in new simulation dialog\n  * GUI: update registers and CSR views for bare RV64IMA support\n  * Machine and GUI: simple level 2 cache implementation\n  * GUI: increase cache set count limit to 1024\n  * CLI: add isa-variant, cycle-limit and l2-cache options\n  * CLI: dump-ranges allows to use symbols even from internal assembly\n  * Memory: correctly propagate external/DMA changes to GUI\n  * Machine: where possible, re-implement pseudo instructions by aliase tables\n  * os_emulation: resolve problem with write and read from/to stack area on RV32\n  * GUI: fix double free of children widgets in control register widget\n  * GUI: refactor gui source file to tree structure\n  * GUI: program view - collapse address and breakpoint if space is limited\n  * GUI: split central widget tabs to coreview and editor\n  * GUI: editor line numbers and highlight error in the editor on message click\n  * GUI: editor toggle comment (ctrl+/)\n  * GUI: ensure that all lines of external make process output are processed\n  * os_emulation: correct ftruncate syscall arguments for 64 and 32-bit ABI\n  * Update README.md to document interrupt, trap, ACLINT+MTIMER and AMO support\n  * CI: drop support for Ubuntu 18\n  * Project: bump to c++17\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Mon, 11 Dec 2023 11:12:15 +0100\n\nqtrvsim (0.9.5-1) unstable; urgency=medium\n\n  * Machine: use cvector in instruction args to spedup decoding\n  * Machine: move controlstate to csr and prepare BitArg to be usd there\n  * Machine: use method for CSR writes to enable mutual register dependencies\n  * Machine: CSR: define mie register.\n  * Machine: CSR: fix conditions for register write and add mie to the list.\n  * Machine: fix range for branch instructions B-encoding\n  * CLI: add simple tracer for memory reads and writes.\n  * CLI: initial support to enable OS emulation even for CLI version\n  * GUI: update coreview graphics by Michal Stepanosvky\n  * GUI: fix wrong svg label connection and reset all to zero\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Mon, 16 Jan 2023 11:22:08 +0200\n\nqtrvsim (0.9.4-1) unstable; urgency=medium\n\n  * GUI: Async modal library to overcome WebAssembly/Emscripten limitations\n  * Wasm: support and build improved\n  * os_emulation: correct open flags O_xxx values to match RISC-V Linux ABI.\n  * packaging: fix Fedora build according to Jan Grulich advice.\n  * README.md: add reference to Embedded World Conference 2022 article.\n  * qtrvsim_tester: Tomas Veznik implemented testing against official RISC/V ISA tests.\n  * CI: speedup by using common build of official tests\n  * Machine: initial support for CSR instructions by Jakub Dupak\n  * GUI: CSR: syntax highlight CSR reg names\n  * Machine: CSR: disassemble CSR register based on the mnemonic register settings\n  * GUI: save mnemonic registers settings\n  * Machine: add support for 64-bit RV64IM target and related 32-bit/word limited instructions\n  * README.md: update information about basic 64-bit support.\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Mon, 24 Oct 2022 23:07:19 +0200\n\nqtrvsim (0.9.3-1) unstable; urgency=medium\n\n  * Debian package updated to version 0.9.3.\n  * Machine: fix LCD display endianness.\n  * Machine: correct memory stall cycles computation.\n  * Machine: correct unaligned and partial (lb, sb, lh, sh) to peripheral registers.\n  * Packaging: flatpak support kindly provided by David Heidelberg <david@ixit.cz>\n  * Machine and GUI: switch to RISC-V CSR names and remove references to MIPS COP0.\n  * Machine: correct parsing of registers s10 and s11 names.\n  * Machine: fix null pointer usage in cache\n  * GUI: fix null pointer usage in cache\n  * Machine: correct cache graphics visualization for byte accesses.\n  * Machine: LFU cache policy incorrect use of sets count instead of degree of associativity.\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Thu, 21 Apr 2022 09:40:37 +0200\n\nqtrvsim (0.9.2-1) unstable; urgency=medium\n\n  * Debian package updated to version 0.9.2.\n  * GUI: rework of coreview graphics to correspond to Mr. Stepanovsky slides\n  * CI: downgrade runner os to win2019 to prevent failing builds\n  * WASM: fix exception support\n  * LRU cache policy fix (check was incoorect only for flush, i.e. fence instruction)\n  * Machine: basic RV32M support (no specific GUI provided, considered as part of ALU)\n  * README.md update, document RV32M support\n  * Machine: RISC-V ABI places stack pointer into x2 register.\n  * Machine: rewrite all core_alu_forward and complex memory tests for RISC-V.\n  * Machine: RISC-V is by default little endian, even when ELF file is not loaded\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Tue, 12 Mar 2022 20:45:12 +0200\n\nqtrvsim (0.9.1-1) unstable; urgency=medium\n\n  * Debian package updated to version 0.9.1.\n  * Recognize FENCE and FENCE.I instructions, they flush whole cache for now.\n  * Corrected register numbers visualization.\n  * Corrected negative numbers in disassembler.\n  * Bugfixes and cleanup.\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Tue, 1 Mar 2022 19:15:22 +0200\n\nqtrvsim (0.9.0-1) unstable; urgency=medium\n\n  * Debian package updated to version 0.9.0.\n  * JALR.\n  * Basic pseudoinstruction support.\n  * Partial recreation of testing.\n  * Bugfixes and cleanup.\n\n -- Jakub Dupak <dev@jakubdupak.com>  Tue, 15 Feb 2022 03:32:50 +0200\n\nqtrvsim (0.8.1-1) unstable; urgency=medium\n\n  * Debian package updated to version 0.8.1-1.\n  * Fixes visualization of stalls.\n  * Moves jump evaluation from EX to MEM as initially intended and as shown in the cpu diagram.\n  * Fixes WASM build - keyboard handling.\n  * Adds Qt6 support.\n\n -- Jakub Dupak <dev@jakubdupak.com>  Wed, 15 Dec 2021 18:55:55 +0100\n\nqtrvsim (0.8.0) unstable; urgency=medium\n\n  * Debian package updated to version 0.8.0.\n\n -- Jakub Dupak <dev@jakubdupak.com>  Mon, 17 May 2021 15:27:10 +0200\n\nqtrvsim (0.8.0-1) unstable; urgency=medium\n\n  * Debian package updated to version 0.8.0.\n  * Switch to RISC-V simulator\n -- Jakub Dupak <dev@jakubdupak.com>  Tue, 04 May 2021 23:01:01 +0200\n\nqtmips (0.7.5) unstable; urgency=medium\n\n  * Debian package updated to version 0.7.5.\n  * Improve UI compatbility with dark color schemes (by Michal Maly, MadCatX)\n  * prepare-release: document alternative Debian package preparation in quilt mode.\n  * Enable dock widows nesting to enable show cache + memory simultaneously (by Frantisek Vacek)\n  * AppImage build run (by Frantisek Vacek)\n  * qtmips_cli: add option to connect serial port input and output to file.\n  * samples: template-os.S template.S fixed SPILED_REG_BASE definition.\n  * README: add documentation how to modify comprocessor 0 regs (by Jan Kaisrlik)\n  * EADME.md: fix reference to uart rx interrupt mask (by Jan Kaisrlik)\n  * Fix open of ELF files using national alphabets in the path names on Windows.\n  * qtmips_gui: fix response to user decision to save modified file when make is invoked.\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Sun, 06 Sep 2020 13:16:34 +0200\n\nqtmips (0.7.4) unstable; urgency=medium\n\n  * Debian package updated to version 0.7.4.\n  * debian packaging: add git attribute to merge changelog.\n  * debian packaging: add watch file for GitHub QtMips repository.\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Sun, 15 Sep 2019 20:32:41 +0200\n\nqtmips (0.7.3) unstable; urgency=medium\n\n  * Debian package updated to version 0.7.3.\n  * Implemented SKIP/SPACE assembler directives.\n  * Add OpenHub statistic page link.\n  * Provide support for include directive in simple assembler.\n  * In include, use content from editor if file is already open.\n  * Add #pragma processing to integrated assembler and its usage to control windows.\n  * Use #pragma in examples to lower initial learning curve.\n  * samples: simple-lw-sw-ia.S: place data section to 0x2000 address.\n \n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Thu, 12 Sep 2019 11:47:46 +0200\n\nqtmips (0.7.2) unstable; urgency=medium\n\n  * Debian package updated to version 0.7.2.\n  * More changes to use dialog open instead of exec (Emscripten does not support exec).\n  * Updates to RPM packaging and spec file to better follow Suse rules.\n  * Move fixmatheval and assembly compiler to separate library independent on Qt GUI API.\n  * Implemented message window to report compilation errors.\n  * Store operator text description in the fixmatheval operators tree.\n  * Simple assembler moved to separate class which is independent on Qt GUI API.\n  * Do not open editor twice for same filename.\n  * Enable CLI version to use simple assembler.\n  * Update editor search algorithm to prefer current editor for unnamed buffers.\n  * Add config option to reset machine before internal assembler starts.\n  * Include C language syntax highlighter for alternative sources editing.\n  * Action to execute external make command and ask for unsaved sources.\n  * Ask to save modified files on exit.\n  * Ask for modified source close and handle unnamed sources close.\n  * Replace shortcuts to not hide Ctrl+C and some others.\n  * Implemented ASCII and ASCIZ operations.\n  * Include actions New source and Close source on toolbar.\n  * Make program and memory window visible when address requested from symbol dialog.\n  * Add embedded examples menu and resources.\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Wed, 21 Aug 2019 00:13:06 +0200\n\nqtmips (0.7.1) unstable; urgency=medium\n\n  * Debian package updated to version 0.7.1.\n  * Add option to parse and show symbolic registers names.\n  * Implemented simple integrated assembler - it is not recommended for advanced users.\n  * Updated instructions parsing to be usable for integrated assembler.\n  * Change instruction parsing to allow multiple words pseudo-operations.\n  * Implemented simple expressions and labels/symbols evaluation.\n  * Simple highlighter for assembly language added.\n  * Include simple text editor in QtMips emulator.\n  * Fix memory leakages which repeat during program operation.\n  * Externally caused address-space changes (i.e. from peripherals) update memory view.\n  * Provide option to hide coreview to speedup simulation.\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Wed, 03 Jul 2019 12:18:15 +0200\n\nqtmips (0.7.0) unstable; urgency=medium\n\n  * Debian package updated to version 0.7.0.\n  * Include simple LCD frame-buffer and display implementation.\n  * Simulate push of dial buttons by check box.\n  * Allow to create simulator without loaded executable.\n  * Printing/export to PDF file reduces print area/page to actual image size.\n  * Disable text elide for memory and program views (fix for MAC OS).\n  * Implement standard zoom handling by mouse wheel and keys.\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Fri, 28 Jun 2019 15:38:52 +0200\n\nqtmips (0.6.8) unstable; urgency=medium\n\n  * Debian package updated to version 0.6.8.\n  * Coreview multiplexers updated and added for branch compare forward.\n  * qtmips_gui: set application window icon.\n  * Set gray background to stalled instructions/idled stages.\n  * Setting background color dial\n  * qtmips_cli: start report in decimal mode.\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Mon, 01 Apr 2019 15:26:46 +0200\n\nqtmips (0.6.7) unstable; urgency=medium\n\n  * Debian package updated to version 0.6.7.\n  * Change single cycle core with delay slot to use separate fetch stage.\n  * Program listing and stages use color background.\n  * Correct write through spelling. Reported by Richard Susta.\n  * qtmips_cli can be used for cache statistic and result memory tests.\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Wed, 27 Mar 2019 00:38:24 +0100\n\nqtmips (0.6.6) unstable; urgency=medium\n\n  * Corrected row and column output in cache address fields.\n  * Highlight cache read and write acesses.\n  * Highlight registers and coprocessor 0 read and writes.\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Sun, 17 Mar 2019 20:51:55 +0100\n\nqtmips (0.6.5) unstable; urgency=medium\n\n  * Initial Debian packaging.\n\n -- Pavel Pisa <pisa@cmp.felk.cvut.cz>  Wed, 13 Mar 2019 19:38:33 +0100\n"
  },
  {
    "path": "extras/packaging/deb/debian/compat",
    "content": "9\n"
  },
  {
    "path": "extras/packaging/deb/debian/control.in",
    "content": "Source: @PACKAGE_NAME@\nSection: devel\nPriority: optional\nMaintainer: @PACKAGE_MAINTAINER@\nBuild-Depends: debhelper (>= 9), qtbase5-dev, libelf-dev, cmake\nStandards-Version: 3.9.8\nHomepage: @PACKAGE_URL@\nVcs-Git: @PACKAGE_GIT@\nVcs-Browser: @PACKAGE_URL@\n\nPackage: @PACKAGE_NAME@\nArchitecture: any\nDepends: ${shlibs:Depends}, ${misc:Depends}\nDescription: @PACKAGE_DESCRIPTION@\n @PACKAGE_LONG_DESCRIPTION@\n"
  },
  {
    "path": "extras/packaging/deb/debian/copyright",
    "content": "Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: qtrvsim\nSource: https://github.com/cvut/qtrvsim/\n\nFiles: *\nCopyright: 2017-2019 Karel Koci<cynerd@email.cz>\n           2019-2022 Pavel Pisa <pisa@cmp.felk.cvut.cz>\n           2020-2022 Jakub Dupak <dev@jakubdupak.com>\n           2020-2021 Max Hollmann <hollmmax@fel.cvut.cz>\nLicense: GPL-3.0+\n This program is free software: you can redistribute it and/or modify it under the terms of the\n GNU General Public License as published by the Free Software Foundation, either version 3 of the\n License, or (at your option) any later version.\n .\n This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n General Public License for more details.\n .\n You should have received a copy of the GNU General Public License along with this program. If not,\n see <https://www.gnu.org/licenses/>.\n\n"
  },
  {
    "path": "extras/packaging/deb/debian/docs",
    "content": "README.md\ndocs"
  },
  {
    "path": "extras/packaging/deb/debian/rules",
    "content": "#!/usr/bin/make -f\n# -*- makefile -*-\n\nexport DH_VERBOSE = 1\n\nexport DEB_BUILD_MAINT_OPTIONS=hardening=+all\nexport DEB_LDFLAGS_MAINT_APPEND=-Wl,--as-needed\nexport QT_SELECT := qt5\n\n#  --parallel\n\n%:\n\tdh $@ --buildsystem=cmake\n"
  },
  {
    "path": "extras/packaging/deb/debian/source/format",
    "content": "3.0 (quilt)\n"
  },
  {
    "path": "extras/packaging/deb/dsc.in",
    "content": "Format: 3.0 (quilt)\nSource: @PACKAGE_NAME@\nBinary: @PACKAGE_NAME@\nArchitecture: any\nVersion: @PACKAGE_VERSION@-@PACKAGE_RELEASE@\nMaintainer: @PACKAGE_MAINTAINER@\nHomepage: @PACKAGE_URL@\nStandards-Version: 3.9.8\nVcs-Browser: @PACKAGE_URL@\nVcs-Git: @PACKAGE_GIT@\nBuild-Depends: debhelper (>= 9), qtbase5-dev, libelf-dev, cmake\nPackage-List:\n @PACKAGE_NAME@ deb devel optional arch=any\nChecksums-Sha1:\n @DEBIAN_SHA1@ @DEBIAN_SIZE@ @DEBIAN_ARCHIVE_FILE@\n @FILE_SHA1@ @FILE_SIZE@ @PACKAGE_SOURCE_ARCHIVE_FILE@\nChecksums-Sha256:\n @DEBIAN_SHA256@ @DEBIAN_SIZE@ @DEBIAN_ARCHIVE_FILE@\n @FILE_SHA256@ @FILE_SIZE@ @PACKAGE_SOURCE_ARCHIVE_FILE@\nFiles:\n @DEBIAN_MD5@ @DEBIAN_SIZE@ @DEBIAN_ARCHIVE_FILE@\n @FILE_MD5@ @FILE_SIZE@ @PACKAGE_SOURCE_ARCHIVE_FILE@\n"
  },
  {
    "path": "extras/packaging/flatpak/cz.cvut.edu.comparch.qtrvsim.json",
    "content": "{\n    \"app-id\" : \"cz.cvut.edu.comparch.qtrvsim\",\n    \"runtime\" : \"org.kde.Platform\",\n    \"runtime-version\" : \"6.2\",\n    \"sdk\" : \"org.kde.Sdk\",\n    \"tags\" : [\n        \"nightly\"\n    ],\n    \"command\" : \"qtrvsim_gui\",\n    \"rename-desktop-file\" : \"qtrvsim.desktop\",\n    \"rename-icon\" : \"qtrvsim_gui\",\n    \"finish-args\" : [\n        \"--device=dri\",\n        \"--share=ipc\",\n        \"--socket=fallback-x11\",\n        \"--socket=wayland\"\n    ],\n    \"modules\" : [\n        {\n            \"name\" : \"qtrvsim\",\n            \"buildsystem\" : \"cmake-ninja\",\n            \"builddir\" : true,\n            \"config-opts\" : [\n                \"-DCMAKE_BUILD_TYPE=RelWithDebInfo\"\n            ],\n            \"sources\" : [\n                {\n                    \"type\" : \"git\",\n                    \"url\" : \"https://github.com/cvut/qtrvsim/\"\n                }\n            ]\n        }\n    ]\n}\n"
  },
  {
    "path": "extras/packaging/mingw/cmake-i686-w64-mingw32.conf",
    "content": "# cmake -DCMAKE_TOOLCHAIN_FILE=cmake-i686-w64-mingw32.conf -DCMAKE_BUILD_TYPE=Release ../qtrvsim\n\nset(CMAKE_SYSTEM_NAME Windows)\nset(CMAKE_SYSTEM_PROCESSOR x86)\n\nset(CMAKE_SYSROOT /usr/i686-w64-mingw32)\nset(CMAKE_STAGING_PREFIX /home/devel/stage)\n\nset(tools /usr)\nset(CMAKE_C_COMPILER ${tools}/bin/i686-w64-mingw32-gcc)\nset(CMAKE_CXX_COMPILER ${tools}/bin/i686-w64-mingw32-g++)\n\nset(Qt5_DIR /usr/i686-w64-mingw32/qt/lib/cmake/Qt5)\nset(QT_DIR /usr/i686-w64-mingw32/qt/lib/cmake/Qt5)\n\nset(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\nset(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)\nset(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)\nset(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)\n"
  },
  {
    "path": "extras/packaging/nix/qtrvsim.nix",
    "content": "{ lib, stdenv, cmake, wrapQtAppsHook, qtbase, qtsvg }:\nstdenv.mkDerivation {\n    name = \"QtRVSim\";\n    src = builtins.fetchGit ../../..;\n    nativeBuildInputs = [ cmake wrapQtAppsHook ];\n    buildInputs = [ qtbase qtsvg ];\n    meta = {\n        description = \"RISC-V CPU simulator for education purposes.\";\n        longDescription = ''\n          RISC-V CPU simulator for education purposes with pipeline and cache visualization.\n          Developed at FEE CTU for computer architecture classes.\n        '';\n        homepage = \"https://github.com/cvut/qtrvsim\";\n        license = lib.licenses.gpl3Plus;\n        maintainers = [ \"Jakub Dupak <dev@jakubdupak.com>\" ];\n    };\n}"
  },
  {
    "path": "extras/packaging/rpm/spec.in",
    "content": "#\n# spec file for package @PACKAGE_NAME@\n#\n@COPYRIGHT_HASH_COMMENT@\n#\n# This program is free software: you can redistribute it and/or modify it\n# under the terms of the GNU General Public License as published by the Free\n# Software Foundation, either version 3 of the License, or (at your option)\n# any later version.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n# more details.\n\n# You should have received a copy of the GNU General Public License along with\n# this program. If not, see <https://www.gnu.org/licenses/>\n#\n# Please submit bugfixes or comments via\n#   @PROJECT_HOME_PAGE@\n# issues tracker.\n#\n\n\nName:           @PACKAGE_NAME@\nVersion:        @PACKAGE_VERSION@\nRelease:        @PACKAGE_RELEASE@\nSummary:        @PACKAGE_DESCRIPTION@\nLicense:        @PACKAGE_LICENCE@\nGroup:          System/Emulators/Other\nURL:            @PACKAGE_URL@\nSource:         @PACKAGE_SOURCE_ARCHIVE_FILE@\nBuildRequires:  cmake\nBuildRequires:  gcc-c++\nBuildRequires:  hicolor-icon-theme\nBuildRequires:  pkgconfig\nBuildRequires:  pkgconfig(Qt5Core)\nBuildRequires:  pkgconfig(Qt5PrintSupport)\nBuildRequires:  pkgconfig(Qt5Test)\nBuildRequires:  pkgconfig(Qt5Widgets)\n%if ! 0%{?suse_version}\nBuildRequires:  desktop-file-utils\nBuildRequires:  pkgconfig(libelf)\n%endif\n\n%if 0%{?suse_version}\nBuildRequires:  libelf-devel\nBuildRequires:  update-desktop-files\n%endif\n\n%description\n@PACKAGE_LONG_DESCRIPTION@\n\n%prep\n%setup -q\n\n%build\n%if 0%{?suse_version}\n%cmake -DCMAKE_CXX_FLAGS=\"-Wno-error\" -DCMAKE_C_FLAGS=\"-Wno-error\"\n%else\n%cmake\n%endif\n%cmake_build\n\n%install\n%cmake_install\n\n#desktop icon\n%if 0%{?suse_version}\n%suse_update_desktop_file -r -i @PACKAGE_NAME@ 'System Emulator'\n%endif\n\n%if 0%{?fedora} || 0%{?rhel} || 0%{?centos}\ndesktop-file-validate %{buildroot}%{_datadir}/applications/@PACKAGE_NAME@.desktop\n%endif\n\n# TODO: this should be generated from CMake\n%files\n%{_bindir}/qtrvsim_gui\n%{_bindir}/qtrvsim_cli\n%{_datadir}/icons/hicolor/scalable/apps/qtrvsim_gui.svg\n%{_datadir}/icons/hicolor/48x48/apps/qtrvsim_gui.png\n%{_datadir}/applications/qtrvsim.desktop\n%{_datadir}/metainfo/cz.cvut.edu.comparch.qtrvsim.metainfo.xml\n\n%license LICENSE\n%doc README.md\n\n%changelog\n"
  },
  {
    "path": "qtlogging.ini",
    "content": "[Rules]\n*.debug=false"
  },
  {
    "path": "src/assembler/CMakeLists.txt",
    "content": "project(assembler\n        DESCRIPTION \"Simple assembler library to use in the UI.\")\n\nset(CMAKE_AUTOMOC ON)\n\nset(assembler_SOURCES\n        fixmatheval.cpp\n        simpleasm.cpp\n        )\nset(assembler_HEADERS\n        fixmatheval.h\n        messagetype.h\n        simpleasm.h\n        )\n\nadd_library(assembler STATIC\n        ${assembler_SOURCES}\n        ${assembler_HEADERS})\ntarget_link_libraries(assembler\n\t\tPRIVATE ${QtLib}::Core)\n"
  },
  {
    "path": "src/assembler/fixmatheval.cpp",
    "content": "#include \"fixmatheval.h\"\n\n#include \"common/math/bit_ops.h\"\n#include \"memory/address.h\"\n\n#include <climits>\n#include <utility>\n\nusing namespace fixmatheval;\n\n// Consumes part of string that is a valid symbol name, returns it and removes it from the input\n// string.\nQStringView tokenize_symbol(QStringView &expression) {\n    QStringView symbol = expression;\n    int i = 0;\n    for (QChar ch : expression) {\n        if (!(ch.isLetterOrNumber() || (ch == '_'))) { break; }\n        i++;\n    }\n    expression = expression.mid(i);\n    return symbol;\n}\n\nFmeSymbolDb::~FmeSymbolDb() = default;\n\nbool FmeSymbolDb::getValue(FmeValue &value, QString name) {\n    (void)value;\n    (void)name;\n    return false;\n}\n\nFmeNode::FmeNode(int priority) {\n    prio = priority;\n}\n\nFmeNode::~FmeNode() = default;\n\nint FmeNode::priority() const {\n    return prio;\n}\n\nbool FmeNode::insert(FmeNode *node) {\n    (void)node;\n    return false;\n}\n\nFmeNode *FmeNode::child() {\n    return nullptr;\n}\n\nFmeNode *FmeNode::find_last_child() {\n    FmeNode *current = this;\n    FmeNode *child = this->child();\n    while (child != nullptr) {\n        current = child;\n        child = child->child();\n    }\n    return current;\n}\n\nFmeNodeConstant::FmeNodeConstant(FmeValue value) : FmeNode(INT_MAX) {\n    this->value = value;\n}\n\nFmeNodeConstant::~FmeNodeConstant() = default;\n\nbool FmeNodeConstant::eval(\n    FmeValue &value,\n    FmeSymbolDb *symdb,\n    QString &error,\n    machine::Address inst_addr) {\n    std::ignore = symdb;\n    std::ignore = error;\n    std::ignore = inst_addr;\n    value = this->value;\n    return true;\n}\n\nQString FmeNodeConstant::dump() {\n    return QString::number(value);\n}\n\nFmeNodeSymbol::FmeNodeSymbol(QString &name) : FmeNode(INT_MAX) {\n    this->name = name;\n}\n\nFmeNodeSymbol::~FmeNodeSymbol() = default;\n\nbool FmeNodeSymbol::eval(\n    FmeValue &value,\n    FmeSymbolDb *symdb,\n    QString &error,\n    machine::Address inst_addr) {\n    std::ignore = inst_addr;\n\n    if (!symdb) {\n        error = QString(\"no symbol table to find value for %1\").arg(name);\n        return false;\n    }\n    bool ok = symdb->getValue(value, name);\n    if (!ok) { error = QString(\"value for symbol \\\"%1\\\" not found\").arg(name); }\n    return ok;\n}\n\nQString FmeNodeSymbol::dump() {\n    return name;\n}\n\nFmeNodeUnaryOp::FmeNodeUnaryOp(\n    int priority,\n    FmeValue (*op)(FmeValue &a, machine::Address inst_addr),\n    QString description)\n    : FmeNode(priority) {\n    this->operand_a = nullptr;\n    this->op = op;\n    this->description = std::move(description);\n}\n\nFmeNodeUnaryOp::~FmeNodeUnaryOp() {\n    delete operand_a;\n}\n\nbool FmeNodeUnaryOp::FmeNodeUnaryOp::eval(\n    FmeValue &value,\n    FmeSymbolDb *symdb,\n    QString &error,\n    machine::Address inst_addr) {\n    FmeValue value_a;\n    if (!operand_a) { return false; }\n    if (!operand_a->eval(value_a, symdb, error, inst_addr)) { return false; }\n    value = op(value_a, inst_addr);\n    return true;\n}\n\nFmeNode *FmeNodeUnaryOp::child() {\n    return operand_a;\n}\n\nbool FmeNodeUnaryOp::insert(FmeNode *node) {\n    operand_a = node;\n    return true;\n}\n\nQString FmeNodeUnaryOp::dump() {\n    return \"(\" + description + \" \" + (operand_a ? operand_a->dump() : \"nullptr\") + \")\";\n}\n\nFmeNodeBinaryOp::FmeNodeBinaryOp(\n    int priority,\n    FmeValue (*op)(FmeValue &a, FmeValue &b),\n    FmeNode *left,\n    QString description)\n    : FmeNode(priority) {\n    this->operand_a = left;\n    this->operand_b = nullptr;\n    this->op = op;\n    this->description = std::move(description);\n}\n\nFmeNodeBinaryOp::~FmeNodeBinaryOp() {\n    delete operand_a;\n    delete operand_b;\n}\n\nbool FmeNodeBinaryOp::eval(\n    FmeValue &value,\n    FmeSymbolDb *symdb,\n    QString &error,\n    machine::Address inst_addr) {\n    FmeValue value_a;\n    FmeValue value_b;\n    if (!operand_a || !operand_b) { return false; }\n    if (!operand_a->eval(value_a, symdb, error, inst_addr)\n        || !operand_b->eval(value_b, symdb, error, inst_addr)) {\n        return false;\n    }\n    value = op(value_a, value_b);\n    return true;\n}\n\nFmeNode *FmeNodeBinaryOp::child() {\n    return operand_b;\n}\n\nbool FmeNodeBinaryOp::insert(FmeNode *node) {\n    operand_b = node;\n    return true;\n}\n\nQString FmeNodeBinaryOp::dump() {\n    return \"(\" + (operand_a ? operand_a->dump() : \"nullptr\") + \" \" + description + \" \"\n           + (operand_b ? operand_b->dump() : \"nullptr\") + \")\";\n}\n\nFmeExpression::FmeExpression() : FmeNode(0) {\n    root = nullptr;\n}\n\nbool FmeExpression::parse(const QString &expression, QString &error) {\n    delete root;\n    int base_prio = 100;\n    root = nullptr;\n    bool ok = true;\n    int i;\n    int word_start = 0;\n    bool in_word = false;\n    bool is_unary = true;\n    QString optxtx;\n    for (i = 0; true; i++) {\n        QChar ch {};\n        if (i < expression.size()) { ch = expression.at(i); }\n        if (!(ch.isLetterOrNumber() || (ch == '_')) || (i >= expression.size())) {\n            if (ch.isSpace()) { continue; }\n            if (in_word) {\n                FmeNode *new_node = nullptr;\n                QString word = expression.mid(word_start, i - word_start);\n                if (word.at(0).isDigit()) {\n                    new_node = new FmeNodeConstant(word.toLongLong(&ok, 0));\n                    if (!ok) {\n                        error = QString(\"cannot convert \\\"%1\\\" to number\").arg(word);\n                        delete new_node;\n                        break;\n                    }\n                } else {\n                    new_node = new FmeNodeSymbol(word);\n                }\n                FmeNode *node = this->find_last_child();\n                ok = node->insert(new_node);\n                if (!ok) {\n                    error = QString(\"parse stuck at \\\"%1\\\"\").arg(word);\n                    break;\n                }\n\n                in_word = false;\n                is_unary = false;\n            }\n            if (i >= expression.size()) { break; }\n            FmeValue (*binary_op)(FmeValue &a, FmeValue &b) = nullptr;\n            FmeValue (*unary_op)(FmeValue &a, machine::Address inst_addr) = nullptr;\n            int prio = base_prio;\n\n            optxtx = ch;\n            if (ch == '%') {\n                // `%` MODIFIER `(` SYMBOL `)`\n                // MODIFIER := `hi` | `lo` | `pcrel_hi` | `pcrel_lo`\n\n                prio += 90;\n                // The opening parenthesis is peeked and asserted but not consumed.\n                QStringView expr = QStringView(expression).mid(i + 1);\n                if (expr.startsWith(QStringLiteral(\"hi(\"))) {\n                    i += 2;\n                    optxtx = QStringLiteral(\"%hi\");\n                    unary_op = [](FmeValue &a, machine::Address) -> FmeValue {\n                        return get_bits(a + 0x800, 31, 12);\n                    };\n                } else if (expr.startsWith(QStringLiteral(\"lo(\"))) {\n                    i += 2;\n                    optxtx = QStringLiteral(\"%lo\");\n                    unary_op = [](FmeValue &a, machine::Address) -> FmeValue {\n                        return sign_extend(get_bits(a, 11, 0), 12);\n                    };\n                } else if (expr.startsWith(QStringLiteral(\"pcrel_hi(\"))) {\n                    i += 8;\n                    optxtx = QStringLiteral(\"%pcrel_hi\");\n                    unary_op = [](FmeValue &a, machine::Address inst_addr) -> FmeValue {\n                        return get_bits(a - inst_addr.get_raw(), 31, 12)\n                               + get_bit(a - inst_addr.get_raw(), 11);\n                    };\n                } else if (expr.startsWith(QStringLiteral(\"pcrel_lo(\"))) {\n                    i += 8;\n                    optxtx = QStringLiteral(\"%pcrel_lo\");\n                    unary_op = [](FmeValue &a, machine::Address inst_addr) -> FmeValue {\n                        return sign_extend(get_bits(a - inst_addr.get_raw() + 4, 11, 0), 12);\n                    };\n                } else {\n                    auto modifier = tokenize_symbol(expr);\n                    error = QString(\"Unknown modifier \\\"%1\\\"\").arg(modifier);\n                    ok = false;\n                    break;\n                }\n            } else if (ch == '~') {\n                prio += 90;\n                unary_op = [](FmeValue &a, machine::Address) -> FmeValue { return ~a; };\n            } else if (ch == '-') {\n                if (is_unary) {\n                    prio += 90;\n                    unary_op = [](FmeValue &a, machine::Address) -> FmeValue { return -a; };\n                } else {\n                    binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a - b; };\n                    prio += 20;\n                }\n            } else if (ch == '+') {\n                if (is_unary) { continue; }\n                binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a + b; };\n                prio += 20;\n            } else if (ch == '*') {\n                binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a * b; };\n                prio += 30;\n            } else if (ch == '/') {\n                binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a / b; };\n                prio += 30;\n            } else if (ch == '|') {\n                binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a | b; };\n                prio += 10;\n            } else if (ch == '&') {\n                binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a & b; };\n                prio += 15;\n            } else if (ch == '^') {\n                binary_op = [](FmeValue &a, FmeValue &b) -> FmeValue { return a ^ b; };\n                prio += 15;\n            } else if (ch == '(') {\n                base_prio += 100;\n            } else if (ch == ')') {\n                base_prio -= 100;\n                if (base_prio <= 0) {\n                    ok = false;\n                    error = QString(\"Unbalanced brackets.\");\n                    break;\n                }\n            } else {\n                error = QString(\"Unknown character \\\"%1\\\" in expression.\").arg(ch);\n                ok = false;\n                break;\n            }\n            if ((binary_op != nullptr) || (unary_op != nullptr)) {\n                FmeNode *node;\n                FmeNode *child;\n                for (node = this; (child = node->child()) != nullptr; node = child) {\n                    if (child->priority() >= prio) { break; }\n                }\n                if (binary_op != nullptr) {\n                    ok = node->insert(new FmeNodeBinaryOp(prio, binary_op, child, optxtx));\n                    is_unary = true;\n                } else {\n                    ok = node->insert(new FmeNodeUnaryOp(prio, unary_op, optxtx));\n                }\n                if (!ok) {\n                    error = QString(\"parse stuck at \\\"%1\\\"\").arg(QString(ch));\n                    break;\n                }\n            }\n        } else {\n            if (!in_word) { word_start = i; }\n            in_word = true;\n        }\n    }\n\n    return ok;\n}\n\nFmeExpression::~FmeExpression() {\n    delete root;\n    root = nullptr;\n}\n\nbool FmeExpression::eval(\n    FmeValue &value,\n    FmeSymbolDb *symdb,\n    QString &error,\n    machine::Address inst_addr) {\n    if (!root) { return false; }\n    return root->eval(value, symdb, error, inst_addr);\n}\n\nbool FmeExpression::insert(FmeNode *node) {\n    root = node;\n    return true;\n}\n\nFmeNode *FmeExpression::child() {\n    return root;\n}\n\nQString FmeExpression::dump() {\n    return \"(\" + (root ? root->dump() : \"nullptr\") + \")\";\n}\n"
  },
  {
    "path": "src/assembler/fixmatheval.h",
    "content": "#ifndef FIXMATHEVAL_H\n#define FIXMATHEVAL_H\n\n#include \"memory/address.h\"\n\n#include <QString>\n\nnamespace fixmatheval {\n\ntypedef int64_t FmeValue;\n\nclass FmeSymbolDb {\npublic:\n    virtual ~FmeSymbolDb();\n    virtual bool getValue(FmeValue &value, QString name) = 0;\n};\n\nclass FmeNode {\npublic:\n    explicit FmeNode(int priority);\n    virtual ~FmeNode();\n    virtual bool\n    eval(FmeValue &value, FmeSymbolDb *symdb, QString &error, machine::Address inst_addr)\n        = 0;\n    virtual bool insert(FmeNode *node);\n    virtual FmeNode *child();\n    virtual QString dump() = 0;\n    FmeNode *find_last_child();\n    [[nodiscard]] int priority() const;\n\nprivate:\n    int prio;\n};\n\nclass FmeNodeConstant : public FmeNode {\npublic:\n    explicit FmeNodeConstant(FmeValue value);\n    ~FmeNodeConstant() override;\n    bool\n    eval(FmeValue &value, FmeSymbolDb *symdb, QString &error, machine::Address inst_addr) override;\n    QString dump() override;\n\nprivate:\n    FmeValue value;\n};\n\nclass FmeNodeSymbol : public FmeNode {\npublic:\n    explicit FmeNodeSymbol(QString &name);\n    ~FmeNodeSymbol() override;\n    bool\n    eval(FmeValue &value, FmeSymbolDb *symdb, QString &error, machine::Address inst_addr) override;\n    QString dump() override;\n\nprivate:\n    QString name;\n};\n\nclass FmeNodeUnaryOp : public FmeNode {\npublic:\n    FmeNodeUnaryOp(\n        int priority,\n        FmeValue (*op)(FmeValue &a, machine::Address inst_addr),\n        QString description = \"??\");\n    ~FmeNodeUnaryOp() override;\n    bool\n    eval(FmeValue &value, FmeSymbolDb *symdb, QString &error, machine::Address inst_addr) override;\n    bool insert(FmeNode *node) override;\n    FmeNode *child() override;\n    QString dump() override;\n\nprivate:\n    FmeValue (*op)(FmeValue &a, machine::Address inst_addr);\n    FmeNode *operand_a;\n    QString description;\n};\n\nclass FmeNodeBinaryOp : public FmeNode {\npublic:\n    FmeNodeBinaryOp(\n        int priority,\n        FmeValue (*op)(FmeValue &a, FmeValue &b),\n        FmeNode *left,\n        QString description = \"??\");\n    ~FmeNodeBinaryOp() override;\n    bool\n    eval(FmeValue &value, FmeSymbolDb *symdb, QString &error, machine::Address inst_addr) override;\n    bool insert(FmeNode *node) override;\n    FmeNode *child() override;\n    QString dump() override;\n\nprivate:\n    FmeValue (*op)(FmeValue &a, FmeValue &b);\n    FmeNode *operand_a;\n    FmeNode *operand_b;\n    QString description;\n};\n\nclass FmeExpression : public FmeNode {\npublic:\n    FmeExpression();\n    ~FmeExpression() override;\n    virtual bool parse(const QString &expression, QString &error);\n    bool\n    eval(FmeValue &value, FmeSymbolDb *symdb, QString &error, machine::Address inst_addr) override;\n    bool insert(FmeNode *node) override;\n    FmeNode *child() override;\n    QString dump() override;\n\nprivate:\n    FmeNode *root;\n};\n\n} // namespace fixmatheval\n\n#endif /*FIXMATHEVAL_H*/\n"
  },
  {
    "path": "src/assembler/messagetype.h",
    "content": "#ifndef MESSAGETYPE_H\n#define MESSAGETYPE_H\n\nnamespace messagetype {\n\nenum Type {\n    MSG_START,\n    MSG_FINISH,\n    MSG_INFO,\n    MSG_WARNING,\n    MSG_ERROR,\n};\n\n}\n\n#endif /*MESSAGETYPE*/\n"
  },
  {
    "path": "src/assembler/simpleasm.cpp",
    "content": "#include \"simpleasm.h\"\n\n#include \"machine/memory/address.h\"\n#include \"machine/memory/memory_utils.h\"\n\n#include <QDir>\n#include <QFile>\n#include <QFileInfo>\n#include <QObject>\n#include <QString>\n\nusing namespace fixmatheval;\nusing machine::Address;\nusing ae = machine::AccessEffects; // For enum values, type is obvious from\n                                   // context.\n\nSymbolTableDb::SymbolTableDb(machine::SymbolTable *symbol_table) {\n    this->symbol_table = symbol_table;\n}\n\nbool SymbolTableDb::getValue(fixmatheval::FmeValue &value, QString name) {\n    SymbolValue val;\n    if (!symbol_table->name_to_value(val, name)) { return false; }\n    value = val;\n    return true;\n}\n\nvoid SymbolTableDb::setSymbol(\n    const QString &name,\n    SymbolValue value,\n    SymbolSize size,\n    SymbolInfo info,\n    SymbolOther other) {\n    symbol_table->set_symbol(name, value, size, info, other);\n}\n\nuint64_t SimpleAsm::string_to_uint64(const QString &str, int base, int *chars_taken) {\n    int i;\n    int64_t val;\n    char *p, *r;\n    char cstr[str.count() + 1];\n    for (i = 0; i < str.count(); i++) {\n        cstr[i] = str.at(i).toLatin1();\n    }\n    cstr[i] = 0;\n    p = cstr;\n    val = std::strtoll(p, &r, base);\n    if (chars_taken != nullptr) { *chars_taken = r - p; }\n    return val;\n}\n\nSimpleAsm::SimpleAsm(QObject *parent) : Super(parent) {\n    clear();\n}\n\nSimpleAsm::~SimpleAsm() {\n    clear();\n}\n\nvoid SimpleAsm::clear() {\n    symtab = nullptr;\n    mem = nullptr;\n    while (!reloc.isEmpty()) {\n        delete reloc.takeFirst();\n    }\n    error_occured = false;\n    fatal_occured = false;\n}\n\nvoid SimpleAsm::setup(\n    machine::FrontendMemory *mem,\n    SymbolTableDb *symtab,\n    machine::Address address,\n    machine::Xlen xlen) {\n    this->mem = mem;\n    this->symtab = symtab;\n    this->address = address;\n    this->symtab->setSymbol(\"XLEN\", static_cast<uint64_t>(xlen), sizeof(uint64_t));\n}\n\nstatic const machine::BitArg wordArg = { { { 32, 0 } }, 0 };\n\nbool SimpleAsm::process_line(\n    const QString &line,\n    const QString &filename,\n    int line_number,\n    QString *error_ptr) {\n    QString error;\n    QString label = \"\";\n    QString op = \"\";\n    QStringList operands;\n    int pos;\n    bool in_quotes = false;\n    bool backslash = false;\n    bool maybe_label = true;\n    bool final = false;\n    bool separator;\n    bool space_separated = false;\n    int token_beg = -1;\n    int token_last = -1;\n    int operand_num = -1;\n\n    for (pos = 0; pos <= line.count(); pos++) {\n        QChar ch = ' ';\n        if (pos >= line.count()) { final = true; }\n        if (!final) { ch = line.at(pos); }\n        if (!in_quotes) {\n            if (ch == '#') {\n                if (line.mid(pos).startsWith(\"#include\")) {\n                    if ((line.count() > pos + 8) && !line.at(pos + 8).isSpace()) { final = true; }\n                } else if (line.mid(pos).startsWith(\"#pragma\")) {\n                    if ((line.count() > pos + 7) && !line.at(pos + 7).isSpace()) {\n                        final = true;\n                    } else {\n                        space_separated = true;\n                    }\n                } else {\n                    final = true;\n                }\n            }\n            if (ch == ';') { final = true; }\n            if (ch == '/') {\n                if (pos + 1 < line.count()) {\n                    if (line.at(pos + 1) == '/') { final = true; }\n                }\n            }\n            separator\n                = final || (maybe_label && (ch == ':'))\n                  || ((operand_num >= 0)\n                      && ((ch == ',') || (space_separated && ch.isSpace() && (token_beg != -1))));\n            if (maybe_label && (ch == ':')) {\n                maybe_label = false;\n                if (token_beg == -1) {\n                    error = \"empty label\";\n                    emit report_message(\n                        messagetype::MSG_ERROR, filename, line_number, pos, error, \"\");\n                    error_occured = true;\n                    if (error_ptr != nullptr) { *error_ptr = error; }\n                    return false;\n                }\n                label = line.mid(token_beg, token_last - token_beg + 1);\n                token_beg = -1;\n            } else if (\n                ((!ch.isSpace() && (token_beg >= 0) && (token_last < pos - 1)) || final)\n                && (operand_num == -1)) {\n                maybe_label = false;\n                if (token_beg != -1) {\n                    op = line.mid(token_beg, token_last - token_beg + 1).toLower();\n                }\n                token_beg = -1;\n                operand_num = 0;\n                if (ch == ',') {\n                    error = \"empty first operand\";\n                    emit report_message(\n                        messagetype::MSG_ERROR, filename, line_number, pos, error, \"\");\n                    error_occured = true;\n                    if (error_ptr != nullptr) { *error_ptr = error; }\n                    return false;\n                }\n            } else if (separator || final) {\n                if (token_beg == -1) {\n                    error = \"empty operand\";\n                    emit report_message(\n                        messagetype::MSG_ERROR, filename, line_number, pos, error, \"\");\n                    error_occured = true;\n                    if (error_ptr != nullptr) { *error_ptr = error; }\n                    return false;\n                }\n                operands.append(line.mid(token_beg, token_last - token_beg + 1));\n                token_beg = -1;\n                operand_num++;\n            }\n            if (final) { break; }\n            if (!ch.isSpace() && !separator) {\n                if (token_beg == -1) { token_beg = pos; }\n                token_last = pos;\n            }\n            backslash = false;\n            if (ch == '\"') {\n                if (operand_num == -1) {\n                    error = \"unexpected quoted text\";\n                    emit report_message(\n                        messagetype::MSG_ERROR, filename, line_number, pos, error, \"\");\n                    error_occured = true;\n                    if (error_ptr != nullptr) { *error_ptr = error; }\n                    return false;\n                }\n                in_quotes = true;\n            }\n        } else {\n            token_last = pos;\n            if (final) {\n                error = \"unterminated quoted text\";\n                emit report_message(messagetype::MSG_ERROR, filename, line_number, pos, error, \"\");\n                error_occured = true;\n                if (error_ptr != nullptr) { *error_ptr = error; }\n                return false;\n            }\n            if ((ch == '\"') && !backslash) { in_quotes = false; }\n            if ((ch == '\\\\') && !backslash) {\n                backslash = true;\n            } else {\n                backslash = false;\n            }\n        }\n    }\n\n    if (!label.isEmpty()) { symtab->setSymbol(label, address.get_raw(), 4); }\n\n    if (op.isEmpty()) {\n        if (operands.count() != 0) {\n            error = \"operands for empty operation\";\n            emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n            error_occured = true;\n            if (error_ptr != nullptr) { *error_ptr = error; }\n            return false;\n        }\n        return true;\n    }\n\n    if (op == \"#pragma\") { return process_pragma(operands, filename, line_number, error_ptr); }\n    if (op == \"#include\") {\n        bool res = true;\n        QString incname;\n        if ((operands.count() != 1) || operands.at(0).isEmpty()) {\n            error = \"the single file has to be specified for include\";\n            emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n            error_occured = true;\n            if (error_ptr != nullptr) { *error_ptr = error; }\n            return false;\n        }\n        incname = operands.at(0);\n        if (incname.at(0) == '\"') { incname = incname.mid(1, incname.count() - 2); }\n        QFileInfo fi(QFileInfo(filename).dir(), incname);\n        incname = fi.filePath();\n        include_stack.append(filename);\n        if (include_stack.contains(incname)) {\n            error = QString(\"recursive include of file: \\\"%1\\\"\").arg(incname);\n            res = false;\n        } else {\n            if (!process_file(incname, error_ptr)) {\n                res = false;\n                error = QString(\"error in included file: \\\"%1\\\"\").arg(incname);\n            }\n        }\n        if (!res) {\n            emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n            error_occured = true;\n            if (error_ptr != nullptr) {\n                if (error_ptr->isEmpty()) { *error_ptr = error; }\n            }\n        }\n        include_stack.removeLast();\n        return res;\n    }\n    if ((op == \".text\") || (op == \".data\") || (op == \".bss\") || (op == \".globl\") || (op == \".end\")\n        || (op == \".ent\") || (op == \".option\")) {\n        return true;\n    }\n    if (op == \".org\") {\n        bool ok;\n        fixmatheval::FmeExpression expression;\n        fixmatheval::FmeValue value;\n        if (operands.count() != 1) {\n            error = \".org unexpected number of operands\";\n            emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n            error_occured = true;\n            if (error_ptr != nullptr) { *error_ptr = error; }\n            return false;\n        }\n        ok = expression.parse(operands.at(0), error);\n        if (!ok) {\n            fatal_occured = true;\n            error = tr(\".orig %1 parse error.\").arg(line);\n            emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n            error_occured = true;\n            if (error_ptr != nullptr) { *error_ptr = error; }\n            return false;\n        }\n        ok = expression.eval(value, symtab, error, address);\n        if (!ok) {\n            fatal_occured = true;\n            error = tr(\".orig %1 evaluation error.\").arg(line);\n            emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n            error_occured = true;\n            if (error_ptr != nullptr) { *error_ptr = error; }\n            return false;\n        }\n        address = machine::Address(value);\n        return true;\n    }\n    if ((op == \".space\") || (op == \".skip\")) {\n        bool ok;\n        fixmatheval::FmeExpression expression;\n        fixmatheval::FmeValue value;\n        fixmatheval::FmeValue fill = 0;\n        if ((operands.count() != 1) && (operands.count() != 2)) {\n            error = \".space/.skip unexpected number of operands\";\n            emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n            error_occured = true;\n            if (error_ptr != nullptr) { *error_ptr = error; }\n            return false;\n        }\n        if (operands.count() > 1) {\n            ok = expression.parse(operands.at(1), error);\n            if (!ok) {\n                fatal_occured = true;\n                error = tr(\".space/.skip %1 parse error.\").arg(line);\n                emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n                error_occured = true;\n                if (error_ptr != nullptr) { *error_ptr = error; }\n                return false;\n            }\n            ok = expression.eval(fill, symtab, error, address);\n            if (!ok) {\n                fatal_occured = true;\n                error = tr(\".space/.skip %1 evaluation error.\").arg(line);\n                emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n                error_occured = true;\n                if (error_ptr != nullptr) { *error_ptr = error; }\n                return false;\n            }\n        }\n        ok = expression.parse(operands.at(0), error);\n        if (!ok) {\n            fatal_occured = true;\n            error = tr(\".space/.skip %1 parse error.\").arg(line);\n            emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n            error_occured = true;\n            if (error_ptr != nullptr) { *error_ptr = error; }\n            return false;\n        }\n        ok = expression.eval(value, symtab, error, address);\n        if (!ok) {\n            fatal_occured = true;\n            error = tr(\".space/.skip %1 evaluation error.\").arg(line);\n            emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n            error_occured = true;\n            if (error_ptr != nullptr) { *error_ptr = error; }\n            return false;\n        }\n        while (value-- > 0) {\n            if (!fatal_occured) { mem->write_u8(address, (uint8_t)fill, ae::INTERNAL); }\n            address += 1;\n        }\n        return true;\n    }\n    if ((op == \".equ\") || (op == \".set\")) {\n        if ((operands.count() > 2) || (operands.count() < 1)) {\n            error = tr(\".set or .equ incorrect arguments number.\");\n            emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n            error_occured = true;\n            if (error_ptr != nullptr) { *error_ptr = error; }\n            return false;\n        }\n        QString name = operands.at(0).trimmed();\n        if ((name == \"noat\") || (name == \"noreored\")) { return true; }\n        bool ok;\n        fixmatheval::FmeValue value = 1;\n        if (operands.count() > 1) {\n            fixmatheval::FmeExpression expression;\n            ok = expression.parse(operands.at(1), error);\n            if (ok) { ok = expression.eval(value, symtab, error, address); }\n            if (!ok) {\n                error = tr(\".set or .equ %1 parse error.\").arg(operands.at(1));\n                emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n                error_occured = true;\n                if (error_ptr != nullptr) { *error_ptr = error; }\n                return false;\n            }\n        }\n        symtab->setSymbol(name, value, 0);\n        return true;\n    }\n    if ((op == \".ascii\") || (op == \".asciz\")) {\n        bool append_zero = op == \".asciz\";\n        for (QString s : operands) {\n            if (s.count() < 2) {\n                error = \"ascii empty string\";\n                emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n                error_occured = true;\n                if (error_ptr != nullptr) { *error_ptr = error; }\n                return false;\n            }\n            if ((s.at(0) != '\"') || (s.at(s.count() - 1) != '\"')) {\n                error = \"ascii missing quotes\";\n                emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n                error_occured = true;\n                if (error_ptr != nullptr) { *error_ptr = error; }\n                return false;\n            }\n            s = s.mid(1, s.count() - 2);\n            for (pos = 0; pos < s.count(); pos++) {\n                // target byte is in ASCII encoding\n                uint8_t target_byte = 0x00;\n\n                QChar host_char = s.at(pos);\n                if (host_char == '\\\\') {\n                    // if the host encoding recognizes this as a backslash (escape char)\n\n                    // handle end of the string check\n                    if (pos + 1 >= s.count()) {\n                        error = \"ascii - invalid escape sequence at the end of the string\";\n                        emit report_message(\n                            messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n                        error_occured = true;\n                        if (error_ptr != nullptr) { *error_ptr = error; }\n                        return false;\n                    }\n\n                    // compare the host_char in host encoding but emit byte in ASCII encoding\n                    host_char = s.at(++pos);\n                    if (host_char == '0') {\n                        target_byte = 0x00;\n                    } else if (host_char == 'b') {\n                        target_byte = 0x08;\n                    } else if (host_char == 't') {\n                        target_byte = 0x09;\n                    } else if (host_char == 'n') {\n                        target_byte = 0x0A;\n                    } else if (host_char == 'r') {\n                        target_byte = 0x0D;\n                    } else if (host_char == '\"') {\n                        target_byte = 0x22;\n                    } else if (host_char == '\\\\') {\n                        target_byte = 0x5C;\n                    } else {\n                        error = QString(\"ascii - incorrect escape sequence '\\\\\") + host_char + \"'\";\n                        emit report_message(\n                            messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n                        error_occured = true;\n                        if (error_ptr != nullptr) { *error_ptr = error; }\n                        return false;\n                    }\n                } else {\n                    // otherwise convert it to ASCII and write it as-is\n                    target_byte = host_char.toLatin1();\n                }\n\n                if (!fatal_occured) { mem->write_u8(address, target_byte, ae::INTERNAL); }\n                address += 1;\n            }\n            if (append_zero) {\n                if (!fatal_occured) { mem->write_u8(address, 0, ae::INTERNAL); }\n                address += 1;\n            }\n        }\n        return true;\n    }\n    if (op == \".byte\") {\n        bool ok;\n        for (const QString &s : operands) {\n            uint32_t val = 0;\n            int chars_taken;\n            val = string_to_uint64(s, 0, &chars_taken);\n            if (chars_taken != s.size()) {\n                fixmatheval::FmeExpression expression;\n                fixmatheval::FmeValue value;\n                ok = expression.parse(s, error);\n                if (!ok) {\n                    fatal_occured = true;\n                    error = tr(\".byte %1 parse error.\").arg(line);\n                    emit report_message(\n                        messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n                    error_occured = true;\n                    if (error_ptr != nullptr) { *error_ptr = error; }\n                    return false;\n                }\n                ok = expression.eval(value, symtab, error, address);\n                if (!ok) {\n                    fatal_occured = true;\n                    error = tr(\".byte %1 evaluation error.\").arg(line);\n                    emit report_message(\n                        messagetype::MSG_ERROR, filename, line_number, 0, error, \"\");\n                    error_occured = true;\n                    if (error_ptr != nullptr) { *error_ptr = error; }\n                    return false;\n                }\n                val = (uint8_t)value;\n            }\n            if (!fatal_occured) { mem->write_u8(address, (uint8_t)val, ae::INTERNAL); }\n            address += 1;\n        }\n        return true;\n    }\n\n    while (address.get_raw() & 3) {\n        if (!fatal_occured) { mem->write_u8(address, 0, ae::INTERNAL); }\n        address += 1;\n    }\n\n    if (op == \".word\") {\n        for (QString s : operands) {\n            s = s.simplified();\n            uint32_t val = 0;\n            int chars_taken;\n            val = string_to_uint64(s, 0, &chars_taken);\n            if (chars_taken != s.size()) {\n                val = 0;\n                reloc.append(new machine::RelocExpression(\n                    address, s, 0, -0xffffffff, 0xffffffff, &wordArg, filename, line_number));\n            }\n            if (!fatal_occured) { mem->write_u32(address, val, ae::INTERNAL); }\n            address += 4;\n        }\n        return true;\n    }\n\n    uint32_t inst[2] = { 0, 0 };\n    size_t size = 0;\n    try {\n        machine::TokenizedInstruction inst_tok { op, operands, address, filename,\n                                                 static_cast<unsigned>(line_number) };\n        size = machine::Instruction::code_from_tokens(inst, 8, inst_tok, &reloc);\n    } catch (machine::Instruction::ParseError &e) {\n        error = tr(\"instruction %1 parse error - %2.\").arg(line, e.message);\n        emit report_message(messagetype::MSG_ERROR, filename, line_number, 0, e.message, \"\");\n        error_occured = true;\n        if (error_ptr != nullptr) { *error_ptr = error; }\n        return false;\n    }\n    uint32_t *p = inst;\n    for (size_t l = 0; l < size; l += 4) {\n        if (!fatal_occured) { mem->write_u32(address, *(p++), ae::INTERNAL); }\n        address += 4;\n    }\n    return true;\n}\n\nbool SimpleAsm::process_file(const QString &filename, QString *error_ptr) {\n    QString error;\n    bool res = true;\n    QFile srcfile(filename);\n    if (!srcfile.open(QFile::ReadOnly | QFile::Text)) {\n        error = QString(\"cannot open file: \\\"%1\\\"\").arg(filename);\n        emit report_message(messagetype::MSG_ERROR, \"\", 0, 0, error, \"\");\n        error_occured = true;\n        if (error_ptr != nullptr) { *error_ptr = error; }\n        return false;\n    }\n    for (int ln = 1; !srcfile.atEnd(); ln++) {\n        QString line = srcfile.readLine();\n        if ((line.count() > 0) && (line.at(line.count() - 1) == '\\n')) {\n            line.truncate(line.count() - 1);\n        }\n        if (!process_line(line, filename, ln, error_ptr)) { res = false; }\n    }\n    srcfile.close();\n    return res;\n}\n\nbool SimpleAsm::finish(QString *error_ptr) {\n    bool error_reported = false;\n    for (machine::RelocExpression *r : reloc) {\n        QString error;\n        fixmatheval::FmeExpression expression;\n        if (!expression.parse(r->expression, error)) {\n            error = tr(\"expression parse error %1 at line %2, expression %3.\")\n                        .arg(error, QString::number(r->line), expression.dump());\n            emit report_message(messagetype::MSG_ERROR, r->filename, r->line, 0, error, \"\");\n            if (error_ptr != nullptr && !error_reported) { *error_ptr = error; }\n            error_occured = true;\n            error_reported = true;\n        } else {\n            fixmatheval::FmeValue value;\n            if (!expression.eval(value, symtab, error, r->location)) {\n                error = tr(\"expression evalution error %1 at line %2 , \"\n                           \"expression %3.\")\n                            .arg(error, QString::number(r->line), expression.dump());\n                emit report_message(messagetype::MSG_ERROR, r->filename, r->line, 0, error, \"\");\n                if (error_ptr != nullptr && !error_reported) { *error_ptr = error; }\n                error_occured = true;\n                error_reported = true;\n            } else {\n                if (false) {\n                    emit report_message(\n                        messagetype::MSG_INFO, r->filename, r->line, 0,\n                        expression.dump() + \" -> \" + QString::number(value), \"\");\n                }\n                machine::Instruction inst(mem->read_u32(r->location, ae::INTERNAL));\n                if (!inst.update(value, r)) {\n                    error = tr(\"instruction update error %1 at line %2, \"\n                               \"expression %3 -> value %4.\")\n                                .arg(\n                                    error, QString::number(r->line), expression.dump(),\n                                    QString::number(value));\n                    emit report_message(messagetype::MSG_ERROR, r->filename, r->line, 0, error, \"\");\n                    if (error_ptr != nullptr && !error_reported) { *error_ptr = error; }\n                    error_occured = true;\n                    error_reported = true;\n                    // Remove address\n                }\n                if (!fatal_occured) {\n                    mem->write_u32(Address(r->location), inst.data(), ae::INTERNAL);\n                }\n            }\n        }\n    }\n    while (!reloc.isEmpty()) {\n        delete reloc.takeFirst();\n    }\n\n    emit mem->external_change_notify(mem, Address::null(), Address(0xffffffff), ae::INTERNAL);\n\n    return !error_occured;\n}\n\nbool SimpleAsm::process_pragma(\n    QStringList &operands,\n    const QString &filename,\n    int line_number,\n    QString *error_ptr) {\n    (void)operands;\n    (void)filename;\n    (void)line_number;\n    (void)error_ptr;\n    return true;\n}\n"
  },
  {
    "path": "src/assembler/simpleasm.h",
    "content": "#ifndef SIMPLEASM_H\n#define SIMPLEASM_H\n\n#include \"fixmatheval.h\"\n#include \"machine/machine.h\"\n#include \"machine/memory/frontend_memory.h\"\n#include \"messagetype.h\"\n\n#include <QString>\n#include <QStringList>\n\nusing machine::SymbolInfo;\nusing machine::SymbolOther;\nusing machine::SymbolSize;\nusing machine::SymbolValue;\n\nclass SymbolTableDb : public fixmatheval::FmeSymbolDb {\npublic:\n    explicit SymbolTableDb(machine::SymbolTable *symbol_table);\n    bool getValue(fixmatheval::FmeValue &value, QString name) override;\n    void setSymbol(\n        const QString &name,\n        SymbolValue value,\n        SymbolSize size,\n        SymbolInfo info = 0,\n        SymbolOther other = 0);\n\nprivate:\n    machine::SymbolTable *symbol_table;\n};\n\nclass SimpleAsm : public QObject {\n    Q_OBJECT\n\n    using Super = QObject;\n\nsignals:\n    void report_message(\n        messagetype::Type type,\n        QString file,\n        int line,\n        int column,\n        QString text,\n        QString hint);\n\npublic:\n    explicit SimpleAsm(QObject *parent = nullptr);\n    ~SimpleAsm() override;\n\npublic:\n    static uint64_t string_to_uint64(const QString &str, int base, int *chars_taken = nullptr);\n    void clear();\n    void setup(\n        machine::FrontendMemory *mem,\n        SymbolTableDb *symtab,\n        machine::Address address,\n        machine::Xlen xlen);\n    bool process_line(\n        const QString &line,\n        const QString &filename = \"\",\n        int line_number = 0,\n        QString *error_ptr = nullptr);\n    virtual bool process_file(const QString &filename, QString *error_ptr = nullptr);\n    bool finish(QString *error_ptr = nullptr);\n\nprotected:\n    virtual bool process_pragma(\n        QStringList &operands,\n        const QString &filename = \"\",\n        int line_number = 0,\n        QString *error_ptr = nullptr);\n    bool error_occured {};\n    bool fatal_occured {};\n    SymbolTableDb *symtab {};\n    machine::Address address {};\n\nprivate:\n    QStringList include_stack;\n    machine::FrontendMemory *mem {};\n    machine::RelocExpressionList reloc;\n};\n\n#endif /*SIMPLEASM_H*/\n"
  },
  {
    "path": "src/cli/CMakeLists.txt",
    "content": "project(cli\n        LANGUAGES C CXX\n        VERSION ${MAIN_PROJECT_VERSION}\n        DESCRIPTION \"Simulator command-line UI.\")\n\nset(CMAKE_AUTOMOC ON)\n\nset(cli_SOURCES\n        chariohandler.cpp\n        main.cpp\n        msgreport.cpp\n        reporter.cpp\n        tracer.cpp\n)\nset(cli_HEADERS\n        chariohandler.h\n        msgreport.h\n        reporter.h\n        tracer.h\n)\n\nadd_executable(cli\n        ${cli_SOURCES}\n        ${cli_HEADERS})\ntarget_link_libraries(cli\n        PRIVATE ${QtLib}::Core machine os_emulation assembler)\ntarget_compile_definitions(cli\n        PRIVATE\n        APP_ORGANIZATION=\\\"${MAIN_PROJECT_ORGANIZATION}\\\"\n        APP_ORGANIZATION_DOMAIN=\\\"${MAIN_PROJECT_HOMEPAGE_URL}\\\"\n        APP_NAME=\\\"${MAIN_PROJECT_NAME}\\\"\n        APP_VERSION=\\\"${PROJECT_VERSION}\\\"\n        ENV_CONFIG_FILE_NAME=\\\"${MAIN_PROJECT_NAME_UPPER}_CONFIG_FILE\\\")\nset_target_properties(cli PROPERTIES\n        OUTPUT_NAME \"${MAIN_PROJECT_NAME_LOWER}_${PROJECT_NAME}\")\n\n# =============================================================================\n# Installation\n# =============================================================================\n\n# Prior to CMake version 3.13, installation must be performed in the subdirectory,\n# there the target was created. Therefore executable installation is to be found\n# in corresponding CMakeLists.txt.\n\ninstall(TARGETS cli\n        RUNTIME DESTINATION bin)\n\ninclude(../../cmake/TestingTools.cmake)\n\nenable_testing()\n\nadd_cli_test(\n        NAME stalls\n        ARGS\n        --asm \"${CMAKE_SOURCE_DIR}/tests/cli/stalls/program.S\"\n        --dump-registers\n        EXPECTED_OUTPUT \"tests/cli/stalls/stdout.txt\"\n)\n\nadd_cli_test(\n        NAME asm_error\n        ARGS\n        --asm \"${CMAKE_SOURCE_DIR}/tests/cli/asm-error/program.S\"\n)\nset_tests_properties(cli_asm_error PROPERTIES WILL_FAIL TRUE)\n\nadd_cli_test(\n        NAME modifiers\n        ARGS\n        --asm \"${CMAKE_SOURCE_DIR}/tests/cli/modifiers/program.S\"\n        EXPECTED_OUTPUT \"tests/cli/modifiers/stdout.txt\"\n)\n\nadd_cli_test(\n        NAME modifiers-pcrel\n        ARGS\n        --asm \"${CMAKE_SOURCE_DIR}/tests/cli/modifiers-pcrel/program.S\"\n        EXPECTED_OUTPUT \"tests/cli/modifiers-pcrel/stdout.txt\"\n)\n\nadd_cli_test(\n        NAME virtual_memory_template\n        ARGS\n        --asm \"${CMAKE_SOURCE_DIR}/tests/cli/virtual_memory/template/program.S\"\n        --dump-registers\n        EXPECTED_OUTPUT \"tests/cli/virtual_memory/template/stdout.txt\"\n)\n\nadd_cli_test(\n        NAME virtual_memory_dtlb\n        ARGS\n        --asm \"${CMAKE_SOURCE_DIR}/tests/cli/virtual_memory/dtlb/program.S\"\n        --dump-registers\n        EXPECTED_OUTPUT \"tests/cli/virtual_memory/dtlb/stdout.txt\"\n)\n\nadd_cli_test(\n        NAME virtual_memory_itlb\n        ARGS\n        --asm \"${CMAKE_SOURCE_DIR}/tests/cli/virtual_memory/itlb/program.S\"\n        --dump-registers\n        EXPECTED_OUTPUT \"tests/cli/virtual_memory/itlb/stdout.txt\"\n)\n\nadd_cli_test(\n        NAME virtual_memory_memrw\n        ARGS\n        --asm \"${CMAKE_SOURCE_DIR}/tests/cli/virtual_memory/memrw/program.S\"\n        --dump-registers\n        EXPECTED_OUTPUT \"tests/cli/virtual_memory/memrw/stdout.txt\"\n)\n\nadd_cli_test(\n        NAME virtual_memory_exec\n        ARGS\n        --asm \"${CMAKE_SOURCE_DIR}/tests/cli/virtual_memory/exec/program.S\"\n        --dump-registers\n        EXPECTED_OUTPUT \"tests/cli/virtual_memory/exec/stdout.txt\"\n)"
  },
  {
    "path": "src/cli/chariohandler.cpp",
    "content": "#include \"chariohandler.h\"\n\nCharIOHandler::CharIOHandler(QIODevice *iodev, QObject *parent) : QIODevice(parent), fd_list() {\n    this->iodev = iodev;\n    if (!iodev->parent()) { iodev->setParent(this); }\n    fd_specific = false;\n    if (iodev->isOpen()) { Super::open(iodev->openMode()); }\n    connect(iodev, &Super::aboutToClose, this, &CharIOHandler::aboutToClose);\n    connect(iodev, &Super::bytesWritten, this, &CharIOHandler::bytesWritten);\n    connect(iodev, &Super::channelBytesWritten, this, &CharIOHandler::channelBytesWritten);\n    connect(iodev, &Super::channelReadyRead, this, &CharIOHandler::channelReadyRead);\n    connect(iodev, &Super::readChannelFinished, this, &CharIOHandler::readChannelFinished);\n    connect(iodev, &Super::readyRead, this, &CharIOHandler::readyRead);\n}\n\nCharIOHandler::~CharIOHandler() {\n    if (iodev->parent() == this) { delete iodev; }\n}\n\nvoid CharIOHandler::writeByte(unsigned int data) {\n    char ch = (char)data;\n    write(&ch, 1);\n}\n\nvoid CharIOHandler::writeByte(int fd, unsigned int data) {\n    if (!fd_specific || fd_list.contains(fd)) { writeByte(data); }\n}\n\nvoid CharIOHandler::readBytePoll(int fd, unsigned int &data, bool &available) {\n    char ch;\n    qint64 res;\n    if (!fd_specific || fd_list.contains(fd)) {\n        if (bytesAvailable() > 0) {\n            res = read(&ch, 1);\n            if (res > 0) {\n                data = ch & 0xff;\n                available = true;\n            }\n        }\n    }\n}\n\nvoid CharIOHandler::insertFd(const int &fd) {\n    fd_list.insert(fd);\n}\n\nvoid CharIOHandler::removeFd(const int &fd) {\n    fd_list.remove(fd);\n}\n\nbool CharIOHandler::isSequential() const {\n    return iodev->isSequential();\n}\n\nbool CharIOHandler::open(OpenMode mode) {\n    if (!iodev->open(mode)) { return false; }\n    Super::open(mode);\n    return true;\n}\n\nvoid CharIOHandler::close() {\n    Super::close();\n    iodev->close();\n}\n\nqint64 CharIOHandler::pos() const {\n    return iodev->pos();\n}\n\nqint64 CharIOHandler::size() const {\n    return iodev->size();\n}\n\nbool CharIOHandler::seek(qint64 pos) {\n    return iodev->seek(pos);\n}\n\nbool CharIOHandler::atEnd() const {\n    return iodev->atEnd();\n}\n\nbool CharIOHandler::reset() {\n    return iodev->reset();\n}\n\nqint64 CharIOHandler::bytesAvailable() const {\n    return iodev->bytesAvailable() + Super::bytesAvailable();\n}\n\nqint64 CharIOHandler::bytesToWrite() const {\n    return iodev->bytesToWrite() + Super::bytesToWrite();\n}\n\nbool CharIOHandler::canReadLine() const {\n    return iodev->canReadLine();\n}\n\nbool CharIOHandler::waitForReadyRead(int msecs) {\n    return iodev->waitForReadyRead(msecs);\n}\n\nbool CharIOHandler::waitForBytesWritten(int msecs) {\n    return iodev->waitForBytesWritten(msecs);\n}\n\nqint64 CharIOHandler::readData(char *data, qint64 maxSize) {\n    return iodev->read(data, maxSize);\n}\n\nqint64 CharIOHandler::readLineData(char *data, qint64 maxSize) {\n    return iodev->readLine(data, maxSize);\n}\n\nqint64 CharIOHandler::writeData(const char *data, qint64 maxSize) {\n    return iodev->write(data, maxSize);\n}\n"
  },
  {
    "path": "src/cli/chariohandler.h",
    "content": "#ifndef CHARIOHANDLER_H\n#define CHARIOHANDLER_H\n\n#include <QIODevice>\n#include <QObject>\n#include <QSet>\n\nclass CharIOHandler : public QIODevice {\n    Q_OBJECT\n\n    using Super = QIODevice;\n\npublic:\n    explicit CharIOHandler(QIODevice *iodev, QObject *parent = nullptr);\n    ~CharIOHandler() override;\n\npublic slots:\n    void writeByte(unsigned int data);\n    void writeByte(int fd, unsigned int data);\n    void readBytePoll(int fd, unsigned int &data, bool &available);\n\npublic:\n    void insertFd(const int &fd);\n    void removeFd(const int &fd);\n\n    [[nodiscard]] bool isSequential() const override;\n    bool open(OpenMode mode) override;\n    void close() override;\n    [[nodiscard]] qint64 pos() const override;\n    [[nodiscard]] qint64 size() const override;\n    bool seek(qint64 pos) override;\n    [[nodiscard]] bool atEnd() const override;\n    bool reset() override;\n    [[nodiscard]] qint64 bytesAvailable() const override;\n    [[nodiscard]] qint64 bytesToWrite() const override;\n    [[nodiscard]] bool canReadLine() const override;\n    bool waitForReadyRead(int msecs) override;\n    bool waitForBytesWritten(int msecs) override;\n\nprotected:\n    qint64 readData(char *data, qint64 maxSize) override;\n    qint64 readLineData(char *data, qint64 maxSize) override;\n    qint64 writeData(const char *data, qint64 maxSize) override;\n\nprivate:\n    QIODevice *iodev;\n    bool fd_specific;\n    QSet<int> fd_list;\n};\n\n#endif // CHARIOHANDLER_H\n"
  },
  {
    "path": "src/cli/main.cpp",
    "content": "#include \"assembler/simpleasm.h\"\n#include \"chariohandler.h\"\n#include \"common/logging.h\"\n#include \"common/logging_format_colors.h\"\n#include \"machine/machineconfig.h\"\n#include \"msgreport.h\"\n#include \"os_emulation/ossyscall.h\"\n#include \"reporter.h\"\n#include \"tracer.h\"\n\n#include <QCommandLineParser>\n#include <QCoreApplication>\n#include <QFile>\n#include <cctype>\n#include <fstream>\n#include <iostream>\n\nusing namespace machine;\nusing namespace std;\n\nusing ae = machine::AccessEffects; // For enum values, type is obvious from\n                                   // context.\n\nvoid create_parser(QCommandLineParser &p) {\n    p.setApplicationDescription(\"QtRvSim CLI machine simulator\");\n    p.addHelpOption();\n    p.addVersionOption();\n\n    p.addPositionalArgument(\"FILE\", \"Input ELF executable file or assembler source\");\n\n    // p.addOptions({}); available only from Qt 5.4+\n    p.addOption({ \"asm\", \"Treat provided file argument as assembler source.\" });\n    p.addOption({ \"pipelined\", \"Configure CPU to use five stage pipeline.\" });\n    p.addOption({ \"no-delay-slot\", \"Disable jump delay slot.\" });\n    p.addOption(\n        { \"hazard-unit\", \"Specify hazard unit implementation [none|stall|forward].\", \"HUKIND\" });\n    p.addOption(\n        { { \"trace-fetch\", \"tr-fetch\" },\n          \"Trace fetched instruction (for both pipelined and not core).\" });\n    p.addOption(\n        { { \"trace-decode\", \"tr-decode\" },\n          \"Trace instruction in decode stage. (only for pipelined core)\" });\n    p.addOption(\n        { { \"trace-execute\", \"tr-execute\" },\n          \"Trace instruction in execute stage. (only for pipelined core)\" });\n    p.addOption(\n        { { \"trace-memory\", \"tr-memory\" },\n          \"Trace instruction in memory stage. (only for pipelined core)\" });\n    p.addOption(\n        { { \"trace-writeback\", \"tr-writeback\" },\n          \"Trace instruction in write back stage. (only for pipelined core)\" });\n    p.addOption({ { \"trace-pc\", \"tr-pc\" }, \"Print program counter register changes.\" });\n    p.addOption({ { \"trace-wrmem\", \"tr-wr\" }, \"Trace writes into memory.\" });\n    p.addOption({ { \"trace-rdmem\", \"tr-rd\" }, \"Trace reads from memory.\" });\n    p.addOption(\n        { { \"trace-gp\", \"tr-gp\" },\n          \"Print general purpose register changes. You can use * for \"\n          \"all registers.\",\n          \"REG\" });\n    p.addOption({ \"dump-to-json\", \"Configure reportor dump to json file.\", \"FNAME\" });\n    p.addOption({ \"only-dump\", \"Do not start the processor.\" });\n    p.addOption({ \"disable-console-dump\", \"Configure reporter not to dump to console.\" });\n    p.addOption({ { \"dump-registers\", \"d-regs\" }, \"Dump registers state at program exit.\" });\n    p.addOption({ \"dump-cache-stats\", \"Dump cache statistics at program exit.\" });\n    p.addOption({ \"dump-cycles\", \"Dump number of CPU cycles till program end.\" });\n    p.addOption({ \"dump-range\", \"Dump memory range.\", \"START,LENGTH,FNAME\" });\n    p.addOption({ \"dump-symbol-table\", \"Dump the symbol table.\" });\n    p.addOption({ \"dump-branch-predictor\", \"Dump branch predictor statistics at program exit.\" });\n    p.addOption({ \"dump-all\", \"Dump all available information at program exit.\" });\n    p.addOption({ \"load-range\", \"Load memory range.\", \"START,FNAME\" });\n    p.addOption({ \"expect-fail\", \"Expect that program causes CPU trap and fail if it doesn't.\" });\n    p.addOption(\n        { \"fail-match\",\n          \"Program should exit with exactly this CPU TRAP. Possible values are \"\n          \"I(unsupported Instruction), A(Unsupported ALU operation), \"\n          \"O(Overflow/underflow) and J(Unaligned Jump). You can freely combine \"\n          \"them. Using this implies expect-fail option.\",\n          \"TRAP\" });\n    p.addOption(\n        { \"d-cache\",\n          \"Data cache. Format policy,sets,words_in_blocks,associativity[,writeback] where \"\n          \"policy is random/lru/lfu \"\n          \"and writeback is optional wb/wt/wtna/wta\",\n          \"DCACHE\" });\n    p.addOption(\n        { \"i-cache\",\n          \"Instruction cache. Format policy,sets,words_in_blocks,associativity \"\n          \"where policy is random/lru/lfu\",\n          \"ICACHE\" });\n    p.addOption(\n        { \"l2-cache\",\n          \"L2 cache. Format policy,sets,words_in_blocks,associativity[,writeback] where \"\n          \"policy is random/lru/lfu \"\n          \"and writeback is optional wb/wt/wtna/wta\",\n          \"L2CACHE\" });\n    p.addOption(\n        { \"branch-predictor\",\n          \"Branch predictor. Format type,init_state,btb,bhr,bht\\n\"\n          \"type is always_not_taken/always_taken/btfnt/smith_1_bit/smith_2_bit/smith_2_bit_hysteresis\\n\"\n          \"init_state is (for smith 1) not_taken/taken, (for smith 2) strongly_not_taken/weakly_not_taken/weakly_taken/strongly_taken\\n\"\n          \"for other predictors it is ignored/undefined\\n\"\n          \"btb is number of branch target buffer bits\\n\"\n          \"bhr is number of branch history register bits\\n\"\n          \"bht is number of branch history table address bits\",\n          \"BPRED\" });\n    p.addOption({ \"read-time\", \"Memory read access time (cycles).\", \"RTIME\" });\n    p.addOption({ \"write-time\", \"Memory read access time (cycles).\", \"WTIME\" });\n    p.addOption({ \"burst-time\", \"Memory read access time (cycles).\", \"BTIME\" });\n    p.addOption({ \"l2-time\", \"L2 cache access time (cycles).\", \"L2TIME\" });\n    p.addOption({ { \"serial-in\", \"serin\" }, \"File connected to the serial port input.\", \"FNAME\" });\n    p.addOption(\n        { { \"serial-out\", \"serout\" }, \"File connected to the serial port output.\", \"FNAME\" });\n    p.addOption({ { \"os-emulation\", \"osemu\" }, \"Operating system emulation.\" });\n    p.addOption(\n        { { \"std-in\", \"stdin\" }, \"File connected to the syscall standard input.\", \"FNAME\" });\n    p.addOption(\n        { { \"std-out\", \"stdout\" }, \"File connected to the syscall standard output.\", \"FNAME\" });\n    p.addOption(\n        { { \"os-fs-root\", \"osfsroot\" }, \"Emulated system root/prefix for opened files\", \"DIR\" });\n    p.addOption(\n        { { \"isa-variant\", \"isavariant\" }, \"Instruction set to emulate (default RV32IMA)\", \"STR\" });\n    p.addOption({ \"cycle-limit\", \"Limit execution to specified maximum clock cycles\", \"NUMBER\" });\n}\n\nvoid configure_cache(CacheConfig &cacheconf, const QStringList &cachearg, const QString &which) {\n    if (cachearg.empty()) { return; }\n    cacheconf.set_enabled(true);\n    QStringList pieces = cachearg.at(cachearg.size() - 1).split(\",\");\n    if (pieces.size() < 3) {\n        fprintf(\n            stderr, \"Parameters %s cache incorrect (correct lru,4,2,2,wb).\\n\", qPrintable(which));\n        exit(EXIT_FAILURE);\n    }\n    if (pieces.at(0).size() < 1) {\n        fprintf(stderr, \"Policy for %s cache is incorrect.\\n\", qPrintable(which));\n        exit(EXIT_FAILURE);\n    }\n    if (!pieces.at(0).at(0).isDigit()) {\n        if (pieces.at(0).toLower() == \"random\") {\n            cacheconf.set_replacement_policy(CacheConfig::RP_RAND);\n        } else if (pieces.at(0).toLower() == \"lru\") {\n            cacheconf.set_replacement_policy(CacheConfig::RP_LRU);\n        } else if (pieces.at(0).toLower() == \"lfu\") {\n            cacheconf.set_replacement_policy(CacheConfig::RP_LFU);\n        } else {\n            fprintf(stderr, \"Policy for %s cache is incorrect.\\n\", qPrintable(which));\n            exit(EXIT_FAILURE);\n        }\n        pieces.removeFirst();\n    }\n    if (pieces.size() < 3) {\n        fprintf(\n            stderr, \"Parameters for  %s  cache incorrect (correct lru,4,2,2,wb). \\n\",\n            qPrintable(which));\n        exit(EXIT_FAILURE);\n    }\n    cacheconf.set_set_count(pieces.at(0).toLong());\n    cacheconf.set_block_size(pieces.at(1).toLong());\n    cacheconf.set_associativity(pieces.at(2).toLong());\n    if (cacheconf.set_count() == 0 || cacheconf.block_size() == 0\n        || cacheconf.associativity() == 0) {\n        fprintf(\n            stderr, \"Parameters for  %s  cache cannot have zero component. \\n\", qPrintable(which));\n        exit(EXIT_FAILURE);\n    }\n    if (pieces.size() > 3) {\n        if (pieces.at(3).toLower() == \"wb\") {\n            cacheconf.set_write_policy(CacheConfig::WP_BACK);\n        } else if (pieces.at(3).toLower() == \"wt\" || pieces.at(3).toLower() == \"wtna\") {\n            cacheconf.set_write_policy(CacheConfig::WP_THROUGH_NOALLOC);\n        } else if (pieces.at(3).toLower() == \"wta\") {\n            cacheconf.set_write_policy(CacheConfig::WP_THROUGH_ALLOC);\n        } else {\n            fprintf(\n                stderr, \"Write policy for %s cache is incorrect (correct wb/wt/wtna/wta).\\n\",\n                qPrintable(which));\n            exit(EXIT_FAILURE);\n        }\n    }\n}\n\nvoid configure_branch_predictor(MachineConfig &config, const QStringList &bpred) {\n    if (bpred.empty()) { return; }\n    config.set_bp_enabled(true);\n    QStringList pieces = bpred.at(bpred.size() - 1).split(\",\");\n    if (pieces.size() < 5) {\n        fprintf(\n            stderr,\n            \"Parameters for branch predictor are incorrect.\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    PredictorType ptype =\n        pieces.at(0).toLower() == \"always_not_taken\"   ? PredictorType::ALWAYS_NOT_TAKEN\n        : pieces.at(0).toLower() == \"always_taken\"  ? PredictorType::ALWAYS_TAKEN\n        : pieces.at(0).toLower() == \"btfnt\"  ? PredictorType::BTFNT\n        : pieces.at(0).toLower() == \"smith_1_bit\"  ? PredictorType::SMITH_1_BIT\n        : pieces.at(0).toLower() == \"smith_2_bit\"  ? PredictorType::SMITH_2_BIT\n        : pieces.at(0).toLower() == \"smith_2_bit_hysteresis\" ? PredictorType::SMITH_2_BIT_HYSTERESIS\n        : PredictorType::UNDEFINED;\n\n    PredictorState init_state =\n        pieces.at(1).toLower() == \"not_taken\"   ? PredictorState::NOT_TAKEN\n        : pieces.at(1).toLower() == \"taken\"  ? PredictorState::TAKEN\n        : pieces.at(1).toLower() == \"strongly_not_taken\" ? PredictorState::STRONGLY_NOT_TAKEN\n        : pieces.at(1).toLower() == \"weakly_not_taken\" ? PredictorState::WEAKLY_NOT_TAKEN\n        : pieces.at(1).toLower() == \"weakly_taken\"  ? PredictorState::WEAKLY_TAKEN\n        : pieces.at(1).toLower() == \"strongly_taken\"  ? PredictorState::STRONGLY_TAKEN\n        : PredictorState::UNDEFINED;\n\n    switch (ptype) {\n      case PredictorType::ALWAYS_NOT_TAKEN:\n      case PredictorType::ALWAYS_TAKEN:\n      case PredictorType::BTFNT:\n        init_state = PredictorState::UNDEFINED;\n        break;\n      case PredictorType::SMITH_1_BIT:\n        if (init_state != PredictorState::NOT_TAKEN &&\n            init_state != PredictorState::TAKEN) {\n          fprintf(stderr,\n                  \"Initial state for Smith 1 bit predictor must be \"\n                  \"not_taken/taken.\\n\");\n          exit(EXIT_FAILURE);\n        }\n        break;\n      case PredictorType::SMITH_2_BIT:\n      case PredictorType::SMITH_2_BIT_HYSTERESIS:\n        if (init_state != PredictorState::STRONGLY_NOT_TAKEN &&\n            init_state != PredictorState::WEAKLY_NOT_TAKEN &&\n            init_state != PredictorState::WEAKLY_TAKEN &&\n            init_state != PredictorState::STRONGLY_TAKEN) {\n          fprintf(stderr,\n                  \"Initial state for Smith 2 bit predictor must be \"\n                  \"strongly_not_taken/weakly_not_taken/weakly_taken/\"\n                  \"strongly_taken.\\n\");\n          exit(EXIT_FAILURE);\n        }\n        break;\n\n      default:\n        fprintf(stderr, \"Unknown predictor type specified.\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    config.set_bp_type(ptype);\n    config.set_bp_init_state(init_state);\n    config.set_bp_btb_bits(pieces.at(2).toLong());\n    config.set_bp_bhr_bits(pieces.at(3).toLong());\n    config.set_bp_bht_addr_bits(pieces.at(4).toLong());\n}\n\nvoid parse_u32_option(\n    QCommandLineParser &parser,\n    const QString &option_name,\n    MachineConfig &config,\n    void (MachineConfig::*setter)(uint32_t value)) {\n    auto values = parser.values(option_name);\n    if (!values.empty()) {\n        bool ok = true;\n        // Try to parse supplied value.\n        uint32_t value = values.last().toUInt(&ok);\n        if (ok) {\n            // Set the value if successfully parsed.\n            (config.*setter)(value);\n        } else {\n            fprintf(\n                stderr, \"Value of option %s is not a valid unsigned integer.\\n\",\n                qPrintable(option_name));\n            exit(EXIT_FAILURE);\n        }\n    }\n}\n\nvoid configure_machine(QCommandLineParser &parser, MachineConfig &config) {\n    QStringList arguments = parser.positionalArguments();\n    if (arguments.size() != 1) {\n        fprintf(stderr, \"Single ELF file has to be specified\\n\");\n        parser.showHelp();\n    }\n    config.set_elf(arguments[0]);\n\n    config.set_delay_slot(!parser.isSet(\"no-delay-slot\"));\n    config.set_pipelined(parser.isSet(\"pipelined\"));\n\n    auto hazard_unit_values = parser.values(\"hazard-unit\");\n    if (!hazard_unit_values.empty()) {\n        if (!config.set_hazard_unit(hazard_unit_values.last().toLower())) {\n            fprintf(stderr, \"Unknown kind of hazard unit specified\\n\");\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    parse_u32_option(parser, \"read-time\", config, &MachineConfig::set_memory_access_time_read);\n    parse_u32_option(parser, \"write-time\", config, &MachineConfig::set_memory_access_time_write);\n    parse_u32_option(parser, \"burst-time\", config, &MachineConfig::set_memory_access_time_burst);\n    parse_u32_option(parser, \"l2-time\", config, &MachineConfig::set_memory_access_time_level2);\n    if (!parser.values(\"burst-time\").empty()) config.set_memory_access_enable_burst(true);\n\n    configure_cache(*config.access_cache_data(), parser.values(\"d-cache\"), \"data\");\n    configure_cache(*config.access_cache_program(), parser.values(\"i-cache\"), \"instruction\");\n    configure_cache(*config.access_cache_level2(), parser.values(\"l2-cache\"), \"level2\");\n\n    configure_branch_predictor(config, parser.values(\"branch-predictor\"));\n\n    config.set_osemu_enable(parser.isSet(\"os-emulation\"));\n    config.set_osemu_known_syscall_stop(false);\n\n    int siz = parser.values(\"os-fs-root\").size();\n    if (siz >= 1) {\n        QString osemu_fs_root = parser.values(\"os-fs-root\").at(siz - 1);\n        if (osemu_fs_root.length() > 0) { config.set_osemu_fs_root(osemu_fs_root); }\n    }\n    siz = parser.values(\"isa-variant\").size();\n    for (int i = 0; i < siz; i++) {\n        int pos = 0;\n        bool first = true;\n        bool subtract = false;\n        QString isa_str = parser.values(\"isa-variant\").at(i).toUpper();\n        if (isa_str.startsWith(\"RV32\")) {\n            config.set_simulated_xlen(machine::Xlen::_32);\n            pos = 4;\n        } else if (isa_str.startsWith(\"RV64\")) {\n            config.set_simulated_xlen(machine::Xlen::_64);\n            pos = 4;\n        }\n        for (; pos < isa_str.size(); pos++, first = false) {\n            char ch = isa_str.at(pos).toLatin1();\n            if (ch == '+') {\n                subtract = false;\n                continue;\n            } else if (ch == '-') {\n                subtract = true;\n                continue;\n            }\n            auto flag = machine::ConfigIsaWord::byChar(ch);\n            if (flag.isEmpty()) continue;\n            if (first)\n                config.modify_isa_word(\n                    ~machine::ConfigIsaWord::empty(), machine::ConfigIsaWord::empty());\n            if (subtract)\n                config.modify_isa_word(flag, machine::ConfigIsaWord::empty());\n            else\n                config.modify_isa_word(flag, flag);\n        }\n    }\n}\n\nvoid configure_tracer(QCommandLineParser &p, Tracer &tr) {\n    if (p.isSet(\"trace-fetch\")) { tr.trace_fetch = true; }\n    if (p.isSet(\"pipelined\")) { // Following are added only if we have stages\n        if (p.isSet(\"trace-decode\")) { tr.trace_decode = true; }\n        if (p.isSet(\"trace-execute\")) { tr.trace_execute = true; }\n        if (p.isSet(\"trace-memory\")) { tr.trace_memory = true; }\n        if (p.isSet(\"trace-writeback\")) { tr.trace_writeback = true; }\n    }\n\n    if (p.isSet(\"trace-pc\")) { tr.trace_pc = true; }\n    if (p.isSet(\"trace-gp\")) { tr.trace_regs_gp = true; }\n\n    QStringList gps = p.values(\"trace-gp\");\n    for (const auto &gp : gps) {\n        if (gp == \"*\") {\n            tr.regs_to_trace.fill(true);\n        } else {\n            bool res;\n            size_t num = gp.toInt(&res);\n            if (res && num <= machine::REGISTER_COUNT) {\n                tr.regs_to_trace.at(num) = true;\n            } else {\n                fprintf(stderr, \"Unknown register number given for trace-gp: %s\\n\", qPrintable(gp));\n                exit(EXIT_FAILURE);\n            }\n        }\n    }\n\n    if (p.isSet(\"trace-rdmem\")) { tr.trace_rdmem = true; }\n    if (p.isSet(\"trace-wrmem\")) { tr.trace_wrmem = true; }\n\n    QStringList clim = p.values(\"cycle-limit\");\n    if (!clim.empty()) {\n        bool ok;\n        tr.cycle_limit = clim.at(clim.size() - 1).toLong(&ok);\n        if (!ok) {\n            fprintf(stderr, \"Cycle limit parse error\\n\");\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // TODO\n}\n\nvoid configure_reporter(QCommandLineParser &p, Reporter &r, const SymbolTable *symtab) {\n    if (p.isSet(\"dump-to-json\")) {\n        r.dump_format = (DumpFormat)(r.dump_format | DumpFormat::JSON);\n        r.dump_file_json = p.value(\"dump-to-json\");\n    }\n    if (p.isSet(\"disable-console-dump\")) {\n        r.dump_format = (DumpFormat)(r.dump_format & ~(DumpFormat::CONSOLE));\n    }\n    if (p.isSet(\"dump-registers\")) { r.enable_regs_reporting(); }\n    if (p.isSet(\"dump-cache-stats\")) { r.enable_cache_stats(); }\n    if (p.isSet(\"dump-cycles\")) { r.enable_cycles_reporting(); }\n    if (p.isSet(\"dump-symbol-table\")) { r.enable_symbol_table_reporting(); }\n    if (p.isSet(\"dump-branch-predictor\")) { r.enable_branch_predictor_stats(); }\n    if (p.isSet(\"dump-all\")) { r.enable_all_reporting(); }\n\n    QStringList fail = p.values(\"fail-match\");\n    for (const auto &i : fail) {\n        for (int y = 0; y < i.length(); y++) {\n            enum Reporter::FailReason reason;\n            switch (tolower(i.toStdString()[y])) {\n            case 'i': reason = Reporter::FR_UNSUPPORTED_INSTR; break;\n            default:\n                fprintf(stderr, \"Unknown fail condition: %c\\n\", qPrintable(i)[y]);\n                exit(EXIT_FAILURE);\n            }\n            r.expect_fail(reason);\n        }\n    }\n    if (p.isSet(\"expect-fail\") && !p.isSet(\"fail-match\")) { r.expect_fail(Reporter::FailAny); }\n\n    foreach (QString range_arg, p.values(\"dump-range\")) {\n        uint64_t len;\n        bool ok1 = true;\n        bool ok2 = true;\n        QString str;\n        int comma1 = range_arg.indexOf(\",\");\n        if (comma1 < 0) {\n            fprintf(stderr, \"Range start missing\\n\");\n            exit(EXIT_FAILURE);\n        }\n        int comma2 = range_arg.indexOf(\",\", comma1 + 1);\n        if (comma2 < 0) {\n            fprintf(stderr, \"Range length/name missing\\n\");\n            exit(EXIT_FAILURE);\n        }\n        str = range_arg.mid(0, comma1);\n        Address start;\n        if (str.size() >= 1 && !str.at(0).isDigit() && symtab != nullptr) {\n            SymbolValue _start;\n            ok1 = symtab->name_to_value(_start, str);\n            start = Address(_start);\n        } else {\n            start = Address(str.toULong(&ok1, 0));\n        }\n        str = range_arg.mid(comma1 + 1, comma2 - comma1 - 1);\n        if (str.size() >= 1 && !str.at(0).isDigit() && symtab != nullptr) {\n            ok2 = symtab->name_to_value(len, str);\n        } else {\n            len = str.toULong(&ok2, 0);\n        }\n        if (!ok1 || !ok2) {\n            fprintf(stderr, \"Range start/length specification error.\\n\");\n            exit(EXIT_FAILURE);\n        }\n        r.add_dump_range(start, len, range_arg.mid(comma2 + 1));\n    }\n\n    // TODO\n}\n\nvoid configure_serial_port(QCommandLineParser &p, SerialPort *ser_port) {\n    CharIOHandler *ser_in = nullptr;\n    CharIOHandler *ser_out = nullptr;\n    int siz;\n\n    if (!ser_port) { return; }\n\n    siz = p.values(\"serial-in\").size();\n    if (siz >= 1) {\n        QIODevice::OpenMode mode = QFile::ReadOnly;\n        auto *qf = new QFile(p.values(\"serial-in\").at(siz - 1));\n        ser_in = new CharIOHandler(qf, ser_port);\n        siz = p.values(\"serial-out\").size();\n        if (siz) {\n            if (p.values(\"serial-in\").at(siz - 1) == p.values(\"serial-out\").at(siz - 1)) {\n                mode = QFile::ReadWrite;\n                ser_out = ser_in;\n            }\n        }\n        if (!ser_in->open(mode)) {\n            fprintf(stderr, \"Serial port input file cannot be open for read.\\n\");\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    if (!ser_out) {\n        siz = p.values(\"serial-out\").size();\n        if (siz >= 1) {\n            auto *qf = new QFile(p.values(\"serial-out\").at(siz - 1));\n            ser_out = new CharIOHandler(qf, ser_port);\n            if (!ser_out->open(QFile::WriteOnly)) {\n                fprintf(stderr, \"Serial port output file cannot be open for write.\\n\");\n                exit(EXIT_FAILURE);\n            }\n        }\n    }\n\n    if (ser_in) {\n        QObject::connect(ser_in, &QIODevice::readyRead, ser_port, &SerialPort::rx_queue_check);\n        QObject::connect(ser_port, &SerialPort::rx_byte_pool, ser_in, &CharIOHandler::readBytePoll);\n        if (ser_in->bytesAvailable()) { ser_port->rx_queue_check(); }\n    }\n\n    if (ser_out) {\n        QObject::connect(\n            ser_port, &SerialPort::tx_byte, ser_out,\n            QOverload<unsigned>::of(&CharIOHandler::writeByte));\n    }\n}\n\nvoid configure_osemu(QCommandLineParser &p, MachineConfig &config, Machine *machine) {\n    CharIOHandler *std_out = nullptr;\n    QScopedPointer<QString> std_in_path;\n    int siz;\n\n    siz = p.values(\"std-in\").size();\n    if (siz >= 1) {\n        std_in_path.reset(new QString(p.values(\"std-in\").at(siz - 1)));\n    }\n    siz = p.values(\"std-out\").size();\n    if (siz >= 1) {\n        auto *qf = new QFile(p.values(\"std-out\").at(siz - 1));\n        std_out = new CharIOHandler(qf, machine);\n        if (!std_out->open(QFile::WriteOnly)) {\n            fprintf(stderr, \"Emulated system standard output file cannot be open for write.\\n\");\n            exit(EXIT_FAILURE);\n        }\n    }\n    const static machine::ExceptionCause ecall_variats[]\n        = { machine::EXCAUSE_ECALL_ANY, machine::EXCAUSE_ECALL_M, machine::EXCAUSE_ECALL_S,\n            machine::EXCAUSE_ECALL_U };\n\n    if (config.osemu_enable()) {\n        auto *osemu_handler = new osemu::OsSyscallExceptionHandler(\n            config.osemu_known_syscall_stop(), config.osemu_unknown_syscall_stop(),\n            config.osemu_fs_root());\n        if (!std_in_path.isNull() && !std_in_path->isEmpty()) {\n            if (!osemu_handler->map_stdin_to_hostfile(*std_in_path)) {\n                fprintf(stderr, \"Failed to map stdin to host file '%s'\\n\", std_in_path->toLatin1().data());\n                exit(EXIT_FAILURE);\n            }\n        }\n        if (std_out) {\n            machine::Machine::connect(\n                osemu_handler, &osemu::OsSyscallExceptionHandler::char_written, std_out,\n                QOverload<int, unsigned>::of(&CharIOHandler::writeByte));\n        }\n        /*connect(\n            osemu_handler, &osemu::OsSyscallExceptionHandler::rx_byte_pool, terminal,\n            &TerminalDock::rx_byte_pool);*/\n        for (auto ecall_variat : ecall_variats) {\n            machine->register_exception_handler(ecall_variat, osemu_handler);\n            machine->set_step_over_exception(ecall_variat, true);\n            machine->set_stop_on_exception(ecall_variat, false);\n        }\n    } else {\n        for (auto ecall_variat : ecall_variats) {\n            machine->set_step_over_exception(ecall_variat, false);\n            machine->set_stop_on_exception(ecall_variat, config.osemu_exception_stop());\n        }\n    }\n}\n\nvoid load_ranges(Machine &machine, const QStringList &ranges) {\n    for (const QString &range_arg : ranges) {\n        bool ok = true;\n        QString str;\n        int comma1 = range_arg.indexOf(\",\");\n        if (comma1 < 0) {\n            fprintf(stderr, \"Range start missing\\n\");\n            exit(EXIT_FAILURE);\n        }\n        str = range_arg.mid(0, comma1);\n        Address start;\n        if (str.size() >= 1 && !str.at(0).isDigit() && machine.symbol_table() != nullptr) {\n            SymbolValue _start;\n            ok = machine.symbol_table()->name_to_value(_start, str);\n            start = Address(_start);\n        } else {\n            start = Address(str.toULong(&ok, 0));\n        }\n        if (!ok) {\n            fprintf(stderr, \"Range start/length specification error.\\n\");\n            exit(EXIT_FAILURE);\n        }\n        ifstream in;\n        in.open(range_arg.mid(comma1 + 1).toLocal8Bit().data(), ios::in);\n        Address addr = start;\n        for (std::string line; getline(in, line);) {\n            size_t end_pos = line.find_last_not_of(\" \\t\\n\");\n            if (std::string::npos == end_pos) { continue; }\n\n            size_t start_pos = line.find_first_not_of(\" \\t\\n\");\n            line = line.substr(0, end_pos + 1);\n            line = line.substr(start_pos);\n\n            size_t idx;\n            uint32_t val = stoul(line, &idx, 0);\n            if (idx != line.size()) {\n                fprintf(stderr, \"cannot parse load range data.\\n\");\n                exit(EXIT_FAILURE);\n            }\n            machine.memory_data_bus_rw()->write_u32(addr, val, ae::INTERNAL);\n            addr += 4;\n        }\n        in.close();\n    }\n}\n\nbool assemble(Machine &machine, MsgReport &msgrep, const QString &filename) {\n    SymbolTableDb symbol_table_db(machine.symbol_table_rw(true));\n    machine::FrontendMemory *mem = machine.memory_data_bus_rw();\n    if (mem == nullptr) { return false; }\n    machine.cache_sync();\n    SimpleAsm assembler;\n\n    SimpleAsm::connect(&assembler, &SimpleAsm::report_message, &msgrep, &MsgReport::report_message);\n\n    assembler.setup(mem, &symbol_table_db, 0x00000200_addr, machine.core()->get_xlen());\n\n    if (!assembler.process_file(filename)) { return false; }\n\n    return assembler.finish();\n}\n\nint main(int argc, char *argv[]) {\n    QCoreApplication app(argc, argv);\n    QCoreApplication::setApplicationName(APP_NAME);\n    QCoreApplication::setApplicationVersion(APP_VERSION);\n    set_default_log_pattern();\n\n    QCommandLineParser p;\n    create_parser(p);\n    p.process(app);\n\n    MachineConfig config;\n    configure_machine(p, config);\n\n    bool asm_source = p.isSet(\"asm\");\n    Machine machine(config, !asm_source, !asm_source);\n\n    Tracer tr(&machine);\n    configure_tracer(p, tr);\n\n    configure_serial_port(p, machine.serial_port());\n\n    configure_osemu(p, config, &machine);\n\n    if (asm_source) {\n        MsgReport msg_report(&app);\n        if (!assemble(machine, msg_report, p.positionalArguments()[0])) { exit(EXIT_FAILURE); }\n    }\n\n    Reporter r(&app, &machine);\n    configure_reporter(p, r, machine.symbol_table());\n\n    QObject::connect(&tr, &Tracer::cycle_limit_reached, &r, &Reporter::cycle_limit_reached);\n\n    load_ranges(machine, p.values(\"load-range\"));\n\n    if (p.isSet(\"only-dump\")) {\n        QMetaObject::invokeMethod(&machine, &Machine::program_exit, Qt::QueuedConnection);\n    } else {\n        // QTimer::singleShot(0, &machine, &Machine::play); alternative\n        QMetaObject::invokeMethod(&machine, &Machine::play, Qt::QueuedConnection);\n    }\n    return QCoreApplication::exec();\n}\n"
  },
  {
    "path": "src/cli/msgreport.cpp",
    "content": "#include \"msgreport.h\"\n\n#include <iostream>\n\nusing namespace std;\n\nMsgReport::MsgReport(QCoreApplication *app) : Super(app) {}\n\nvoid MsgReport::report_message(\n    messagetype::Type type,\n    const QString &file,\n    int line,\n    int column,\n    const QString &text,\n    const QString &hint) {\n    (void)hint;\n\n    QString typestr = \"error\";\n    switch (type) {\n    case messagetype::MSG_ERROR: typestr = \"error\"; break;\n    case messagetype::MSG_WARNING: typestr = \"warning\"; break;\n    case messagetype::MSG_INFO: typestr = \"info\"; break;\n    default: return;\n    }\n\n    printf(\"%s:\", qPrintable(file));\n    if (line != 0) { printf(\"%d:\", line); }\n    if (column != 0) { printf(\"%d:\", column); }\n    printf(\"%s:%s\\n\", qPrintable(typestr), qPrintable(text));\n}\n"
  },
  {
    "path": "src/cli/msgreport.h",
    "content": "#ifndef MSGREPORT_H\n#define MSGREPORT_H\n\n#include \"assembler/messagetype.h\"\n\n#include <QCoreApplication>\n#include <QObject>\n#include <QString>\n#include <QVector>\n\nclass MsgReport : public QObject {\n    Q_OBJECT\n\n    using Super = QObject;\n\npublic:\n    explicit MsgReport(QCoreApplication *app);\n\npublic slots:\n    static void report_message(\n        messagetype::Type type,\n        const QString &file,\n        int line,\n        int column,\n        const QString &text,\n        const QString &hint);\n};\n\n#endif // MSGREPORT_H\n"
  },
  {
    "path": "src/cli/reporter.cpp",
    "content": "#include \"reporter.h\"\n\n#include <cinttypes>\n\nusing namespace machine;\nusing namespace std;\n\nReporter::Reporter(QCoreApplication *app, Machine *machine)\n    : QObject()\n    , app(app)\n    , machine(machine) {\n    connect(machine, &Machine::program_exit, this, &Reporter::machine_exit);\n    connect(machine, &Machine::program_trap, this, &Reporter::machine_trap);\n    connect(\n        machine->core(), &Core::stop_on_exception_reached, this,\n        &Reporter::machine_exception_reached);\n}\n\nvoid Reporter::add_dump_range(Address start, size_t len, const QString &path_to_write) {\n    dump_ranges.append({ start, len, path_to_write });\n}\n\nvoid Reporter::machine_exit() {\n    report();\n    if (e_fail != 0) {\n        printf(\"Machine was expected to fail but it didn't.\\n\");\n        exit(1);\n    } else {\n        exit(0);\n    }\n}\n\n/* TODO: Decide whether this should be moved to machine to avoid duplication or kept aside as\n         a visualization concern */\nconstexpr const char *get_exception_name(ExceptionCause exception_cause) {\n    switch (exception_cause) {\n    case EXCAUSE_NONE: return \"NONE\";\n    case EXCAUSE_INSN_FAULT: return \"INSN_FAULT\";\n    case EXCAUSE_INSN_ILLEGAL: return \"INSN_ILLEGAL\";\n    case EXCAUSE_BREAK: return \"BREAK\";\n    case EXCAUSE_LOAD_MISALIGNED: return \"LOAD_MISALIGNED\";\n    case EXCAUSE_LOAD_FAULT: return \"LOAD_FAULT\";\n    case EXCAUSE_STORE_MISALIGNED: return \"STORE_MISALIGNED\";\n    case EXCAUSE_STORE_FAULT: return \"STORE_FAULT\";\n    case EXCAUSE_ECALL_U: return \"ECALL_U\";\n    case EXCAUSE_ECALL_S: return \"ECALL_S\";\n    case EXCAUSE_RESERVED_10: return \"RESERVED_10\";\n    case EXCAUSE_ECALL_M: return \"ECALL_M\";\n    case EXCAUSE_INSN_PAGE_FAULT: return \"INSN_PAGE_FAULT\";\n    case EXCAUSE_LOAD_PAGE_FAULT: return \"LOAD_PAGE_FAULT\";\n    case EXCAUSE_RESERVED_14: return \"RESERVED_14\";\n    case EXCAUSE_STORE_PAGE_FAULT: return \"STORE_PAGE_FAULT\";\n    // Simulator specific exception cause codes, alliases\n    case EXCAUSE_HWBREAK: return \"HWBREAK\";\n    case EXCAUSE_ECALL_ANY: return \"ECALL_ANY\";\n    case EXCAUSE_INT: return \"INT\";\n    default: UNREACHABLE\n    }\n}\n\nvoid Reporter::machine_exception_reached() {\n    ExceptionCause excause = machine->get_exception_cause();\n    printf(\"Machine stopped on %s exception.\\n\", get_exception_name(excause));\n    report();\n    exit(0);\n}\n\nvoid Reporter::cycle_limit_reached() {\n    printf(\"Specified cycle limit reached\\n\");\n    report();\n    exit(0);\n}\n\nvoid Reporter::machine_trap(SimulatorException &e) {\n    report();\n\n    bool expected = false;\n    if (typeid(e) == typeid(SimulatorExceptionUnsupportedInstruction)) {\n        expected = e_fail & FR_UNSUPPORTED_INSTR;\n    }\n\n    printf(\"Machine trapped: %s\\n\", qPrintable(e.msg(false)));\n    exit(expected ? 0 : 1);\n}\n\nvoid Reporter::report() {\n    if (dump_format & DumpFormat::CONSOLE) {\n        if (e_regs | e_cycles | e_cycles | e_fail) { printf(\"Machine state report:\\n\"); }\n    }\n\n    if (e_regs) { report_regs(); }\n    if (e_cache_stats) { report_caches(); }\n    if (e_cycles) {\n        QString cycle_count = QString::asprintf(\"%\" PRIu32, machine->core()->get_cycle_count());\n        QString stall_count = QString::asprintf(\"%\" PRIu32, machine->core()->get_stall_count());\n        if (dump_format & DumpFormat::JSON) {\n            QJsonObject temp = {};\n            temp[\"cycles\"] = cycle_count;\n            temp[\"stalls\"] = stall_count;\n            dump_data_json[\"cycles\"] = temp;\n        }\n        if (dump_format & DumpFormat::CONSOLE) {\n            printf(\"cycles: %s\\n\", qPrintable(cycle_count));\n            printf(\"stalls: %s\\n\", qPrintable(stall_count));\n        }\n    }\n    for (const DumpRange &range : dump_ranges) {\n        report_range(range);\n    }\n\n    if (e_symtab) {\n        QJsonObject symtab_json = {};\n\n        for (const auto &name : machine->symbol_table()->names()) {\n            SymbolValue sym_val;\n            machine->symbol_table()->name_to_value(sym_val, name);\n            QString value = QString::asprintf(\n                machine->core()->get_xlen() == Xlen::_32 ? \"0x%08\" PRIx64 : \"0x%016\" PRIx64,\n                sym_val);\n            if (dump_format & DumpFormat::JSON) { symtab_json[name] = value; }\n            if (dump_format & DumpFormat::CONSOLE) {\n                printf(\"SYM[%s]: %s\\n\", qPrintable(name), qPrintable(value));\n            }\n        }\n\n        if (dump_format & DumpFormat::JSON) { dump_data_json[\"symbols\"] = symtab_json; }\n    }\n\n    if (e_predictor) { report_predictor(); }\n\n    if (dump_format & DumpFormat::JSON) {\n        QFile file(dump_file_json);\n        QByteArray bytes = QJsonDocument(dump_data_json).toJson(QJsonDocument::Indented);\n        if (file.open(QIODevice::WriteOnly)) {\n            file.write(bytes);\n            file.close();\n            printf(\"JSON object written to file\\n\");\n        } else {\n            printf(\"Could not open file for writing\\n\");\n        }\n    }\n}\n\nvoid Reporter::report_regs() {\n    if (dump_format & DumpFormat::JSON) { dump_data_json[\"regs\"] = {}; }\n    report_pc();\n    for (unsigned i = 0; i < REGISTER_COUNT; i++) {\n        report_gp_reg(i, (i == REGISTER_COUNT - 1));\n    }\n    for (size_t i = 0; i < CSR::REGISTERS.size(); i++) {\n        report_csr_reg(i, (i == CSR::REGISTERS.size() - 1));\n    }\n}\n\nvoid Reporter::report_pc() {\n    QString value = QString::asprintf(\"0x%08\" PRIx64, machine->registers()->read_pc().get_raw());\n    if (dump_format & DumpFormat::JSON) {\n        QJsonObject regs = dump_data_json[\"regs\"].toObject();\n        regs[\"PC\"] = value;\n        dump_data_json[\"regs\"] = regs;\n    }\n    if (dump_format & DumpFormat::CONSOLE) { printf(\"PC:%s\\n\", qPrintable(value)); }\n}\n\nvoid Reporter::report_gp_reg(unsigned int i, bool last) {\n    QString key = QString::asprintf(\"R%u\", i);\n    QString value = QString::asprintf(\"0x%08\" PRIx64, machine->registers()->read_gp(i).as_u64());\n    if (dump_format & DumpFormat::JSON) {\n        QJsonObject regs = dump_data_json[\"regs\"].toObject();\n        regs[key] = value;\n        dump_data_json[\"regs\"] = regs;\n    }\n    if (dump_format & DumpFormat::CONSOLE) {\n        printf(\"%s:%s%s\", qPrintable(key), qPrintable(value), (last) ? \"\\n\" : \" \");\n    }\n}\n\nvoid Reporter::report_csr_reg(size_t internal_id, bool last) {\n    QString key = QString::asprintf(\"%s\", CSR::REGISTERS[internal_id].name);\n    QString value = QString::asprintf(\n        \"0x%08\" PRIx64, machine->control_state()->read_internal(internal_id).as_u64());\n    if (dump_format & DumpFormat::JSON) {\n        QJsonObject regs = dump_data_json[\"regs\"].toObject();\n        regs[key] = value;\n        dump_data_json[\"regs\"] = regs;\n    }\n    if (dump_format & DumpFormat::CONSOLE) {\n        printf(\"%s: %s%s\", qPrintable(key), qPrintable(value), (last) ? \"\\n\" : \" \");\n    }\n}\n\nvoid Reporter::report_caches() {\n    if (dump_format & DumpFormat::JSON) { dump_data_json[\"caches\"] = {}; }\n    printf(\"Cache statistics report:\\n\");\n    report_cache(\"i-cache\", *machine->cache_program());\n    report_cache(\"d-cache\", *machine->cache_data());\n    if (machine->config().cache_level2().enabled()) {\n        report_cache(\"l2-cache\", *machine->cache_level2());\n    }\n}\n\nvoid Reporter::report_cache(const char *cache_name, const Cache &cache) {\n    if (dump_format & DumpFormat::JSON) {\n        QJsonObject caches = dump_data_json[\"caches\"].toObject();\n        QJsonObject temp = {};\n        temp[\"reads\"] = QString::asprintf(\"%\" PRIu32, cache.get_read_count());\n        temp[\"hit\"] = QString::asprintf(\"%\" PRIu32, cache.get_hit_count());\n        temp[\"miss\"] = QString::asprintf(\"%\" PRIu32, cache.get_miss_count());\n        temp[\"hit_rate\"] = QString::asprintf(\"%.3lf\", cache.get_hit_rate());\n        temp[\"stalled_cycles\"] = QString::asprintf(\"%\" PRIu32, cache.get_stall_count());\n        temp[\"improved_speed\"] = QString::asprintf(\"%.3lf\", cache.get_speed_improvement());\n        caches[cache_name] = temp;\n        dump_data_json[\"caches\"] = caches;\n    }\n    if (dump_format & DumpFormat::CONSOLE) {\n        printf(\"%s:reads: %\" PRIu32 \"\\n\", cache_name, cache.get_read_count());\n        printf(\"%s:hit: %\" PRIu32 \"\\n\", cache_name, cache.get_hit_count());\n        printf(\"%s:miss: %\" PRIu32 \"\\n\", cache_name, cache.get_miss_count());\n        printf(\"%s:hit-rate: %.3lf\\n\", cache_name, cache.get_hit_rate());\n        printf(\"%s:stalled-cycles: %\" PRIu32 \"\\n\", cache_name, cache.get_stall_count());\n        printf(\"%s:improved-speed: %.3lf\\n\", cache_name, cache.get_speed_improvement());\n    }\n}\n\nvoid Reporter::report_predictor() {\n    const BranchPredictor *predictor = machine->branch_predictor();\n    if (predictor == nullptr) { return; }\n    const PredictionStatistics *stats = predictor->get_stats();\n    if (stats == nullptr) { return; }\n\n    if (dump_format & DumpFormat::JSON) { \n        QJsonObject predictorObj = dump_data_json[\"predictor\"].toObject(); \n        QJsonObject temp = {};\n        temp[\"total_predictions\"] = QString::asprintf(\"%\" PRIu32, stats->total);\n        temp[\"hits\"] = QString::asprintf(\"%\" PRIu32,  stats->correct);\n        temp[\"misses\"] = QString::asprintf(\"%\" PRIu32, stats->wrong);\n        temp[\"accuracy\"] = QString::asprintf(\"%.3lf\", stats->accuracy);\n        predictorObj = temp;\n        dump_data_json[\"predictor\"] = predictorObj;\n    }\n    if (dump_format & DumpFormat::CONSOLE) {\n        printf(\"branch predictor:total predictions: %\" PRIu32 \"\\n\", stats->total);\n        printf(\"branch predictor:hits: %\" PRIu32 \"\\n\", stats->correct);\n        printf(\"branch predictor:misses: %\" PRIu32 \"\\n\", stats->wrong);\n        printf(\"branch predictor:accuracy: %.3lf\\n\", stats->accuracy);\n    }\n}\n\nvoid Reporter::report_range(const Reporter::DumpRange &range) {\n    FILE *out = fopen(range.path_to_write.toLocal8Bit().data(), \"w\");\n    if (out == nullptr) {\n        fprintf(\n            stderr, \"Failed to open %s for writing\\n\", range.path_to_write.toLocal8Bit().data());\n        return;\n    }\n    Address start = range.start & ~3;\n    Address end = range.start + range.len;\n    if (end < start) { end = 0xffffffff_addr; }\n    // TODO: report also cached memory?\n    const MemoryDataBus *mem = machine->memory_data_bus();\n    for (Address addr = start; addr < end; addr += 4) {\n        fprintf(out, \"0x%08\" PRIx32 \"\\n\", mem->read_u32(addr, ae::INTERNAL));\n    }\n    if (fclose(out)) {\n        fprintf(stderr, \"Failure closing %s\\n\", range.path_to_write.toLocal8Bit().data());\n    }\n}\n\nvoid Reporter::exit(int retcode) {\n    // Exit might not happen immediately, so we need to stop the machine explicitly.\n    machine->pause();\n    QCoreApplication::exit(retcode);\n}\n"
  },
  {
    "path": "src/cli/reporter.h",
    "content": "#ifndef REPORTER_H\n#define REPORTER_H\n\n#include \"common/memory_ownership.h\"\n#include \"machine/machine.h\"\n\n#include <QCoreApplication>\n#include <QFile>\n#include <QJsonDocument>\n#include <QJsonObject>\n#include <QObject>\n#include <QString>\n\nusing machine::Address;\n\nenum DumpFormat {\n    CONSOLE = 1 << 0,\n    JSON = 1 << 1,\n};\n\n/**\n * Watches for special events in the machine (e.g. stop, exception, trap) and prints related\n * information.\n */\nclass Reporter : public QObject {\n    Q_OBJECT\n\npublic:\n    Reporter(QCoreApplication *app, machine::Machine *machine);\n\n    void enable_regs_reporting() { e_regs = true; };\n    void enable_cache_stats() { e_cache_stats = true; };\n    void enable_cycles_reporting() { e_cycles = true; };\n    void enable_symbol_table_reporting() { e_symtab = true; };\n    void enable_branch_predictor_stats() { e_predictor = true; };\n    void enable_all_reporting() {\n        e_regs = true;\n        e_cache_stats = true;\n        e_cycles = true;\n        e_symtab = true;\n        e_predictor = true;\n    }\n\n    enum FailReason {\n        FR_NONE = 0,\n        FR_UNSUPPORTED_INSTR = (1 << 0),\n    };\n    static const enum FailReason FailAny = FR_UNSUPPORTED_INSTR;\n    void expect_fail(enum FailReason reason) { e_fail = (FailReason)(e_fail | reason); };\n\n    struct DumpRange {\n        Address start;\n        size_t len;\n        /** Path to file, where this range will be dumped. */\n        QString path_to_write;\n    };\n    void add_dump_range(Address start, size_t len, const QString &path_to_write);\n\npublic slots:\n    void cycle_limit_reached();\n\nprivate slots:\n    void machine_exit();\n    void machine_trap(machine::SimulatorException &e);\n    void machine_exception_reached();\n\nprivate:\n    BORROWED QCoreApplication *const app;\n    BORROWED machine::Machine *const machine;\n    QVector<DumpRange> dump_ranges;\n\n    bool e_regs = false;\n    bool e_cache_stats = false;\n    bool e_cycles = false;\n    bool e_symtab = false;\n    bool e_predictor = false;\n    FailReason e_fail = FR_NONE;\n\n    void report();\n    void report_pc();\n    void report_regs();\n    void report_caches();\n    void report_range(const DumpRange &range);\n    void report_csr_reg(size_t internal_id, bool last);\n    void report_gp_reg(unsigned int i, bool last);\n    void report_cache(const char *cache_name, const machine::Cache &cache);\n    void report_predictor();\n\n    void exit(int retcode);\n\npublic:\n    DumpFormat dump_format = DumpFormat::CONSOLE;\n    QString dump_file_json;\n    QJsonObject dump_data_json = {};\n};\n\n#endif // REPORTER_H\n"
  },
  {
    "path": "src/cli/tracer.cpp",
    "content": "#include \"tracer.h\"\n\n#include <cinttypes>\n\nusing namespace machine;\n\nTracer::Tracer(Machine *machine) : core_state(machine->core()->get_state()) {\n    cycle_limit = 0;\n\n    connect(machine->core(), &Core::step_done, this, &Tracer::step_output);\n}\n\ntemplate<typename StageStruct>\nvoid trace_instruction_in_stage(\n    const char *stage_name,\n    const StageStruct &stage,\n    const WritebackInternalState &wb) {\n    printf(\n        \"%s: %s%s\\n\", stage_name, (stage.excause != EXCAUSE_NONE) ? \"!\" : \"\",\n        qPrintable(wb.inst.to_str(stage.inst_addr)));\n}\n\nvoid Tracer::step_output() {\n    const auto &if_id = core_state.pipeline.fetch.final;\n    const auto &id_ex = core_state.pipeline.decode.final;\n    const auto &ex_mem = core_state.pipeline.execute.final;\n    const auto &mem = core_state.pipeline.memory.internal;\n    const auto &mem_wb = core_state.pipeline.memory.final;\n    const auto &wb = core_state.pipeline.writeback.internal;\n    if (trace_fetch) { trace_instruction_in_stage(\"Fetch\", if_id, wb); }\n    if (trace_decode) { trace_instruction_in_stage(\"Decode\", id_ex, wb); }\n    if (trace_execute) { trace_instruction_in_stage(\"Execute\", ex_mem, wb); }\n    if (trace_memory) { trace_instruction_in_stage(\"Memory\", mem_wb, wb); }\n    if (trace_writeback) {\n        // All exceptions are resolved in memory, therefore there is no excause field in WB.\n        printf(\"Writeback: %s\\n\", qPrintable(wb.inst.to_str(wb.inst_addr)));\n    }\n    if (trace_pc) { printf(\"PC: %\" PRIx64 \"\\n\", if_id.inst_addr.get_raw()); }\n    if (trace_regs_gp && wb.regwrite && regs_to_trace.at(wb.num_rd)) {\n        printf(\"GP %zu: %\" PRIx64 \"\\n\", size_t(wb.num_rd), wb.value.as_u64());\n    }\n    if (trace_rdmem && mem_wb.memtoreg) {\n        printf(\n            \"MEM[%\" PRIx64 \"]:  RD %\" PRIx64 \"\\n\", mem_wb.mem_addr.get_raw(),\n            mem_wb.towrite_val.as_u64());\n    }\n    if (trace_wrmem && mem.memwrite) {\n        printf(\n            \"MEM[%\" PRIx64 \"]:  WR %\" PRIx64 \"\\n\", mem_wb.mem_addr.get_raw(),\n            mem.mem_write_val.as_u64());\n    }\n    if ((cycle_limit != 0) && (core_state.cycle_count >= cycle_limit)) {\n        emit cycle_limit_reached();\n    }\n}\n"
  },
  {
    "path": "src/cli/tracer.h",
    "content": "#ifndef TRACER_H\n#define TRACER_H\n\n#include \"machine/instruction.h\"\n#include \"machine/machine.h\"\n#include \"machine/memory/address.h\"\n#include \"machine/registers.h\"\n\n#include <QObject>\n\n/**\n * Watches the step by step execution of the machine and prints requested state.\n */\nclass Tracer final : public QObject {\n    Q_OBJECT\npublic:\n    explicit Tracer(machine::Machine *machine);\n\nsignals:\n    void cycle_limit_reached();\n\nprivate slots:\n    void step_output();\n\nprivate:\n    const machine::CoreState &core_state;\n\npublic:\n    std::array<bool, machine::REGISTER_COUNT> regs_to_trace = {};\n    bool trace_fetch = false, trace_decode = false, trace_execute = false, trace_memory = false,\n         trace_writeback = false, trace_pc = false, trace_wrmem = false, trace_rdmem = false,\n         trace_regs_gp = false;\n    quint64 cycle_limit;\n};\n\n#endif // TRACER_H\n"
  },
  {
    "path": "src/common/CMakeLists.txt",
    "content": "project(common\n\t\tDESCRIPTION \"Utils shared between multiple subprojects.\")\n\nadd_subdirectory(polyfills)\n\nset(common_HEADERS\n\t\tendian.h\n\t\tstring_utils.h\n\t\tlogging.h\n\t\tlogging_format_colors.h\n\t\tcontainers/cvector.h\n\t\tmath/bit_ops.h\n\t\tmemory_ownership.h\n\t\ttype_utils/lens.h\n\t\t)\n\n# =============================================================================\n# Target for usage of this header library\n# - this is header only library and it is implicitly available in the whole\n#   project\n# =============================================================================\n\n# =============================================================================\n# Tests\n# - It is highly recommended to run these tests anytime you use a different\n#   compiler or platform as this code is platform/compiler specific.\n# =============================================================================\n\nenable_testing()\nset(CMAKE_AUTOMOC ON)\n\n# Put tests here...\n\nadd_custom_target(common_unit_tests\n\t\tDEPENDS mulh64_test)\n"
  },
  {
    "path": "src/common/containers/cvector.h",
    "content": "/*\n * Based on:\n * Frozen\n * Copyright 2016 QuarksLab\n *\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied.  See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\n#ifndef QTRVSIM_CVECTOR_H\n#define QTRVSIM_CVECTOR_H\n\n#include <array>\n#include <iterator>\n#include <string>\n#include <utility>\n\n/** Constexpr vector with inline storage */\ntemplate<typename T, std::size_t N>\nclass cvector {\n    T data[N]; // Uninitialized storage cannot be used in constexpr.\n    std::size_t dsize = 0;\n\npublic:\n    // Container typdefs\n    using value_type = T;\n    using reference = value_type &;\n    using const_reference = const value_type &;\n    using pointer = value_type *;\n    using const_pointer = const value_type *;\n    using iterator = pointer;\n    using const_iterator = const_pointer;\n    using size_type = std::size_t;\n    using difference_type = std::ptrdiff_t;\n\n    // Constructors\n    constexpr cvector() = default;\n    constexpr cvector(size_type count, const T &value) : dsize(count) {\n        for (std::size_t i = 0; i < N; ++i)\n            data[i] = value;\n    }\n    constexpr cvector(std::initializer_list<value_type> initializer_list) {\n        for (auto item : initializer_list) {\n            push_back(item);\n        }\n    }\n\n    // Iterators\n    constexpr iterator begin() noexcept { return data; }\n    constexpr const_iterator begin() const noexcept { return data; }\n    constexpr const_iterator cbegin() const noexcept { return data; }\n    constexpr iterator end() noexcept { return data + dsize; }\n    constexpr const_iterator end() const noexcept { return data + dsize; }\n    constexpr const_iterator cend() const noexcept { return data + dsize; }\n\n    // Capacity\n    [[nodiscard]] constexpr size_type size() const { return dsize; }\n\n    // Element access\n    constexpr reference operator[](std::size_t index) { return data[index]; }\n    constexpr const_reference operator[](std::size_t index) const { return data[index]; }\n\n    constexpr reference back() { return data[dsize - 1]; }\n    constexpr const_reference back() const { return data[dsize - 1]; }\n\n    // Modifiers\n    constexpr void push_back(const T &a) { data[dsize++] = a; }\n    constexpr void push_back(T &&a) { data[dsize++] = std::move(a); }\n    constexpr void pop_back() { --dsize; }\n\n    constexpr void clear() { dsize = 0; }\n};\n#endif // QTRVSIM_CVECTOR_H\n"
  },
  {
    "path": "src/common/endian.h",
    "content": "#ifndef ENDIAN_H\n#define ENDIAN_H\n\n#include \"polyfills/byteswap.h\"\n#include \"polyfills/endian_detection.h\"\n#include \"utils.h\"\n\n#include <QMetaType>\n#include <algorithm>\n#include <array>\n#include <cstdint>\n#include <cstdlib>\n#include <stdexcept>\n#include <type_traits>\n\n/**\n * Memory endian type (used for bot simulator and host machine).\n * Standard enum is not available until c++20.\n */\nenum Endian { LITTLE, BIG };\n\ninline const char *to_string(Endian val) {\n    switch (val) {\n    case LITTLE: return \"LITTLE\";\n    case BIG: return \"BIG\";\n    }\n    UNREACHABLE;\n}\n\ninline constexpr Endian get_native_endian() {\n#if (defined(__BIG_ENDIAN__))\n    return BIG;\n#elif (defined(__LITTLE_ENDIAN__))\n    return LITTLE;\n#else\n    static_assert(false, \"Could not detect endian or endian is neither big nor little.\");\n#endif\n}\n\nconstexpr Endian NATIVE_ENDIAN = get_native_endian();\n\n/**\n * Full generic byteswap. Used in generic functions.\n * Optimized specializations are provided bellow.\n */\ntemplate<typename T>\ninline T byteswap(T val) {\n    static_assert(sizeof(T) <= 8, \"Byteswap of large types is implementation dependant.\");\n\n    union U {\n        T val;\n        std::array<uint8_t, sizeof(T)> raw;\n    } src, dst;\n\n    src.val = val;\n    std::reverse_copy(src.raw.begin(), src.raw.end(), dst.raw.begin());\n    return dst.val;\n}\ntemplate<>\ninline uint8_t byteswap(uint8_t val) {\n    // NOP for single byte\n    return val;\n}\ntemplate<>\ninline uint16_t byteswap(uint16_t val) {\n    return bswap16(val);\n}\ntemplate<>\ninline uint32_t byteswap(uint32_t val) {\n    return bswap32(val);\n}\ntemplate<>\ninline uint64_t byteswap(uint64_t val) {\n    return bswap64(val);\n}\n/**\n * Conditionally byteswap value. Condition if usually mismatch of endian on\n * interface of two memory levels or memory and core.\n */\ntemplate<typename T>\nT byteswap_if(T value, bool condition) {\n    return (condition) ? byteswap(value) : value;\n}\n\nQ_DECLARE_METATYPE(Endian)\n\n#endif // ENDIAN_H\n"
  },
  {
    "path": "src/common/logging.h",
    "content": "/**\n * Wrapper for QT logging library.\n *\n * Each source file is expected to declare a log category name what is\n * implicitly used for all logging macros. When logging in header files take\n * precaution not to pollute global scope. Either log manually or declare the\n * log within class scope.\n * Log categories can be structured using dots in name: `machine.core\n * .decode`.\n *\n * @see\n * https://www.kdab.com/wp-content/uploads/stories/slides/Day2/KaiKoehne_Qt%20Logging%20Framework%2016_9_0.pdf\n */\n#ifndef LOGGING_H\n#define LOGGING_H\n\n#include <QLoggingCategory>\n\n#define LOG_CATEGORY(NAME) static QLoggingCategory _loging_category_(NAME)\n\n#if !defined(QT_NO_QDEBUG_MACRO)\n    #define QT_NO_QDEBUG_MACRO                                                                     \\\n        while (false)                                                                              \\\n        QMessageLogger().noDebug\n#endif\n\n#if defined(QT_NO_DEBUG_OUTPUT)\n    #define DEBUG QT_NO_QDEBUG_MACRO\n#else\n    #define DEBUG(...) qCDebug(_loging_category_, __VA_ARGS__)\n#endif\n#define LOG(...)   qCInfo(_loging_category_, __VA_ARGS__)\n#define WARN(...)  qCWarning(_loging_category_, __VA_ARGS__)\n#define ERROR(...) qCCritical(_loging_category_, __VA_ARGS__)\n\n#endif\n"
  },
  {
    "path": "src/common/logging_format_colors.h",
    "content": "#ifndef LOGGING_FORMAT_COLORS_H\n#define LOGGING_FORMAT_COLORS_H\n\n#include <QtGlobal>\n\nstatic void set_default_log_pattern() {\n    qSetMessagePattern(\n        \"%{if-info}\\033[34m[INFO]  %{endif}\"\n        \"%{if-debug}\\033[37m[DEBUG] %{endif}\"\n        \"%{if-warning}\\033[33m[WARN]  %{endif}\"\n        \"%{if-critical}\\033[31m[ERROR] %{endif}\"\n        \"%{if-fatal}\\033[31m[FATAL ERROR] %{endif}\"\n        \"%{if-category}%{category}:%{endif}\"\n        \"\\033[0m\\t%{message}\");\n}\n\n#endif // LOGGING_FORMAT_COLORS_H\n"
  },
  {
    "path": "src/common/math/bit_ops.h",
    "content": "/**\n * Bit manipulation library\n *\n * @file\n */\n\n#ifndef QTRVSIM_BIT_OPS_H\n#define QTRVSIM_BIT_OPS_H\n\n#include <cassert>\n#include <cstdint>\n#include <limits>\n\nusing std::size_t;\n\n/**\n * Get value of single bit as lowest bit\n *\n * Corresponds to instruction notation `SYMBOL[<bit_index>]`.\n */\ntemplate<typename T>\nconstexpr inline T get_bit(T val, size_t bit_index) {\n    return (val >> bit_index) & 1;\n}\n\n/**\n * Generates a bitmask to mask a range of bits.\n */\ntemplate<typename T>\nconstexpr inline T get_bitmask(size_t start, size_t end) {\n    const size_t len = start - end + 1;\n    return ((1 << len) - 1) << end;\n}\n\n/**\n * Mask a range of bits in an integer-like value.\n */\ntemplate<typename T>\nconstexpr inline T mask_bits(T val, size_t start, size_t end) {\n    return val & get_bitmask<T>(start, end);\n}\n\n/**\n * Extracts range of bits from an integer-like value.\n *\n * Corresponds to instruction notation `SYMBOL[start:end]`.\n */\ntemplate<typename T>\nconstexpr inline T get_bits(T val, size_t start, size_t end) {\n    assert(start >= end);\n    return mask_bits(val >> end, start - end, 0);\n}\n\n/**\n * Sign extend arbitrary bit range.\n */\ntemplate<typename T>\nconstexpr inline T sign_extend(T val, size_t size) {\n    size_t T_size = std::numeric_limits<T>::digits;\n    if (std::numeric_limits<T>::is_signed) T_size += 1;\n    size_t shift = T_size - size;\n    return ((int64_t)val << shift) >> shift;\n}\n\n#endif // QTRVSIM_BIT_OPS_H\n"
  },
  {
    "path": "src/common/memory_ownership.h",
    "content": "/**\n * Manual memory ownership toolkit.\n */\n#ifndef QTRVSIM_MEMORY_OWNERSHIP_H\n#define QTRVSIM_MEMORY_OWNERSHIP_H\n\n#include <QScopedPointer>\n\n/**\n * Tag for pointer owned by someone else. It is recommended to mention owner\n * in comment to make lifetimes manually verifiable.\n */\n#define BORROWED\n\n/**\n * Pointer with static lifetime.\n */\n#define STATIC\n\n/**\n * Pointer is owned and managed by Qt.\n */\n#define QT_OWNED\n\n/**\n * Owned pointer deallocated by automatic destructor.\n */\ntemplate<typename T>\nusing Box = QScopedPointer<T>;\n\n#endif // QTRVSIM_MEMORY_OWNERSHIP_H\n"
  },
  {
    "path": "src/common/polyfills/CMakeLists.txt",
    "content": "project(polyfills\n\t\tDESCRIPTION \"Helper code to unify access to compiler intrinsics and\n                     provide fallback implementations.\")\n\nset(polyfills_HEADERS\n\t\tendian_detection.h\n\t\tmulh64.h\n\t\tclz32.h\n\t\tbyteswap.h\n\t\tqstring_hash.h\n\t\tqt5/qfontmetrics.h\n\t\tqt5/qlinef.h\n\t\tqt5/qtableview.h\n\t\t)\n\n# =============================================================================\n# Target for usage of this header library\n# - this is header only library and it is implicitly available in the whole\n#   project\n# =============================================================================\n\n# =============================================================================\n# Tests\n# - It is highly recommended to run these tests anytime you use a different\n#   compiler or platform as this code is platform/compiler specific.\n# =============================================================================\n\nif(NOT \"${WASM}\")\n\tset(CMAKE_AUTOMOC ON)\n\n\tadd_executable(mulh64_test\n\t\t\tmulh64.h\n\t\t\tmulh64.test.h\n\t\t\tmulh64.test.cpp\n\t\t\t)\n\ttarget_link_libraries(mulh64_test PRIVATE ${QtLib}::Test)\n\tadd_test(NAME mulh64\n\t\t\tCOMMAND mulh64_test)\nendif()\n\n"
  },
  {
    "path": "src/common/polyfills/byteswap.h",
    "content": "#ifndef BYTESWAP_H\n#define BYTESWAP_H\n\n/* Define byte-swap functions, using fast processor-native built-ins where\n * possible */\n#if defined(_MSC_VER) // needs to be first because msvc doesn't short-circuit\n                      // after failing defined(__has_builtin)\n    #define bswap16(x) _byteswap_ushort((x))\n    #define bswap32(x) _byteswap_ulong((x))\n    #define bswap64(x) _byteswap_uint64((x))\n#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)\n    #define bswap16(x) __builtin_bswap16((x))\n    #define bswap32(x) __builtin_bswap32((x))\n    #define bswap64(x) __builtin_bswap64((x))\n#elif defined(__has_builtin) && __has_builtin(__builtin_bswap64) /* for clang; gcc 5 fails on this \\\n                                                                    and                            \\\n                                                                    && short circuit fails; must   \\\n                                                                    be after GCC check */\n    #define bswap16(x) __builtin_bswap16((x))\n    #define bswap32(x) __builtin_bswap32((x))\n    #define bswap64(x) __builtin_bswap64((x))\n#else\n/* even in this case, compilers often optimize by using native instructions */\nstatic inline uint16_t bswap16(uint16_t x) {\n    return (val << 8) | (val >> 8);\n}\nstatic inline uint32_t bswap32(uint32_t x) {\n    val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);\n    return (val << 16) | (val >> 16);\n}\nstatic inline uint64_t bswap64(uint64_t x) {\n    val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL);\n    val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL);\n    return (val << 32) | (val >> 32);\n}\n#endif\n\n#endif // BYTESWAP_H\n"
  },
  {
    "path": "src/common/polyfills/clz32.h",
    "content": "#ifndef CLZ32_H\n#define CLZ32_H\n\n#if defined(__GNUC__) && __GNUC__ >= 4\n\nstatic inline uint32_t clz32(uint32_t n) {\n    int intbits = sizeof(int) * CHAR_BIT;\n    if (n == 0) { return 32; }\n    return __builtin_clz(n) - (intbits - 32);\n}\n\n#else /* Fallback for generic compiler */\n\n// see https://en.wikipedia.org/wiki/Find_first_set#CLZ\nstatic const uint8_t sig_table_4bit[16] = { 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4 };\n\nstatic inline uint32_t clz32(uint32_t n) {\n    int len = 32;\n\n    if (n & 0xFFFF0000) {\n        len -= 16;\n        n >>= 16;\n    }\n    if (n & 0xFF00) {\n        len -= 8;\n        n >>= 8;\n    }\n    if (n & 0xF0) {\n        len -= 4;\n        n >>= 4;\n    }\n    len -= sig_table_4bit[n];\n    return len;\n}\n\n#endif\n\n#endif // CLZ32_H\n"
  },
  {
    "path": "src/common/polyfills/endian_detection.h",
    "content": "#ifndef ENDIAN_DETECTION_H\n#define ENDIAN_DETECTION_H\n/**\n * Including this file ensures that GCC macros `__LITTLE_ENDIAN__` or\n * `__BIG_ENDIAN__` are defined regardless of OS and compiler. If used\n * system/compiler is not supported, it will stop the compilation using a\n * static assert. Precisely one of the given macros is always guaranteed to be\n * defined.\n *\n * @file\n */\n\n// Cross-platform endian detection source\n// https://gist.github.com/jtbr/7a43e6281e6cca353b33ee501421860c (MIT license)\n\n#ifdef __has_include // C++17, supported as extension to C++11 in clang, GCC 5+,\n                     // vs2015\n    #if __has_include(<endian.h>)\n        #include <endian.h> // gnu libc normally provides, linux\n    #elif __has_include(<machine/endian.h>)\n        #include <machine/endian.h> //open bsd, macos\n    #elif __has_include(<sys/param.h>)\n        #include <sys/param.h> // mingw, some bsd (not open/macos)\n    #elif __has_include(<sys/isadefs.h>)\n        #include <sys/isadefs.h> // solaris\n    #endif\n#endif\n\n#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)\n    #if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)                        \\\n        || (defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN)                                 \\\n        || (defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN)                                    \\\n        || (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN)                                       \\\n        || (defined(__sun) && defined(__SVR4) && defined(_BIG_ENDIAN)) || defined(__ARMEB__)       \\\n        || defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) \\\n        || defined(__MIBSEB__) || defined(_M_PPC)\n        #define __BIG_ENDIAN__\n    #elif (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || /* gcc */      \\\n        (defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN)                  /* linux       \\\n                                                                                       header      \\\n                                                                                     */            \\\n        || (defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN)                                 \\\n        || (defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN)              /* mingw              \\\n                                                                                header */          \\\n        || (defined(__sun) && defined(__SVR4) && defined(_LITTLE_ENDIAN)) || /* solaris */         \\\n        defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL)   \\\n        || defined(__MIPSEL) || defined(__MIPSEL__) || defined(_M_IX86) || defined(_M_X64)         \\\n        || defined(_M_IA64) || /* msvc for intel                                                   \\\n                                  processors */                                                    \\\n        defined(_M_ARM)        /* msvc code on arm executes in little endian mode */\n        #define __LITTLE_ENDIAN__\n    #endif\n#endif\n\n#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)\nstatic_assert(\n    false,\n    \"Current compiler/system is not supported by the endian \"\n    \"detection polyfill code or it uses unsupported endian.\\n\"\n    \"Supported endians are: BIG | LITTLE.\");\n#endif\n\n#if defined(__LITTLE_ENDIAN__) && defined(__BIG_ENDIAN__)\nstatic_assert(\n    false,\n    \"Both endianness macros are defined. This is a bug of endian \"\n    \"detection. Please report it via a github issue.\");\n#endif\n\n#endif // ENDIAN_DETECTION_H\n"
  },
  {
    "path": "src/common/polyfills/mulh64.h",
    "content": "/**\n * Provides set of functions to calculate high bits of 64 bit multiplication.\n * Naming reflects RISC-V specs.\n *\n * `mulh64` = signed x signed\n * `mulhu64` = unsigned x unsigned\n * `mulhsu64` = signed x unsigned\n *\n * @file\n */\n\n#ifndef MULH64_H\n#define MULH64_H\n\n#include <cstdint>\n\n/**\n * Get high bits of 64 bit unsigned multiplication.\n *\n * Source (BSD-3 in the referred project):\n * https://stackoverflow.com/questions/28868367/getting-the-high-part-of-64-bit-integer-multiplication\n */\nstatic inline constexpr uint64_t mulhu64_fallback(uint64_t a, uint64_t b) {\n    const uint64_t a_lower = (uint32_t)a;\n    const uint64_t a_upper = (uint32_t)(a >> 32);\n    const uint64_t b_lower = (uint32_t)b;\n    const uint64_t b_upper = (uint32_t)(b >> 32);\n    const uint64_t p11 = a_upper * b_upper, p01 = a_lower * b_upper;\n    const uint64_t p10 = a_upper * b_lower, p00 = a_lower * b_lower;\n    /*\n        This is implementing schoolbook multiplication:\n\n                x1 x0\n        X       y1 y0\n        -------------\n                   00  LOW PART\n        -------------\n                00\n             10 10     MIDDLE PART\n        +       01\n        -------------\n             01\n        + 11 11        HIGH PART\n        -------------\n    */\n\n    const uint64_t middle = p10 + (p00 >> 32) + (uint32_t)p01;\n    return p11 + (middle >> 32) + (p01 >> 32);\n}\n\n/**\n * mulhu64_fallback modified for signed multiplication\n *\n * SIGN marks show where sign extension has been added.\n * `*_upper` variables are considered signed and everything multiplied with them\n * is also signed.\n *\n * We use the fact, that in most compilers right shift of signed value is done\n * by arithmetic shift.\n */\nstatic inline constexpr uint64_t mulh64_fallback(int64_t a, int64_t b) {\n    const uint64_t a_lower = (uint32_t)a;\n    const int64_t a_upper = (int32_t)(a >> 32); // SIGN\n    const uint64_t b_lower = (uint32_t)b;\n    const int64_t b_upper = (int32_t)(b >> 32); // SIGN\n    const int64_t p11 = a_upper * b_upper;\n    const int64_t p01 = a_lower * b_upper;\n    const int64_t p10 = a_upper * b_lower;\n    const uint64_t p00 = a_lower * b_lower;\n    const int64_t middle = p10 + (p00 >> 32) + (uint32_t)p01;\n    return p11 + (middle >> 32) + (p01 >> 32); // SIGN\n}\n\n/**\n * mulh64_fallback modified for mixed sign multiplication\n *\n * SIGN marks show where sign extension has been added.\n * `a_upper` is considered signed and everything multiplied with it is also\n * signed.\n *\n * We use the fact, that in most compilers right shift of signed value is done\n * by arithmetic shift.\n */\nstatic inline constexpr uint64_t mulhsu64_fallback(int64_t a, uint64_t b) {\n    const uint64_t a_lower = (uint32_t)a;\n    const int64_t a_upper = (int32_t)(a >> 32); // SIGN\n    const uint64_t b_lower = (uint32_t)b;\n    const uint64_t b_upper = (uint32_t)(b >> 32);\n    const int64_t p11 = a_upper * b_upper;\n    const uint64_t p01 = a_lower * b_upper;\n    const int64_t p10 = a_upper * b_lower;\n    const uint64_t p00 = a_lower * b_lower;\n    const int64_t middle = p10 + (p00 >> 32) + (uint32_t)p01;\n    return p11 + (middle >> 32) + (p01 >> 32); // SIGN\n}\n\n#if defined(__SIZEOF_INT128__) // GNU C\n\nstatic inline constexpr uint64_t mulh64(int64_t a, int64_t b) {\n    unsigned __int128 prod = (unsigned __int128)a * (unsigned __int128)b;\n    return (uint64_t)(prod >> 64);\n}\n\nstatic inline constexpr uint64_t mulhu64(uint64_t a, uint64_t b) {\n    unsigned __int128 prod = (unsigned __int128)a * (unsigned __int128)b;\n    return (uint64_t)(prod >> 64);\n}\n\nstatic inline constexpr uint64_t mulhsu64(int64_t a, uint64_t b) {\n    unsigned __int128 prod = (unsigned __int128)a * (unsigned __int128)b;\n    return (uint64_t)(prod >> 64);\n}\n\n#elif defined(_M_X64) || defined(_M_ARM64) // MSVC\n    // MSVC for x86-64 or AArch64\n    // possibly also  || defined(_M_IA64) || defined(_WIN64)\n    // but the docs only guarantee x86-64!  Don't use *just* _WIN64; it doesn't\n    // include AArch64 Android / Linux\n    // https://docs.microsoft.com/en-gb/cpp/intrinsics/arm64-intrinsics\n\n    #include <intrin.h>\n    // https://docs.microsoft.com/en-gb/cpp/intrinsics/umulh\n    #define mulhu64  __umulh\n    // https://docs.microsoft.com/en-gb/cpp/intrinsics/mulh\n    #define mulh64   __mulh\n    // Not provided by MVSC\n    #define mulhsu64 mulhsu64_fallback\n#else\n    #define mulh64   mulh64_fallback\n    #define mulhu64  mulhu64_fallback\n    #define mulhsu64 mulhsu64_fallback\n#endif\n\n#endif // MULH64_H\n"
  },
  {
    "path": "src/common/polyfills/mulh64.test.cpp",
    "content": "#include \"mulh64.h\"\n\n#include \"mulh64.test.h\"\n\nvoid TestMULH64::test_mulh64() {\n    QCOMPARE(mulh64(15, 10), (uint64_t)0);\n    QCOMPARE(mulh64(15, -10), (uint64_t)0xffffffffffffffffULL);\n    QCOMPARE(mulh64(-10, 15), (uint64_t)0xffffffffffffffffULL);\n    QCOMPARE(mulh64(-15, -10), (uint64_t)0);\n}\n\nvoid TestMULH64::test_mulhu64() {\n    QCOMPARE(mulhu64(15, 10), (uint64_t)0);\n    QCOMPARE(mulhu64(15, -10), (uint64_t)14);\n    QCOMPARE(mulhu64(-10, 15), (uint64_t)14);\n    QCOMPARE(mulhu64(-10, -15), (uint64_t)0xffffffffffffffe7ULL);\n}\n\nvoid TestMULH64::test_mulhsu64() {\n    QCOMPARE(mulhsu64(15, 10), (uint64_t)0);\n    QCOMPARE(mulhsu64(15, -10), (uint64_t)14);\n    QCOMPARE(mulhsu64(-10, 15), (uint64_t)0xffffffffffffffffULL);\n    QCOMPARE(mulhsu64(-15, -10), (uint64_t)0xfffffffffffffff1ULL);\n}\n\nvoid TestMULH64::test_mulh64_fallback() {\n    QCOMPARE(mulh64_fallback(15, 10), (uint64_t)0);\n    QCOMPARE(mulh64_fallback(15, -10), (uint64_t)0xffffffffffffffffULL);\n    QCOMPARE(mulh64_fallback(-10, 15), (uint64_t)0xffffffffffffffffULL);\n    QCOMPARE(mulh64_fallback(-15, -10), (uint64_t)0);\n}\n\nvoid TestMULH64::test_mulhu64_fallback() {\n    QCOMPARE(mulhu64_fallback(15, 10), (uint64_t)0);\n    QCOMPARE(mulhu64_fallback(15, -10), (uint64_t)14);\n    QCOMPARE(mulhu64_fallback(-10, 15), (uint64_t)14);\n    QCOMPARE(mulhu64_fallback(-10, -15), (uint64_t)0xffffffffffffffe7ULL);\n}\n\nvoid TestMULH64::test_mulhsu64_fallback() {\n    QCOMPARE(mulhsu64_fallback(15, 10), (uint64_t)0);\n    QCOMPARE(mulhsu64_fallback(15, -10), (uint64_t)14);\n    QCOMPARE(mulhsu64_fallback(-10, 15), (uint64_t)0xffffffffffffffffULL);\n    QCOMPARE(mulhsu64_fallback(-15, -10), (uint64_t)0xfffffffffffffff1ULL);\n}\n\nQTEST_APPLESS_MAIN(TestMULH64)\n"
  },
  {
    "path": "src/common/polyfills/mulh64.test.h",
    "content": "#ifndef MULH64_TEST_H\n#define MULH64_TEST_H\n\n#include <QtTest/QTest>\n#include <cstdint>\n\nclass TestMULH64 : public QObject {\n    Q_OBJECT\nprivate slots:\n    static void test_mulh64();\n    static void test_mulhu64();\n    static void test_mulhsu64();\n    static void test_mulh64_fallback();\n    static void test_mulhu64_fallback();\n    static void test_mulhsu64_fallback();\n};\n\n#endif // MULH64_TEST_H\n"
  },
  {
    "path": "src/common/polyfills/qstring_hash.h",
    "content": "#ifndef QTRVSIM_QSTRING_HASH_H\n#define QTRVSIM_QSTRING_HASH_H\n\n#include <QHash>\n#include <QtGlobal>\n\n#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n\nnamespace std {\ntemplate<>\nstruct hash<QString> {\n    std::size_t operator()(const QString &s) const noexcept { return qHash(s); }\n};\n    #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)\n\ntemplate<>\nstruct hash<QStringView> {\n    std::size_t operator()(const QStringView &s) const noexcept { return qHash(s); }\n};\n    #endif\n\n} // namespace std\n\n    #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)\n// Supported since Qt5.10\nusing QStringView = QString;\n    #endif\n\n#endif\n\n#endif // QTRVSIM_QSTRING_HASH_H\n"
  },
  {
    "path": "src/common/polyfills/qt5/qfontmetrics.h",
    "content": "#ifndef POLYFILLS_QFONTMETRICS_H\n#define POLYFILLS_QFONTMETRICS_H\n\nint QFontMetrics_horizontalAdvance(const QFontMetrics &self, const QString &str, int len = -1) {\n#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)\n    return self.horizontalAdvance(str, len);\n#else\n    return self.width(str, len);\n#endif\n}\n\n#endif // QTMIPS_QFONTMETRICS_H\n"
  },
  {
    "path": "src/common/polyfills/qt5/qlinef.h",
    "content": "#ifndef POLYFILLS_QLINEF_H\n#define POLYFILLS_QLINEF_H\n\n#include <QLine>\n\nQLineF::IntersectType\nQLineF_intersect(const QLineF &l1, const QLineF &l2, QPointF *intersectionPoint) {\n#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)\n    return l1.intersects(l2, intersectionPoint);\n#else\n    return l1.intersect(l2, intersectionPoint);\n#endif\n}\n\n#endif // QTMIPS_QLINEF_H\n"
  },
  {
    "path": "src/common/polyfills/qt5/qtableview.h",
    "content": "#ifndef POLYFILLS_QTABLEVIEW_H\n#define POLYFILLS_QTABLEVIEW_H\n\n#include <QTableView>\n\n/**\n * QTableView polyfill\n *\n * initViewItemOption is protected, therefore the whole class needs to be wrapped.\n */\nclass Poly_QTableView : public QTableView {\npublic:\n    explicit Poly_QTableView(QWidget *parent) : QTableView(parent) {}\n\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\nprotected:\n    void initViewItemOption(QStyleOptionViewItem *viewOpts) {\n        *viewOpts = QTableView::viewOptions();\n    }\n#endif\n};\n\n#endif // QTMIPS_QTABLEVIEW_H\n"
  },
  {
    "path": "src/common/string_utils.h",
    "content": "#ifndef QTRVSIM_STRING_UTILS_H\n#define QTRVSIM_STRING_UTILS_H\n\n#include <QString>\n\nnamespace str {\ntemplate<typename T>\nQString asHex(T number) {\n    if (number < 0) {\n        return QString::asprintf(\"-0x%x\", -number);\n    } else {\n        return QString::asprintf(\"0x%x\", number);\n    }\n}\n} // namespace str\n\n#endif // QTRVSIM_STRING_UTILS_H\n"
  },
  {
    "path": "src/common/type_utils/lens.h",
    "content": "/**\n * Tiny lenses library\n *\n * TL;DR: Getter function which works on nested structs.\n *\n * Example:\n * ```\n *  struct A {\n *    int b,\n *    struct {\n *      int d;\n *    } c;\n *  }\n *\n *  Lens<A, int> lens = LENS(A, c.d);\n * ```\n * `Lens<A, int>` is a function pointer to a function with a signature\n * `const A& -> const int&`\n * int`, i.e., it takes a reference to an instance of type A and \"shows\" you\n * something somewhere inside the object -- the integer `d`.\n *\n * In functional programming, the term lens is used and a generalized and\n * more flexible form of getter method. First, it is a function. Second, it\n * works on arbitrarily nested structures and it hides the inner structure.\n *\n * @file\n */\n#ifndef QTRVSIM_LENS_H\n#define QTRVSIM_LENS_H\n\ntemplate<typename BASE, typename FIELD_TYPE>\nusing Lens = const FIELD_TYPE &(*const)(const BASE &);\n\n#define LENS(BASE_TYPE, MEMBER) ([](const BASE_TYPE &base) -> const auto & { return base.MEMBER; })\n\ntemplate<typename BASE, typename FIELD_TYPE_A, typename FIELD_TYPE_B>\nusing LensPair = std::pair<const FIELD_TYPE_A &, const FIELD_TYPE_B &> (*const)(const BASE &);\n\n#define LENS_PAIR(BASE_TYPE, MEMBER_A, MEMBER_B)                                                   \\\n    [](const BASE_TYPE &base) -> std::pair<typeof(base.MEMBER_A) &, typeof(base.MEMBER_B) &> {     \\\n        return { base.MEMBER_A, base.MEMBER_B };                                                   \\\n    }\n\n#endif // QTRVSIM_LENS_H\n"
  },
  {
    "path": "src/gui/CMakeLists.txt",
    "content": "project(gui\n        LANGUAGES C CXX\n        VERSION ${MAIN_PROJECT_VERSION}\n        DESCRIPTION \"Graphical UI for the simulator\")\n\nset(CMAKE_AUTOMOC ON)\nset(CMAKE_AUTORCC ON)\nset(CMAKE_AUTOUIC ON)\n\nset(gui_SOURCES\n        dialogs/about/aboutdialog.cpp\n        windows/cache/cachedock.cpp\n        windows/cache/cacheview.cpp\n        windows/csr/csrdock.cpp\n        windows/coreview/scene.cpp\n        extprocess.cpp\n        fontsize.cpp\n        dialogs/gotosymbol/gotosymboldialog.cpp\n        graphicsview.cpp\n        windows/memory/memorydock.cpp\n        windows/memory/memorymodel.cpp\n        windows/memory/memorytableview.cpp\n        windows/messages/messagesdock.cpp\n        windows/messages/messagesmodel.cpp\n        windows/messages/messagesview.cpp\n        dialogs/new/newdialog.cpp\n        ui/hexlineedit.cpp\n        ui/pow2spinbox.cpp\n        windows/tlb/tlbview.cpp\n        windows/tlb/tlbdock.cpp\n        windows/editor/highlighterasm.cpp\n        windows/editor/highlighterc.cpp\n        windows/editor/linenumberarea.cpp\n        windows/editor/editordock.cpp\n        windows/editor/editortab.cpp\n        hinttabledelegate.cpp\n        windows/lcd/lcddisplaydock.cpp\n        windows/lcd/lcddisplayview.cpp\n        main.cpp\n        mainwindow/mainwindow.cpp\n        windows/peripherals/peripheralsdock.cpp\n        windows/peripherals/peripheralsview.cpp\n        windows/program/programdock.cpp\n        windows/program/programmodel.cpp\n        windows/program/programtableview.cpp\n        windows/registers/registersdock.cpp\n        dialogs/savechanged/savechangeddialog.cpp\n        windows/editor/srceditor.cpp\n        statictable.cpp\n        windows/terminal/terminaldock.cpp\n        textsignalaction.cpp\n        windows/coreview/components/value_handlers.cpp\n        windows/coreview/components/cache.cpp\n        widgets/hidingtabwidget.cpp\n        windows/predictor/predictor_btb_dock.cpp\n        windows/predictor/predictor_bht_dock.cpp\n        windows/predictor/predictor_info_dock.cpp\n)\nset(gui_HEADERS\n        dialogs/about/aboutdialog.h\n        windows/cache/cachedock.h\n        windows/cache/cacheview.h\n        windows/csr/csrdock.h\n        windows/coreview/scene.h\n        extprocess.h\n        fontsize.h\n        dialogs/gotosymbol/gotosymboldialog.h\n        graphicsview.h\n        windows/memory/memorydock.h\n        windows/memory/memorymodel.h\n        windows/memory/memorytableview.h\n        windows/messages/messagesdock.h\n        windows/messages/messagesmodel.h\n        windows/messages/messagesview.h\n        dialogs/new/newdialog.h\n        ui/hexlineedit.h\n        ui/pow2spinbox.h\n        windows/tlb/tlbview.h\n        windows/tlb/tlbdock.h\n        windows/editor/highlighterasm.h\n        windows/editor/highlighterc.h\n        windows/editor/linenumberarea.h\n        windows/editor/editordock.h\n        hinttabledelegate.h\n        windows/lcd/lcddisplaydock.h\n        windows/lcd/lcddisplayview.h\n        mainwindow/mainwindow.h\n        windows/peripherals/peripheralsdock.h\n        windows/peripherals/peripheralsview.h\n        windows/program/programdock.h\n        windows/program/programmodel.h\n        windows/program/programtableview.h\n        windows/registers/registersdock.h\n        dialogs/savechanged/savechangeddialog.h\n        windows/editor/srceditor.h\n        statictable.h\n        windows/terminal/terminaldock.h\n        textsignalaction.h\n        windows/coreview/components/value_handlers.h\n        windows/coreview/data.h\n        windows/coreview/components/cache.h\n        helper/async_modal.h\n        widgets/hidingtabwidget.h\n        windows/predictor/predictor_btb_dock.h\n        windows/predictor/predictor_bht_dock.h\n        windows/predictor/predictor_info_dock.h\n)\nset(gui_UI\n        dialogs/gotosymbol/gotosymboldialog.ui\n        dialogs/new/NewDialog.ui\n        windows/peripherals/peripheralsview.ui\n        mainwindow/MainWindow.ui\n        dialogs/new/NewDialogCache.ui\n)\nset(gui_RESOURCES\n        resources/icons/icons.qrc\n        resources/samples/samples.qrc\n        windows/coreview/schemas/schemas.qrc\n)\n\n\nif (\"${WASM}\")\n    message(STATUS \"gui :: Including WASM only files.\")\n    list(APPEND gui_SOURCES qhtml5file_html5.cpp)\n    list(APPEND gui_HEADERS qhtml5file.h)\nendif ()\n\n# MACOS\nset(ICON_NAME gui)\nset(ICON_PATH ${CMAKE_SOURCE_DIR}/data/icons/macos/${ICON_NAME}.icns)\n# END MACOS\n\nadd_executable(gui\n        ${ICON_PATH}\n        ${gui_SOURCES}\n        ${gui_HEADERS}\n        ${gui_UI}\n        ${gui_RESOURCES})\ntarget_include_directories(gui PUBLIC . windows/coreview)\ntarget_link_libraries(gui\n        PRIVATE ${QtLib}::Core ${QtLib}::Widgets ${QtLib}::Gui\n        PRIVATE machine os_emulation assembler svgscene)\ntarget_compile_definitions(gui\n        PRIVATE\n        APP_ORGANIZATION=\\\"${MAIN_PROJECT_ORGANIZATION}\\\"\n        APP_ORGANIZATION_DOMAIN=\\\"${MAIN_PROJECT_HOMEPAGE_URL}\\\"\n        APP_GIT=\\\"${MAIN_PROJECT_HOMEPAGE_URL}\\\"\n        APP_NAME=\\\"${MAIN_PROJECT_NAME}\\\"\n        APP_VERSION=\\\"${MAIN_PROJECT_VERSION}\\\"\n        APP_USER_MANUAL_URL=\\\"https://comparch.edu.cvut.cz/qtrvsim/manual\\\"\n        APP_REPORT_PROBLEM_URL=\\\"https://github.com/cvut/qtrvsim/issues/new\\\"\n        ENV_CONFIG_FILE_NAME=\\\"${MAIN_PROJECT_NAME_UPPER}_CONFIG_FILE\\\")\nset_target_properties(gui PROPERTIES\n        OUTPUT_NAME \"${MAIN_PROJECT_NAME_LOWER}_${PROJECT_NAME}\")\n\nif (${${QtLib}PrintSupport_FOUND} AND NOT ${WASM})\n    target_link_libraries(gui PRIVATE ${QtLib}::PrintSupport)\n    target_compile_definitions(gui PRIVATE WITH_PRINTING=1)\nendif ()\n\n# MACOS\nset_property(SOURCE ${ICON_PATH}\n        PROPERTY MACOSX_PACKAGE_LOCATION Resources)\nset_target_properties(gui PROPERTIES\n        MACOSX_BUNDLE true\n        MACOSX_BUNDLE_GUI_IDENTIFIER cz.cvut.fel.${MAIN_PROJECT_ORGANIZATION}.gui\n        MACOSX_BUNDLE_BUNDLE_NAME ${MAIN_PROJECT_NAME}\n        MACOSX_BUNDLE_BUNDLE_VERSION \"${MAIN_PROJECT_VERSION}\"\n        MACOSX_BUNDLE_SHORT_VERSION_STRING \"${MAIN_PROJECT_VERSION}\"\n        MACOSX_BUNDLE_ICONFILE ${ICON_NAME}\n)\n# END MACOS\n\n# =============================================================================\n# Installation\n# =============================================================================\n\n# Prior to CMake version 3.13, installation must be performed in the subdirectory,\n# there the target was created. Therefore executable installation is to be found\n# in corresponding CMakeLists.txt.\n\ninstall(TARGETS gui\n        RUNTIME DESTINATION bin\n        BUNDLE DESTINATION ${EXECUTABLE_OUTPUT_PATH}\n)\n"
  },
  {
    "path": "src/gui/dialogs/about/aboutdialog.cpp",
    "content": "#include \"aboutdialog.h\"\n\n#include \"project_info.h\"\n\n#include <QApplication>\n#include <QLabel>\n#include <QObject>\n#include <QPlainTextEdit>\n#include <QPushButton>\n#include <QTextBrowser>\n#include <QVBoxLayout>\n\nAboutDialog::AboutDialog(QWidget *parent) : QDialog(parent) {\n    QLabel *lbl;\n\n    setAttribute(Qt::WA_DeleteOnClose);\n    setAttribute(Qt::WA_ShowModal);\n    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n\n    setWindowTitle(tr(\"About \" APP_NAME));\n\n    all = new QVBoxLayout(this);\n\n    auto *hbox = new QWidget();\n    auto *hl = new QHBoxLayout(hbox);\n    hl->setContentsMargins(0, 0, 0, 0);\n\n    all->addWidget(hbox);\n\n    auto *vbox = new QWidget();\n    auto *vl = new QVBoxLayout(vbox);\n    hl->addWidget(vbox);\n\n    QString versionText;\n    versionText = \"Version \" APP_VERSION \"\\n\";\n\n    vl->addWidget(new QLabel(\n        \"<span style='font-size:x-large; font-weight:bold;\"\n        \"'>\" APP_NAME \" \"\n        \"- RISC-V Architecture Simulator</span>\"));\n    lbl = new QLabel(versionText);\n    lbl->setAlignment(Qt::AlignHCenter);\n    lbl->setOpenExternalLinks(true);\n    vl->addWidget(lbl);\n    vl->addWidget(new QLabel(COPYRIGHT_HTML));\n\n    QString supportText;\n    supportText\n        = \"Home Page : <a \"\n          \"href=\\\"\" APP_GIT \"\\\">\" APP_GIT \"</a><br/>\"\n          \"Implemented for <a \"\n          \"href=\\\"https://cw.fel.cvut.cz/wiki/courses/b35apo/\"\n          \"start\\\">Computer Architectures</a> and <a \"\n          \"href=\\\"https://cw.fel.cvut.cz/wiki/courses/b4m35pap/\"\n          \"start\\\">Advanced Computer Architectures</a> courses \"\n          \"at <a href=\\\"https://www.cvut.cz/\\\">Czech Technical \"\n          \"University in Prague</a>\"\n          \" <a href=\\\"https://www.fel.cvut.cz/\\\">Faculty of Electrical \"\n          \"Engineering</a><br/>\"\n          \"QtRvSim on-line version and links to course materials at<br/>\"\n          \"<a href=\\\"https://comparch.edu.cvut.cz/\\\">https://comparch.edu.cvut.cz/</a><br/>\";\n\n    auto *supportBrowser = new QTextBrowser;\n    supportBrowser->setOpenExternalLinks(true);\n    supportBrowser->setHtml(supportText);\n    vl->addWidget(supportBrowser);\n\n    auto *licenseBrowser = new QTextBrowser;\n    licenseBrowser->setOpenExternalLinks(true);\n    licenseBrowser->setHtml(LICENCE_HTML);\n    vl->addWidget(licenseBrowser);\n\n    auto *hbBtn = new QWidget();\n    auto *hlBtn = new QHBoxLayout(hbBtn);\n    hlBtn->setContentsMargins(0, 0, 0, 0);\n    vl->addWidget(hbBtn);\n\n    auto *okButton = new QPushButton(tr(\"&OK\"), parent);\n    okButton->setFocus();\n    connect(okButton, &QAbstractButton::clicked, this, &QWidget::close);\n    hlBtn->addStretch();\n    hlBtn->addWidget(okButton);\n\n    setMinimumSize(480, 500);\n\n    // the first Tab is selected by default\n}\n"
  },
  {
    "path": "src/gui/dialogs/about/aboutdialog.h",
    "content": "#ifndef ABOUTDIALOG_H\n#define ABOUTDIALOG_H\n\n#include <QDialog>\n#include <QVBoxLayout>\n#include <array>\n#include <random>\n\nclass QString;\nclass QTextBrowser;\n\nclass AboutDialog : public QDialog {\n    Q_OBJECT\n\npublic:\n    explicit AboutDialog(QWidget *parent = nullptr);\n\nprivate:\n    QVBoxLayout *all;\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/dialogs/gotosymbol/gotosymboldialog.cpp",
    "content": "#include \"gotosymboldialog.h\"\n\n#include \"ui_gotosymboldialog.h\"\n\nGoToSymbolDialog::GoToSymbolDialog(QWidget *parent, const QStringList &symbol_names)\n    : QDialog(parent)\n    , ui(new Ui::GoToSymbolDialog) {\n    ui->setupUi(this);\n\n    connect(ui->pushShowProg, &QAbstractButton::clicked, this, &GoToSymbolDialog::show_prog);\n    connect(ui->pushShowMem, &QAbstractButton::clicked, this, &GoToSymbolDialog::show_mem);\n    connect(ui->pushClose, &QAbstractButton::clicked, this, &QWidget::close);\n\n    ui->listSymbols->addItems(symbol_names);\n}\n\nvoid GoToSymbolDialog::show_prog() {\n    uint64_t address = 0;\n    emit obtain_value_for_name(address, ui->listSymbols->currentItem()->text());\n    emit program_focus_addr(machine::Address(address));\n}\n\nvoid GoToSymbolDialog::show_mem() {\n    uint64_t address = 0;\n    emit obtain_value_for_name(address, ui->listSymbols->currentItem()->text());\n    emit memory_focus_addr(machine::Address(address));\n}\n"
  },
  {
    "path": "src/gui/dialogs/gotosymbol/gotosymboldialog.h",
    "content": "#ifndef GOTOSYMBOLDIALOG_H\n#define GOTOSYMBOLDIALOG_H\n\n#include \"common/memory_ownership.h\"\n#include \"machine/memory/address.h\"\n#include \"ui_gotosymboldialog.h\"\n\n#include <QDialog>\n#include <QList>\n#include <QStringList>\n\nclass GoToSymbolDialog : public QDialog {\n    Q_OBJECT\n\npublic:\n    GoToSymbolDialog(QWidget *parent, const QStringList &symbol_names);\nsignals:\n    void program_focus_addr(machine::Address);\n    void memory_focus_addr(machine::Address);\n    bool obtain_value_for_name(uint64_t &value, const QString &name) const;\npublic slots:\n    void show_prog();\n    void show_mem();\n\nprivate:\n    Box<Ui::GoToSymbolDialog> ui {};\n};\n\n#endif // GOTOSYMBOLDIALOG_H\n"
  },
  {
    "path": "src/gui/dialogs/gotosymbol/gotosymboldialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GoToSymbolDialog</class>\n <widget class=\"QDialog\" name=\"GoToSymbolDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>331</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Go To Symbol DIalog</string>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout\">\n   <item row=\"0\" column=\"0\">\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n     <property name=\"sizeConstraint\">\n      <enum>QLayout::SetMaximumSize</enum>\n     </property>\n     <item>\n      <widget class=\"QListWidget\" name=\"listSymbols\"/>\n     </item>\n     <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n       <item>\n        <widget class=\"QPushButton\" name=\"pushShowProg\">\n         <property name=\"text\">\n          <string>Show program</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer name=\"horizontalSpacer\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>40</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item>\n        <widget class=\"QPushButton\" name=\"pushShowMem\">\n         <property name=\"text\">\n          <string>Show memory</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer name=\"horizontalSpacer_2\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>40</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item>\n        <widget class=\"QPushButton\" name=\"pushClose\">\n         <property name=\"text\">\n          <string>Close</string>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <tabstops>\n  <tabstop>listSymbols</tabstop>\n  <tabstop>pushShowProg</tabstop>\n  <tabstop>pushShowMem</tabstop>\n  <tabstop>pushClose</tabstop>\n </tabstops>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/gui/dialogs/new/NewDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>NewDialog</class>\n <widget class=\"QDialog\" name=\"NewDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>745</width>\n    <height>472</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <property name=\"modal\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_1\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n     <item>\n      <widget class=\"QTreeWidget\" name=\"page_select_tree\">\n       <property name=\"minimumSize\">\n        <size>\n         <width>135</width>\n         <height>0</height>\n        </size>\n       </property>\n       <column>\n        <property name=\"text\">\n         <string notr=\"true\">Config Pages</string>\n        </property>\n       </column>\n      </widget>\n     </item>\n     <item>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n       <property name=\"spacing\">\n        <number>0</number>\n       </property>\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>4</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <item>\n        <widget class=\"QLabel\" name=\"config_page_title\">\n         <property name=\"text\">\n          <string>Page Name</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QStackedWidget\" name=\"config_pages\">\n         <widget class=\"QWidget\" name=\"tab_preset_load\">\n          <property name=\"accessibleName\">\n           <string>Presets and ELF File</string>\n          </property>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout_5\">\n           <item>\n            <widget class=\"QGroupBox\" name=\"preset_box\">\n             <property name=\"title\">\n              <string>Preset</string>\n             </property>\n             <layout class=\"QVBoxLayout\" name=\"verticalLayout_6\">\n              <item>\n               <widget class=\"QRadioButton\" name=\"preset_no_pipeline\">\n                <property name=\"text\">\n                 <string>No pipeline no cache</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QRadioButton\" name=\"preset_no_pipeline_cache\">\n                <property name=\"text\">\n                 <string>No pipeline with cache</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QRadioButton\" name=\"preset_pipelined_bare\">\n                <property name=\"text\">\n                 <string>Pipelined without hazard unit and without cache</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QRadioButton\" name=\"preset_pipelined\">\n                <property name=\"text\">\n                 <string>Pipelined with hazard unit and cache</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QRadioButton\" name=\"preset_custom\">\n                <property name=\"text\">\n                 <string>Custom</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QCheckBox\" name=\"reset_at_compile\">\n             <property name=\"text\">\n              <string>Reset at compile time (reload after make)</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <spacer name=\"verticalSpacer_3\">\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>40</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n           <item>\n            <widget class=\"Line\" name=\"line_2\">\n             <property name=\"orientation\">\n              <enum>Qt::Horizontal</enum>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n             <item>\n              <widget class=\"QLabel\" name=\"label\">\n               <property name=\"text\">\n                <string>Elf executable: </string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QLineEdit\" name=\"elf_file\"/>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"pushButton_browse\">\n               <property name=\"text\">\n                <string>Browse</string>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\" name=\"tab_core\">\n          <property name=\"accessibleName\">\n           <string>Core ISA and Hazards</string>\n          </property>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout_8\">\n           <item>\n            <layout class=\"QGridLayout\" name=\"gridLayoutIsa\">\n             <item row=\"0\" column=\"0\">\n              <widget class=\"QCheckBox\" name=\"pipelined\">\n               <property name=\"text\">\n                <string>Pipelined</string>\n               </property>\n              </widget>\n             </item>\n             <item row=\"0\" column=\"1\">\n              <widget class=\"QCheckBox\" name=\"xlen_64bit\">\n               <property name=\"text\">\n                <string>XLEN 64-bit</string>\n               </property>\n              </widget>\n             </item>\n             <item row=\"0\" column=\"2\">\n              <widget class=\"QCheckBox\" name=\"isa_atomic\">\n               <property name=\"text\">\n                <string>Atomic (A)</string>\n               </property>\n              </widget>\n             </item>\n             <item row=\"1\" column=\"0\">\n              <widget class=\"QCheckBox\" name=\"delay_slot\">\n               <property name=\"text\">\n                <string>Delay slot</string>\n               </property>\n              </widget>\n             </item>\n             <item row=\"1\" column=\"2\">\n              <widget class=\"QCheckBox\" name=\"isa_multiply\">\n               <property name=\"text\">\n                <string>Multiply (M)</string>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item>\n            <widget class=\"QGroupBox\" name=\"hazard_unit\">\n             <property name=\"title\">\n              <string>Hazard unit</string>\n             </property>\n             <property name=\"checkable\">\n              <bool>true</bool>\n             </property>\n             <property name=\"checked\">\n              <bool>false</bool>\n             </property>\n             <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n              <item>\n               <widget class=\"QRadioButton\" name=\"hazard_stall\">\n                <property name=\"text\">\n                 <string>Stall when hazard is detected</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QRadioButton\" name=\"hazard_stall_forward\">\n                <property name=\"text\">\n                 <string>Stall or forward when hazard is detected</string>\n                </property>\n                <property name=\"checked\">\n                 <bool>true</bool>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <spacer name=\"verticalSpacer_1\">\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>40</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\" name=\"tab_branch_predictor\">\n          <property name=\"accessibleName\">\n           <string>Branch Predictor</string>\n          </property>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout_10\">\n           <item>\n            <widget class=\"QGroupBox\" name=\"group_bp\">\n             <property name=\"title\">\n              <string>Branch Predictor</string>\n             </property>\n             <property name=\"checkable\">\n              <bool>true</bool>\n             </property>\n             <property name=\"checked\">\n              <bool>true</bool>\n             </property>\n             <layout class=\"QVBoxLayout\" name=\"verticalLayout_9\">\n              <item>\n               <layout class=\"QHBoxLayout\" name=\"layout_bp_type\">\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <item>\n                 <widget class=\"QLabel\" name=\"text_bp_type\">\n                  <property name=\"text\">\n                   <string>Predictor type:</string>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QComboBox\" name=\"select_bp_type\"/>\n                </item>\n               </layout>\n              </item>\n              <item>\n               <layout class=\"QHBoxLayout\" name=\"layout_bp_init_state\">\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <item>\n                 <widget class=\"QLabel\" name=\"text_bp_init_state\">\n                  <property name=\"text\">\n                   <string>Initial state:</string>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QComboBox\" name=\"select_bp_init_state\"/>\n                </item>\n               </layout>\n              </item>\n              <item>\n               <widget class=\"QGroupBox\" name=\"group_bp_btb\">\n                <property name=\"title\">\n                 <string>Branch Target Buffer (BTB) </string>\n                </property>\n                <property name=\"flat\">\n                 <bool>false</bool>\n                </property>\n                <layout class=\"QGridLayout\" name=\"gridLayout\">\n                 <item row=\"1\" column=\"0\" colspan=\"3\">\n                  <widget class=\"Line\" name=\"line_bp_btb\">\n                   <property name=\"orientation\">\n                    <enum>Qt::Horizontal</enum>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"0\" column=\"2\">\n                  <widget class=\"QLabel\" name=\"text_bp_btb_addr_bits_number\">\n                   <property name=\"minimumSize\">\n                    <size>\n                     <width>40</width>\n                     <height>0</height>\n                    </size>\n                   </property>\n                   <property name=\"text\">\n                    <string>0</string>\n                   </property>\n                   <property name=\"alignment\">\n                    <set>Qt::AlignCenter</set>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"0\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"text_bp_btb_addr_bits\">\n                   <property name=\"toolTip\">\n                    <string>PC - Program Counter register</string>\n                   </property>\n                   <property name=\"text\">\n                    <string>Bits from PC address: </string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"0\" column=\"1\">\n                  <widget class=\"QSlider\" name=\"slider_bp_btb_addr_bits\">\n                   <property name=\"maximum\">\n                    <number>8</number>\n                   </property>\n                   <property name=\"orientation\">\n                    <enum>Qt::Horizontal</enum>\n                   </property>\n                   <property name=\"invertedAppearance\">\n                    <bool>false</bool>\n                   </property>\n                   <property name=\"tickPosition\">\n                    <enum>QSlider::TicksBothSides</enum>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"3\" column=\"0\" colspan=\"3\">\n                  <layout class=\"QHBoxLayout\" name=\"layout_bp_btb_status\">\n                   <item>\n                    <widget class=\"QLabel\" name=\"text_bp_btb_entries\">\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>140</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                     <property name=\"text\">\n                      <string>Number of entries:</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item>\n                    <widget class=\"QLabel\" name=\"text_bp_btb_entries_number\">\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>40</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                     <property name=\"text\">\n                      <string>1</string>\n                     </property>\n                     <property name=\"alignment\">\n                      <set>Qt::AlignCenter</set>\n                     </property>\n                    </widget>\n                   </item>\n                   <item>\n                    <spacer name=\"spacer_bp_btb_status\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>40</width>\n                       <height>20</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item>\n                    <widget class=\"Line\" name=\"line_bp_btb_status\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Vertical</enum>\n                     </property>\n                    </widget>\n                   </item>\n                   <item>\n                    <widget class=\"QLabel\" name=\"text_bp_btb_bits\">\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>140</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                     <property name=\"text\">\n                      <string>Number of bits:</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item>\n                    <widget class=\"QLabel\" name=\"text_bp_btb_bits_number\">\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>40</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                     <property name=\"text\">\n                      <string>0</string>\n                     </property>\n                     <property name=\"alignment\">\n                      <set>Qt::AlignCenter</set>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </item>\n                </layout>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QGroupBox\" name=\"group_bp_bht\">\n                <property name=\"title\">\n                 <string>Branch History Table (BHT)</string>\n                </property>\n                <layout class=\"QGridLayout\" name=\"gridLayout_2\">\n                 <item row=\"0\" column=\"2\">\n                  <widget class=\"QLabel\" name=\"text_bp_bht_bhr_bits_number\">\n                   <property name=\"minimumSize\">\n                    <size>\n                     <width>40</width>\n                     <height>0</height>\n                    </size>\n                   </property>\n                   <property name=\"text\">\n                    <string>0</string>\n                   </property>\n                   <property name=\"alignment\">\n                    <set>Qt::AlignCenter</set>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"1\" column=\"2\">\n                  <widget class=\"QLabel\" name=\"text_bp_bht_addr_bits_number\">\n                   <property name=\"minimumSize\">\n                    <size>\n                     <width>40</width>\n                     <height>0</height>\n                    </size>\n                   </property>\n                   <property name=\"text\">\n                    <string>0</string>\n                   </property>\n                   <property name=\"alignment\">\n                    <set>Qt::AlignCenter</set>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"0\" column=\"1\">\n                  <widget class=\"QSlider\" name=\"slider_bp_bht_bhr_bits\">\n                   <property name=\"maximum\">\n                    <number>8</number>\n                   </property>\n                   <property name=\"orientation\">\n                    <enum>Qt::Horizontal</enum>\n                   </property>\n                   <property name=\"tickPosition\">\n                    <enum>QSlider::TicksBothSides</enum>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"1\" column=\"1\">\n                  <widget class=\"QSlider\" name=\"slider_bp_bht_addr_bits\">\n                   <property name=\"maximum\">\n                    <number>8</number>\n                   </property>\n                   <property name=\"orientation\">\n                    <enum>Qt::Horizontal</enum>\n                   </property>\n                   <property name=\"tickPosition\">\n                    <enum>QSlider::TicksBothSides</enum>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"0\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"text_bp_bht_bhr_bits\">\n                   <property name=\"minimumSize\">\n                    <size>\n                     <width>140</width>\n                     <height>0</height>\n                    </size>\n                   </property>\n                   <property name=\"toolTip\">\n                    <string>BHR - Branch History Register</string>\n                   </property>\n                   <property name=\"text\">\n                    <string>Bits in BHR:</string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"2\" column=\"0\" colspan=\"3\">\n                  <widget class=\"Line\" name=\"line_bp_bht\">\n                   <property name=\"orientation\">\n                    <enum>Qt::Horizontal</enum>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"1\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"text_bp_bht_addr_bits\">\n                   <property name=\"minimumSize\">\n                    <size>\n                     <width>140</width>\n                     <height>0</height>\n                    </size>\n                   </property>\n                   <property name=\"toolTip\">\n                    <string>PC - Program Counter register</string>\n                   </property>\n                   <property name=\"text\">\n                    <string>Bits from PC address: </string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"3\" column=\"0\" colspan=\"3\">\n                  <layout class=\"QHBoxLayout\" name=\"layout_bp_bht_status\">\n                   <item>\n                    <widget class=\"QLabel\" name=\"text_bp_bht_entries\">\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>140</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                     <property name=\"text\">\n                      <string>Number of entries:</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item>\n                    <widget class=\"QLabel\" name=\"text_bp_bht_entries_number\">\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>40</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                     <property name=\"text\">\n                      <string>1</string>\n                     </property>\n                     <property name=\"alignment\">\n                      <set>Qt::AlignCenter</set>\n                     </property>\n                    </widget>\n                   </item>\n                   <item>\n                    <spacer name=\"spacer_bp_bht_status\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>40</width>\n                       <height>20</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item>\n                    <widget class=\"Line\" name=\"line_bp_bht_status\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Vertical</enum>\n                     </property>\n                    </widget>\n                   </item>\n                   <item>\n                    <widget class=\"QLabel\" name=\"text_bp_bht_bits\">\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>140</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                     <property name=\"text\">\n                      <string>Number of bits:</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item>\n                    <widget class=\"QLabel\" name=\"text_bp_bht_bits_number\">\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>40</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                     <property name=\"text\">\n                      <string>0</string>\n                     </property>\n                     <property name=\"alignment\">\n                      <set>Qt::AlignCenter</set>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </item>\n                </layout>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <spacer name=\"verticalSpacer_5\">\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>40</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\" name=\"tab_virtual_memory\">\n          <property name=\"accessibleName\">\n           <string>Virtual Memory</string>\n          </property>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout_11\">\n           <item>\n            <widget class=\"QGroupBox\" name=\"group_vm\">\n             <property name=\"enabled\">\n               <bool>true</bool>\n             </property>\n             <property name=\"title\">\n             <string>Virtual Memory</string>\n             </property>\n             <property name=\"checkable\">\n              <bool>true</bool>\n             </property>\n             <property name=\"checked\">\n              <bool>true</bool>\n             </property>\n             <layout class=\"QVBoxLayout\" name=\"verticalLayout_12\">\n              <item>\n               <widget class=\"QGroupBox\" name=\"itlb_groupBox\">\n                <property name=\"enabled\">\n                 <bool>true</bool>\n                </property>\n                <property name=\"title\">\n                <string>Program Translation Lookaside Buffer (TLB)</string>\n                </property>\n                <property name=\"checkable\">\n                 <bool>false</bool>\n                </property>\n                <layout class=\"QFormLayout\" name=\"formLayout_2\">\n                 <item row=\"0\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"label_2\">\n                   <property name=\"text\">\n                    <string>Number of sets:</string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"0\" column=\"1\">\n                  <widget class=\"Pow2SpinBox\" name=\"itlb_number_of_sets\">\n                   <property name=\"minimum\">\n                    <number>1</number>\n                   </property>\n                   <property name=\"maximum\">\n                    <number>1024</number>\n                   </property>\n                   <property name=\"singleStep\">\n                    <number>1</number>\n                   </property>\n                   <property name=\"stepType\">\n                    <enum>QAbstractSpinBox::DefaultStepType</enum>\n                   </property>\n                   <property name=\"value\">\n                    <number>1</number>\n                   </property>\n                   <property name=\"displayIntegerBase\">\n                    <number>10</number>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"1\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"label_7\">\n                   <property name=\"text\">\n                    <string>Degree of associativity:</string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"1\" column=\"1\">\n                  <widget class=\"QSpinBox\" name=\"itlb_degree_of_associativity\">\n                   <property name=\"minimum\">\n                    <number>1</number>\n                   </property>\n                   <property name=\"singleStep\">\n                    <number>1</number>\n                   </property>\n                   <property name=\"value\">\n                    <number>1</number>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"2\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"label_8\">\n                   <property name=\"text\">\n                    <string>Replacement policy:</string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"2\" column=\"1\">\n                  <widget class=\"QComboBox\" name=\"itlb_replacement_policy\">\n                   <item>\n                    <property name=\"text\">\n                     <string>Random</string>\n                    </property>\n                   </item>\n                   <item>\n                    <property name=\"text\">\n                     <string>Least Recently Used (LRU)</string>\n                    </property>\n                   </item>\n                   <item>\n                    <property name=\"text\">\n                     <string>Least Frequently Used (LFU)</string>\n                    </property>\n                   </item>\n                   <item>\n                    <property name=\"text\">\n                     <string>Pseudo Least Recently Used (PLRU)</string>\n                    </property>\n                   </item>\n                  </widget>\n                 </item>\n                </layout>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QGroupBox\" name=\"dtlb_groupBox\">\n                <property name=\"enabled\">\n                 <bool>true</bool>\n                </property>\n                <property name=\"title\">\n                <string>Data Translation Lookaside Buffer (TLB)</string>\n                </property>\n                <property name=\"checkable\">\n                 <bool>false</bool>\n                </property>\n                <layout class=\"QFormLayout\" name=\"formLayout_3\">\n                 <item row=\"0\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"label_3\">\n                   <property name=\"text\">\n                    <string>Number of sets:</string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"0\" column=\"1\">\n                  <widget class=\"Pow2SpinBox\" name=\"dtlb_number_of_sets\">\n                   <property name=\"minimum\">\n                    <number>1</number>\n                   </property>\n                   <property name=\"maximum\">\n                    <number>1024</number>\n                   </property>\n                   <property name=\"singleStep\">\n                    <number>1</number>\n                   </property>\n                   <property name=\"stepType\">\n                    <enum>QAbstractSpinBox::DefaultStepType</enum>\n                   </property>\n                   <property name=\"value\">\n                    <number>1</number>\n                   </property>\n                   <property name=\"displayIntegerBase\">\n                    <number>10</number>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"1\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"label_5\">\n                   <property name=\"text\">\n                    <string>Degree of associativity:</string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"1\" column=\"1\">\n                  <widget class=\"QSpinBox\" name=\"dtlb_degree_of_associativity\">\n                   <property name=\"minimum\">\n                    <number>1</number>\n                   </property>\n                   <property name=\"singleStep\">\n                    <number>1</number>\n                   </property>\n                   <property name=\"value\">\n                    <number>1</number>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"2\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"label_6\">\n                   <property name=\"text\">\n                    <string>Replacement policy:</string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"2\" column=\"1\">\n                  <widget class=\"QComboBox\" name=\"dtlb_replacement_policy\">\n                   <item>\n                    <property name=\"text\">\n                     <string>Random</string>\n                    </property>\n                   </item>\n                   <item>\n                    <property name=\"text\">\n                     <string>Least Recently Used (LRU)</string>\n                    </property>\n                   </item>\n                   <item>\n                    <property name=\"text\">\n                     <string>Least Frequently Used (LFU)</string>\n                    </property>\n                   </item>\n                   <item>\n                    <property name=\"text\">\n                     <string>Pseudo Least Recently Used (PLRU)</string>\n                    </property>\n                   </item>\n                  </widget>\n                 </item>\n                </layout>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <spacer name=\"verticalSpacer_6\">\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>21</width>\n               <height>40</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\" name=\"tab_memory\">\n          <property name=\"accessibleName\">\n           <string>Memory Timings</string>\n          </property>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n           <item>\n            <widget class=\"QCheckBox\" name=\"mem_protec_write\">\n             <property name=\"text\">\n              <string>Program memory write protection</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QCheckBox\" name=\"mem_protec_exec\">\n             <property name=\"text\">\n              <string>Data memory executable protection</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QGroupBox\" name=\"groupBox\">\n             <property name=\"title\">\n              <string>Access time (in cycles)</string>\n             </property>\n             <layout class=\"QFormLayout\" name=\"formLayout\">\n              <item row=\"0\" column=\"0\">\n               <widget class=\"QLabel\" name=\"label_read\">\n                <property name=\"text\">\n                 <string>Read:</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"0\" column=\"1\">\n               <widget class=\"QSpinBox\" name=\"mem_time_read\">\n                <property name=\"minimum\">\n                 <number>1</number>\n                </property>\n                <property name=\"maximum\">\n                 <number>999999999</number>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"0\">\n               <widget class=\"QLabel\" name=\"label_write\">\n                <property name=\"text\">\n                 <string>Write:</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"1\">\n               <widget class=\"QSpinBox\" name=\"mem_time_write\">\n                <property name=\"minimum\">\n                 <number>1</number>\n                </property>\n                <property name=\"maximum\">\n                 <number>999999999</number>\n                </property>\n               </widget>\n              </item>\n              <item row=\"2\" column=\"0\">\n               <widget class=\"QLabel\" name=\"label_burts_enable\">\n                <property name=\"text\">\n                 <string>Burst enable:</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"2\" column=\"1\">\n               <widget class=\"QCheckBox\" name=\"mem_enable_burst\">\n                <property name=\"checked\">\n                 <bool>false</bool>\n                </property>\n               </widget>\n              </item>\n              <item row=\"3\" column=\"0\">\n               <widget class=\"QLabel\" name=\"label_burts\">\n                <property name=\"text\">\n                 <string>Burst:</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"3\" column=\"1\">\n               <widget class=\"QSpinBox\" name=\"mem_time_burst\">\n                <property name=\"minimum\">\n                 <number>0</number>\n                </property>\n                <property name=\"maximum\">\n                 <number>999999999</number>\n                </property>\n               </widget>\n              </item>\n              <item row=\"4\" column=\"0\">\n               <widget class=\"QLabel\" name=\"label_level2\">\n                <property name=\"text\">\n                 <string>L2 Access:</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"4\" column=\"1\">\n               <widget class=\"QSpinBox\" name=\"mem_time_level2\">\n                <property name=\"minimum\">\n                 <number>0</number>\n                </property>\n                <property name=\"maximum\">\n                 <number>999999999</number>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <spacer name=\"verticalSpacer_2\">\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>40</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\" name=\"tab_cache_program\">\n          <property name=\"accessibleName\">\n           <string>L1 Program Cache</string>\n          </property>\n         </widget>\n         <widget class=\"QWidget\" name=\"tab_cache_data\">\n          <property name=\"accessibleName\">\n           <string>L1 Data Cache</string>\n          </property>\n         </widget>\n         <widget class=\"QWidget\" name=\"tab_cache_level2\">\n          <property name=\"accessibleName\">\n           <string>L2 Cache</string>\n          </property>\n         </widget>\n         <widget class=\"QWidget\" name=\"tab_os_emulation\">\n          <property name=\"accessibleName\">\n           <string>System Emulation and IRQ</string>\n          </property>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout_7\">\n           <item>\n            <widget class=\"QCheckBox\" name=\"osemu_enable\">\n             <property name=\"text\">\n              <string>Enable emulation of operating system services</string>\n             </property>\n             <property name=\"checked\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QCheckBox\" name=\"osemu_known_syscall_stop\">\n             <property name=\"text\">\n              <string>Stop on known system call</string>\n             </property>\n             <property name=\"checked\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QCheckBox\" name=\"osemu_unknown_syscall_stop\">\n             <property name=\"text\">\n              <string>Stop on unknown system call</string>\n             </property>\n             <property name=\"checked\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QCheckBox\" name=\"osemu_interrupt_stop\">\n             <property name=\"text\">\n              <string>Stop on interrupt entry</string>\n             </property>\n             <property name=\"checked\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QCheckBox\" name=\"osemu_exception_stop\">\n             <property name=\"text\">\n              <string>Stop and step over exceptions (overflow, etc.)</string>\n             </property>\n             <property name=\"checked\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n             <item>\n              <widget class=\"QLabel\" name=\"label_fs_root\">\n               <property name=\"text\">\n                <string>Filesystem root:</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QLineEdit\" name=\"osemu_fs_root\"/>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"osemu_fs_root_browse\">\n               <property name=\"text\">\n                <string>Browse</string>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item>\n            <spacer name=\"verticalSpacer_4\">\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>21</width>\n               <height>40</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n        </widget>\n       </item>\n      </layout>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <property name=\"leftMargin\">\n      <number>9</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>9</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>0</number>\n     </property>\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"pushButton_example\">\n       <property name=\"text\">\n        <string>Example</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"pushButton_start_empty\">\n       <property name=\"text\">\n        <string>Start empty</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"pushButton_load\">\n       <property name=\"text\">\n        <string>Load machine</string>\n       </property>\n       <property name=\"default\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"pushButton_cancel\">\n       <property name=\"text\">\n        <string>Cancel</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>Pow2SpinBox</class>\n   <extends>QSpinBox</extends>\n   <header>ui/pow2spinbox.h</header>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/gui/dialogs/new/NewDialogCache.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>NewDialogCache</class>\n <widget class=\"QWidget\" name=\"NewDialogCache\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>435</width>\n    <height>204</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QGroupBox\" name=\"enabled\">\n     <property name=\"title\">\n      <string>Enable cache</string>\n     </property>\n     <property name=\"checkable\">\n      <bool>true</bool>\n     </property>\n     <layout class=\"QFormLayout\" name=\"formLayout\">\n      <item row=\"0\" column=\"0\">\n       <widget class=\"QLabel\" name=\"label_2\">\n        <property name=\"text\">\n         <string>Number of sets:</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"0\" column=\"1\">\n       <widget class=\"QSpinBox\" name=\"number_of_sets\">\n        <property name=\"minimum\">\n         <number>1</number>\n        </property>\n        <property name=\"maximum\">\n         <number>1024</number>\n        </property>\n       </widget>\n      </item>\n      <item row=\"1\" column=\"0\">\n       <widget class=\"QLabel\" name=\"label_3\">\n        <property name=\"text\">\n         <string>Block size:</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"1\" column=\"1\">\n       <widget class=\"QSpinBox\" name=\"block_size\">\n        <property name=\"minimum\">\n         <number>1</number>\n        </property>\n       </widget>\n      </item>\n      <item row=\"2\" column=\"0\">\n       <widget class=\"QLabel\" name=\"label_5\">\n        <property name=\"text\">\n         <string>Degree of associativity:</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"2\" column=\"1\">\n       <widget class=\"QSpinBox\" name=\"degree_of_associativity\">\n        <property name=\"minimum\">\n         <number>1</number>\n        </property>\n       </widget>\n      </item>\n      <item row=\"3\" column=\"0\">\n       <widget class=\"QLabel\" name=\"label_6\">\n        <property name=\"text\">\n         <string>Replacement policy:</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"3\" column=\"1\">\n       <widget class=\"QComboBox\" name=\"replacement_policy\">\n        <item>\n         <property name=\"text\">\n          <string>Random</string>\n         </property>\n        </item>\n        <item>\n         <property name=\"text\">\n          <string>Least Recently Used (LRU)</string>\n         </property>\n        </item>\n        <item>\n         <property name=\"text\">\n          <string>Least Frequently Used (LFU)</string>\n         </property>\n        </item>\n        <item>\n         <property name=\"text\">\n          <string>Pseudo Least Recently Used (PLRU)</string>\n         </property>\n        </item>\n        <item>\n         <property name=\"text\">\n          <string>Not Most Recently Used (NMRU)</string>\n         </property>\n        </item>\n       </widget>\n      </item>\n      <item row=\"4\" column=\"0\">\n       <widget class=\"QLabel\" name=\"label_writeback\">\n        <property name=\"text\">\n         <string>Writeback policy:</string>\n        </property>\n       </widget>\n      </item>\n      <item row=\"4\" column=\"1\">\n       <widget class=\"QComboBox\" name=\"writeback_policy\">\n        <item>\n         <property name=\"text\">\n          <string>Write through - noallocate</string>\n         </property>\n        </item>\n        <item>\n         <property name=\"text\">\n          <string>Write through - write allocate</string>\n         </property>\n        </item>\n        <item>\n         <property name=\"text\">\n          <string>Write back</string>\n         </property>\n        </item>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/gui/dialogs/new/newdialog.cpp",
    "content": "#include \"newdialog.h\"\n\n#include \"helper/async_modal.h\"\n#include \"machine/simulator_exception.h\"\n#include \"mainwindow/mainwindow.h\"\n\n#include <utility>\n\n#ifdef __EMSCRIPTEN__\n    #include \"qhtml5file.h\"\n\n    #include <QFileInfo>\n#endif\n\nNewDialog::NewDialog(QWidget *parent, QSettings *settings) : QDialog(parent) {\n    setWindowTitle(\"New machine\");\n\n    this->settings = settings;\n    config.reset();\n\n    ui.reset(new Ui::NewDialog());\n    ui->setupUi(this);\n    ui_cache_p.reset(new Ui::NewDialogCache());\n    ui_cache_p->setupUi(ui->tab_cache_program);\n    ui_cache_p->writeback_policy->hide();\n    ui_cache_p->label_writeback->hide();\n    ui_cache_d.reset(new Ui::NewDialogCache());\n    ui_cache_d->setupUi(ui->tab_cache_data);\n    ui_cache_l2.reset(new Ui::NewDialogCache());\n    ui_cache_l2->setupUi(ui->tab_cache_level2);\n\n    QList<QTreeWidgetItem *> config_pages_items;\n    for (int i = 0; i < ui->config_pages->count(); ++i) {\n        QString page_id = ui->config_pages->widget(i)->objectName();\n        QString page_name = ui->config_pages->widget(i)->accessibleName();\n        config_pages_items.append(new QTreeWidgetItem(\n            static_cast<QTreeWidget *>(nullptr), QStringList { page_name, page_id }));\n    }\n    ui->page_select_tree->insertTopLevelItems(0, config_pages_items);\n\n    connect(ui->page_select_tree, &QTreeWidget::currentItemChanged, this, &NewDialog::switch2page);\n\n    connect(ui->pushButton_example, &QAbstractButton::clicked, this, &NewDialog::create_example);\n    connect(ui->pushButton_start_empty, &QAbstractButton::clicked, this, &NewDialog::create_empty);\n    connect(ui->pushButton_load, &QAbstractButton::clicked, this, &NewDialog::create);\n    connect(ui->pushButton_cancel, &QAbstractButton::clicked, this, &NewDialog::cancel);\n    connect(ui->pushButton_browse, &QAbstractButton::clicked, this, &NewDialog::browse_elf);\n    connect(ui->elf_file, &QLineEdit::textChanged, this, &NewDialog::elf_change);\n    connect(ui->preset_no_pipeline, &QAbstractButton::toggled, this, &NewDialog::set_preset);\n    connect(ui->preset_no_pipeline_cache, &QAbstractButton::toggled, this, &NewDialog::set_preset);\n    connect(ui->preset_pipelined_bare, &QAbstractButton::toggled, this, &NewDialog::set_preset);\n    connect(ui->preset_pipelined, &QAbstractButton::toggled, this, &NewDialog::set_preset);\n    connect(\n        ui->reset_at_compile, &QAbstractButton::clicked, this, &NewDialog::reset_at_compile_change);\n\n    connect(ui->xlen_64bit, &QAbstractButton::clicked, this, &NewDialog::xlen_64bit_change);\n    connect(ui->isa_atomic, &QAbstractButton::clicked, this, &NewDialog::isa_atomic_change);\n    connect(ui->isa_multiply, &QAbstractButton::clicked, this, &NewDialog::isa_multiply_change);\n    connect(ui->pipelined, &QAbstractButton::clicked, this, &NewDialog::pipelined_change);\n    connect(ui->delay_slot, &QAbstractButton::clicked, this, &NewDialog::delay_slot_change);\n    connect(ui->hazard_unit, &QGroupBox::clicked, this, &NewDialog::hazard_unit_change);\n    connect(ui->hazard_stall, &QAbstractButton::clicked, this, &NewDialog::hazard_unit_change);\n    connect(\n        ui->hazard_stall_forward, &QAbstractButton::clicked, this, &NewDialog::hazard_unit_change);\n\n    connect(\n        ui->mem_protec_exec, &QAbstractButton::clicked, this, &NewDialog::mem_protec_exec_change);\n    connect(\n        ui->mem_protec_write, &QAbstractButton::clicked, this, &NewDialog::mem_protec_write_change);\n    connect(\n        ui->mem_time_read, QOverload<int>::of(&QSpinBox::valueChanged), this,\n        &NewDialog::mem_time_read_change);\n    connect(\n        ui->mem_time_write, QOverload<int>::of(&QSpinBox::valueChanged), this,\n        &NewDialog::mem_time_write_change);\n    connect(\n        ui->mem_enable_burst, &QAbstractButton::clicked, this, &NewDialog::mem_enable_burst_change);\n    connect(\n        ui->mem_time_burst, QOverload<int>::of(&QSpinBox::valueChanged), this,\n        &NewDialog::mem_time_burst_change);\n    connect(\n        ui->mem_time_level2, QOverload<int>::of(&QSpinBox::valueChanged), this,\n        &NewDialog::mem_time_level2_change);\n\n    connect(ui->osemu_enable, &QAbstractButton::clicked, this, &NewDialog::osemu_enable_change);\n    connect(\n        ui->osemu_known_syscall_stop, &QAbstractButton::clicked, this,\n        &NewDialog::osemu_known_syscall_stop_change);\n    connect(\n        ui->osemu_unknown_syscall_stop, &QAbstractButton::clicked, this,\n        &NewDialog::osemu_unknown_syscall_stop_change);\n    connect(\n        ui->osemu_interrupt_stop, &QAbstractButton::clicked, this,\n        &NewDialog::osemu_interrupt_stop_change);\n    connect(\n        ui->osemu_exception_stop, &QAbstractButton::clicked, this,\n        &NewDialog::osemu_exception_stop_change);\n    connect(\n        ui->osemu_fs_root_browse, &QAbstractButton::clicked, this,\n        &NewDialog::browse_osemu_fs_root);\n    connect(ui->osemu_fs_root, &QLineEdit::textChanged, this, &NewDialog::osemu_fs_root_change);\n\n    // Branch predictor\n    connect(\n        ui->group_bp, QOverload<bool>::of(&QGroupBox::toggled), this,\n        &NewDialog::bp_enabled_change);\n    connect(\n        ui->select_bp_type, QOverload<int>::of(&QComboBox::activated), this,\n        &NewDialog::bp_type_change);\n    connect(\n        ui->select_bp_init_state, QOverload<int>::of(&QComboBox::activated), this,\n        &NewDialog::bp_init_state_change);\n    connect(\n        ui->slider_bp_btb_addr_bits, &QAbstractSlider::valueChanged, this,\n        &NewDialog::bp_btb_addr_bits_change);\n    connect(\n        ui->slider_bp_bht_bhr_bits, &QAbstractSlider::valueChanged, this,\n        &NewDialog::bp_bht_bhr_bits_change);\n    connect(\n        ui->slider_bp_bht_addr_bits, &QAbstractSlider::valueChanged, this,\n        &NewDialog::bp_bht_addr_bits_change);\n\n    // Virtual Memory\n    connect(\n        ui->group_vm, QOverload<bool>::of(&QGroupBox::toggled), this,\n        &NewDialog::vm_enabled_change);\n    connect(\n        ui->itlb_number_of_sets, QOverload<int>::of(&QSpinBox::valueChanged), this,\n        &NewDialog::itlb_num_sets_changed);\n    connect(\n        ui->itlb_degree_of_associativity, QOverload<int>::of(&QSpinBox::valueChanged), this,\n        &NewDialog::itlb_assoc_changed);\n    connect(\n        ui->itlb_replacement_policy, QOverload<int>::of(&QComboBox::activated), this,\n        &NewDialog::itlb_policy_changed);\n    connect(\n        ui->dtlb_number_of_sets, QOverload<int>::of(&QSpinBox::valueChanged), this,\n        &NewDialog::dtlb_num_sets_changed);\n    connect(\n        ui->dtlb_degree_of_associativity, QOverload<int>::of(&QSpinBox::valueChanged), this,\n        &NewDialog::dtlb_assoc_changed);\n    connect(\n        ui->dtlb_replacement_policy, QOverload<int>::of(&QComboBox::activated), this,\n        &NewDialog::dtlb_policy_changed);\n\n    cache_handler_d = new NewDialogCacheHandler(this, ui_cache_d.data());\n    cache_handler_p = new NewDialogCacheHandler(this, ui_cache_p.data());\n    cache_handler_l2 = new NewDialogCacheHandler(this, ui_cache_l2.data());\n\n    // TODO remove this block when protections are implemented\n    ui->mem_protec_exec->setVisible(false);\n    ui->mem_protec_write->setVisible(false);\n\n    load_settings(); // Also configures gui\n\n    ui->config_page_title->setStyleSheet(\"font-weight: bold\");\n    switch2page(config_pages_items.at(0));\n}\n\nvoid NewDialog::switch2page(QTreeWidgetItem *current, QTreeWidgetItem *previous) {\n    (void)previous;\n    QWidget *page\n        = ui->config_pages->findChild<QWidget *>(current->text(1), Qt::FindDirectChildrenOnly);\n    if (page != nullptr) {\n        ui->config_pages->setCurrentWidget(page);\n        ui->config_page_title->setText(current->text(0));\n    }\n}\n\nvoid NewDialog::switch_to_custom() {\n    if (!ui->preset_custom->isChecked()) {\n        ui->preset_custom->setChecked(true); // Select \"Custom\" preset and refresh GUI (no-op if\n                                             // already selected).\n        config_gui();\n    }\n}\n\nvoid NewDialog::closeEvent(QCloseEvent *) {\n    load_settings(); // Reset from settings\n    // Close the main window if not already configured\n    auto *prnt = (MainWindow *)parent();\n    if (!prnt->configured()) { prnt->close(); }\n}\n\nvoid NewDialog::cancel() {\n    this->close();\n}\n\nvoid NewDialog::create() {\n    auto *p_window = (MainWindow *)parent();\n\n    try {\n        p_window->create_core(*config, true, false);\n    } catch (const machine::SimulatorExceptionInput &e) {\n        showAsyncCriticalBox(\n            this, \"Error while initializing new machine\", e.msg(false), e.msg(true),\n            \"Please check that ELF executable really exists and is in correct format.\");\n        return;\n    }\n\n    store_settings(); // Save to settings\n    this->close();\n}\n\nvoid NewDialog::create_empty() {\n    auto *p_window = (MainWindow *)parent();\n    p_window->create_core(*config, false, true);\n    store_settings(); // Save to settings\n    this->close();\n}\n\nvoid NewDialog::create_example() {\n    auto *p_window = (MainWindow *)parent();\n    QString example(\":/samples/template.S\");\n    p_window->create_core(*config, false, true);\n    store_settings(); // Save to settings\n    p_window->close_source_by_name(example, false);\n    p_window->example_source(example);\n    p_window->show_program();\n    p_window->compile_source();\n    this->close();\n}\n\nvoid NewDialog::browse_elf() {\n#ifndef __EMSCRIPTEN__\n    QFileDialog elf_dialog(this);\n    elf_dialog.setFileMode(QFileDialog::ExistingFile);\n    if (elf_dialog.exec()) {\n        QString path = elf_dialog.selectedFiles()[0];\n        ui->elf_file->setText(path);\n        config->set_elf(path);\n    }\n    // Elf shouldn't have any other effect, so we skip config_gui here\n#else\n    QHtml5File::load(\"*\", [&](const QByteArray &content, const QString &fileName) {\n        QFileInfo fi(fileName);\n        QString elf_name = fi.fileName();\n        QFile file(elf_name);\n        file.open(QIODevice::WriteOnly | QIODevice::Truncate);\n        file.write(content);\n        file.close();\n        ui->elf_file->setText(elf_name);\n        config->set_elf(elf_name);\n    });\n#endif\n}\n\nvoid NewDialog::elf_change(QString val) {\n    config->set_elf(std::move(val));\n}\n\nvoid NewDialog::set_preset() {\n    unsigned pres_n = preset_number();\n    if (pres_n > 0) {\n        config->preset((enum machine::ConfigPresets)(pres_n - 1));\n        config_gui();\n    }\n}\n\nvoid NewDialog::xlen_64bit_change(bool val) {\n    if (val)\n        config->set_simulated_xlen(machine::Xlen::_64);\n    else\n        config->set_simulated_xlen(machine::Xlen::_32);\n    switch_to_custom();\n}\n\nvoid NewDialog::isa_atomic_change(bool val) {\n    auto isa_mask = machine::ConfigIsaWord::byChar('A');\n    if (val)\n        config->modify_isa_word(isa_mask, isa_mask);\n    else\n        config->modify_isa_word(isa_mask, machine::ConfigIsaWord::empty());\n    switch_to_custom();\n}\n\nvoid NewDialog::isa_multiply_change(bool val) {\n    auto isa_mask = machine::ConfigIsaWord::byChar('M');\n    if (val)\n        config->modify_isa_word(isa_mask, isa_mask);\n    else\n        config->modify_isa_word(isa_mask, machine::ConfigIsaWord::empty());\n    switch_to_custom();\n}\n\nvoid NewDialog::pipelined_change(bool val) {\n    config->set_pipelined(val);\n    ui->hazard_unit->setEnabled(config->pipelined());\n    switch_to_custom();\n}\n\nvoid NewDialog::delay_slot_change(bool val) {\n    config->set_delay_slot(val);\n    switch_to_custom();\n}\n\nvoid NewDialog::hazard_unit_change() {\n    if (ui->hazard_unit->isChecked()) {\n        config->set_hazard_unit(\n            ui->hazard_stall->isChecked() ? machine::MachineConfig::HU_STALL\n                                          : machine::MachineConfig::HU_STALL_FORWARD);\n    } else {\n        config->set_hazard_unit(machine::MachineConfig::HU_NONE);\n    }\n    switch_to_custom();\n}\n\nvoid NewDialog::mem_protec_exec_change(bool v) {\n    config->set_memory_execute_protection(v);\n    switch_to_custom();\n}\n\nvoid NewDialog::mem_protec_write_change(bool v) {\n    config->set_memory_write_protection(v);\n    switch_to_custom();\n}\n\nvoid NewDialog::mem_time_read_change(int v) {\n    if (config->memory_access_time_read() != (unsigned)v) {\n        config->set_memory_access_time_read(v);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::mem_time_write_change(int v) {\n    if (config->memory_access_time_write() != (unsigned)v) {\n        config->set_memory_access_time_write(v);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::mem_enable_burst_change(bool v) {\n    if (config->memory_access_enable_burst() != v) {\n        config->set_memory_access_enable_burst(v);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::mem_time_burst_change(int v) {\n    if (config->memory_access_time_burst() != (unsigned)v) {\n        config->set_memory_access_time_burst(v);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::mem_time_level2_change(int v) {\n    if (config->memory_access_time_level2() != (unsigned)v) {\n        config->set_memory_access_time_level2(v);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::osemu_enable_change(bool v) {\n    config->set_osemu_enable(v);\n}\n\nvoid NewDialog::osemu_known_syscall_stop_change(bool v) {\n    config->set_osemu_known_syscall_stop(v);\n}\n\nvoid NewDialog::osemu_unknown_syscall_stop_change(bool v) {\n    config->set_osemu_unknown_syscall_stop(v);\n}\n\nvoid NewDialog::osemu_interrupt_stop_change(bool v) {\n    config->set_osemu_interrupt_stop(v);\n}\n\nvoid NewDialog::osemu_exception_stop_change(bool v) {\n    config->set_osemu_exception_stop(v);\n}\n\nvoid NewDialog::browse_osemu_fs_root() {\n    auto osemu_fs_root_dialog = new QFileDialog(this);\n    osemu_fs_root_dialog->setFileMode(QFileDialog::Directory);\n    osemu_fs_root_dialog->setOption(QFileDialog::ShowDirsOnly, true);\n    QFileDialog::connect(osemu_fs_root_dialog, &QFileDialog::finished, [=](int result) {\n        if (result > 0) {\n            QString path = osemu_fs_root_dialog->selectedFiles()[0];\n            ui->osemu_fs_root->setText(path);\n            config->set_osemu_fs_root(path);\n            delete osemu_fs_root_dialog;\n        }\n    });\n    osemu_fs_root_dialog->open();\n}\n\nvoid NewDialog::osemu_fs_root_change(QString val) {\n    config->set_osemu_fs_root(std::move(val));\n}\n\nvoid NewDialog::reset_at_compile_change(bool v) {\n    config->set_reset_at_compile(v);\n}\n\nvoid NewDialog::bp_toggle_widgets() {\n    // Enables or disables all branch predictor widgets\n    // depending on the setting\n\n    const machine::PredictorType predictor_type { config->get_bp_type() };\n    const bool is_predictor_dynamic { machine::is_predictor_type_dynamic(predictor_type) };\n    const bool is_predictor_enabled { config->get_bp_enabled() };\n\n    ui->group_bp_bht->setEnabled(is_predictor_enabled && is_predictor_dynamic);\n    ui->text_bp_init_state->setEnabled(is_predictor_enabled && is_predictor_dynamic);\n    ui->select_bp_init_state->setEnabled(is_predictor_enabled && is_predictor_dynamic);\n}\n\nvoid NewDialog::bp_type_change() {\n    // Read branch predictor type from GUI and store it in the config\n    const machine::PredictorType predictor_type {\n        ui->select_bp_type->currentData().value<machine::PredictorType>()\n    };\n\n    bool need_switch2custom = (config->get_bp_type() != predictor_type);\n\n    config->set_bp_type(predictor_type);\n\n    // Remove all items from init state list\n    ui->select_bp_init_state->clear();\n\n    // Configure GUI based on predictor selection\n    switch (predictor_type) {\n    case machine::PredictorType::SMITH_1_BIT: {\n        // Add items to the combo box\n        ui->select_bp_init_state->addItem(\n            predictor_state_to_string(machine::PredictorState::NOT_TAKEN, false).toString(),\n            QVariant::fromValue(machine::PredictorState::NOT_TAKEN));\n        ui->select_bp_init_state->addItem(\n            predictor_state_to_string(machine::PredictorState::TAKEN, false).toString(),\n            QVariant::fromValue(machine::PredictorState::TAKEN));\n\n        // Set selected value, or set default if not found\n        const int index { ui->select_bp_init_state->findData(\n            QVariant::fromValue(config->get_bp_init_state())) };\n        if (index >= 0) {\n            ui->select_bp_init_state->setCurrentIndex(index);\n        } else {\n            ui->select_bp_init_state->setCurrentIndex(ui->select_bp_init_state->findData(\n                QVariant::fromValue(machine::PredictorState::NOT_TAKEN)));\n            config->set_bp_init_state(machine::PredictorState::NOT_TAKEN);\n        }\n    } break;\n\n    case machine::PredictorType::SMITH_2_BIT:\n    case machine::PredictorType::SMITH_2_BIT_HYSTERESIS: {\n        // Add items to the combo box\n        ui->select_bp_init_state->addItem(\n            predictor_state_to_string(machine::PredictorState::STRONGLY_NOT_TAKEN, false).toString(),\n            QVariant::fromValue(machine::PredictorState::STRONGLY_NOT_TAKEN));\n        ui->select_bp_init_state->addItem(\n            predictor_state_to_string(machine::PredictorState::WEAKLY_NOT_TAKEN, false).toString(),\n            QVariant::fromValue(machine::PredictorState::WEAKLY_NOT_TAKEN));\n        ui->select_bp_init_state->addItem(\n            predictor_state_to_string(machine::PredictorState::WEAKLY_TAKEN, false).toString(),\n            QVariant::fromValue(machine::PredictorState::WEAKLY_TAKEN));\n        ui->select_bp_init_state->addItem(\n            predictor_state_to_string(machine::PredictorState::STRONGLY_TAKEN, false).toString(),\n            QVariant::fromValue(machine::PredictorState::STRONGLY_TAKEN));\n\n        // Set selected value, or set default if not found\n        const int index { ui->select_bp_init_state->findData(\n            QVariant::fromValue(config->get_bp_init_state())) };\n        if (index >= 0) {\n            ui->select_bp_init_state->setCurrentIndex(index);\n        } else {\n            ui->select_bp_init_state->setCurrentIndex(ui->select_bp_init_state->findData(\n                QVariant::fromValue(machine::PredictorState::WEAKLY_NOT_TAKEN)));\n            config->set_bp_init_state(machine::PredictorState::WEAKLY_NOT_TAKEN);\n        }\n    } break;\n\n    default: break;\n    }\n    bp_toggle_widgets();\n\n    if (need_switch2custom) switch_to_custom();\n}\n\nvoid NewDialog::bp_enabled_change(bool v) {\n    if (config->get_bp_enabled() != v) {\n        config->set_bp_enabled(v);\n        bp_toggle_widgets();\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::bp_init_state_change(void) {\n    auto v = ui->select_bp_init_state->currentData().value<machine::PredictorState>();\n    if (v != config->get_bp_init_state()) {\n        config->set_bp_init_state(v);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::bp_btb_addr_bits_change(int v) {\n    if (config->get_bp_btb_bits() != v) {\n        config->set_bp_btb_bits((uint8_t)v);\n        switch_to_custom();\n    }\n    ui->text_bp_btb_addr_bits_number->setText(QString::number(config->get_bp_btb_bits()));\n    ui->text_bp_btb_bits_number->setText(QString::number(config->get_bp_btb_bits()));\n    ui->text_bp_btb_entries_number->setText(QString::number(qPow(2, config->get_bp_btb_bits())));\n}\n\nvoid NewDialog::bp_bht_bits_texts_update(void) {\n    ui->text_bp_bht_bhr_bits_number->setText(QString::number(config->get_bp_bhr_bits()));\n    ui->text_bp_bht_addr_bits_number->setText(QString::number(config->get_bp_bht_addr_bits()));\n    ui->text_bp_bht_bits_number->setText(QString::number(config->get_bp_bht_bits()));\n    ui->text_bp_bht_entries_number->setText(QString::number(qPow(2, config->get_bp_bht_bits())));\n}\n\nvoid NewDialog::bp_bht_bhr_bits_change(int v) {\n    if (config->get_bp_bhr_bits() != v) {\n        config->set_bp_bhr_bits((uint8_t)v);\n        switch_to_custom();\n    }\n    bp_bht_bits_texts_update();\n}\n\nvoid NewDialog::bp_bht_addr_bits_change(int v) {\n    if (config->get_bp_bht_addr_bits() != v) {\n        config->set_bp_bht_addr_bits((uint8_t)v);\n        switch_to_custom();\n    }\n    bp_bht_bits_texts_update();\n}\n\nvoid NewDialog::vm_enabled_change(bool v) {\n    if (config->get_vm_enabled() != v) {\n        config->set_vm_enabled(v);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::itlb_num_sets_changed(int v) {\n    unsigned u = v >= 0 ? v : 0;\n    if (config->access_tlb_program()->get_tlb_num_sets() != u) {\n        config->access_tlb_program()->set_tlb_num_sets(u);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::itlb_assoc_changed(int v) {\n    unsigned u = v >= 0 ? v : 0;\n    if (config->access_tlb_program()->get_tlb_associativity() != u) {\n        config->access_tlb_program()->set_tlb_associativity(u);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::itlb_policy_changed(int idx) {\n    machine::TLBConfig::ReplacementPolicy pol;\n    pol = static_cast<machine::TLBConfig::ReplacementPolicy>(idx);\n    if (config->access_tlb_program()->get_tlb_replacement_policy() != pol) {\n        config->access_tlb_program()->set_tlb_replacement_policy(pol);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::dtlb_num_sets_changed(int v) {\n    unsigned u = v >= 0 ? v : 0;\n    if (config->access_tlb_data()->get_tlb_num_sets() != u) {\n        config->access_tlb_data()->set_tlb_num_sets(u);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::dtlb_assoc_changed(int v) {\n    unsigned u = v >= 0 ? v : 0;\n    if (config->access_tlb_data()->get_tlb_associativity() != u) {\n        config->access_tlb_data()->set_tlb_associativity(u);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::dtlb_policy_changed(int idx) {\n    machine::TLBConfig::ReplacementPolicy pol;\n    pol = static_cast<machine::TLBConfig::ReplacementPolicy>(idx);\n    if (config->access_tlb_data()->get_tlb_replacement_policy() != pol) {\n        config->access_tlb_data()->set_tlb_replacement_policy(pol);\n        switch_to_custom();\n    }\n}\n\nvoid NewDialog::config_gui() {\n    // Basic\n    ui->elf_file->setText(config->elf());\n    ui->reset_at_compile->setChecked(config->reset_at_compile());\n    // Core\n    ui->xlen_64bit->setChecked(config->get_simulated_xlen() == machine::Xlen::_64);\n    ui->isa_multiply->setChecked(config->get_isa_word().contains('M'));\n    ui->isa_atomic->setChecked(config->get_isa_word().contains('A'));\n    ui->pipelined->setChecked(config->pipelined());\n    ui->delay_slot->setChecked(config->delay_slot());\n    ui->hazard_unit->setChecked(config->hazard_unit() != machine::MachineConfig::HU_NONE);\n    ui->hazard_stall->setChecked(config->hazard_unit() == machine::MachineConfig::HU_STALL);\n    ui->hazard_stall_forward->setChecked(\n        config->hazard_unit() == machine::MachineConfig::HU_STALL_FORWARD);\n\n    // Branch predictor\n    ui->group_bp->setChecked(config->get_bp_enabled());\n    ui->select_bp_type->clear();\n    ui->select_bp_type->addItem(\n        predictor_type_to_string(machine::PredictorType::ALWAYS_NOT_TAKEN).toString(),\n        QVariant::fromValue(machine::PredictorType::ALWAYS_NOT_TAKEN));\n    ui->select_bp_type->addItem(\n        predictor_type_to_string(machine::PredictorType::ALWAYS_TAKEN).toString(),\n        QVariant::fromValue(machine::PredictorType::ALWAYS_TAKEN));\n    ui->select_bp_type->addItem(\n        predictor_type_to_string(machine::PredictorType::BTFNT).toString(),\n        QVariant::fromValue(machine::PredictorType::BTFNT));\n    ui->select_bp_type->addItem(\n        predictor_type_to_string(machine::PredictorType::SMITH_1_BIT).toString(),\n        QVariant::fromValue(machine::PredictorType::SMITH_1_BIT));\n    ui->select_bp_type->addItem(\n        predictor_type_to_string(machine::PredictorType::SMITH_2_BIT).toString(),\n        QVariant::fromValue(machine::PredictorType::SMITH_2_BIT));\n    ui->select_bp_type->addItem(\n        predictor_type_to_string(machine::PredictorType::SMITH_2_BIT_HYSTERESIS).toString(),\n        QVariant::fromValue(machine::PredictorType::SMITH_2_BIT_HYSTERESIS));\n    const int index { ui->select_bp_type->findData(QVariant::fromValue(config->get_bp_type())) };\n    if (index >= 0) {\n        ui->select_bp_type->setCurrentIndex(index);\n    } else {\n        ui->select_bp_type->setCurrentIndex(\n            ui->select_bp_type->findData(QVariant::fromValue(machine::PredictorType::SMITH_1_BIT)));\n        config->set_bp_type(machine::PredictorType::SMITH_1_BIT);\n    }\n    ui->slider_bp_btb_addr_bits->setMaximum(BP_MAX_BTB_BITS);\n    ui->slider_bp_btb_addr_bits->setValue(config->get_bp_btb_bits());\n    ui->text_bp_btb_addr_bits_number->setText(QString::number(config->get_bp_btb_bits()));\n    ui->text_bp_btb_bits_number->setText(QString::number(config->get_bp_btb_bits()));\n    ui->text_bp_btb_entries_number->setText(QString::number(qPow(2, config->get_bp_btb_bits())));\n    ui->slider_bp_bht_bhr_bits->setMaximum(BP_MAX_BHR_BITS);\n    ui->slider_bp_bht_bhr_bits->setValue(config->get_bp_bhr_bits());\n    ui->text_bp_bht_bhr_bits_number->setText(QString::number(config->get_bp_bhr_bits()));\n    ui->slider_bp_bht_addr_bits->setMaximum(BP_MAX_BHT_ADDR_BITS);\n    ui->slider_bp_bht_addr_bits->setValue(config->get_bp_bht_addr_bits());\n    ui->text_bp_bht_addr_bits_number->setText(QString::number(config->get_bp_bht_addr_bits()));\n    ui->text_bp_bht_bits_number->setText(QString::number(config->get_bp_bht_bits()));\n    ui->text_bp_bht_entries_number->setText(QString::number(qPow(2, config->get_bp_bht_bits())));\n    bp_type_change();\n\n    // Virtual\n    ui->group_vm->setChecked(config->get_vm_enabled());\n    ui->itlb_number_of_sets->setValue(config->tlbc_program().get_tlb_num_sets());\n    ui->itlb_degree_of_associativity->setValue(config->tlbc_program().get_tlb_associativity());\n    ui->itlb_replacement_policy->setCurrentIndex(\n        (int)config->tlbc_program().get_tlb_replacement_policy());\n    ui->dtlb_number_of_sets->setValue(config->tlbc_data().get_tlb_num_sets());\n    ui->dtlb_degree_of_associativity->setValue(config->tlbc_data().get_tlb_associativity());\n    ui->dtlb_replacement_policy->setCurrentIndex(\n        (int)config->tlbc_data().get_tlb_replacement_policy());\n    // Memory\n    ui->mem_protec_exec->setChecked(config->memory_execute_protection());\n    ui->mem_protec_write->setChecked(config->memory_write_protection());\n    ui->mem_time_read->setValue((int)config->memory_access_time_read());\n    ui->mem_time_write->setValue((int)config->memory_access_time_write());\n    ui->mem_time_burst->setValue((int)config->memory_access_time_burst());\n    ui->mem_time_level2->setValue((int)config->memory_access_time_level2());\n    ui->mem_enable_burst->setChecked((int)config->memory_access_enable_burst());\n    // Cache\n    cache_handler_d->config_gui();\n    cache_handler_p->config_gui();\n    cache_handler_l2->config_gui();\n    // Operating system and exceptions\n    ui->osemu_enable->setChecked(config->osemu_enable());\n    ui->osemu_known_syscall_stop->setChecked(config->osemu_known_syscall_stop());\n    ui->osemu_unknown_syscall_stop->setChecked(config->osemu_unknown_syscall_stop());\n    ui->osemu_interrupt_stop->setChecked(config->osemu_interrupt_stop());\n    ui->osemu_exception_stop->setChecked(config->osemu_exception_stop());\n    ui->osemu_fs_root->setText(config->osemu_fs_root());\n\n    // Disable various sections according to configuration\n    ui->delay_slot->setEnabled(false);\n    ui->hazard_unit->setEnabled(config->pipelined());\n}\n\nunsigned NewDialog::preset_number() {\n    enum machine::ConfigPresets preset;\n    if (ui->preset_no_pipeline->isChecked())\n        preset = machine::CP_SINGLE;\n    else if (ui->preset_no_pipeline_cache->isChecked())\n        preset = machine::CP_SINGLE_CACHE;\n    else if (ui->preset_pipelined_bare->isChecked())\n        preset = machine::CP_PIPE_NO_HAZARD;\n    else if (ui->preset_pipelined->isChecked())\n        preset = machine::CP_PIPE;\n    else\n        return 0;\n    return (unsigned)preset + 1;\n}\n\nvoid NewDialog::load_settings() {\n    // Load config\n    config.reset(new machine::MachineConfig(settings));\n    cache_handler_d->set_config(config->access_cache_data());\n    cache_handler_p->set_config(config->access_cache_program());\n    cache_handler_l2->set_config(config->access_cache_level2());\n\n    // Load preset\n    unsigned preset = settings->value(\"Preset\", 1).toUInt();\n    if (preset != 0) {\n        auto p = (enum machine::ConfigPresets)(preset - 1);\n        config->preset(p);\n        switch (p) {\n        case machine::CP_SINGLE: ui->preset_no_pipeline->setChecked(true); break;\n        case machine::CP_SINGLE_CACHE: ui->preset_no_pipeline_cache->setChecked(true); break;\n        case machine::CP_PIPE_NO_HAZARD: ui->preset_pipelined_bare->setChecked(true); break;\n        case machine::CP_PIPE: ui->preset_pipelined->setChecked(true); break;\n        }\n    } else {\n        ui->preset_custom->setChecked(true);\n    }\n\n    config_gui();\n}\n\nvoid NewDialog::store_settings() {\n    config->store(settings);\n\n    // Presets are not stored in settings, so we have to store them explicitly\n    if (ui->preset_custom->isChecked()) {\n        settings->setValue(\"Preset\", 0);\n    } else {\n        settings->setValue(\"Preset\", preset_number());\n    }\n}\n\nNewDialogCacheHandler::NewDialogCacheHandler(NewDialog *nd, Ui::NewDialogCache *cui) : Super(nd) {\n    this->nd = nd;\n    this->ui = cui;\n    this->config = nullptr;\n    connect(ui->enabled, &QGroupBox::clicked, this, &NewDialogCacheHandler::enabled);\n    connect(\n        ui->number_of_sets, &QAbstractSpinBox::editingFinished, this,\n        &NewDialogCacheHandler::numsets);\n    connect(\n        ui->block_size, &QAbstractSpinBox::editingFinished, this,\n        &NewDialogCacheHandler::blocksize);\n    connect(\n        ui->degree_of_associativity, &QAbstractSpinBox::editingFinished, this,\n        &NewDialogCacheHandler::degreeassociativity);\n    connect(\n        ui->replacement_policy, QOverload<int>::of(&QComboBox::activated), this,\n        &NewDialogCacheHandler::replacement);\n    connect(\n        ui->writeback_policy, QOverload<int>::of(&QComboBox::activated), this,\n        &NewDialogCacheHandler::writeback);\n}\n\nvoid NewDialogCacheHandler::set_config(machine::CacheConfig *cache_config) {\n    this->config = cache_config;\n}\n\nvoid NewDialogCacheHandler::config_gui() {\n    ui->enabled->setChecked(config->enabled());\n    ui->number_of_sets->setValue((int)config->set_count());\n    ui->block_size->setValue((int)config->block_size());\n    ui->degree_of_associativity->setValue((int)config->associativity());\n    ui->replacement_policy->setCurrentIndex((int)config->replacement_policy());\n    ui->writeback_policy->setCurrentIndex((int)config->write_policy());\n}\n\nvoid NewDialogCacheHandler::enabled(bool val) {\n    config->set_enabled(val);\n    nd->switch_to_custom();\n}\n\nvoid NewDialogCacheHandler::numsets() {\n    config->set_set_count(ui->number_of_sets->value());\n    nd->switch_to_custom();\n}\n\nvoid NewDialogCacheHandler::blocksize() {\n    config->set_block_size(ui->block_size->value());\n    nd->switch_to_custom();\n}\n\nvoid NewDialogCacheHandler::degreeassociativity() {\n    config->set_associativity(ui->degree_of_associativity->value());\n    nd->switch_to_custom();\n}\n\nvoid NewDialogCacheHandler::replacement(int val) {\n    config->set_replacement_policy((enum machine::CacheConfig::ReplacementPolicy)val);\n    nd->switch_to_custom();\n}\n\nvoid NewDialogCacheHandler::writeback(int val) {\n    config->set_write_policy((enum machine::CacheConfig::WritePolicy)val);\n    nd->switch_to_custom();\n}\n"
  },
  {
    "path": "src/gui/dialogs/new/newdialog.h",
    "content": "#ifndef NEWDIALOG_H\n#define NEWDIALOG_H\n\n#include \"common/memory_ownership.h\"\n#include \"machine/machineconfig.h\"\n#include \"predictor_types.h\"\n#include \"ui_NewDialog.h\"\n#include \"ui_NewDialogCache.h\"\n\n#include <QDialog>\n#include <QFileDialog>\n#include <QMessageBox>\n#include <QSettings>\n\nclass NewDialogCacheHandler;\n\nclass NewDialog : public QDialog {\n    Q_OBJECT\npublic:\n    NewDialog(QWidget *parent, QSettings *settings);\n\n    void switch_to_custom();\n\nprotected:\n    void closeEvent(QCloseEvent *) override;\n\nprivate slots:\n    void cancel();\n    void create();\n    void create_empty();\n    void create_example();\n    void browse_elf();\n    void elf_change(QString val);\n    void set_preset();\n    void xlen_64bit_change(bool);\n    void isa_atomic_change(bool);\n    void isa_multiply_change(bool);\n    void pipelined_change(bool);\n    void delay_slot_change(bool);\n    void hazard_unit_change();\n    void mem_protec_exec_change(bool);\n    void mem_protec_write_change(bool);\n    void mem_time_read_change(int);\n    void mem_time_write_change(int);\n    void mem_enable_burst_change(bool);\n    void mem_time_burst_change(int);\n    void mem_time_level2_change(int);\n    void osemu_enable_change(bool);\n    void osemu_known_syscall_stop_change(bool);\n    void osemu_unknown_syscall_stop_change(bool);\n    void osemu_interrupt_stop_change(bool);\n    void osemu_exception_stop_change(bool);\n    void browse_osemu_fs_root();\n    void osemu_fs_root_change(QString val);\n    void reset_at_compile_change(bool);\n    void switch2page(QTreeWidgetItem *current, QTreeWidgetItem *previous = nullptr);\n\n    // Branch Predictor\n    void bp_toggle_widgets();\n    void bp_enabled_change(bool);\n    void bp_type_change(void);\n    void bp_init_state_change(void);\n    void bp_btb_addr_bits_change(int);\n    void bp_bht_bhr_bits_change(int);\n    void bp_bht_addr_bits_change(int);\n\n    // Virtual Memory\n    void vm_enabled_change(bool);\n    void itlb_num_sets_changed(int);\n    void itlb_assoc_changed(int);\n    void itlb_policy_changed(int);\n    void dtlb_num_sets_changed(int);\n    void dtlb_assoc_changed(int);\n    void dtlb_policy_changed(int);\n\nprivate:\n    Box<Ui::NewDialog> ui {};\n    Box<Ui::NewDialogCache> ui_cache_p {}, ui_cache_d {}, ui_cache_l2 {};\n    QSettings *settings;\n\n    Box<machine::MachineConfig> config;\n    void config_gui(); // Apply configuration to gui\n    void bp_bht_bits_texts_update(void);\n\n    unsigned preset_number();\n    void load_settings();\n    void store_settings();\n    NewDialogCacheHandler *cache_handler_p {}, *cache_handler_d {}, *cache_handler_l2 {};\n};\n\nclass NewDialogCacheHandler : public QObject {\n    Q_OBJECT\n\n    using Super = QObject;\n\npublic:\n    NewDialogCacheHandler(NewDialog *nd, Ui::NewDialogCache *ui);\n\n    void set_config(machine::CacheConfig *cache_config);\n\n    void config_gui();\n\nprivate slots:\n    void enabled(bool);\n    void numsets();\n    void blocksize();\n    void degreeassociativity();\n    void replacement(int);\n    void writeback(int);\n\nprivate:\n    NewDialog *nd;\n    Ui::NewDialogCache *ui {};\n    machine::CacheConfig *config;\n};\n\n#endif // NEWDIALOG_H\n"
  },
  {
    "path": "src/gui/dialogs/savechanged/savechangeddialog.cpp",
    "content": "#include \"savechangeddialog.h\"\n\n#include <QLabel>\n#include <QListView>\n#include <QPlainTextEdit>\n#include <QPushButton>\n#include <QStandardItem>\n#include <QTabWidget>\n#include <QVBoxLayout>\n\nSaveChangedDialog::SaveChangedDialog(QStringList &changedlist, QWidget *parent) : QDialog(parent) {\n    setAttribute(Qt::WA_DeleteOnClose);\n    setAttribute(Qt::WA_ShowModal);\n    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n    setWindowTitle(tr(\"Save next modified files?\"));\n\n    model = new QStandardItemModel(this);\n    bool unknown_inserted = false;\n\n    for (const auto &fname : changedlist) {\n        int row = model->rowCount();\n        auto *item = new QStandardItem();\n        item->setData(fname, Qt::UserRole);\n        if (!fname.isEmpty()) {\n            item->setText(fname);\n        } else {\n            if (!unknown_inserted) {\n                item->setText(\"Unknown\");\n                unknown_inserted = true;\n            }\n        }\n        item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);\n        item->setCheckState(Qt::Checked);\n        model->setItem(row, 0, item);\n    }\n\n    auto *all = new QVBoxLayout(this);\n\n    auto *listview = new QListView(this);\n    listview->setModel(model);\n    listview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n    all->addWidget(listview);\n\n    auto *hbBtn = new QWidget();\n    auto *hlBtn = new QHBoxLayout(hbBtn);\n\n    auto *cancelButton = new QPushButton(tr(\"&Cancel\"), parent);\n    auto *ignoreButton = new QPushButton(tr(\"&Ignore\"), parent);\n    auto *saveButton = new QPushButton(tr(\"&Save\"), parent);\n    saveButton->setFocus();\n    connect(cancelButton, &QAbstractButton::clicked, this, &SaveChangedDialog::cancel_clicked);\n    connect(ignoreButton, &QAbstractButton::clicked, this, &SaveChangedDialog::ignore_clicked);\n    connect(saveButton, &QAbstractButton::clicked, this, &SaveChangedDialog::save_clicked);\n    hlBtn->addWidget(cancelButton);\n    hlBtn->addStretch();\n    hlBtn->addWidget(ignoreButton);\n    hlBtn->addStretch();\n    hlBtn->addWidget(saveButton);\n\n    all->addWidget(hbBtn);\n\n    setMinimumSize(400, 300);\n}\n\nvoid SaveChangedDialog::cancel_clicked() {\n    QStringList list;\n    emit user_decision(true, list);\n    close();\n}\n\nvoid SaveChangedDialog::ignore_clicked() {\n    QStringList list;\n    emit user_decision(false, list);\n    close();\n}\n\nvoid SaveChangedDialog::save_clicked() {\n    QStringList list;\n    for (int r = 0; r < model->rowCount(); ++r) {\n        if (model->item(r)->checkState() == Qt::Checked) {\n            list.append(model->item(r)->data(Qt::UserRole).toString());\n        }\n    }\n    emit user_decision(false, list);\n    close();\n}\n"
  },
  {
    "path": "src/gui/dialogs/savechanged/savechangeddialog.h",
    "content": "#ifndef SAVECHANGED_H\n#define SAVECHANGED_H\n\n#include <QDialog>\n#include <QList>\n#include <QStandardItemModel>\n#include <QStringList>\n\nclass SaveChangedDialog : public QDialog {\n    Q_OBJECT\n\npublic:\n    explicit SaveChangedDialog(QStringList &changedlist, QWidget *parent = nullptr);\nsignals:\n    void user_decision(bool cancel, QStringList tosavelist);\nprivate slots:\n    void cancel_clicked();\n    void ignore_clicked();\n    void save_clicked();\n\nprivate:\n    QStandardItemModel *model;\n};\n\n#endif // SAVECHANGED_H\n"
  },
  {
    "path": "src/gui/extprocess.cpp",
    "content": "#include \"extprocess.h\"\n\n#include <QDir>\n#include <QFileInfo>\n\nusing namespace std;\n\nExtProcess::ExtProcess(QObject *parent) : Super(parent) {\n    setProcessChannelMode(QProcess::MergedChannels);\n    connect(this, &QProcess::readyReadStandardOutput, this, &ExtProcess::process_output);\n    connect(\n        this, QOverload<int, ExitStatus>::of(&QProcess::finished), this,\n        &ExtProcess::report_finished);\n    connect(this, &QProcess::started, this, &ExtProcess::report_started);\n}\n\nvoid ExtProcess::report_finished(int exitCode, QProcess::ExitStatus exitStatus) {\n    if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) {\n        report_message(\n            messagetype::MSG_FINISH, \"\", 0, 0,\n            program() + \": failed - exit code \" + QString::number(exitCode), \"\");\n    } else {\n        report_message(messagetype::MSG_FINISH, \"\", 0, 0, program() + \": finished\", \"\");\n    }\n    deleteLater();\n}\n\nvoid ExtProcess::report_started() {\n    report_message(messagetype::MSG_START, \"\", 0, 0, program() + \": started\", \"\");\n}\n\nvoid ExtProcess::process_output() {\n    QString file = \"\";\n    int ln = 0;\n    int col = 0;\n    messagetype::Type type = messagetype::MSG_INFO;\n\n    while (canReadLine()) {\n        QString line = QString::fromLocal8Bit(readLine());\n        while (line.count() > 0) {\n            if (line.at(line.count() - 1) != '\\n' && line.at(line.count() - 1) != '\\r') { break; }\n            line.truncate(line.count() - 1);\n        }\n\n        int pos = line.indexOf(':');\n        if (pos >= 0) {\n            QFileInfo fi(QDir(workingDirectory()), line.mid(0, pos));\n            line = line.mid(pos + 1);\n            file = fi.absoluteFilePath();\n        }\n\n        for (pos = 0; line.count() > pos && line.at(pos).isDigit(); pos++) {}\n\n        if ((pos < line.count()) && (line.at(pos) == ':')) {\n            ln = line.mid(0, pos).toInt();\n            line = line.mid(pos + 1);\n        }\n\n        for (pos = 0; line.count() > pos && line.at(pos).isDigit(); pos++) {}\n\n        if ((pos < line.count()) && (line.at(pos) == ':')) {\n            col = line.mid(0, pos).toInt();\n            line = line.mid(pos + 1);\n        }\n\n        if (line.startsWith(' ')) { line = line.mid(1); }\n        if (line.startsWith('\\t')) { line = line.mid(1); }\n        if (line.startsWith(\"error:\", Qt::CaseInsensitive)) {\n            type = messagetype::MSG_ERROR;\n        } else if (line.startsWith(\"warning:\", Qt::CaseInsensitive)) {\n            type = messagetype::MSG_WARNING;\n        }\n\n        report_message(type, file, ln, col, line, \"\");\n    }\n}\n"
  },
  {
    "path": "src/gui/extprocess.h",
    "content": "#ifndef MSGREPORT_H\n#define MSGREPORT_H\n\n#include \"assembler/messagetype.h\"\n\n#include <QProcess>\n#include <QString>\n\nclass ExtProcess : public QProcess {\n    Q_OBJECT\n\n    using Super = QProcess;\n\npublic:\n    explicit ExtProcess(QObject *parent = nullptr);\n\nsignals:\n    void report_message(\n        messagetype::Type type,\n        QString file,\n        int line,\n        int column,\n        QString text,\n        QString hint);\n\nprotected slots:\n    void process_output();\n    void report_started();\n    void report_finished(int exitCode, QProcess::ExitStatus exitStatus);\n\nprotected:\n    QByteArray m_buffer;\n};\n\n#endif // MSGREPORT_H\n"
  },
  {
    "path": "src/gui/fontsize.cpp",
    "content": "#include \"fontsize.h\"\n\n#include \"common/logging.h\"\n\n#include <QApplication>\n#include <QFont>\n#include <QFontMetrics>\n\nLOG_CATEGORY(\"gui.font\");\n\nint FontSize::SIZE5 = 5;\nint FontSize::SIZE6 = 6;\nint FontSize::SIZE7 = 7;\nint FontSize::SIZE8 = 8;\n\nvoid FontSize::init() {\n    int h = QFontMetrics(QApplication::font()).height();\n    LOG() << \"Font size:\" << h;\n    h /= 3;\n    int d = h / 4 + 1;\n    FontSize::SIZE5 = h - 2 * d;\n    FontSize::SIZE6 = h - d;\n    FontSize::SIZE7 = h;\n    FontSize::SIZE8 = h + d / 2;\n}\n"
  },
  {
    "path": "src/gui/fontsize.h",
    "content": "#ifndef FONTSIZES_H\n#define FONTSIZES_H\n\nstruct FontSize {\n    static int SIZE5;\n    static int SIZE6;\n    static int SIZE7;\n    static int SIZE8;\n\n    static void init();\n};\n\n#endif // FONTSIZES_H\n"
  },
  {
    "path": "src/gui/graphicsview.cpp",
    "content": "#include \"graphicsview.h\"\n\nGraphicsView::GraphicsView(QWidget *parent) : Super(parent) {\n    prev_height = 0;\n    prev_width = 0;\n    setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);\n}\n\nvoid GraphicsView::setScene(QGraphicsScene *scene) {\n    Super::setScene(scene);\n    update_scale();\n}\n\nvoid GraphicsView::resizeEvent(QResizeEvent *event) {\n    Super::resizeEvent(event);\n    if ((width() != prev_height) || (height() != prev_width)) { update_scale(); }\n}\n\nvoid GraphicsView::update_scale() {\n    if (scene() == nullptr) {\n        return; // Skip if we have no scene\n    }\n\n    // Note: there is somehow a three-pixel error when viewing, so we have to\n    // always compensate\n    const int w = scene()->width() + 3;\n    const int h = scene()->height() + 3;\n    prev_height = width();\n    prev_width = height();\n\n    qreal scale_x = (qreal)width() / w;\n    qreal scale_y = (qreal)height() / h;\n    qreal scale = qMin(scale_x, scale_y);\n    QTransform t;\n    t.scale(scale, scale);\n    setTransform(t, false);\n}\n\nvoid GraphicsView::wheelEvent(QWheelEvent *event) {\n    if (event->modifiers() & Qt::ControlModifier) {\n        // zoom\n        const ViewportAnchor anchor = transformationAnchor();\n        setTransformationAnchor(QGraphicsView::AnchorUnderMouse);\n        int angle = event->angleDelta().y();\n        qreal factor;\n        if (angle > 0) {\n            factor = 1.1;\n        } else {\n            factor = 0.9;\n        }\n        scale(factor, factor);\n        setTransformationAnchor(anchor);\n    } else {\n        Super::wheelEvent(event);\n    }\n}\n\nvoid GraphicsView::keyPressEvent(QKeyEvent *event) {\n    qreal factor = 1.1;\n    if (event->matches(QKeySequence::ZoomIn) || (event->key() == Qt::Key_Equal)\n        || (event->key() == Qt::Key_Plus)) {\n        scale(factor, factor);\n    } else if (event->matches(QKeySequence::ZoomOut) || (event->key() == Qt::Key_Minus)) {\n        scale(1 / factor, 1 / factor);\n    } else {\n        Super::keyPressEvent(event);\n    }\n}\n"
  },
  {
    "path": "src/gui/graphicsview.h",
    "content": "#ifndef GRAPHICSVIEW_H\n#define GRAPHICSVIEW_H\n\n#include <QGraphicsScene>\n#include <QGraphicsView>\n#include <QKeyEvent>\n#include <QResizeEvent>\n#include <QWheelEvent>\n\nclass GraphicsView : public QGraphicsView {\n    Q_OBJECT\n    using Super = QGraphicsView;\n\npublic:\n    explicit GraphicsView(QWidget *parent);\n    void setScene(QGraphicsScene *scene);\n\nprotected:\n    void resizeEvent(QResizeEvent *event) override;\n    void wheelEvent(QWheelEvent *event) override;\n    void keyPressEvent(QKeyEvent *event) override;\n\nprivate:\n    void update_scale();\n    int prev_height;\n    int prev_width;\n};\n\n#endif // GRAPHICSVIEW_H\n"
  },
  {
    "path": "src/gui/helper/async_modal.h",
    "content": "/**\n * This file provides API for modal windows (dialogs, message boxes) which runs asynchronously\n * and therefore is safe in WASM, where blocking will cause the app to freeze!\n *\n * None of these classes shall be used directly, unless special callback is required. In such\n * cases, high caution needs to be applied.\n *\n * NOTE: This code could be optimized by adding async behavior only to emscripten, however modals\n * are typically not performance sensitive and an special case would complicate debugging.\n *\n * @file\n */\n\n#ifndef ASYNC_MODAL_H\n#define ASYNC_MODAL_H\n\n#include <QMessageBox>\n\ninline void showAsyncMessageBox(\n    QWidget *parent,\n    QMessageBox::Icon icon,\n    const QString &title,\n    const QString &text,\n    const QString &detailed_text = QString(),\n    const QString &tooltip_text = QString()) {\n    auto msg = new QMessageBox(icon, title, text, QMessageBox::Ok, parent);\n    msg->setDetailedText(detailed_text);\n    msg->setToolTip(tooltip_text);\n    // This is necessary as WASM does not support blocking APIs.\n    msg->setAttribute(Qt::WA_DeleteOnClose);\n    msg->open();\n}\n\ninline void showAsyncCriticalBox(\n    QWidget *parent,\n    const QString &title,\n    const QString &text,\n    const QString &detailed_text = QString(),\n    const QString &tooltip_text = QString()) {\n    showAsyncMessageBox(parent, QMessageBox::Critical, title, text, detailed_text, tooltip_text);\n}\n\n#endif"
  },
  {
    "path": "src/gui/hinttabledelegate.cpp",
    "content": "#include \"hinttabledelegate.h\"\n\n#include <QApplication>\n\nQSize HintTableDelegate::sizeHintForText(\n    const QStyleOptionViewItem &option,\n    const QModelIndex &index,\n    const QString &str) const {\n    QStyleOptionViewItem opt = option;\n    initStyleOption(&opt, index);\n    const QWidget *widget = option.widget;\n    QStyle *style = widget ? widget->style() : QApplication::style();\n\n    opt.features |= QStyleOptionViewItem::HasDisplay;\n    opt.text = str;\n\n    return style->sizeFromContents(QStyle::CT_ItemViewItem, &opt, QSize(), widget);\n}\n"
  },
  {
    "path": "src/gui/hinttabledelegate.h",
    "content": "#ifndef HINTTABLEDELEGATE_H\n#define HINTTABLEDELEGATE_H\n\n#include <QObject>\n#include <QStyledItemDelegate>\n\nclass HintTableDelegate : public QStyledItemDelegate {\n    Q_OBJECT\n\n    using Super = QStyledItemDelegate;\n\npublic:\n    explicit HintTableDelegate(QWidget *parent = nullptr) : Super(parent) {}\n\n    [[nodiscard]] QSize sizeHintForText(\n        const QStyleOptionViewItem &option,\n        const QModelIndex &index,\n        const QString &str) const;\n};\n\n#endif // HINTTABLEDELEGATE_H\n"
  },
  {
    "path": "src/gui/main.cpp",
    "content": "#include \"common/logging.h\"\n#include \"common/logging_format_colors.h\"\n#include \"mainwindow/mainwindow.h\"\n\n#include <QApplication>\n\nLOG_CATEGORY(\"gui.main\");\n\nint main(int argc, char *argv[]) {\n    QApplication app(argc, argv);\n    set_default_log_pattern();\n\n    // There constants are set in CMake.\n    QApplication::setApplicationName(APP_NAME);\n    QApplication::setOrganizationName(APP_ORGANIZATION);\n    QApplication::setOrganizationDomain(APP_ORGANIZATION_DOMAIN);\n    QApplication::setApplicationVersion(APP_VERSION);\n\n    LOG(\"Started %s version %s.\", APP_NAME, APP_VERSION);\n    LOG(\"Developed at %s (%s).\", APP_ORGANIZATION, APP_ORGANIZATION_DOMAIN);\n\n    /*\n     * If environment variable specified in define ENV_CONFIG_FILE_NAME is\n     * present, the app should behave in a portable manner and use ini file on the\n     * specified location. Otherwise, Qt defaults are used.\n     */\n    auto env = QProcessEnvironment::systemEnvironment();\n    QSettings *settings;\n    if (env.contains(ENV_CONFIG_FILE_NAME)) {\n        // Behave as a portable app.\n        settings = new QSettings(env.value(ENV_CONFIG_FILE_NAME), QSettings::IniFormat);\n    } else {\n        // Qt defaults\n        settings = new QSettings(APP_ORGANIZATION, APP_NAME);\n    }\n\n    MainWindow w(settings); // Moving settings ownership.\n    w.start();\n\n    return QApplication::exec();\n}\n"
  },
  {
    "path": "src/gui/mainwindow/MainWindow.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MainWindow</class>\n <widget class=\"QMainWindow\" name=\"MainWindow\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>900</width>\n    <height>600</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>MainWindow</string>\n  </property>\n  <property name=\"windowIcon\">\n   <iconset>\n    <normaloff>\n           :/icons/gui.png</normaloff>\n           :/icons/gui.png</iconset>\n  </property>\n  <property name=\"dockNestingEnabled\">\n   <bool>false</bool>\n  </property>\n  <property name=\"unifiedTitleAndToolBarOnMac\">\n   <bool>true</bool>\n  </property>\n  <widget class=\"QWidget\" name=\"centralWidget\">\n   <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"QWidget\" name=\"widget\" native=\"true\"/>\n    </item>\n   </layout>\n  </widget>\n  <widget class=\"QMenuBar\" name=\"menuBar\">\n   <property name=\"geometry\">\n    <rect>\n     <x>0</x>\n     <y>0</y>\n     <width>900</width>\n     <height>19</height>\n    </rect>\n   </property>\n   <widget class=\"QMenu\" name=\"menuFile\">\n    <property name=\"title\">\n     <string>&amp;File</string>\n    </property>\n    <widget class=\"QMenu\" name=\"menuExamples\">\n     <property name=\"title\">\n      <string>&amp;Examples</string>\n     </property>\n    </widget>\n    <addaction name=\"actionNewMachine\"/>\n    <addaction name=\"actionReload\"/>\n    <addaction name=\"actionPrint\"/>\n    <addaction name=\"actionNew\"/>\n    <addaction name=\"actionOpen\"/>\n    <addaction name=\"actionSave\"/>\n    <addaction name=\"actionSaveAs\"/>\n    <addaction name=\"actionClose\"/>\n    <addaction name=\"menuExamples\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionExit\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuWindows\">\n    <property name=\"title\">\n     <string>&amp;Windows</string>\n    </property>\n    <addaction name=\"actionRegisters\"/>\n    <addaction name=\"actionProgram_memory\"/>\n    <addaction name=\"actionMemory\"/>\n    <addaction name=\"actionProgram_Cache\"/>\n    <addaction name=\"actionData_Cache\"/>\n    <addaction name=\"actionL2_Cache\"/>\n    <addaction name=\"actionInstruction_TLB\"/>\n    <addaction name=\"actionData_TLB\"/>\n    <addaction name=\"actionBranch_Predictor_History_table\"/>\n    <addaction name=\"actionBranch_Predictor_Target_table\"/>\n    <addaction name=\"actionBranch_Predictor_Info\"/>\n    <addaction name=\"actionPeripherals\"/>\n    <addaction name=\"actionTerminal\"/>\n    <addaction name=\"actionLcdDisplay\"/>\n    <addaction name=\"actionCsrShow\"/>\n    <addaction name=\"actionCore_View_show\"/>\n    <addaction name=\"actionMessages\"/>\n    <addaction name=\"actionResetWindows\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuMachine\">\n    <property name=\"title\">\n     <string>M&amp;achine</string>\n    </property>\n    <addaction name=\"actionRun\"/>\n    <addaction name=\"actionPause\"/>\n    <addaction name=\"actionStep\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"ips1\"/>\n    <addaction name=\"ips2\"/>\n    <addaction name=\"ips5\"/>\n    <addaction name=\"ips10\"/>\n    <addaction name=\"ips25\"/>\n    <addaction name=\"ipsUnlimited\"/>\n    <addaction name=\"ipsMax\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionRestart\"/>\n    <addaction name=\"actionMnemonicRegisters\"/>\n    <addaction name=\"actionShow_Symbol\"/>\n    <addaction name=\"actionCompileSource\"/>\n    <addaction name=\"actionBuildExe\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuHelp\">\n    <property name=\"title\">\n     <string>&amp;Help</string>\n    </property>\n    <addaction name=\"actionUserManual\"/>\n    <addaction name=\"actionReportProblem\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionAbout\"/>\n    <addaction name=\"actionAboutQt\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuOptions\">\n    <property name=\"title\">\n     <string>Options</string>\n    </property>\n    <addaction name=\"actionEditorShowLineNumbers\"/>\n   </widget>\n   <addaction name=\"menuFile\"/>\n   <addaction name=\"menuMachine\"/>\n   <addaction name=\"menuWindows\"/>\n   <addaction name=\"menuOptions\"/>\n   <addaction name=\"menuHelp\"/>\n  </widget>\n  <widget class=\"QStatusBar\" name=\"statusBar\"/>\n  <widget class=\"QToolBar\" name=\"toolBar\">\n   <property name=\"windowTitle\">\n    <string>toolBar</string>\n   </property>\n   <property name=\"movable\">\n    <bool>false</bool>\n   </property>\n   <property name=\"allowedAreas\">\n    <set>Qt::TopToolBarArea</set>\n   </property>\n   <attribute name=\"toolBarArea\">\n    <enum>TopToolBarArea</enum>\n   </attribute>\n   <attribute name=\"toolBarBreak\">\n    <bool>false</bool>\n   </attribute>\n   <addaction name=\"actionNewMachine\"/>\n   <addaction name=\"actionReload\"/>\n   <addaction name=\"separator\"/>\n   <addaction name=\"actionRun\"/>\n   <addaction name=\"actionPause\"/>\n   <addaction name=\"actionStep\"/>\n   <addaction name=\"separator\"/>\n   <addaction name=\"ips1\"/>\n   <addaction name=\"ips2\"/>\n   <addaction name=\"ips5\"/>\n   <addaction name=\"ips10\"/>\n   <addaction name=\"ips25\"/>\n   <addaction name=\"ipsUnlimited\"/>\n   <addaction name=\"ipsMax\"/>\n   <addaction name=\"actionNew\"/>\n   <addaction name=\"actionOpen\"/>\n   <addaction name=\"actionSave\"/>\n   <addaction name=\"actionClose\"/>\n   <addaction name=\"actionCompileSource\"/>\n   <addaction name=\"actionBuildExe\"/>\n  </widget>\n  <action name=\"actionNewMachine\">\n   <property name=\"icon\">\n    <iconset resource=\"../resources/icons/icons.qrc\">\n     <normaloff>:/icons/document-import.png</normaloff>:/icons/document-import.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;New simulation...</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+N</string>\n   </property>\n  </action>\n  <action name=\"actionRestart\">\n   <property name=\"text\">\n    <string>R&amp;estart</string>\n   </property>\n  </action>\n  <action name=\"actionNew\">\n   <property name=\"icon\">\n    <iconset resource=\"../resources/icons/icons.qrc\">\n     <normaloff>:/icons/new.png</normaloff>:/icons/new.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Ne&amp;w source</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>New source</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+F</string>\n   </property>\n  </action>\n  <action name=\"actionOpen\">\n   <property name=\"icon\">\n    <iconset resource=\"../resources/icons/icons.qrc\">\n     <normaloff>:/icons/open.png</normaloff>:/icons/open.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Open source</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Open source</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+O</string>\n   </property>\n  </action>\n  <action name=\"actionSave\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset resource=\"../resources/icons/icons.qrc\">\n     <normaloff>:/icons/save.png</normaloff>:/icons/save.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Save source</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Save source</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+S</string>\n   </property>\n  </action>\n  <action name=\"actionSaveAs\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Save source &amp;as</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Save source as</string>\n   </property>\n  </action>\n  <action name=\"actionClose\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset resource=\"../resources/icons/icons.qrc\">\n     <normaloff>:/icons/closetab.png</normaloff>:/icons/closetab.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Close source</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Close source</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+W</string>\n   </property>\n  </action>\n  <action name=\"actionCompileSource\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset resource=\"../resources/icons/icons.qrc\">\n     <normaloff>:/icons/compfile-256.png</normaloff>:/icons/compfile-256.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Compile Source</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Compile source and update memory</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+E</string>\n   </property>\n  </action>\n  <action name=\"actionBuildExe\">\n   <property name=\"enabled\">\n    <bool>true</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset resource=\"../resources/icons/icons.qrc\">\n     <normaloff>:/icons/build-256.png</normaloff>:/icons/build-256.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Build Executable</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Build executable by external make</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+B</string>\n   </property>\n  </action>\n  <action name=\"actionExit\">\n   <property name=\"icon\">\n    <iconset resource=\"../resources/icons/icons.qrc\">\n     <normaloff>:/icons/application-exit.png</normaloff>:/icons/application-exit.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>E&amp;xit</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Exit program</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Q</string>\n   </property>\n  </action>\n  <action name=\"actionRun\">\n   <property name=\"icon\">\n    <iconset resource=\"../resources/icons/icons.qrc\">\n     <normaloff>:/icons/play.png</normaloff>:/icons/play.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Run</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+R</string>\n   </property>\n  </action>\n  <action name=\"actionStep\">\n   <property name=\"icon\">\n    <iconset resource=\"../resources/icons/icons.qrc\">\n     <normaloff>:/icons/next.png</normaloff>:/icons/next.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Step</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+T</string>\n   </property>\n  </action>\n  <action name=\"actionPause\">\n   <property name=\"icon\">\n    <iconset resource=\"../resources/icons/icons.qrc\">\n     <normaloff>:/icons/pause.png</normaloff>:/icons/pause.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Pause</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+P</string>\n   </property>\n  </action>\n  <action name=\"ips1\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>&amp;1 instruction per second</string>\n   </property>\n   <property name=\"iconText\">\n    <string>1x</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Run CPU in speed of single instruction per second</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+1</string>\n   </property>\n  </action>\n  <action name=\"ips5\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>&amp;5 instructions per second</string>\n   </property>\n   <property name=\"iconText\">\n    <string>5x</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Run CPU in speed of 5 instructions per second</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+5</string>\n   </property>\n  </action>\n  <action name=\"ips10\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>1&amp;0 instructions per second</string>\n   </property>\n   <property name=\"iconText\">\n    <string>10x</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Run CPU in speed of 10 instructions per second</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+0</string>\n   </property>\n  </action>\n  <action name=\"ips25\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>2&amp;5 instructions per second</string>\n   </property>\n   <property name=\"iconText\">\n    <string>25x</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Run CPU in speed of 25 instructions per second</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+F5</string>\n   </property>\n  </action>\n  <action name=\"ipsUnlimited\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Unlimited</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Run CPU without any clock constrains</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+U</string>\n   </property>\n  </action>\n  <action name=\"actionMemory\">\n   <property name=\"text\">\n    <string>&amp;Memory</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Data memory view</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+M</string>\n   </property>\n  </action>\n  <action name=\"actionProgram_memory\">\n   <property name=\"text\">\n    <string>&amp;Program</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Program memory view</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+P</string>\n   </property>\n  </action>\n  <action name=\"actionRegisters\">\n   <property name=\"text\">\n    <string>&amp;Registers</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+D</string>\n   </property>\n  </action>\n  <action name=\"actionCsrShow\">\n   <property name=\"text\">\n    <string>C&amp;ontrol and Status Registers</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+I</string>\n   </property>\n  </action>\n  <action name=\"actionReload\">\n   <property name=\"icon\">\n    <iconset resource=\"../resources/icons/icons.qrc\">\n     <normaloff>:/icons/reload.png</normaloff>:/icons/reload.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Reload simulation</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Shift+R</string>\n   </property>\n  </action>\n  <action name=\"actionPrint\">\n   <property name=\"text\">\n    <string>&amp;Print</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Alt+P</string>\n   </property>\n  </action>\n  <action name=\"actionProgram_Cache\">\n   <property name=\"text\">\n    <string>Pro&amp;gram Cache</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Shift+P</string>\n   </property>\n  </action>\n  <action name=\"actionData_Cache\">\n   <property name=\"text\">\n    <string>&amp;Data Cache</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Shift+M</string>\n   </property>\n  </action>\n  <action name=\"actionL2_Cache\">\n   <property name=\"text\">\n    <string>L2 Cache</string>\n   </property>\n  </action>\n  <action name=\"actionResetWindows\">\n   <property name=\"text\">\n    <string>Reset Windows</string>\n   </property>\n  </action>\n  <action name=\"ips2\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>&amp;2 instructions per second</string>\n   </property>\n   <property name=\"iconText\">\n    <string>2x</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Run CPU in speed of two instructions per second</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+2</string>\n   </property>\n  </action>\n  <action name=\"actionAbout\">\n   <property name=\"text\">\n    <string>About Qt&amp;RVSim</string>\n   </property>\n  </action>\n  <action name=\"ipsMax\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Max</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Run at maximal speed, skip visualization for 100 msec</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+A</string>\n   </property>\n  </action>\n  <action name=\"actionAboutQt\">\n   <property name=\"text\">\n    <string>About &amp;Qt</string>\n   </property>\n  </action>\n  <action name=\"actionUserManual\">\n   <property name=\"text\">\n    <string>&amp;User Manual</string>\n   </property>\n  </action>\n  <action name=\"actionReportProblem\">\n   <property name=\"text\">\n    <string>&amp;Report a Problem...</string>\n   </property>\n  </action>\n  <action name=\"actionPeripherals\">\n   <property name=\"text\">\n    <string>P&amp;eripherals</string>\n   </property>\n  </action>\n  <action name=\"actionTerminal\">\n   <property name=\"text\">\n    <string>&amp;Terminal</string>\n   </property>\n  </action>\n  <action name=\"actionLcdDisplay\">\n   <property name=\"text\">\n    <string>&amp;LCD display</string>\n   </property>\n  </action>\n  <action name=\"actionShow_Symbol\">\n   <property name=\"text\">\n    <string>Sh&amp;ow Symbol</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Show Symbol</string>\n   </property>\n  </action>\n  <action name=\"actionCore_View_show\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>&amp;Core View</string>\n   </property>\n  </action>\n  <action name=\"actionMessages\">\n   <property name=\"text\">\n    <string>Me&amp;ssages</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Show compile/build messages</string>\n   </property>\n  </action>\n  <action name=\"actionMnemonicRegisters\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>M&amp;nemonics Registers</string>\n   </property>\n  </action>\n  <action name=\"actionEditorShowLineNumbers\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Show &amp;Line Numbers</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Show line number in the code editor.</string>\n   </property>\n  </action>\n  <action name=\"actionBranch_Predictor_History_table\">\n   <property name=\"text\">\n    <string>Branch Predictor (History table)</string>\n   </property>\n  </action>\n  <action name=\"actionBranch_Predictor_Target_table\">\n   <property name=\"text\">\n    <string>Branch Predictor (Target table)</string>\n   </property>\n  </action>\n  <action name=\"actionBranch_Predictor_Info\">\n   <property name=\"text\">\n    <string>Branch Predictor (Info)</string>\n   </property>\n  </action>\n<action name=\"actionData_TLB\">\n   <property name=\"text\">\n    <string>&amp;Data TLB</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Data TLB</string>\n   </property>\n  </action>\n  <action name=\"actionInstruction_TLB\">\n   <property name=\"text\">\n    <string>Instruction TLB</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Instruction TLB</string>\n   </property>\n  </action>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <resources>\n  <include location=\"../resources/icons/icons.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/gui/mainwindow/mainwindow.cpp",
    "content": "#include \"mainwindow.h\"\n\n#include \"assembler/fixmatheval.h\"\n#include \"assembler/simpleasm.h\"\n#include \"dialogs/about/aboutdialog.h\"\n#include \"dialogs/gotosymbol/gotosymboldialog.h\"\n#include \"dialogs/savechanged/savechangeddialog.h\"\n#include \"extprocess.h\"\n#include \"helper/async_modal.h\"\n#include \"os_emulation/ossyscall.h\"\n#include \"textsignalaction.h\"\n#include \"windows/editor/editordock.h\"\n#include \"windows/editor/editortab.h\"\n\n#include <QDesktopServices>\n#include <QFileInfo>\n#include <QMessageBox>\n#include <QMetaObject>\n#include <QProcessEnvironment>\n#include <QSysInfo>\n#include <QTextDocument>\n#include <QUrl>\n#include <QUrlQuery>\n#include <QtWidgets>\n#include <qactiongroup.h>\n#include <qwidget.h>\n\n\n\nLOG_CATEGORY(\"gui.mainwindow\");\n\n#ifdef __EMSCRIPTEN__\n    #include \"qhtml5file.h\"\n\n    #include <QFileInfo>\n\nconstexpr bool WEB_ASSEMBLY = true;\n#else\nconstexpr bool WEB_ASSEMBLY = false;\n#endif\n\nMainWindow::MainWindow(QSettings *settings, QWidget *parent)\n    : QMainWindow(parent)\n    , settings(settings) {\n    machine.reset();\n    corescene.reset();\n\n    ui.reset(new Ui::MainWindow());\n    ui->setupUi(this);\n    setWindowTitle(APP_NAME);\n    setDockNestingEnabled(true);\n\n    frequency_label.reset(new QLabel(this));\n    ui->statusBar->addPermanentWidget(frequency_label.data());\n\n    // Setup central widget\n\n    central_widget_tabs.reset(new HidingTabWidget(this));\n    central_widget_tabs->setTabBarAutoHide(true);\n    this->setCentralWidget(central_widget_tabs.data());\n\n    coreview.reset(new GraphicsView(this));\n    coreview->setWindowTitle(\"&Core\");\n    central_widget_tabs->addTab(coreview.data(), coreview->windowTitle());\n\n    // Setup editor\n\n    editor_tabs.reset(new EditorDock(this->settings, central_widget_tabs.data()));\n    editor_tabs->setTabBarAutoHide(true);\n    editor_tabs->setWindowTitle(\"&Editor\");\n    ui->actionBuildExe->setEnabled(false);\n    connect(ui->actionNew, &QAction::triggered, editor_tabs.data(), &EditorDock::create_empty_tab);\n    connect(ui->actionOpen, &QAction::triggered, editor_tabs.data(), &EditorDock::open_file_dialog);\n    connect(ui->actionSave, &QAction::triggered, editor_tabs.data(), &EditorDock::save_current_tab);\n    connect(\n        ui->actionSaveAs, &QAction::triggered, editor_tabs.data(),\n        &EditorDock::save_current_tab_as);\n    connect(\n        ui->actionClose, &QAction::triggered, editor_tabs.data(), &EditorDock::close_current_tab);\n    connect(\n        editor_tabs.data(), &EditorDock::requestAddRemoveTab, central_widget_tabs.data(),\n        &HidingTabWidget::addRemoveTabRequested);\n\n    connect(\n        editor_tabs.data(), &EditorDock::editor_available_changed, this, [this](bool available) {\n            ui->actionSave->setEnabled(available);\n            ui->actionSaveAs->setEnabled(available);\n            ui->actionClose->setEnabled(available);\n            ui->actionCompileSource->setEnabled(available);\n        });\n    if constexpr (!WEB_ASSEMBLY) {\n        // Only enable build action if we know there to look for the Makefile.\n        connect(editor_tabs.data(), &EditorDock::currentChanged, this, [this](int index) {\n            bool has_elf_file = machine != nullptr && !machine->config().elf().isEmpty();\n            bool current_tab_is_file\n                = (index >= 0) && !editor_tabs->get_tab(index)->get_editor()->filename().isEmpty();\n            ui->actionBuildExe->setEnabled(has_elf_file || current_tab_is_file);\n        });\n    }\n    connect(\n        ui->actionEditorShowLineNumbers, &QAction::triggered, editor_tabs.data(),\n        &EditorDock::set_show_line_numbers);\n\n    bool line_numbers_visible = settings->value(\"EditorShowLineNumbers\", true).toBool();\n    editor_tabs->set_show_line_numbers(line_numbers_visible);\n    ui->actionEditorShowLineNumbers->setChecked(line_numbers_visible);\n\n    // Create/prepare other widgets\n\n    ndialog.reset(new NewDialog(this, settings));\n    registers.reset(new RegistersDock(this, machine::Xlen::_32));\n    registers->hide();\n    program.reset(new ProgramDock(this, settings));\n    addDockWidget(Qt::LeftDockWidgetArea, program.data());\n    program->show();\n    memory.reset(new MemoryDock(this, settings));\n    memory->hide();\n    cache_program.reset(new CacheDock(this, \"Program\"));\n    cache_program->hide();\n    cache_data.reset(new CacheDock(this, \"Data\"));\n    cache_data->hide();\n    cache_level2.reset(new CacheDock(this, \"L2\"));\n    cache_level2->hide();\n    tlb_program.reset(new TLBDock(this, \"Instruction\"));\n    tlb_program->hide();\n    tlb_data.reset(new TLBDock(this, \"Data\"));\n    tlb_data->hide();\n    bp_btb.reset(new DockPredictorBTB(this));\n    bp_btb->hide();\n    bp_bht.reset(new DockPredictorBHT(this));\n    bp_bht->hide();\n    bp_info.reset(new DockPredictorInfo(this));\n    bp_info->hide();\n    peripherals.reset(new PeripheralsDock(this, settings));\n    peripherals->hide();\n    terminal.reset(new TerminalDock(this, settings));\n    terminal->hide();\n    lcd_display.reset(new LcdDisplayDock(this, settings));\n    lcd_display->hide();\n    csrdock = new CsrDock(this);\n    csrdock->hide();\n    messages = new MessagesDock(this, settings);\n    messages->hide();\n\n    // Execution speed actions\n    speed_group = new QActionGroup(this);\n    speed_group->addAction(ui->ips1);\n    speed_group->addAction(ui->ips2);\n    speed_group->addAction(ui->ips5);\n    speed_group->addAction(ui->ips10);\n    speed_group->addAction(ui->ips25);\n    speed_group->addAction(ui->ipsUnlimited);\n    speed_group->addAction(ui->ipsMax);\n    ui->ips1->setChecked(true);\n\n    // Connect signals from menu\n    connect(ui->actionExit, &QAction::triggered, this, &QWidget::close);\n    connect(ui->actionNewMachine, &QAction::triggered, this, &MainWindow::new_machine);\n    connect(ui->actionReload, &QAction::triggered, this, [this] { machine_reload(false, false); });\n    connect(ui->actionPrint, &QAction::triggered, this, &MainWindow::print_action);\n\n    connect(\n        ui->actionMnemonicRegisters, &QAction::triggered, this,\n        &MainWindow::view_mnemonics_registers);\n    connect(ui->actionCompileSource, &QAction::triggered, this, &MainWindow::compile_source);\n    connect(ui->actionBuildExe, &QAction::triggered, this, &MainWindow::build_execute);\n    connect(ui->actionShow_Symbol, &QAction::triggered, this, &MainWindow::show_symbol_dialog);\n    connect(ui->actionRegisters, &QAction::triggered, this, &MainWindow::show_registers);\n    connect(ui->actionProgram_memory, &QAction::triggered, this, &MainWindow::show_program);\n    connect(ui->actionMemory, &QAction::triggered, this, &MainWindow::show_memory);\n    connect(ui->actionProgram_Cache, &QAction::triggered, this, &MainWindow::show_cache_program);\n    connect(ui->actionData_Cache, &QAction::triggered, this, &MainWindow::show_cache_data);\n    connect(ui->actionL2_Cache, &QAction::triggered, this, &MainWindow::show_cache_level2);\n    connect(ui->actionInstruction_TLB, &QAction::triggered, this, &MainWindow::show_tlb_program);\n    connect(ui->actionData_TLB, &QAction::triggered, this, &MainWindow::show_tlb_data);\n    // Branch predictor\n    connect(\n        ui->actionBranch_Predictor_History_table, &QAction::triggered, this,\n        &MainWindow::show_bp_bht);\n    connect(\n        ui->actionBranch_Predictor_Target_table, &QAction::triggered, this,\n        &MainWindow::show_bp_btb);\n    connect(ui->actionBranch_Predictor_Info, &QAction::triggered, this, &MainWindow::show_bp_info);\n\n    connect(ui->actionPeripherals, &QAction::triggered, this, &MainWindow::show_peripherals);\n    connect(ui->actionTerminal, &QAction::triggered, this, &MainWindow::show_terminal);\n    connect(ui->actionLcdDisplay, &QAction::triggered, this, &MainWindow::show_lcd_display);\n    connect(ui->actionCsrShow, &QAction::triggered, this, &MainWindow::show_csrdock);\n    connect(ui->actionCore_View_show, &QAction::triggered, this, &MainWindow::show_hide_coreview);\n    connect(ui->actionMessages, &QAction::triggered, this, &MainWindow::show_messages);\n    connect(ui->actionResetWindows, &QAction::triggered, this, &MainWindow::reset_windows);\n    connect(ui->actionUserManual, &QAction::triggered, this, [] {\n        QDesktopServices::openUrl(QUrl(APP_USER_MANUAL_URL));\n    });\n    connect(ui->actionReportProblem, &QAction::triggered, this, [] {\n        const QString version = QString::fromUtf8(APP_VERSION);\n        const QString qtVersion = QString::fromUtf8(qVersion());\n        const QString os = QSysInfo::prettyProductName();\n        const QString arch = QSysInfo::currentCpuArchitecture();\n\n        QString body;\n        body += \"## Describe the problem\\n\";\n        body += \"\\n\";\n        body += \"## Steps to reproduce\\n\";\n        body += \"1.\\n\";\n        body += \"\\n\";\n        body += \"## Expected behavior\\n\";\n        body += \"\\n\";\n        body += \"## Actual behavior\\n\";\n        body += \"\\n\";\n        body += \"## Environment\\n\";\n        body += \"- QtRVSim version: \" + version + \"\\n\";\n        body += \"- OS: \" + os + \"\\n\";\n        body += \"- Arch: \" + arch + \"\\n\";\n        body += \"- Qt: \" + qtVersion + \"\\n\";\n\n        QUrl url(QString::fromUtf8(APP_REPORT_PROBLEM_URL));\n        QUrlQuery query;\n        query.addQueryItem(\"title\", \"Bug report\");\n        query.addQueryItem(\"body\", body);\n        url.setQuery(query);\n\n        QDesktopServices::openUrl(url);\n    });\n    connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::about_program);\n    connect(ui->actionAboutQt, &QAction::triggered, this, &MainWindow::about_qt);\n    connect(ui->ips1, &QAction::toggled, this, &MainWindow::set_speed);\n    connect(ui->ips2, &QAction::toggled, this, &MainWindow::set_speed);\n    connect(ui->ips5, &QAction::toggled, this, &MainWindow::set_speed);\n    connect(ui->ips10, &QAction::toggled, this, &MainWindow::set_speed);\n    connect(ui->ips25, &QAction::toggled, this, &MainWindow::set_speed);\n    connect(ui->ipsUnlimited, &QAction::toggled, this, &MainWindow::set_speed);\n    connect(ui->ipsMax, &QAction::toggled, this, &MainWindow::set_speed);\n\n    connect(this, &MainWindow::report_message, messages, &MessagesDock::insert_line);\n    connect(this, &MainWindow::clear_messages, messages, &MessagesDock::clear_messages);\n    connect(messages, &MessagesDock::message_selected, this, &MainWindow::message_selected);\n\n    // Restore application state from settings\n    restoreState(settings->value(\"windowState\").toByteArray());\n    restoreGeometry(settings->value(\"windowGeometry\").toByteArray());\n    if (settings->value(\"viewMnemonicRegisters\").toBool()) {\n        ui->actionMnemonicRegisters->trigger();\n    }\n\n    for (const QString &file_name : settings->value(\"openSrcFiles\").toStringList()) {\n        editor_tabs->open_file(file_name);\n    }\n\n    QDir samples_dir(\":/samples\");\n    for (const QString &fname : samples_dir.entryList(QDir::Files)) {\n        auto *textsigac = new TextSignalAction(fname, \":/samples/\" + fname, ui->menuExamples);\n        ui->menuExamples->addAction(textsigac);\n        connect(textsigac, &TextSignalAction::activated, this, &MainWindow::example_source);\n    }\n}\n\nMainWindow::~MainWindow() {\n    settings->sync();\n}\n\nvoid MainWindow::start() {\n    this->show();\n    ndialog->show();\n}\n\nvoid MainWindow::show_hide_coreview(bool show) {\n    coreview_shown = show;\n    if (!show) {\n        if (corescene == nullptr) {\n        } else {\n            central_widget_tabs->removeTab(central_widget_tabs->indexOf(coreview.data()));\n            corescene.reset();\n            if (coreview != nullptr) { coreview->setScene(corescene.data()); }\n        }\n        return;\n    }\n    if (machine == nullptr) { return; }\n    if (corescene != nullptr) { return; }\n\n    if (machine->config().pipelined()) {\n        corescene.reset(new CoreViewScenePipelined(machine.data()));\n    } else {\n        corescene.reset(new CoreViewSceneSimple(machine.data()));\n    }\n    central_widget_tabs->insertTab(0, coreview.data(), coreview->windowTitle());\n    // Ensures correct zoom.\n    coreview->setScene(corescene.data());\n    this->setCentralWidget(central_widget_tabs.data());\n\n    // Connect scene signals to actions\n    connect(corescene.data(), &CoreViewScene::request_registers, this, &MainWindow::show_registers);\n    connect(\n        corescene.data(), &CoreViewScene::request_program_memory, this, &MainWindow::show_program);\n    connect(corescene.data(), &CoreViewScene::request_data_memory, this, &MainWindow::show_memory);\n    connect(\n        corescene.data(), &CoreViewScene::request_jump_to_program_counter, program.data(),\n        &ProgramDock::jump_to_pc);\n    connect(\n        corescene.data(), &CoreViewScene::request_cache_program, this,\n        &MainWindow::show_cache_program);\n    connect(\n        corescene.data(), &CoreViewScene::request_cache_data, this, &MainWindow::show_cache_data);\n    connect(\n        corescene.data(), &CoreViewScene::request_peripherals, this, &MainWindow::show_peripherals);\n    connect(corescene.data(), &CoreViewScene::request_terminal, this, &MainWindow::show_terminal);\n    coreview->setScene(corescene.data());\n}\n\nvoid MainWindow::create_core(\n    const machine::MachineConfig &config,\n    bool load_executable,\n    bool keep_memory) {\n    // Create machine\n    auto *new_machine = new machine::Machine(config, true, load_executable);\n\n    if (keep_memory && (machine != nullptr)) {\n        new_machine->memory_rw()->reset(*machine->memory());\n    }\n\n    // Remove old machine\n    machine.reset(new_machine);\n\n    // Create machine view\n    auto focused_index = central_widget_tabs->currentIndex();\n    corescene.reset();\n    show_hide_coreview(coreview_shown);\n    central_widget_tabs->setCurrentIndex(focused_index);\n\n    set_speed(); // Update machine speed to current settings\n\n    const static machine::ExceptionCause ecall_variats[]\n        = { machine::EXCAUSE_ECALL_ANY, machine::EXCAUSE_ECALL_M, machine::EXCAUSE_ECALL_S,\n            machine::EXCAUSE_ECALL_U };\n\n    if (config.osemu_enable()) {\n        auto *osemu_handler = new osemu::OsSyscallExceptionHandler(\n            config.osemu_known_syscall_stop(), config.osemu_unknown_syscall_stop(),\n            config.osemu_fs_root());\n        osemu_handler->setParent(new_machine);\n        connect(\n            osemu_handler, &osemu::OsSyscallExceptionHandler::char_written, terminal.data(),\n            QOverload<int, unsigned int>::of(&TerminalDock::tx_byte));\n        connect(\n            osemu_handler, &osemu::OsSyscallExceptionHandler::rx_byte_pool, terminal.data(),\n            &TerminalDock::rx_byte_pool);\n        for (auto ecall_variat : ecall_variats) {\n            machine->register_exception_handler(ecall_variat, osemu_handler);\n            machine->set_step_over_exception(ecall_variat, true);\n            machine->set_stop_on_exception(ecall_variat, false);\n        }\n    } else {\n        for (auto ecall_variat : ecall_variats) {\n            machine->set_step_over_exception(ecall_variat, false);\n            machine->set_stop_on_exception(ecall_variat, config.osemu_exception_stop());\n        }\n    }\n\n    // Connect machine signals and slots\n    connect(ui->actionRun, &QAction::triggered, machine.data(), &machine::Machine::play);\n    connect(ui->actionPause, &QAction::triggered, machine.data(), &machine::Machine::pause);\n    connect(ui->actionStep, &QAction::triggered, machine.data(), &machine::Machine::step);\n    connect(ui->actionRestart, &QAction::triggered, machine.data(), &machine::Machine::restart);\n    connect(machine.data(), &machine::Machine::status_change, this, &MainWindow::machine_status);\n    connect(machine.data(), &machine::Machine::program_exit, this, &MainWindow::machine_exit);\n    connect(machine.data(), &machine::Machine::program_trap, this, &MainWindow::machine_trap);\n    connect(\n        machine.data(), &machine::Machine::report_core_frequency, this,\n        &MainWindow::update_core_frequency);\n    // Connect signal from break to machine pause\n    connect(\n        machine->core(), &machine::Core::stop_on_exception_reached, machine.data(),\n        &machine::Machine::pause);\n\n    // Setup docks\n    registers->connectToMachine(machine.data());\n    program->setup(machine.data());\n    memory->setup(machine.data());\n    cache_program->setup(machine->cache_program());\n    cache_data->setup(machine->cache_data());\n    bool cache_after_cache = config.cache_data().enabled() || config.cache_program().enabled();\n    cache_level2->setup(machine->cache_level2(), cache_after_cache);\n    tlb_program->setup(machine->get_tlb_program_rw());\n    tlb_data->setup(machine->get_tlb_data_rw());\n\n    // Branch predictor\n    bp_btb->setup(machine->core()->get_predictor(), machine->core());\n    bp_bht->setup(machine->core()->get_predictor(), machine->core());\n    bp_info->setup(machine->core()->get_predictor(), machine->core());\n\n    terminal->setup(machine->serial_port());\n    peripherals->setup(machine->peripheral_spi_led());\n    lcd_display->setup(machine->peripheral_lcd_display());\n    csrdock->setup(machine.data());\n\n    connect(\n        machine->core(), &machine::Core::step_done, program.data(),\n        &ProgramDock::update_pipeline_addrs);\n\n    // Set status to ready\n    machine_status(machine::Machine::ST_READY);\n}\n\nbool MainWindow::configured() {\n    return (machine != nullptr);\n}\n\nvoid MainWindow::new_machine() {\n    ndialog->show();\n}\n\nvoid MainWindow::machine_reload(bool force_memory_reset, bool force_elf_load) {\n    if (machine == nullptr) { return new_machine(); }\n    bool load_executable = force_elf_load || machine->executable_loaded();\n    machine::MachineConfig cnf(&machine->config()); // We have to make local copy as create_core\n                                                    // will delete the current machine\n    try {\n        create_core(cnf, load_executable, !load_executable && !force_memory_reset);\n    } catch (const machine::SimulatorExceptionInput &e) {\n        showAsyncCriticalBox(\n            this, \"Error while initializing new machine\", e.msg(false), e.msg(true));\n    }\n}\n\nvoid MainWindow::print_action() {\n#ifdef WITH_PRINTING\n    printer.setColorMode(QPrinter::Color);\n    if (print_dialog.exec() == QDialog::Accepted) {\n        // This vector pre-drawing step is required because Qt fallbacks to\n        // bitmap and produces extremely large and slow to render files.\n        // (https://forum.qt.io/topic/21330/printing-widgets-not-as-bitmap-but-in-a-vector-based-format/3)\n        QPicture scene_as_vector;\n        {\n            QPainter painter(&scene_as_vector);\n            corescene->render(&painter);\n            painter.end();\n        }\n\n        // Prepare printer for PDF printing with appropriate resize.\n        QRectF scene_rect = corescene->sceneRect();\n        if (printer.outputFormat() == QPrinter::PdfFormat && (scene_rect.height() != 0)) {\n            QPageLayout layout = printer.pageLayout();\n            layout.setOrientation(QPageLayout::Portrait);\n            QPageSize pagesize = layout.pageSize();\n            QRectF paint_rect = layout.paintRect(QPageLayout::Point);\n            QSize pointsize = pagesize.sizePoints();\n            qreal ratio = scene_rect.width() / scene_rect.height();\n            // Cut off the excess\n            if (paint_rect.height() * ratio > paint_rect.width()) {\n                pointsize.setHeight(\n                    pointsize.height() - paint_rect.height() + paint_rect.width() / ratio);\n            } else {\n                pointsize.setWidth(\n                    pointsize.width() - paint_rect.width() + paint_rect.height() * ratio);\n            }\n            pagesize = QPageSize(pointsize, \"custom\", QPageSize::ExactMatch);\n            layout.setPageSize(pagesize, layout.margins());\n            printer.setPageLayout(layout);\n        }\n\n        QPainter painter(&printer);\n        // scale to fit paint size\n        QRectF paint_rect = printer.pageLayout().paintRect(QPageLayout::Millimeter);\n        double xscale = paint_rect.width() / scene_as_vector.widthMM();\n        double yscale = paint_rect.height() / scene_as_vector.heightMM();\n        double scale = qMin(xscale, yscale);\n        painter.scale(scale, scale);\n        painter.drawPicture(0, 0, scene_as_vector);\n        painter.end();\n    }\n#else\n    showAsyncMessageBox(\n        this, QMessageBox::Information, \"Printing is not supported.\",\n        \"The simulator was compiled without printing support.\\n\"\n        \"If you compiled the simulator yourself, make sure that the Qt \"\n        \"print support library is present.\\n\"\n        \"Otherwise report this to the executable provider (probably your \"\n        \"teacher).\");\n#endif // WITH_PRINTING\n}\n\n#define SHOW_HANDLER(NAME, DEFAULT_AREA, DEFAULT_VISIBLE)                                          \\\n    void MainWindow::show_##NAME() {                                                               \\\n        show_dockwidget(&*NAME, DEFAULT_AREA, true, false);                                        \\\n    }                                                                                              \\\n    void MainWindow::reset_state_##NAME() {                                                        \\\n        show_dockwidget(&*NAME, DEFAULT_AREA, DEFAULT_VISIBLE, true);                              \\\n    }\n\nSHOW_HANDLER(registers, Qt::TopDockWidgetArea, true)\nSHOW_HANDLER(program, Qt::LeftDockWidgetArea, true)\nSHOW_HANDLER(memory, Qt::RightDockWidgetArea, true)\nSHOW_HANDLER(cache_program, Qt::RightDockWidgetArea, false)\nSHOW_HANDLER(cache_data, Qt::RightDockWidgetArea, false)\nSHOW_HANDLER(cache_level2, Qt::RightDockWidgetArea, false)\nSHOW_HANDLER(tlb_program, Qt::RightDockWidgetArea, false)\nSHOW_HANDLER(tlb_data, Qt::RightDockWidgetArea, false)\nSHOW_HANDLER(bp_btb, Qt::RightDockWidgetArea, false)\nSHOW_HANDLER(bp_bht, Qt::RightDockWidgetArea, false)\nSHOW_HANDLER(bp_info, Qt::RightDockWidgetArea, false)\nSHOW_HANDLER(peripherals, Qt::RightDockWidgetArea, false)\nSHOW_HANDLER(terminal, Qt::RightDockWidgetArea, false)\nSHOW_HANDLER(lcd_display, Qt::RightDockWidgetArea, false)\nSHOW_HANDLER(csrdock, Qt::TopDockWidgetArea, false)\nSHOW_HANDLER(messages, Qt::BottomDockWidgetArea, false)\n#undef SHOW_HANDLER\n\nvoid MainWindow::reset_windows() {\n    reset_state_registers();\n    reset_state_program();\n    reset_state_memory();\n    reset_state_cache_program();\n    reset_state_cache_data();\n    reset_state_cache_level2();\n    reset_state_bp_btb();\n    reset_state_bp_bht();\n    reset_state_bp_info();\n    reset_state_peripherals();\n    reset_state_terminal();\n    reset_state_lcd_display();\n    reset_state_csrdock();\n    reset_state_messages();\n}\n\nvoid MainWindow::show_symbol_dialog() {\n    if (machine == nullptr || machine->symbol_table() == nullptr) { return; }\n    QStringList symbol_names = machine->symbol_table()->names();\n    auto *gotosyboldialog = new GoToSymbolDialog(this, symbol_names);\n    connect(\n        gotosyboldialog, &GoToSymbolDialog::program_focus_addr, program.data(),\n        &ProgramDock::focus_addr_with_save);\n    connect(\n        gotosyboldialog, &GoToSymbolDialog::program_focus_addr, this, &MainWindow::show_program);\n    connect(\n        gotosyboldialog, &GoToSymbolDialog::memory_focus_addr, memory.data(),\n        &MemoryDock::focus_addr);\n    connect(gotosyboldialog, &GoToSymbolDialog::memory_focus_addr, this, &MainWindow::show_memory);\n    connect(\n        gotosyboldialog, &GoToSymbolDialog::obtain_value_for_name, machine->symbol_table(),\n        &machine::SymbolTable::name_to_value);\n    gotosyboldialog->setAttribute(Qt::WA_DeleteOnClose);\n    gotosyboldialog->open();\n}\n\nvoid MainWindow::about_program() {\n    auto *aboutdialog = new AboutDialog(this);\n    aboutdialog->show();\n}\n\nvoid MainWindow::about_qt() {\n    QMessageBox::aboutQt(this);\n}\n\nvoid MainWindow::set_speed() {\n    if (machine == nullptr) {\n        return; // just ignore\n    }\n\n    if (ui->ips1->isChecked()) {\n        machine->set_speed(1000);\n    } else if (ui->ips2->isChecked()) {\n        machine->set_speed(500);\n    } else if (ui->ips5->isChecked()) {\n        machine->set_speed(200);\n    } else if (ui->ips10->isChecked()) {\n        machine->set_speed(100);\n    } else if (ui->ips25->isChecked()) {\n        machine->set_speed(40);\n    } else if (ui->ipsMax->isChecked()) {\n        machine->set_speed(0, 100);\n    } else {\n        machine->set_speed(0);\n    }\n}\n\nvoid MainWindow::view_mnemonics_registers(bool enable) {\n    machine::Instruction::set_symbolic_registers(enable);\n    settings->setValue(\"viewMnemonicRegisters\", enable);\n    if (program == nullptr) { return; }\n    program->request_update_all();\n}\n\nvoid MainWindow::closeEvent(QCloseEvent *event) {\n    settings->setValue(\"windowGeometry\", saveGeometry());\n    settings->setValue(\"windowState\", saveState());\n    settings->setValue(\"openSrcFiles\", editor_tabs->get_open_file_list());\n    settings->sync();\n\n    QStringList list;\n    if (!ignore_unsaved && editor_tabs->get_modified_tab_filenames(list, true)) {\n        event->ignore();\n        auto *dialog = new SaveChangedDialog(list, this);\n        if (!QMetaType::isRegistered(qMetaTypeId<QStringList>())) {\n            qRegisterMetaType<QStringList>();\n        }\n        connect(\n            dialog, &SaveChangedDialog::user_decision, this,\n            [this](bool cancel, const QStringList &tabs_to_save) {\n                if (cancel) return;\n                for (const auto &name : tabs_to_save) {\n                    auto tab_id = editor_tabs->find_tab_id_by_filename(name);\n                    if (tab_id.has_value()) editor_tabs->save_tab(tab_id.value());\n                }\n                ignore_unsaved = true;\n                close();\n            },\n            Qt::QueuedConnection);\n        dialog->open();\n    }\n}\n\nvoid MainWindow::show_dockwidget(\n    QDockWidget *dw,\n    Qt::DockWidgetArea area,\n    bool defaultVisible,\n    bool resetState) {\n    if (dw == nullptr) { return; }\n    if (resetState) {\n        if (dw->isFloating()) {\n            dw->hide();\n            dw->setFloating(false);\n        }\n        addDockWidget(area, dw);\n        if (defaultVisible) {\n            dw->show();\n        } else {\n            dw->hide();\n        }\n    } else if (dw->isHidden()) {\n        dw->show();\n        addDockWidget(area, dw);\n    } else {\n        dw->raise();\n        dw->setFocus();\n    }\n}\n\nvoid MainWindow::machine_status(enum machine::Machine::Status st) {\n    QString status;\n    switch (st) {\n    case machine::Machine::ST_READY:\n        ui->actionPause->setEnabled(false);\n        ui->actionRun->setEnabled(true);\n        ui->actionStep->setEnabled(true);\n        status = \"Ready\";\n        break;\n    case machine::Machine::ST_RUNNING:\n        ui->actionPause->setEnabled(true);\n        ui->actionRun->setEnabled(false);\n        ui->actionStep->setEnabled(false);\n        status = \"Running\";\n        break;\n    case machine::Machine::ST_BUSY:\n        // Busy is not interesting (in such case we should just be running\n        return;\n    case machine::Machine::ST_EXIT:\n        // machine_exit is called, so we disable controls in that\n        status = \"Exited\";\n        break;\n    case machine::Machine::ST_TRAPPED:\n        // machine_trap is called, so we disable controls in that\n        status = \"Trapped\";\n        break;\n    default: status = \"Unknown\"; break;\n    }\n    ui->statusBar->showMessage(status);\n}\n\nvoid MainWindow::machine_exit() {\n    ui->actionPause->setEnabled(false);\n    ui->actionRun->setEnabled(false);\n    ui->actionStep->setEnabled(false);\n}\n\nvoid MainWindow::machine_trap(machine::SimulatorException &e) {\n    machine_exit();\n    showAsyncCriticalBox(this, \"Machine trapped\", e.msg(false), e.msg(true));\n}\n\nvoid MainWindow::close_source_by_name(QString &filename, bool ask) {\n    editor_tabs->close_tab_by_name(filename, ask);\n}\n\nvoid MainWindow::example_source(const QString &source_file) {\n    if (!editor_tabs->open_file(source_file, true)) {\n        showAsyncCriticalBox(\n            this, \"Simulator Error\",\n            tr(\"Cannot open example file '%1' for reading.\").arg(source_file));\n    }\n}\n\nvoid MainWindow::message_selected(\n    messagetype::Type type,\n    const QString &file,\n    int line,\n    int column,\n    const QString &text,\n    const QString &hint) {\n    (void)type;\n    (void)column;\n    (void)text;\n    (void)hint;\n\n    if (file.isEmpty()) { return; }\n    central_widget_tabs->setCurrentWidget(editor_tabs.data());\n    if (!editor_tabs->set_cursor_to(file, line, column)) {\n        editor_tabs->open_file_if_not_open(file, false);\n        if (!editor_tabs->set_cursor_to(file, line, column)) return;\n    }\n\n    // Highlight the line\n    auto editor = editor_tabs->get_current_editor();\n    QTextEdit::ExtraSelection selection;\n    selection.format.setBackground(QColor(Qt::red).lighter(160));\n    selection.format.setProperty(QTextFormat::FullWidthSelection, true);\n    selection.cursor = editor->textCursor();\n    selection.cursor.clearSelection();\n    editor->setExtraSelections({ selection });\n}\n\nvoid MainWindow::update_core_frequency(double frequency) {\n    frequency_label->setText(QString(\"Core frequency: %1 kHz\").arg(frequency / 1000.0, 0, 'f', 3));\n}\n\nbool SimpleAsmWithEditorCheck::process_file(const QString &filename, QString *error_ptr) {\n    EditorTab *tab = mainwindow->editor_tabs->find_tab_by_filename(filename);\n    if (tab == nullptr) { return Super::process_file(filename, error_ptr); }\n    SrcEditor *editor = tab->get_editor();\n    QTextDocument *doc = editor->document();\n    int ln = 1;\n    for (QTextBlock block = doc->begin(); block.isValid(); block = block.next(), ln++) {\n        QString line = block.text();\n        process_line(line, filename, ln);\n    }\n    return !error_occured;\n}\n\nbool SimpleAsmWithEditorCheck::process_pragma(\n    QStringList &operands,\n    const QString &filename,\n    int line_number,\n    QString *error_ptr) {\n    (void)error_ptr;\n#if 0\n    static const QMap<QString, QDockWidget *MainWindow::*> pragma_how_map = {\n        {QString(\"registers\"), static_cast<QDockWidget *MainWindow::*>(&MainWindow::registers)},\n    };\n#endif\n    if (operands.count() < 2\n        || (QString::compare(operands.at(0), \"qtrvsim\", Qt::CaseInsensitive)\n            && QString::compare(operands.at(0), \"qtmips\", Qt::CaseInsensitive))) {\n        return true;\n    }\n    QString op = operands.at(1).toLower();\n    if (op == \"show\") {\n        if (operands.count() < 3) { return true; }\n        QString show_method = \"show_\" + operands.at(2);\n        QString show_method_sig = show_method + \"()\";\n        if (mainwindow->metaObject()->indexOfMethod(show_method_sig.toLatin1().data()) == -1) {\n            emit report_message(\n                messagetype::MSG_WARNING, filename, line_number, 0,\n                \"#pragma qtrvsim show - unknown object \" + operands.at(2), \"\");\n            return true;\n        }\n        QMetaObject::invokeMethod(mainwindow, show_method.toLatin1().data());\n        return true;\n    }\n    if (op == \"tab\") {\n        if ((operands.count() < 3) || error_occured) { return true; }\n        if (!QString::compare(operands.at(2), \"core\", Qt::CaseInsensitive)\n            && (mainwindow->editor_tabs != nullptr) && (mainwindow->coreview != nullptr)) {\n            mainwindow->editor_tabs->setCurrentWidget(mainwindow->coreview.data());\n        }\n        return true;\n    }\n    if (op == \"focus\") {\n        bool ok;\n        if (operands.count() < 4) { return true; }\n        fixmatheval::FmeExpression expression;\n        fixmatheval::FmeValue value;\n        QString error;\n        ok = expression.parse(operands.at(3), error);\n        if (!ok) {\n            emit report_message(\n                messagetype::MSG_WARNING, filename, line_number, 0,\n                \"expression parse error \" + error, \"\");\n            return true;\n        }\n        ok = expression.eval(value, symtab, error, address);\n        if (!ok) {\n            emit report_message(\n                messagetype::MSG_WARNING, filename, line_number, 0,\n                \"expression evaluation error \" + error, \"\");\n            return true;\n        }\n        if (!QString::compare(operands.at(2), \"memory\", Qt::CaseInsensitive)\n            && (mainwindow->memory != nullptr)) {\n            mainwindow->memory->focus_addr(machine::Address(value));\n            return true;\n        }\n        if (!QString::compare(operands.at(2), \"program\", Qt::CaseInsensitive)\n            && (mainwindow->program != nullptr)) {\n            mainwindow->program->focus_addr(machine::Address(value));\n            return true;\n        }\n        emit report_message(\n            messagetype::MSG_WARNING, filename, line_number, 0,\n            \"unknown #pragma qtrvsim focus unknown object \" + operands.at(2), \"\");\n        return true;\n    }\n    emit report_message(\n        messagetype::MSG_WARNING, filename, line_number, 0, \"unknown #pragma qtrvsim \" + op, \"\");\n    return true;\n}\n\nvoid MainWindow::compile_source() {\n    bool error_occured = false;\n    if (machine != nullptr) {\n        if (machine->config().reset_at_compile()) { machine_reload(true); }\n    }\n    if (machine == nullptr) {\n        showAsyncCriticalBox(this, \"Simulator Error\", tr(\"No machine to store program.\"));\n        return;\n    }\n    SymbolTableDb symtab(machine->symbol_table_rw(true));\n    machine::FrontendMemory *mem = machine->memory_data_bus_rw();\n    if (mem == nullptr) {\n        showAsyncCriticalBox(\n            this, \"Simulator Error\", tr(\"No physical addresspace to store program.\"));\n        return;\n    }\n\n    machine->cache_sync();\n    machine->tlb_sync();\n\n    auto editor = editor_tabs->get_current_editor();\n    auto filename = editor->filename().isEmpty() ? \"Unknown\" : editor->filename();\n    auto content = editor->document();\n\n    emit clear_messages();\n    SimpleAsmWithEditorCheck sasm(this);\n\n    connect(&sasm, &SimpleAsm::report_message, this, &MainWindow::report_message);\n\n    sasm.setup(mem, &symtab, machine::Address(0x00000200), machine->core()->get_xlen());\n\n    int ln = 1;\n    for (QTextBlock block = content->begin(); block.isValid(); block = block.next(), ln++) {\n        QString line = block.text();\n        if (!sasm.process_line(line, filename, ln)) { error_occured = true; }\n    }\n    if (!sasm.finish()) { error_occured = true; }\n\n    if (error_occured) { show_messages(); }\n}\n\nvoid MainWindow::build_execute() {\n    QStringList list;\n    if (editor_tabs->get_modified_tab_filenames(list, false)) {\n        auto *dialog = new SaveChangedDialog(list, this);\n        connect(\n            dialog, &SaveChangedDialog::user_decision, this, &MainWindow::build_execute_with_save);\n        dialog->open();\n    } else {\n        build_execute_no_check();\n    }\n}\n\nvoid MainWindow::build_execute_with_save(\n    bool cancel,\n    QStringList tosavelist) { // NOLINT(performance-unnecessary-value-param)\n    if (cancel) { return; }\n    for (const auto &filename : tosavelist) {\n        editor_tabs->find_tab_by_filename(filename)->get_editor()->saveFile();\n    }\n    build_execute_no_check();\n}\n\nvoid MainWindow::build_execute_no_check() {\n    QString work_dir = \"\";\n    ExtProcess *proc;\n    ExtProcess *procptr = build_process;\n    if (procptr != nullptr) {\n        procptr->close();\n        procptr->deleteLater();\n    }\n\n    emit clear_messages();\n    show_messages();\n    proc = new ExtProcess(this);\n    build_process = procptr;\n    connect(proc, &ExtProcess::report_message, this, &MainWindow::report_message);\n    connect(\n        proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,\n        [this](int exitCode, QProcess::ExitStatus exitStatus) {\n            if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) { return; }\n\n            if (machine != nullptr) {\n                if (machine->config().reset_at_compile()) { machine_reload(true, true); }\n            }\n        });\n    auto current_srceditor = editor_tabs->get_current_editor();\n    if (current_srceditor != nullptr && !current_srceditor->filename().isEmpty()) {\n        QFileInfo fi(current_srceditor->filename());\n        work_dir = fi.dir().path();\n    }\n    if (work_dir.isEmpty() && (machine != nullptr)) {\n        if (!machine->config().elf().isEmpty()) {\n            QFileInfo fi(machine->config().elf());\n            work_dir = fi.dir().path();\n        }\n    }\n    if (!work_dir.isEmpty()) {\n        proc->setWorkingDirectory(work_dir);\n        proc->start(\"make\", {}, QProcess::Unbuffered | QProcess::ReadOnly);\n    }\n}\n"
  },
  {
    "path": "src/gui/mainwindow/mainwindow.h",
    "content": "﻿#ifndef MAINWINDOW_H\n#define MAINWINDOW_H\n\n#ifdef WITH_PRINTING\n    #include <QPrintDialog>\n    #include <QPrinter>\n#endif\n#include \"assembler/simpleasm.h\"\n#include \"dialogs/new/newdialog.h\"\n#include \"extprocess.h\"\n#include \"machine/machine.h\"\n#include \"machine/machineconfig.h\"\n#include \"scene.h\"\n#include \"ui_MainWindow.h\"\n#include \"widgets/hidingtabwidget.h\"\n#include \"windows/cache/cachedock.h\"\n#include \"windows/csr/csrdock.h\"\n#include \"windows/editor/editordock.h\"\n#include \"windows/editor/editortab.h\"\n#include \"windows/editor/srceditor.h\"\n#include \"windows/lcd/lcddisplaydock.h\"\n#include \"windows/memory/memorydock.h\"\n#include \"windows/messages/messagesdock.h\"\n#include \"windows/peripherals/peripheralsdock.h\"\n#include \"windows/predictor/predictor_bht_dock.h\"\n#include \"windows/predictor/predictor_btb_dock.h\"\n#include \"windows/predictor/predictor_info_dock.h\"\n#include \"windows/program/programdock.h\"\n#include \"windows/registers/registersdock.h\"\n#include \"windows/terminal/terminaldock.h\"\n#include \"windows/tlb/tlbdock.h\"\n\n#include <QMainWindow>\n#include <QPointer>\n#include <QSettings>\n#include <QTabWidget>\n\nclass MainWindow : public QMainWindow {\n    Q_OBJECT\n\n    friend class SimpleAsmWithEditorCheck;\n\npublic:\n    explicit MainWindow(OWNED QSettings *settings, QWidget *parent = nullptr);\n    ~MainWindow() override;\n\n    void start();\n    void create_core(\n        const machine::MachineConfig &config,\n        bool load_executable = true,\n        bool keep_memory = false);\n\n    bool configured();\n\nsignals:\n    void report_message(\n        messagetype::Type type,\n        QString file,\n        int line,\n        int column,\n        QString text,\n        QString hint);\n    void clear_messages();\n\npublic slots:\n    // Actions signals\n    void new_machine();\n    void machine_reload(bool force_memory_reset = false, bool force_elf_load = false);\n    void print_action();\n    void close_source_by_name(QString &filename, bool ask = false);\n    void example_source(const QString &source_file);\n    void compile_source();\n    void build_execute();\n    void build_execute_no_check();\n    void build_execute_with_save(bool cancel, QStringList tosavelist);\n    void reset_state_registers();\n    void reset_state_program();\n    void reset_state_memory();\n    void reset_state_cache_program();\n    void reset_state_cache_data();\n    void reset_state_cache_level2();\n    void reset_state_tlb_program();\n    void reset_state_tlb_data();\n    void reset_state_peripherals();\n    void reset_state_terminal();\n    void reset_state_lcd_display();\n    void reset_state_csrdock();\n    void reset_state_messages();\n    void show_registers();\n    void show_program();\n    void show_memory();\n    void show_cache_data();\n    void show_cache_program();\n    void show_cache_level2();\n    void show_tlb_program();\n    void show_tlb_data();\n    void show_peripherals();\n    void show_terminal();\n    void show_lcd_display();\n    void show_csrdock();\n    void show_hide_coreview(bool show);\n    void show_messages();\n    void reset_windows();\n    void show_symbol_dialog();\n    // Branch predictor\n    void show_bp_btb();\n    void show_bp_bht();\n    void show_bp_info();\n    void reset_state_bp_btb();\n    void reset_state_bp_bht();\n    void reset_state_bp_info();\n    // Actions - help\n    void about_program();\n    void about_qt();\n    // Actions - execution speed\n    void set_speed();\n    // Machine signals\n    void machine_status(enum machine::Machine::Status st);\n    void machine_exit();\n    void machine_trap(machine::SimulatorException &e);\n    void view_mnemonics_registers(bool enable);\n    void message_selected(\n        messagetype::Type type,\n        const QString &file,\n        int line,\n        int column,\n        const QString &text,\n        const QString &hint);\n    // Update data\n    void update_core_frequency(double frequency);\n\nprotected:\n    void closeEvent(QCloseEvent *cancel) override;\n\nprivate:\n    Box<Ui::MainWindow> ui {};\n    // Placed right on bottom status bar.\n    Box<QLabel> frequency_label {};\n\n    Box<NewDialog> ndialog {};\n    Box<HidingTabWidget> central_widget_tabs {};\n    Box<EditorDock> editor_tabs {};\n\n    Box<GraphicsView> coreview {};\n    Box<CoreViewScene> corescene;\n\n    Box<RegistersDock> registers {};\n    Box<ProgramDock> program {};\n    Box<MemoryDock> memory {};\n    Box<CacheDock> cache_program {}, cache_data {}, cache_level2 {};\n    Box<TLBDock> tlb_program {}, tlb_data {};\n\n    // Branch predictor\n    Box<DockPredictorBTB> bp_btb {};\n    Box<DockPredictorBHT> bp_bht {};\n    Box<DockPredictorInfo> bp_info {};\n\n    Box<PeripheralsDock> peripherals {};\n    Box<TerminalDock> terminal {};\n    Box<LcdDisplayDock> lcd_display {};\n    CsrDock *csrdock {};\n    MessagesDock *messages {};\n    bool coreview_shown = true;\n\n    QActionGroup *speed_group {};\n\n    QSharedPointer<QSettings> settings;\n\n    Box<machine::Machine> machine; // Current simulated machine\n\n    void show_dockwidget(\n        QDockWidget *w,\n        Qt::DockWidgetArea area = Qt::RightDockWidgetArea,\n        bool defaultVisible = false,\n        bool resetState = false);\n    QPointer<ExtProcess> build_process;\n    bool ignore_unsaved = false;\n\n#ifdef WITH_PRINTING\n    QPrinter printer { QPrinter::HighResolution };\n    QPrintDialog print_dialog { &printer, this };\n#endif\n};\n\nclass SimpleAsmWithEditorCheck : public SimpleAsm {\n    Q_OBJECT\n    using Super = SimpleAsm;\n\npublic:\n    explicit SimpleAsmWithEditorCheck(MainWindow *a_mainwindow, QObject *parent = nullptr)\n        : Super(parent)\n        , mainwindow(a_mainwindow) {}\n    bool process_file(const QString &filename, QString *error_ptr = nullptr) override;\n\nprotected:\n    bool process_pragma(\n        QStringList &operands,\n        const QString &filename = \"\",\n        int line_number = 0,\n        QString *error_ptr = nullptr) override;\n\nprivate:\n    MainWindow *mainwindow;\n};\n\n#endif // MAINWINDOW_H\n"
  },
  {
    "path": "src/gui/qhtml5file.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2018 The Qt Company Ltd.\n** Contact: https://www.qt.io/licensing/\n**\n** This file is part of the QtCore module of the Qt Toolkit.\n**\n** $QT_BEGIN_LICENSE:LGPL$\n** Commercial License Usage\n** Licensees holding valid commercial Qt licenses may use this file in\n** accordance with the commercial license agreement provided with the\n** Software or, alternatively, in accordance with the terms contained in\n** a written agreement between you and The Qt Company. For licensing terms\n** and conditions see https://www.qt.io/terms-conditions. For further\n** information use the contact form at https://www.qt.io/contact-us.\n**\n** GNU Lesser General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n** $QT_END_LICENSE$\n**\n****************************************************************************/\n\n#ifndef QHTML5FILE_H\n#define QHTML5FILE_H\n\n#include <QtCore/QByteArray>\n#include <QtCore/QString>\n#include <functional>\n\nQT_BEGIN_NAMESPACE\n\nnamespace QHtml5File {\n\nvoid load(\n    const QString &accept,\n    std::function<void(const QByteArray &, const QString &)> fileDataReady);\nvoid save(const QByteArray &contents, const QString &fileNameHint);\n\n} // namespace QHtml5File\n\nQT_END_NAMESPACE\n\n#endif\n"
  },
  {
    "path": "src/gui/qhtml5file_html5.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2018 The Qt Company Ltd.\n** Contact: https://www.qt.io/licensing/\n**\n** This file is part of the QtCore module of the Qt Toolkit.\n**\n** $QT_BEGIN_LICENSE:LGPL$\n** Commercial License Usage\n** Licensees holding valid commercial Qt licenses may use this file in\n** accordance with the commercial license agreement provided with the\n** Software or, alternatively, in accordance with the terms contained in\n** a written agreement between you and The Qt Company. For licensing terms\n** and conditions see https://www.qt.io/terms-conditions. For further\n** information use the contact form at https://www.qt.io/contact-us.\n**\n** GNU Lesser General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU Lesser\n** General Public License version 3 as published by the Free Software\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\n** packaging of this file. Please review the following information to\n** ensure the GNU Lesser General Public License version 3 requirements\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n**\n** GNU General Public License Usage\n** Alternatively, this file may be used under the terms of the GNU\n** General Public License version 2.0 or (at your option) the GNU General\n** Public license version 3 or any later version approved by the KDE Free\n** Qt Foundation. The licenses are as published by the Free Software\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n** included in the packaging of this file. Please review the following\n** information to ensure the GNU General Public License requirements will\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n** https://www.gnu.org/licenses/gpl-3.0.html.\n**\n** $QT_END_LICENSE$\n**\n****************************************************************************/\n\n#include \"qhtml5file.h\"\n\n#include <emscripten.h>\n#include <emscripten/html5.h>\n#include <qdebug.h>\n\n//\n// This file implements file loading via the HTML file input element and file save via\n// browser download.\n//\n\n// Global user file data ready callback and C helper function. JavaScript will\n// call this function when the file data is ready; the helper then forwards\n// the call to the current handler function. This means there can be only one\n// file open in proress at a given time.\nstd::function<void(char *, size_t, const char *)> g_qtFileDataReadyCallback;\nextern \"C\" EMSCRIPTEN_KEEPALIVE void\nqt_callFileDataReady(char *content, size_t contentSize, const char *fileName) {\n    if (g_qtFileDataReadyCallback == nullptr) { return; }\n\n    g_qtFileDataReadyCallback(content, contentSize, fileName);\n    g_qtFileDataReadyCallback = nullptr;\n}\n\nnamespace {\nvoid loadFile(const char *accept, std::function<void(char *, size_t, const char *)> fileDataReady) {\n    if (::g_qtFileDataReadyCallback) {\n        puts(\n            \"Warning: Concurrent loadFile() calls are not supported. \"\n            \"Cancelling earlier call\");\n    }\n\n    // Call qt_callFileDataReady to make sure the emscripten linker does not\n    // optimize it away, which may happen if the function is called from\n    // JavaScript only. Set g_qtFileDataReadyCallback to null to make it a a\n    // no-op.\n    ::g_qtFileDataReadyCallback = nullptr;\n    ::qt_callFileDataReady(nullptr, 0, nullptr);\n\n    ::g_qtFileDataReadyCallback = fileDataReady;\n    EM_ASM_(\n        {\n            const accept = UTF8ToString($0);\n\n            // Crate file file input which whil display the native file dialog\n            var fileElement = document.createElement(\"input\");\n            document.body.appendChild(fileElement);\n            fileElement.type = \"file\";\n            fileElement.style = \"display:none\";\n            fileElement.accept = accept;\n            fileElement.onchange = function(event) {\n                const files = event.target.files;\n\n                // Read files\n                for (var i = 0; i < files.length; i++) {\n                    const file = files[i];\n                    var reader = new FileReader();\n                    reader.onload = function() {\n                        const name = file.name;\n                        var contentArray = new Uint8Array(reader.result);\n                        const contentSize = reader.result.byteLength;\n\n                        // Copy the file file content to the C++ heap.\n                        // Note: this could be simplified by passing the content\n                        // as an \"array\" type to ccall and then let it copy to\n                        // C++ memory. However, this built-in solution does not\n                        // handle files larger than ~15M (Chrome). Instead,\n                        // allocate memory manually and pass a pointer to the\n                        // C++ side (which will free() it when done).\n\n                        // TODO: consider slice()ing the file to read it\n                        // picewise and then assembling it in a QByteArray on\n                        // the C++ side.\n\n                        const heapPointer = _malloc(contentSize);\n                        const heapBytes\n                            = new Uint8Array(Module.HEAPU8.buffer, heapPointer, contentSize);\n                        heapBytes.set(contentArray);\n\n                        // Null out the first data copy to enable GC\n                        reader = null;\n                        contentArray = null;\n\n                        // Call the C++ file data ready callback\n                        ccall(\n                            \"qt_callFileDataReady\", null, [ \"number\", \"number\", \"string\" ],\n                            [ heapPointer, contentSize, name ]);\n                    };\n                    reader.readAsArrayBuffer(file);\n                }\n\n                // Clean up document\n                document.body.removeChild(fileElement);\n\n            }; // onchange callback\n\n            // Trigger file dialog open\n            fileElement.click();\n        },\n        accept);\n}\n\nvoid saveFile(const char *contentPointer, size_t contentLength, const char *fileNameHint) {\n    EM_ASM_(\n        {\n            // Make the file contents and file name hint accessible to\n            // Javascript: convert the char * to a JavaScript string and create\n            // a subarray view into the C heap.\n            const contentPointer = $0;\n            const contentLength = $1;\n            const fileNameHint = UTF8ToString($2);\n            const fileContent\n                = Module.HEAPU8.subarray(contentPointer, contentPointer + contentLength);\n\n            // Create a hidden download link and click it programatically\n            const fileblob = new Blob([fileContent], {\n                type:\n                    \"application/octet-stream\"\n            });\n            var link = document.createElement(\"a\");\n            document.body.appendChild(link);\n            link.download = fileNameHint;\n            link.href = window.URL.createObjectURL(fileblob);\n            link.style = \"display:none\";\n            link.click();\n            document.body.removeChild(link);\n        },\n        contentPointer, contentLength, fileNameHint);\n}\n} // namespace\n\n/*!\n    \\brief Read local file via file dialog.\n\n    Call this function to make the browser display an open-file dialog. This\n   function returns immediately, and \\a fileDataReady is called when the user\n   has selected a file and the file contents has been read.\n\n    \\a The accept argument specifies which file types to accept, and must follow\n   the <input type=\"file\"> html standard formatting, for example \".png, .jpg,\n   .jpeg\".\n\n    This function is implemented on Qt for WebAssembly only. A nonfunctional\n   cross- platform stub is provided so that code that uses it can compile on all\n   platforms.\n*/\nvoid QHtml5File::load(\n    const QString &accept,\n    std::function<void(const QByteArray &, const QString &)> fileDataReady) {\n    loadFile(accept.toUtf8().constData(), [=](char *content, size_t size, const char *fileName) {\n        // Copy file data into QByteArray and free buffer that was allocated\n        // on the JavaScript side. We could have used\n        // QByteArray::fromRawData() to avoid the copy here, but that would\n        // make memory management awkward.\n        QByteArray qtFileContent(content, size);\n        free(content);\n\n        // Call user-supplied data ready callback\n        fileDataReady(qtFileContent, QString::fromUtf8(fileName));\n    });\n}\n\n/*!\n    \\brief Write local file via browser download\n\n    Call this function to make the browser start a file download. The file\n    will contains the given \\a content, with a suggested \\a fileNameHint.\n\n    This function is implemented on Qt for WebAssembly only. A nonfunctional\n   cross- platform stub is provided so that code that uses it can compile on all\n   platforms.\n*/\nvoid QHtml5File::save(const QByteArray &content, const QString &fileNameHint) {\n    // Convert to C types and save\n    saveFile(content.constData(), content.size(), fileNameHint.toUtf8().constData());\n}\n"
  },
  {
    "path": "src/gui/resources/icons/icons.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/icons/\">\n        <file>gui.png</file>\n        <file>application-exit.png</file>\n        <file>reload.png</file>\n        <file>document-import.png</file>\n        <file>finish.png</file>\n        <file>forward.png</file>\n        <file>next.png</file>\n        <file>pause.png</file>\n        <file>refresh.png</file>\n        <file>play.png</file>\n        <file>stop.png</file>\n        <file>new.png</file>\n        <file>open.png</file>\n        <file>save.png</file>\n        <file>closetab.png</file>\n        <file>compfile-256.png</file>\n        <file>build-256.png</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "src/gui/resources/samples/samples.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/samples/\">\n        <file>simple-lw-sw-ia.S</file>\n        <file>template.S</file>\n        <file>template-os.S</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "src/gui/resources/samples/simple-lw-sw-ia.S",
    "content": "//  Template file with simple memory example\n//  QtRVSim simulator https://github.com/cvut/qtrvsim/\n//\n//  template-os.S       - example file\n//\n//  (C) 2021 by Pavel Pisa\n//      e-mail:   pisa@cmp.felk.cvut.cz\n//      homepage: http://cmp.felk.cvut.cz/~pisa\n//      work:     http://www.pikron.com/\n//      license:  public domain\n\n// Directives to make interesting windows visible\n#pragma qtrvsim show registers\n#pragma qtrvsim show memory\n\n.globl _start\n.option norelax\n\n.text\n\n_start:\nloop:\n\t// load the word from absolute address\n\tlw     x2, 0x400(x0)\n\t// store the word to absolute address\n\tsw     x2, 0x404(x0)\n\n\t// stop execution wait for debugger/user\n\t// ebreak\n\t// ensure that continuation does not\n\t// interpret random data\n\tbeq    x0, x0, loop\n\tnop\n\tnop\n\tebreak\n\n.data\n.org 0x400\n\nsrc_val:\n\t.word  0x12345678\ndst_val:\n\t.word  0\n\n// Specify location to show in memory window\n#pragma qtrvsim focus memory src_val\n"
  },
  {
    "path": "src/gui/resources/samples/template-os.S",
    "content": "//  Template file with defines of system calls\n//  QtRVSim simulator https://github.com/cvut/qtrvsim/\n//\n//  template-os.S       - example file\n//\n//  (C) 2021 by Pavel Pisa\n//      e-mail:   pisa@cmp.felk.cvut.cz\n//      homepage: http://cmp.felk.cvut.cz/~pisa\n//      work:     http://www.pikron.com/\n//      license:  public domain\n\n// Directives to make interesting windows visible\n#pragma qtrvsim show terminal\n#pragma qtrvsim show registers\n#pragma qtrvsim show csrdock\n#pragma qtrvsim show memory\n\n.globl _start\n.globl __start\n.option norelax\n\n// Linux kernel compatible system calls subset\n\n.equ __NR_exit,        93  // void exit(int status)\n.equ __NR_read,        63  // ssize_t read(int fd, void *buf, size_t count)\n.equ __NR_write,       64  // ssize_t write(int fd, const void *buf, size_t count)\n.equ __NR_close,       57  // int close(int fd)\n.equ __NR_openat,      56  // int openat(int fd, const char *pathname, int flags, mode_t mode)\n\t// use fd = -100 for normal open behaviour. Full openat not supported.\n.equ __NR_brk,         214 // void * brk(void *addr)\n.equ __NR_ftruncate64, 46  // int ftruncate64(int fd, off_t length)\n.equ __NR_readv,       65  // ssize_t readv(int fd, const struct iovec *iov, int iovcnt)\n.equ __NR_writev,      66  // ssize_t writev(int fd, const struct iovec *iov, int iovcnt)\n\n.text\n\n__start:\n_start:\n\taddi  a7, zero, __NR_write        // load syscall number\n\taddi  a0, zero, 1                 // load file descriptor\n\tla    a1, text_1                  // load text start address\n\tla    a2, text_1_e                // load text end address\n\tsub   a2, a2, a1                  // compute text length\n\tecall                             // print the text\n\n\taddi  a7, zero, __NR_exit         // load syscall numver\n\taddi  a0, zero, 0                 // load status argument\n\tecall                             // exit\n\nfinal:\n\tebreak                            // request developer interaction\n\tjal   zero, final\n\n.data\n.org 0x400\n\ndata_1:\t.word\t1, 2, 3, 4\n\ntext_1:\t.ascii\t\"Hello world.\\n\"\t// store ASCII text, no termination\ntext_1_e:\n\n// The sample can be compiled by full-featured riscv64-unknown-elf GNU tool-chain\n// for RV32IMA use\n// riscv64-unknown-elf-gcc -c -march=rv64ima -mabi=lp64 template-os.S\n// riscv64-unknown-elf-gcc -march=rv64ima -mabi=lp64 -nostartfiles -nostdlib template-os.o\n// for RV64IMA use\n// riscv64-unknown-elf-gcc -c -march=rv32ima -mabi=ilp32 template-os.S\n// riscv64-unknown-elf-gcc -march=rv32ima -mabi=ilp32 -nostartfiles -nostdlib template-os.o\n// add \"-o template-os\" to change default \"a.out\" output file name\n"
  },
  {
    "path": "src/gui/resources/samples/template.S",
    "content": "//  Template file with defines of peripheral registers\n//  QtRVSim simulator https://github.com/cvut/qtrvsim/\n//\n//  template.S       - example file\n//\n//  (C) 2021-2024 by Pavel Pisa\n//      e-mail:   pisa@cmp.felk.cvut.cz\n//      homepage: http://cmp.felk.cvut.cz/~pisa\n//      work:     http://www.pikron.com/\n//      license:  public domain\n\n// Directives to make interesting windows visible\n#pragma qtrvsim show terminal\n#pragma qtrvsim show registers\n#pragma qtrvsim show memory\n\n.globl _start\n.globl __start\n.option norelax\n\n// Serial port/terminal registers\n// There is mirror of this region at address 0xffff0000\n// to match QtSpim and Mars emulators\n\n.equ SERIAL_PORT_BASE,      0xffffc000 // base address of serial port region\n\n.equ SERP_RX_ST_REG,        0xffffc000 // Receiver status register\n.equ SERP_RX_ST_REG_o,          0x0000 // Offset of RX_ST_REG\n.equ SERP_RX_ST_REG_READY_m,       0x1 // Data byte is ready to be read\n.equ SERP_RX_ST_REG_IE_m,          0x2 // Enable Rx ready interrupt\n\n.equ SERP_RX_DATA_REG,      0xffffc004 // Received data byte in 8 LSB bits\n.equ SERP_RX_DATA_REG_o,        0x0004 // Offset of RX_DATA_REG\n\n.equ SERP_TX_ST_REG,        0xffffc008 // Transmitter status register\n.equ SERP_TX_ST_REG_o,          0x0008 // Offset of TX_ST_REG\n.equ SERP_TX_ST_REG_READY_m,       0x1 // Transmitter can accept next byte\n.equ SERP_TX_ST_REG_IE_m,          0x2 // Enable Tx ready interrupt\n\n.equ SERP_TX_DATA_REG,      0xffffc00c // Write word to send 8 LSB bits to terminal\n.equ SERP_TX_DATA_REG_o,        0x000c // Offset of TX_DATA_REG\n\n// Memory mapped peripheral for dial knobs input,\n// LED and RGB LEDs output designed to match\n// MZ_APO education Zynq based board developed\n// by Petr Porazil and Pavel Pisa at PiKRON.com company\n\n.equ SPILED_REG_BASE,       0xffffc100 // base of SPILED port region\n\n.equ SPILED_REG_LED_LINE,   0xffffc104 // 32 bit word mapped as output\n.equ SPILED_REG_LED_LINE_o,     0x0004 // Offset of the LED_LINE\n.equ SPILED_REG_LED_RGB1,   0xffffc110 // RGB LED 1 color components\n.equ SPILED_REG_LED_RGB1_o,     0x0010 // Offset of LED_RGB1\n.equ SPILED_REG_LED_RGB2,   0xffffc114 // RGB LED 2 color components\n.equ SPILED_REG_LED_RGB2_o,     0x0014 // Offset of LED_RGB2\n.equ SPILED_REG_KNOBS_8BIT, 0xffffc124 // Three 8 bit knob values\n.equ SPILED_REG_KNOBS_8BIT_o,   0x0024 // Offset of KNOBS_8BIT\n\n// The simple 16-bit per pixel (RGB565) frame-buffer\n// display size is 480 x 320 pixel\n// Pixel format RGB565 expect\n//   bits 11 .. 15 red component\n//   bits  5 .. 10 green component\n//   bits  0 ..  4 blue component\n.equ LCD_FB_START,          0xffe00000\n.equ LCD_FB_END,            0xffe4afff\n\n// RISC-V ACLINT MSWI and MTIMER memory mapped peripherals\n.equ ACLINT_MSWI,           0xfffd0000 // core 0 SW interrupt request\n.equ ACLINT_MTIMECMP,       0xfffd4000 // core 0 compare value\n.equ ACLINT_MTIME,          0xfffdbff8 // timer base 10 MHz\n\n// Mapping of interrupts\n// mcause      mie / mip\n// irq number    bit       Source\n//   3            3        ACLINT MSWI\n//   7            7        MTIME reached value of MTIMECMP\n//  16           16        There is received character ready to be read\n//  17           17        Serial port ready to accept character to Tx\n\n// Start address after reset\n.org 0x00000200\n\n.text\n\n__start:\n_start:\n\nloop:\n    li   a0, SERIAL_PORT_BASE           // load base address of serial port\n    la   a1, text_1                     // load address of text\n\nnext_char:\n    lb   t1, 0(a1)                      // load one byte after another\n    beq  t1, zero, end_char             // is this the terminal zero byte\n    addi a1, a1, 1                      // move pointer to next text byte\ntx_busy:\n    lw   t0, SERP_TX_ST_REG_o(a0)       // read status of transmitter\n    andi t0, t0, SERP_TX_ST_REG_READY_m // mask ready bit\n    beq  t0, zero, tx_busy              // if not ready wait for ready condition\n    sw   t1, SERP_TX_DATA_REG_o(a0)     // write byte to Tx data register\n    jal  zero, next_char                // unconditional branch to process next byte\n\nend_char:\n    ebreak // stop continuous execution, request developer interaction\n    jal  zero, end_char\n\n.org 0x400\n.data\n\ndata_1:\t.word\t1, 2, 3, 4\t// example how to fill data words\n\ntext_1: .asciz  \"Hello world.\\n\"    // store zero terminated ASCII text\n\n// if whole source compile is OK the switch to core tab\n#pragma qtrvsim tab core\n\n// The sample can be compiled by full-featured riscv64-unknown-elf GNU tool-chain\n// for RV32IMA use\n// riscv64-unknown-elf-gcc -c -march=rv64ima -mabi=lp64 template.S\n// riscv64-unknown-elf-gcc -march=rv64ima -mabi=lp64 -nostartfiles -nostdlib template.o\n// for RV64IMA use\n// riscv64-unknown-elf-gcc -c -march=rv32ima -mabi=ilp32 template.S\n// riscv64-unknown-elf-gcc -march=rv32ima -mabi=ilp32 -nostartfiles -nostdlib template.o\n// add \"-o template\" to change default \"a.out\" output file name\n"
  },
  {
    "path": "src/gui/statictable.cpp",
    "content": "#include \"statictable.h\"\n\n#include \"machine/simulator_exception.h\"\n\n#include <QPainter>\n\nStaticTableLayout::StaticTableLayout(\n    QWidget *parent,\n    int margin,\n    int horizontal_big_spacing,\n    int horizontal_small_spacing,\n    int vertical_spacing)\n    : QLayout(parent) {\n    setContentsMargins(margin, margin, margin, margin);\n    bhspace = horizontal_big_spacing;\n    shspace = horizontal_small_spacing;\n    vspace = vertical_spacing;\n    setSizeConstraint(QLayout::SetMinAndMaxSize);\n\n    cch_do_layout.size = QSize(0, 0);\n    cch_do_layout.count = 0;\n    cch_heightForWidth.w = 0;\n    cch_heightForWidth.count = 0;\n    cch_heightForWidth.width = 0;\n    cch_minSize.count = 0;\n    cch_minSize.size = QSize(0, 0);\n}\n\nStaticTableLayout::~StaticTableLayout() {\n    for (auto &row : items) {\n        for (auto &col : row)\n            delete col;\n    }\n}\n\nQt::Orientations StaticTableLayout::expandingDirections() const {\n    return Qt::Horizontal;\n}\n\nbool StaticTableLayout::hasHeightForWidth() const {\n    return true;\n}\n\nint StaticTableLayout::heightForWidth(int w) const {\n    if (cch_heightForWidth.w != w || cch_heightForWidth.count != items.count())\n        cch_heightForWidth.width = layout_height(w);\n    cch_heightForWidth.w = w;\n    cch_heightForWidth.count = items.count();\n    return cch_heightForWidth.width;\n}\n\nQSize StaticTableLayout::minimumSize() const {\n    if (cch_minSize.count == items.size()) return cch_minSize.size;\n    cch_minSize.count = items.size();\n\n    cch_minSize.size = QSize();\n    for (const auto &item : items) {\n        QSize ss;\n        for (auto layout_item : item) {\n            ss = cch_minSize.size.expandedTo(layout_item->minimumSize() + QSize(shspace, 0));\n        }\n        cch_minSize.size = cch_minSize.size.expandedTo(ss - QSize(shspace, 0));\n    }\n\n    int left, top, right, bottom;\n    getContentsMargins(&left, &top, &right, &bottom);\n    cch_minSize.size += QSize(left + right, top + bottom);\n\n    return cch_minSize.size;\n}\n\nvoid StaticTableLayout::setGeometry(const QRect &rect) {\n    QLayout::setGeometry(rect);\n    do_layout(rect);\n}\n\nQSize StaticTableLayout::sizeHint() const {\n    return minimumSize();\n}\n\nvoid StaticTableLayout::addItem(QLayoutItem *item __attribute__((unused))) {\n    // Just implement it but it does nothing\n}\n\nQLayoutItem *StaticTableLayout::itemAt(int index __attribute__((unused))) const {\n    return nullptr; // This is just dummy implementation to satisfy\n                    // reimplementation\n}\n\nQLayoutItem *StaticTableLayout::takeAt(int index __attribute__((unused))) {\n    return nullptr; // This is just dummy implementation to satisfy\n                    // reimplementation\n}\n\nint StaticTableLayout::count() const {\n    return items.size();\n}\n\nvoid StaticTableLayout::addRow(const QList<QWidget *> &w) {\n    items.append(list2vec(w));\n}\n\nvoid StaticTableLayout::insertRow(const QList<QWidget *> &w, int i) {\n    items.insert(i, list2vec(w));\n}\n\nvoid StaticTableLayout::removeRow(int i) {\n    for (auto &item : items[i]) {\n        delete item->widget();\n        delete item;\n    }\n    items.remove(i);\n}\n\nvoid StaticTableLayout::clearRows() {\n    for (auto &item : items) {\n        for (auto &layout_item : item) {\n            delete layout_item->widget();\n            delete layout_item;\n        }\n    }\n    items.clear();\n}\n\nvoid StaticTableLayout::itemRect(QRect &rect, QVector<int> &separators, int i) {\n    separators.clear();\n\n    int row = i / columns();\n    int col = i % columns();\n\n    int left, top, right, bottom;\n    getContentsMargins(&left, &top, &right, &bottom);\n\n    int x = left;\n\n    for (int s = 0; s < col; s++) {\n        for (int row_width : row_widths[s]) {\n            x += row_width + shspace;\n        }\n        x += bhspace - shspace;\n    }\n    if (col > 0) { // otherwise we are on the left edge and there was no previous\n                   // column so no big space\n        x -= bhspace / 2;\n    }\n    int y = top + (row * (row_height + vspace));\n\n    int width = 0;\n    for (int row_width : row_widths[col]) {\n        width += row_width + shspace;\n        separators.append(width - shspace / 2);\n    }\n    if (col <= 0) { width -= bhspace / 2; }\n\n    rect = QRect(x, y - vspace / 2, width - shspace + bhspace, row_height + vspace);\n    separators.removeLast(); // drop the last separator as that one we don't want to\n                             // see\n}\n\nint StaticTableLayout::columns() {\n    return row_widths.size();\n}\n\nint StaticTableLayout::real_row_height() const {\n    return row_height + vspace;\n}\n\nint StaticTableLayout::layout_count_approx(const QRect &rect) const {\n    if (items.empty() || rect.width() < rect.height()) return 1;\n    // Note: for some reason (probably optimisation) when qlabel is not\n    //  visible, it reports zero size. So we have to find at least one that is\n    // visible !items[vis][0]->widget()->isVisible()\n    int vis = 0;\n    while (items[vis].empty() || items[vis][0]->widget()->sizeHint().width() == 0) {\n        vis++;\n        if (vis >= items.size())\n            return 1; // If none is visible, then just say that it has to be a single column\n    }\n\n    int w = 0;\n    for (auto item : items[vis])\n        w += item->sizeHint().width() + shspace;\n    w -= shspace;                  // subtract the latest spacing\n    int width = rect.right() / w;  // Note: this always rounds down so this\n                                   // always founds maximal possible count\n    return width <= 0 ? 1 : width; // We have to fit at least one column\n}\n\nint StaticTableLayout::layout_size(int &row_h, QList<QList<int>> &row_w, int count) const {\n    row_h = 0;\n    row_w.clear();\n    int col = 0;\n    for (const auto &item : items) {\n        if (row_w.size() <= col) row_w.append(QList<int>());\n        for (int y = 0; y < item.size(); y++) {\n            QSize s = item[y]->sizeHint();\n            row_h = qMax(row_h, s.height());\n            if (row_w[col].size() <= y) row_w[col].append(0);\n            row_w[col][y] = qMax(row_w[col][y], s.width());\n        }\n        if (++col >= count) col = 0;\n    }\n    SANITY_ASSERT(row_w.size() <= count, \"We should end up with maximum of count columns\");\n\n    int w = 0;\n    for (auto &i : row_w) {\n        for (int y : i) {\n            w += y + shspace;\n        }\n        w += bhspace - shspace; // subtract latest small spacing and add big\n                                // spacing\n    }\n    w -= bhspace; // subtract latest big spacing\n    return w;\n}\n\nvoid StaticTableLayout::layout_parms(QRect &rect, int &row_h, QList<QList<int>> &row_w, int &count)\n    const {\n    int left, top, right, bottom;\n    getContentsMargins(&left, &top, &right, &bottom);\n    rect = rect.adjusted(left, top, -right, -bottom);\n\n    // Firt let's do orientation count only on the first line\n    count = layout_count_approx(rect);\n    while (layout_size(row_h, row_w, count) > rect.right() && count > 1) {\n        // Not using orientation count go down if we can't fit (if we can then\n        // just be happy with what we have)\n        count--;\n    }\n}\n\nvoid StaticTableLayout::do_layout(const QRect &rect) {\n    if (cch_do_layout.size == rect.size() && cch_do_layout.count == items.size())\n        // No effective change so don't do layout\n        return;\n    cch_do_layout.size = rect.size();\n    cch_do_layout.count = items.size();\n\n    int row_h;\n    QList<QList<int>> row_w;\n    int count;\n\n    QRect reff(rect);\n    layout_parms(reff, row_h, row_w, count);\n\n    int col = 0;\n    int x = reff.x(), y = reff.y();\n    for (auto &item : items) {\n        for (int ii = 0; ii < item.size(); ii++) {\n            item[ii]->setGeometry(QRect(x, y, row_w[col][ii], row_h));\n            x += row_w[col][ii] + shspace;\n        }\n        x += bhspace - shspace;\n        if (++col >= count) {\n            col = 0;\n            x = reff.x();\n            y += row_h + vspace;\n        }\n    }\n    row_height = row_h;\n    row_widths = row_w;\n}\n\nint StaticTableLayout::layout_height(int width) const {\n    QRect reff(0, 0, width, 0);\n    int row_h;\n    QList<QList<int>> row_w;\n    int count;\n    layout_parms(reff, row_h, row_w, count);\n\n    return (row_h + vspace) * ((items.size() + count - 1) / count);\n}\n\nQVector<QLayoutItem *> StaticTableLayout::list2vec(const QList<QWidget *> &w) {\n    QVector<QLayoutItem *> v;\n    for (auto &i : w) {\n        addChildWidget(i);\n        v.append(new QWidgetItem(i));\n    }\n    return v;\n}\n\nStaticTable::StaticTable(QWidget *parent) : QWidget(parent), layout(this) {\n    setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);\n}\n\nint StaticTable::count() {\n    return layout.count();\n}\n\nvoid StaticTable::addRow(const QList<QWidget *> &w) {\n    layout.addRow(w);\n}\n\nvoid StaticTable::insertRow(const QList<QWidget *> &w, int i) {\n    layout.insertRow(w, i);\n}\n\nvoid StaticTable::removeRow(int i) {\n    layout.removeRow(i);\n}\n\nvoid StaticTable::clearRows() {\n    layout.clearRows();\n}\n\nint StaticTable::columns() {\n    return qMax(layout.columns(), 1);\n}\n\nint StaticTable::row_size() {\n    return layout.real_row_height();\n}\n\nvoid StaticTable::paintEvent(QPaintEvent *) {\n    if (layout.columns() <= 0) // Don't paint unless we have at least one column\n        return;\n\n    QPainter p(this);\n    p.setPen(QPen(QColor(200, 200, 200)));\n\n    QRect rect;\n    QVector<int> separators;\n    for (int i = 0; i < layout.count(); i++) {\n        int row = i / layout.columns();\n        int col = i % layout.columns();\n\n        if ((col % 2) == (row % 2))\n            p.setBrush(QBrush(QColor(255, 255, 255)));\n        else\n            p.setBrush(QBrush(QColor(235, 235, 235)));\n\n        layout.itemRect(rect, separators, i);\n\n        int x = rect.left(); // just to store x\n        if (col <= 0)        // this is left most row\n            rect.setLeft(-2);\n        if (col >= (layout.columns() - 1)) rect.setRight(width());\n        if (row <= 0) rect.setTop(-2);\n\n        p.drawRect(rect);\n\n        for (int separator : separators) {\n            int sep_x = x + separator;\n            p.drawLine(sep_x, rect.top(), sep_x, rect.bottom());\n        }\n    }\n}\n"
  },
  {
    "path": "src/gui/statictable.h",
    "content": "#ifndef STATICTABLE_H\n#define STATICTABLE_H\n\n#include <QLayout>\n#include <QList>\n#include <QVector>\n#include <QWidget>\n\n/*\n * This implements new layout and widget in the same time.\n * The Basic idea is that we need some table view that can also fill in horizontal space. This\n * widget paints simple table underneath the widgets and lays them out in to them. It shows more\n * than one column when there is enough horizontal space.\n */\n\nclass StaticTableLayout : public QLayout {\npublic:\n    explicit StaticTableLayout(\n        QWidget *parent,\n        int margin = 4,\n        int horizontal_big_spacing = 4,\n        int horizontal_small_spacing = 8,\n        int vertical_spacing = 4);\n    ~StaticTableLayout() override;\n\n    Qt::Orientations expandingDirections() const override;\n    bool hasHeightForWidth() const override;\n    int heightForWidth(int) const override;\n    QSize minimumSize() const override;\n    void setGeometry(const QRect &rect) override;\n    QSize sizeHint() const override;\n\n    void addItem(QLayoutItem *item) override;\n    QLayoutItem *itemAt(int index) const override;\n    QLayoutItem *takeAt(int index) override;\n    int count() const override; // This returns number of item blocks\n\n    void addRow(const QList<QWidget *> &);           // This adds a row of widgets\n    void insertRow(const QList<QWidget *> &, int i); // Insert row to given position while shifting\n                                                     // all others up\n    void removeRow(int i);                           // Remove row\n    void clearRows();                                // Clear all rows from table\n\n    void itemRect(QRect &rect, QVector<int> &separators, int i); // This returns a single item\n                                                                 // rectangle (if expand_margin, and\n                                                                 // it's on edge also count in\n                                                                 // margin)\n    int columns();\n    int real_row_height() const;\n\nprotected:\n    int shspace, bhspace, vspace;\n    QVector<QVector<QLayoutItem *>> items;\n\n    int row_height {};\n    QList<QList<int>> row_widths;\n\n    int layout_count_approx(const QRect &rect) const;\n    int layout_size(int &row_h, QList<QList<int>> &row_w, int count) const;\n    void layout_parms(QRect &rect, int &row_h, QList<QList<int>> &row_w, int &count) const;\n    void do_layout(const QRect &rect);\n    int layout_height(int width) const;\n\n    QVector<QLayoutItem *> list2vec(const QList<QWidget *> &);\n\n    struct {\n        QSize size;\n        int count {};\n    } cch_do_layout;\n    mutable struct {\n        int w, count;\n        int width;\n    } cch_heightForWidth {};\n    mutable struct {\n        int count {};\n        QSize size;\n    } cch_minSize;\n};\n\nclass StaticTable : public QWidget {\npublic:\n    explicit StaticTable(QWidget *parent = nullptr);\n\n    int count();\n    void addRow(const QList<QWidget *> &);\n    void insertRow(const QList<QWidget *> &, int i);\n    void removeRow(int i);\n    void clearRows();\n\n    int columns();\n    int row_size(); // return real row size (height) including spacing\n\nprotected:\n    void paintEvent(QPaintEvent *) override;\n\n    StaticTableLayout layout;\n};\n\n#endif // STATICTABLE_H\n"
  },
  {
    "path": "src/gui/textsignalaction.cpp",
    "content": "#include \"textsignalaction.h\"\n\n#include <QApplication>\n#include <utility>\n\nTextSignalAction::TextSignalAction(QObject *parent) : Super(parent) {\n    connect(this, &TextSignalAction::triggered, this, &TextSignalAction::process_triggered);\n}\n\nTextSignalAction::TextSignalAction(const QString &text, QObject *parent)\n    : Super(text, parent)\n    , signal_text(text) {\n    connect(this, &TextSignalAction::triggered, this, &TextSignalAction::process_triggered);\n}\n\nTextSignalAction::TextSignalAction(const QString &text, QString signal_text, QObject *parent)\n    : Super(text, parent)\n    , signal_text(std::move(signal_text)) {\n    connect(this, &TextSignalAction::triggered, this, &TextSignalAction::process_triggered);\n}\n\nTextSignalAction::TextSignalAction(const QIcon &icon, const QString &text, QObject *parent)\n    : Super(icon, text, parent)\n    , signal_text(text) {\n    connect(this, &TextSignalAction::triggered, this, &TextSignalAction::process_triggered);\n}\n\nTextSignalAction::TextSignalAction(\n    const QIcon &icon,\n    const QString &text,\n    QString signal_text,\n    QObject *parent)\n    : Super(icon, text, parent)\n    , signal_text(std::move(signal_text)) {\n    connect(this, &TextSignalAction::triggered, this, &TextSignalAction::process_triggered);\n}\n\nvoid TextSignalAction::process_triggered(bool checked) {\n    (void)checked;\n    emit activated(signal_text);\n}\n"
  },
  {
    "path": "src/gui/textsignalaction.h",
    "content": "#ifndef TEXTSIGNALACTION_H\n#define TEXTSIGNALACTION_H\n\n#include <QAction>\n#include <QObject>\n\nclass TextSignalAction : public QAction {\n    Q_OBJECT\n\n    using Super = QAction;\n\npublic:\n    explicit TextSignalAction(QObject *parent = nullptr);\n    explicit TextSignalAction(const QString &text, QObject *parent = nullptr);\n    TextSignalAction(const QString &text, QString signal_text, QObject *parent = nullptr);\n    TextSignalAction(const QIcon &icon, const QString &text, QObject *parent = nullptr);\n    TextSignalAction(\n        const QIcon &icon,\n        const QString &text,\n        QString signal_text,\n        QObject *parent = nullptr);\nsignals:\n    void activated(QString signal_text);\n\nprotected slots:\n    void process_triggered(bool checked);\n\nprotected:\n    QString signal_text;\n};\n\n#endif // TEXTSIGNALACTION_H\n"
  },
  {
    "path": "src/gui/ui/hexlineedit.cpp",
    "content": "#include \"hexlineedit.h\"\n\nHexLineEdit::HexLineEdit(QWidget *parent, int digits, int base, const QString &prefix)\n    : Super(parent) {\n    this->base = base;\n    this->digits = digits;\n    this->prefix = prefix;\n    last_set = 0;\n    QChar dmask;\n    QString t = \"\";\n    QString mask = \"\";\n\n    for (int i = 0; i < prefix.count(); i++) {\n        mask += \"\\\\\" + QString(prefix.at(i));\n    }\n    switch (base) {\n    case 10:\n        mask += \"D\";\n        dmask = 'd';\n        break;\n    case 2:\n        mask += \"B\";\n        dmask = 'b';\n        break;\n    case 16:\n    case 0:\n    default:\n        mask += \"H\";\n        dmask = 'h';\n        break;\n    }\n    if (digits > 1) { t.fill(dmask, digits - 1); }\n\n    mask += t;\n\n    setInputMask(mask);\n\n    connect(this, &QLineEdit::editingFinished, this, &HexLineEdit::on_edit_finished);\n\n    set_value(0);\n}\n\nvoid HexLineEdit::set_value(uint32_t value) {\n    QString s, t = \"\";\n    last_set = value;\n    s = QString::number(value, base);\n    if (s.count() < digits) { t.fill('0', digits - s.count()); }\n    setText(prefix + t + s);\n}\n\nvoid HexLineEdit::on_edit_finished() {\n    bool ok;\n    uint32_t val;\n    val = text().toULong(&ok, 16);\n    if (!ok) {\n        set_value(last_set);\n        return;\n    }\n    last_set = val;\n    emit value_edit_finished(val);\n}\n"
  },
  {
    "path": "src/gui/ui/hexlineedit.h",
    "content": "#ifndef HEXLINEEDIT_H\n#define HEXLINEEDIT_H\n\n#include <QLineEdit>\n#include <QObject>\n\nclass HexLineEdit : public QLineEdit {\n    Q_OBJECT\n\n    using Super = QLineEdit;\n\npublic:\n    explicit HexLineEdit(\n        QWidget *parent = nullptr,\n        int digits = 8,\n        int base = 0,\n        const QString &prefix = \"0x\");\n\npublic slots:\n    void set_value(uint32_t value);\n\nsignals:\n    void value_edit_finished(uint32_t value);\n\nprivate slots:\n    void on_edit_finished();\n\nprivate:\n    int base;\n    int digits;\n    QString prefix;\n    uint32_t last_set;\n};\n\n#endif // HEXLINEEDIT_H\n"
  },
  {
    "path": "src/gui/ui/pow2spinbox.cpp",
    "content": "#include \"pow2spinbox.h\"\n\nPow2SpinBox::Pow2SpinBox(QWidget *parent) : QSpinBox(parent) {\n    setRange(1, 1024);\n    setValue(1);\n}\n\nQValidator::State Pow2SpinBox::validate(QString &input, int &pos) const {\n    Q_UNUSED(pos);\n\n    if (input.isEmpty()) return QValidator::Intermediate;\n\n    bool ok = false;\n    qint64 v = input.toLongLong(&ok);\n    if (!ok || v <= 0) return QValidator::Invalid;\n\n    if ((v & (v - 1)) == 0) return QValidator::Acceptable;\n\n    return QValidator::Intermediate;\n}\n\nint Pow2SpinBox::valueFromText(const QString &text) const {\n    return text.toInt();\n}\n\nQString Pow2SpinBox::textFromValue(int value) const {\n    return QString::number(value);\n}\n\nvoid Pow2SpinBox::stepBy(int steps) {\n    int v = value();\n    if (v < 1) v = 1;\n\n    auto isPow2 = [](int x) { return x > 0 && (x & (x - 1)) == 0; };\n\n    auto nextPow2 = [](int x) -> int {\n        if (x <= 1) return 1;\n        int p = 1;\n        while (p < x && (p << 1) > 0) {\n            p <<= 1;\n        }\n        return p;\n    };\n\n    auto prevPow2 = [](int x) -> int {\n        if (x <= 1) return 1;\n        int p = 1;\n        while ((p << 1) <= x) {\n            p <<= 1;\n        }\n        if (p > x) { p >>= 1; }\n        return p;\n    };\n\n    if (steps > 0) {\n        if (!isPow2(v)) {\n            v = nextPow2(v);\n        } else {\n            for (int i = 0; i < steps; ++i) {\n                if (v > (maximum() >> 1)) {\n                    v = maximum();\n                    break;\n                }\n                v <<= 1;\n            }\n        }\n    } else {\n        if (!isPow2(v)) {\n            v = prevPow2(v);\n        } else {\n            for (int i = 0; i < -steps; ++i) {\n                if (v <= 1) {\n                    v = 1;\n                    break;\n                }\n                v >>= 1;\n            }\n        }\n    }\n    setValue(qBound(minimum(), v, maximum()));\n}\n"
  },
  {
    "path": "src/gui/ui/pow2spinbox.h",
    "content": "#ifndef POW2SPINBOX_H\n#define POW2SPINBOX_H\n\n#include <QSpinBox>\n#include <QValidator>\n\nclass Pow2SpinBox : public QSpinBox {\n    Q_OBJECT\npublic:\n    explicit Pow2SpinBox(QWidget *parent = nullptr);\n\nprotected:\n    QValidator::State validate(QString &input, int &pos) const override;\n    int valueFromText(const QString &text) const override;\n    QString textFromValue(int value) const override;\n    void stepBy(int steps) override;\n};\n\n#endif // POW2SPINBOX_H\n"
  },
  {
    "path": "src/gui/widgets/hidingtabwidget.cpp",
    "content": "#include \"hidingtabwidget.h\"\n\nvoid HidingTabWidget::tabInserted(int index) {\n    QTabWidget::tabInserted(index);\n    if (count() == 1) {\n        show();\n        requestAddRemoveTab(this, true);\n    }\n    tabCountChanged();\n}\nvoid HidingTabWidget::tabRemoved(int index) {\n    QTabWidget::tabRemoved(index);\n    if (count() == 0) {\n        hide();\n        requestAddRemoveTab(this, false);\n    }\n    tabCountChanged();\n}\nvoid HidingTabWidget::addRemoveTabRequested(QWidget *tab, bool exists) {\n    if (!exists) {\n        removeTab(indexOf(tab));\n    } else {\n        addTab(tab, tab->windowTitle());\n    }\n}\n"
  },
  {
    "path": "src/gui/widgets/hidingtabwidget.h",
    "content": "#ifndef HIDINGTABWIDGET_H\n#define HIDINGTABWIDGET_H\n\n#include <QObject>\n#include <QTabWidget>\n\n/** A widget that hides itself when it has no tabs. */\nclass HidingTabWidget : public QTabWidget {\n    Q_OBJECT\n    using Super = QTabWidget;\n\npublic:\n    explicit HidingTabWidget(QWidget *parent = nullptr) : Super(parent) {};\n\n    void tabInserted(int index) override;\n    void tabRemoved(int index) override;\n\nsignals:\n    void requestAddRemoveTab(QWidget *tab, bool visible);\n\npublic slots:\n    void addRemoveTabRequested(QWidget *tab, bool exists);\n\nprotected:\n    virtual void tabCountChanged() {};\n};\n\n#endif // HIDINGTABWIDGET_H\n"
  },
  {
    "path": "src/gui/windows/cache/cachedock.cpp",
    "content": "#include \"cachedock.h\"\n\nCacheDock::CacheDock(QWidget *parent, const QString &type) : QDockWidget(parent) {\n    top_widget = new QWidget(this);\n    setWidget(top_widget);\n    layout_box = new QVBoxLayout(top_widget);\n\n    top_form = new QWidget(top_widget);\n    top_form->setVisible(false);\n    layout_box->addWidget(top_form);\n    layout_top_form = new QFormLayout(top_form);\n\n    l_hit = new QLabel(\"0\", top_form);\n    l_hit->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Hit:\", l_hit);\n    l_miss = new QLabel(\"0\", top_form);\n    l_miss->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Miss:\", l_miss);\n    l_m_reads = new QLabel(\"0\", top_form);\n    l_m_reads->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Memory reads:\", l_m_reads);\n    l_m_writes = new QLabel(\"0\", top_form);\n    l_m_writes->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Memory writes:\", l_m_writes);\n    l_stalled = new QLabel(\"0\", top_form);\n    l_stalled->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Memory stall cycles:\", l_stalled);\n    l_hit_rate = new QLabel(\"0.000%\", top_form);\n    l_hit_rate->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Hit rate:\", l_hit_rate);\n    l_speed = new QLabel(\"100%\", top_form);\n    l_speed->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Improved speed:\", l_speed);\n\n    graphicsview = new GraphicsView(top_widget);\n    graphicsview->setVisible(false);\n    layout_box->addWidget(graphicsview);\n    cachescene = nullptr;\n\n    no_cache = new QLabel(\"No \" + type + \" Cache configured\", top_widget);\n    layout_box->addWidget(no_cache);\n\n    setObjectName(type + \"Cache\");\n    setWindowTitle(type + \" Cache\");\n}\n\nvoid CacheDock::setup(const machine::Cache *cache, bool cache_after_cache) {\n    memory_reads = 0;\n    memory_writes = 0;\n    hit = 0;\n    miss = 0;\n    stalled = 0;\n    speed_improv = 0.0;\n    hit_rate = 0.0;\n\n    l_hit->setText(\"0\");\n    l_miss->setText(\"0\");\n    l_stalled->setText(\"0\");\n    l_m_reads->setText(\"0\");\n    l_m_writes->setText(\"0\");\n    l_hit_rate->setText(\"0.000%\");\n    l_speed->setText(\"100%\");\n    l_speed->setHidden(cache_after_cache);\n    if (cache != nullptr) {\n        connect(cache, &machine::Cache::hit_update, this, &CacheDock::hit_update);\n        connect(cache, &machine::Cache::miss_update, this, &CacheDock::miss_update);\n        connect(cache, &machine::Cache::memory_reads_update, this, &CacheDock::memory_reads_update);\n        connect(\n            cache, &machine::Cache::memory_writes_update, this, &CacheDock::memory_writes_update);\n        connect(cache, &machine::Cache::statistics_update, this, &CacheDock::statistics_update);\n    }\n    top_form->setVisible(cache != nullptr);\n    no_cache->setVisible(cache == nullptr || !cache->get_config().enabled());\n\n    delete cachescene;\n    cachescene = new CacheViewScene(cache);\n    graphicsview->setScene(cachescene);\n    graphicsview->setVisible(cache != nullptr && cache->get_config().enabled());\n}\n\nvoid CacheDock::paintEvent(QPaintEvent *event) {\n    l_stalled->setText(QString::number(stalled));\n    l_hit_rate->setText(QString::number(hit_rate, 'f', 3) + QString(\"%\"));\n    l_speed->setText(QString::number(speed_improv, 'f', 0) + QString(\"%\"));\n    l_hit->setText(QString::number(hit));\n    l_miss->setText(QString::number(miss));\n    l_m_reads->setText(QString::number(memory_reads));\n    l_m_writes->setText(QString::number(memory_writes));\n    QDockWidget::paintEvent(event);\n}\n\nvoid CacheDock::hit_update(unsigned val) {\n    hit = val;\n}\n\nvoid CacheDock::miss_update(unsigned val) {\n    miss = val;\n}\n\nvoid CacheDock::memory_reads_update(unsigned val) {\n    memory_reads = val;\n}\n\nvoid CacheDock::memory_writes_update(unsigned val) {\n    memory_writes = val;\n}\n\nvoid CacheDock::statistics_update(unsigned stalled_cycles, double speed_improv, double hit_rate) {\n    this->stalled = stalled_cycles;\n    this->hit = hit_rate;\n    this->speed_improv = speed_improv;\n}\n"
  },
  {
    "path": "src/gui/windows/cache/cachedock.h",
    "content": "#ifndef CACHEDOCK_H\n#define CACHEDOCK_H\n\n#include \"cacheview.h\"\n#include \"graphicsview.h\"\n#include \"machine/machine.h\"\n\n#include <QDockWidget>\n#include <QFormLayout>\n#include <QLabel>\n\nclass CacheDock : public QDockWidget {\n    Q_OBJECT\npublic:\n    CacheDock(QWidget *parent, const QString &type);\n\n    void setup(const machine::Cache *cache, bool cache_after_cache = false);\n\n    void paintEvent(QPaintEvent *event) override;\n\nprivate slots:\n    void hit_update(unsigned);\n    void miss_update(unsigned);\n    void memory_reads_update(unsigned val);\n    void memory_writes_update(unsigned val);\n    void statistics_update(unsigned stalled_cycles, double speed_improv, double hit_rate);\n\nprivate:\n    QVBoxLayout *layout_box;\n    QWidget *top_widget, *top_form;\n    QFormLayout *layout_top_form;\n    QLabel *l_hit, *l_miss, *l_stalled, *l_speed, *l_hit_rate;\n    QLabel *no_cache;\n    QLabel *l_m_reads, *l_m_writes;\n    GraphicsView *graphicsview;\n    CacheViewScene *cachescene;\n\n    // Statistics\n    unsigned memory_reads = 0;\n    unsigned memory_writes = 0;\n    unsigned hit = 0;\n    unsigned miss = 0;\n    unsigned stalled = 0;\n    double speed_improv = 0.0;\n    double hit_rate = 0.0;\n};\n\n#endif // CACHEDOCK_H\n"
  },
  {
    "path": "src/gui/windows/cache/cacheview.cpp",
    "content": "#include \"cacheview.h\"\n\n#include \"fontsize.h\"\n\n#include <QtAlgorithms>\n#include <cmath>\n\n//////////////////////\n#define ROW_HEIGHT 14\n#define VD_WIDTH   10\n#define DATA_WIDTH 72\n#define PENW       1\n#define LETTERW    7\n//////////////////////\n\n#include \"common/endian.h\"\n#include \"machine/memory/cache/cache.h\"\n\n#include <iostream>\nusing namespace std;\n\nstatic inline unsigned int bitsToRepresent(quint32 range_max_val) {\n    return 32 - qCountLeadingZeroBits(range_max_val);\n}\n\nCacheAddressBlock::CacheAddressBlock(const machine::Cache *cache, unsigned width) {\n    rows = cache->get_config().set_count();\n    columns = cache->get_config().block_size();\n    s_row = cache->get_config().set_count() > 1\n                ? bitsToRepresent(cache->get_config().set_count() - 1)\n                : 0;\n    this->width = width;\n    s_col = cache->get_config().block_size() > 1\n                ? bitsToRepresent(cache->get_config().block_size() - 1)\n                : 0;\n    s_tag = 30 - s_row - s_col; // 32 bits - 2 unused and then every bit used\n                                // for different index\n    this->width = width;\n\n    tag = 0;\n    row = 0;\n    col = 0;\n\n    connect(cache, &machine::Cache::cache_update, this, &CacheAddressBlock::cache_update);\n}\n\nQRectF CacheAddressBlock::boundingRect() const {\n    return { 0, 0, static_cast<qreal>(width), 40 };\n}\n\nvoid CacheAddressBlock::paint(\n    QPainter *painter,\n    const QStyleOptionGraphicsItem *option,\n    QWidget *widget) {\n    (void)option;\n    (void)widget;\n    QFont fnt;\n    fnt.setPointSize(7);\n    painter->setFont(fnt);\n\n    unsigned wpos = 5;\n    // Part used for tag (we expect that tag is always used)\n    unsigned wid = s_tag == 0 ? 0 : (((s_tag - 1) / 4) + 1);\n    unsigned tag_center = wpos + wid * LETTERW / 2 + 1;\n    QRectF rect(wpos, 16, wid * LETTERW + 2, ROW_HEIGHT);\n    painter->drawRect(rect);\n    painter->drawText(rect, Qt::AlignCenter, QString(\"%1\").arg(tag, wid, 16, QChar('0')));\n    wpos += wid * LETTERW + 2;\n    // Part used for the set\n    unsigned row_center = wpos;\n    if (s_row > 0) {\n        wid = s_row == 0 ? 0 : (((s_row - 1) / 4) + 1);\n        row_center += wid * LETTERW / 2 + 1;\n        rect = QRectF(wpos, 16, wid * LETTERW + 2, ROW_HEIGHT);\n        painter->drawRect(rect);\n        painter->drawText(rect, Qt::AlignCenter, QString(\"%1\").arg(row, wid, 16, QChar('0')));\n        wpos += wid * LETTERW + 2;\n    }\n    // Part used for block\n    unsigned col_center = wpos;\n    if (s_col > 0) {\n        wid = s_col == 0 ? 0 : (((s_col - 1) / 4) + 1);\n        col_center += wid * LETTERW / 2 + 1;\n        rect = QRectF(wpos, 16, wid * LETTERW + 2, ROW_HEIGHT);\n        painter->drawRect(rect);\n        painter->drawText(rect, Qt::AlignCenter, QString(\"%1\").arg(col, wid, 16, QChar('0')));\n        wpos += wid * LETTERW + 2;\n    }\n    // Part used for two lowers bits\n    painter->setBrush(QBrush(QColor(Qt::gray)));\n    painter->drawRect(wpos, 16, LETTERW + 2, ROW_HEIGHT);\n    painter->setBrush(QBrush(QColor(Qt::black)));\n    wpos += LETTERW + 2;\n\n    // Pain address label\n    painter->drawText(QRectF(5, 0, wpos - 5, 14), Qt::AlignCenter, \"Address\");\n    uint32_t addr = (((tag * rows) + row) * columns + col) * 4;\n    painter->drawText(\n        QRectF(50, 0, wpos + 40, 14), Qt::AlignCenter,\n        \"0x\" + QString(\"%1\").arg(addr, 8, 16, QChar('0')));\n\n    QPen p;\n    p.setWidth(2);\n    painter->setPen(p);\n\n    // Tag line\n    painter->drawLine(-8, 40, -8, 33);\n    painter->drawLine(-8, 33, tag_center, 33);\n    painter->drawLine(tag_center, 33, tag_center, 30);\n\n    // set line\n    if (s_row > 0) {\n        painter->drawLine(-4, 40, row_center, 40);\n        painter->drawLine(row_center, 40, row_center, 30);\n    }\n\n    // block line\n    if (s_col > 0) {\n        painter->drawLine(width - 16, 40, col_center, 40);\n        painter->drawLine(col_center, 40, col_center, 30);\n    }\n}\n\nvoid CacheAddressBlock::cache_update(\n    unsigned associat,\n    unsigned set,\n    unsigned col,\n    bool valid,\n    bool dirty,\n    uint32_t tag,\n    const uint32_t *data,\n    bool write) {\n    (void)associat;\n    (void)valid;\n    (void)dirty;\n    (void)data;\n    (void)write;\n\n    this->tag = tag;\n    this->row = set;\n    this->col = col;\n    update();\n}\n\nCacheViewBlock::CacheViewBlock(const machine::Cache *cache, unsigned block, bool last)\n    : QGraphicsObject(nullptr)\n    , simulated_machine_endian(cache->simulated_machine_endian) {\n    islast = last;\n    this->block = block;\n    rows = cache->get_config().set_count();\n    columns = cache->get_config().block_size();\n    curr_row = 0;\n    last_set = 0;\n    last_col = 0;\n    last_highlighted = false;\n\n    QFont font;\n    font.setPixelSize(FontSize::SIZE7);\n\n    validity = new QGraphicsSimpleTextItem *[rows];\n    if (cache->get_config().write_policy() == machine::CacheConfig::WP_BACK) {\n        dirty = new QGraphicsSimpleTextItem *[rows];\n    } else {\n        dirty = nullptr;\n    }\n    tag = new QGraphicsSimpleTextItem *[rows];\n    data = new QGraphicsSimpleTextItem **[rows];\n    int row_y = 1;\n    for (unsigned i = 0; i < rows; i++) {\n        int row_x = 2;\n        validity[i] = new QGraphicsSimpleTextItem(\"0\", this);\n        validity[i]->setPos(row_x, row_y);\n        validity[i]->setFont(font);\n        row_x += VD_WIDTH;\n        if (dirty) {\n            dirty[i] = new QGraphicsSimpleTextItem(this);\n            dirty[i]->setPos(row_x, row_y);\n            dirty[i]->setFont(font);\n            row_x += VD_WIDTH;\n        }\n        tag[i] = new QGraphicsSimpleTextItem(this);\n        tag[i]->setPos(row_x, row_y);\n        tag[i]->setFont(font);\n        row_x += DATA_WIDTH;\n\n        data[i] = new QGraphicsSimpleTextItem *[columns];\n        for (unsigned y = 0; y < columns; y++) {\n            data[i][y] = new QGraphicsSimpleTextItem(this);\n            data[i][y]->setPos(row_x, row_y);\n            data[i][y]->setFont(font);\n            row_x += DATA_WIDTH;\n        }\n\n        row_y += ROW_HEIGHT;\n    }\n\n    unsigned wd = 1;\n    auto *l_validity = new QGraphicsSimpleTextItem(\"V\", this);\n    l_validity->setFont(font);\n    QRectF box = l_validity->boundingRect();\n    l_validity->setPos(wd + (VD_WIDTH - box.width()) / 2, -1 - box.height());\n    wd += VD_WIDTH;\n    if (cache->get_config().write_policy() == machine::CacheConfig::WP_BACK) {\n        auto *l_dirty = new QGraphicsSimpleTextItem(\"D\", this);\n        l_dirty->setFont(font);\n        box = l_dirty->boundingRect();\n        l_dirty->setPos(wd + (VD_WIDTH - box.width()) / 2, -1 - box.height());\n        wd += VD_WIDTH;\n    }\n    auto *l_tag = new QGraphicsSimpleTextItem(\"Tag\", this);\n    l_tag->setFont(font);\n    box = l_tag->boundingRect();\n    l_tag->setPos(wd + (DATA_WIDTH - box.width()) / 2, -1 - box.height());\n    wd += DATA_WIDTH;\n    auto *l_data = new QGraphicsSimpleTextItem(\"Data\", this);\n    l_data->setFont(font);\n    box = l_data->boundingRect();\n    l_data->setPos(wd + (columns * DATA_WIDTH - box.width()) / 2, -1 - box.height());\n\n    connect(cache, &machine::Cache::cache_update, this, &CacheViewBlock::cache_update);\n}\n\nCacheViewBlock::~CacheViewBlock() {\n    delete[] validity;\n    delete[] dirty;\n    delete[] tag;\n    for (unsigned y = 0; y < rows; y++) {\n        delete[] data[y];\n    }\n    delete[] data;\n}\n\nQRectF CacheViewBlock::boundingRect() const {\n    return QRectF(\n        -PENW / 2 - 11, -PENW / 2 - 16,\n        VD_WIDTH + (dirty ? VD_WIDTH : 0) + DATA_WIDTH * (columns + 1) + PENW + 12\n            + (columns > 1 ? 7 : 0),\n        ROW_HEIGHT * rows + PENW + 50);\n}\n\nvoid CacheViewBlock::paint(\n    QPainter *painter,\n    const QStyleOptionGraphicsItem *option __attribute__((unused)),\n    QWidget *widget __attribute__((unused))) {\n    // Draw horizontal lines\n    for (unsigned i = 0; i <= rows; i++) {\n        painter->drawLine(\n            0, i * ROW_HEIGHT, VD_WIDTH + (dirty ? VD_WIDTH : 0) + DATA_WIDTH * (columns + 1),\n            i * ROW_HEIGHT);\n    }\n    // Draw vertical lines\n    painter->drawLine(0, 0, 0, rows * ROW_HEIGHT);\n    int c_width = VD_WIDTH;\n    painter->drawLine(c_width, 0, c_width, rows * ROW_HEIGHT);\n    if (dirty) {\n        c_width += VD_WIDTH;\n        painter->drawLine(c_width, 0, c_width, rows * ROW_HEIGHT);\n    }\n    c_width += DATA_WIDTH;\n    painter->drawLine(c_width, 0, c_width, rows * ROW_HEIGHT);\n    for (unsigned i = 0; i < columns; i++) {\n        c_width += DATA_WIDTH;\n        painter->drawLine(c_width, 0, c_width, rows * ROW_HEIGHT);\n    }\n\n    QPen p_wide, p;\n    p.setWidth(1);\n    p_wide.setWidth(2);\n\n    painter->setPen(p);\n\n    // Tag compare\n    unsigned allright = (dirty ? 2 : 1) * VD_WIDTH + DATA_WIDTH * (columns + 1);\n    unsigned bottom = ROW_HEIGHT * rows;\n    unsigned tag_center = (dirty ? 2 : 1) * VD_WIDTH + DATA_WIDTH / 2;\n    painter->drawEllipse(QPointF(tag_center, bottom + 15), 5, 5);\n    painter->drawText(QRectF(tag_center - 5, bottom + 9.5, 10, 10), Qt::AlignCenter, \"=\");\n    painter->setPen(p_wide);\n    painter->drawLine(tag_center, bottom, tag_center, bottom + 10);\n    painter->setPen(p);\n\n    // And\n    painter->drawLine(tag_center + 10, bottom + 25, tag_center + 10, bottom + 35);\n    painter->drawLine(tag_center + 10, bottom + 25, tag_center + 15, bottom + 25);\n    painter->drawLine(tag_center + 10, bottom + 35, tag_center + 15, bottom + 35);\n    painter->drawArc(tag_center + 10, bottom + 25, 10, 10, 270 * 16, 180 * 16);\n\n    // Connection from and to right\n    painter->drawLine(tag_center + 20, bottom + 30, allright, bottom + 30);\n    // Connection from valid to and\n    painter->drawLine(VD_WIDTH / 2, bottom, VD_WIDTH / 2, bottom + 32);\n    painter->drawLine(VD_WIDTH / 2, bottom + 32, tag_center + 10, bottom + 32);\n    // Connection from tag comparison to and\n    painter->drawLine(tag_center, bottom + 20, tag_center, bottom + 28);\n    painter->drawLine(tag_center, bottom + 28, tag_center + 10, bottom + 28);\n\n    unsigned data_start = (dirty ? 2 : 1) * VD_WIDTH + DATA_WIDTH;\n    if (columns > 1) {\n        // Output mutex\n        const QPointF poly[] = { QPointF(data_start, bottom + 10),\n                                 QPointF(data_start + columns * DATA_WIDTH, bottom + 10),\n                                 QPointF(data_start + columns * DATA_WIDTH - 10, bottom + 20),\n                                 QPointF(data_start + 10, bottom + 20) };\n        painter->drawPolygon(poly, sizeof(poly) / sizeof(QPointF));\n        unsigned data_center = data_start + DATA_WIDTH * columns / 2;\n        painter->setPen(p_wide);\n        painter->drawLine(data_center, bottom + 20, data_center, bottom + 25);\n        painter->drawLine(data_center, bottom + 25, allright, bottom + 25);\n        for (unsigned i = 0; i < columns; i++) {\n            unsigned xpos = data_start + i * DATA_WIDTH + DATA_WIDTH / 2;\n            painter->drawLine(xpos, bottom, xpos, bottom + 10);\n        }\n\n        // Mutex source\n        painter->drawLine(allright + 5, -16, allright + 5, bottom + 15);\n        painter->drawLine(\n            allright + 5, bottom + 15, data_start + columns * DATA_WIDTH - 4, bottom + 15);\n        if (!islast) painter->drawLine(allright + 5, bottom + 15, allright + 5, bottom + 40);\n    } else {\n        // Wire with data to right\n        painter->setPen(p_wide);\n        painter->drawLine(\n            data_start + DATA_WIDTH / 2, bottom, data_start + DATA_WIDTH / 2, bottom + 25);\n        painter->drawLine(data_start + DATA_WIDTH / 2, bottom + 25, allright, bottom + 25);\n    }\n\n    // Connection with tag\n    painter->setPen(p_wide);\n    painter->drawLine(-9, -16, -9, bottom + 15);\n    painter->drawLine(-9, bottom + 15, tag_center - 5, bottom + 15);\n    if (!islast) { painter->drawLine(-9, bottom + 15, -9, bottom + 40); }\n\n    // Connection with row\n    if (rows > 1) {\n        unsigned selected = ROW_HEIGHT * curr_row + ROW_HEIGHT / 2;\n        painter->drawLine(-5, -16, -5, islast ? selected : bottom + 40);\n        painter->drawLine(-5, selected, 0, selected);\n    }\n}\n\nvoid CacheViewBlock::cache_update(\n    unsigned associat,\n    unsigned set,\n    unsigned col,\n    bool valid,\n    bool dirty,\n    uint32_t tag,\n    const uint32_t *data,\n    bool write) {\n    (void)col;\n    if (associat != block) {\n        if (last_highlighted) { this->data[last_set][last_col]->setBrush(QBrush(QColor(0, 0, 0))); }\n        last_highlighted = false;\n        return; // Ignore blocks that are not used\n    }\n    validity[set]->setText(valid ? \"1\" : \"0\");\n    if (this->dirty) { this->dirty[set]->setText(valid ? (dirty ? \"1\" : \"0\") : \"\"); }\n    // TODO calculate correct size of tag\n    this->tag[set]->setText(\n        valid ? QString(\"0x\") + QString(\"%1\").arg(tag, 8, 16, QChar('0')) : QString(\"\"));\n    for (unsigned i = 0; i < columns; i++) {\n        this->data[set][i]->setText(\n            valid ? QString(\"0x\")\n                        + QString(\"%1\").arg(\n                            byteswap_if(data[i], simulated_machine_endian != NATIVE_ENDIAN), 8, 16,\n                            QChar('0'))\n                  : QString(\"\"));\n        //  TODO Use cache API\n    }\n\n    if (last_highlighted) { this->data[last_set][last_col]->setBrush(QBrush(QColor(0, 0, 0))); }\n    if (write) {\n        this->data[set][col]->setBrush(QBrush(QColor(240, 0, 0)));\n    } else {\n        this->data[set][col]->setBrush(QBrush(QColor(0, 0, 240)));\n    }\n    last_highlighted = true;\n\n    curr_row = set;\n    last_set = set;\n    last_col = col;\n    update();\n}\n\nCacheViewScene::CacheViewScene(const machine::Cache *cache) {\n    associativity = cache->get_config().associativity();\n    block = new CacheViewBlock *[associativity];\n    int offset = 0;\n    for (unsigned i = 0; i < associativity; i++) {\n        block[i] = new CacheViewBlock(cache, i, i >= (associativity - 1));\n        addItem(block[i]);\n        block[i]->setPos(1, offset);\n        offset += block[i]->boundingRect().height();\n    }\n    ablock = new CacheAddressBlock(cache, block[0]->boundingRect().width());\n    addItem(ablock);\n    ablock->setPos(0, -ablock->boundingRect().height() - 16);\n}\n\nCacheViewScene::~CacheViewScene() {\n    delete[] block;\n}\n"
  },
  {
    "path": "src/gui/windows/cache/cacheview.h",
    "content": "#ifndef CACHEVIEW_H\n#define CACHEVIEW_H\n\n#include \"common/endian.h\"\n#include \"graphicsview.h\"\n#include \"machine/machine.h\"\n\n#include <QGraphicsObject>\n#include <QGraphicsScene>\n#include <QGraphicsView>\n\nclass CacheAddressBlock : public QGraphicsObject {\n    Q_OBJECT\npublic:\n    CacheAddressBlock(const machine::Cache *cache, unsigned width);\n\n    [[nodiscard]] QRectF boundingRect() const override;\n\n    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;\n\nprivate slots:\n    void cache_update(\n        unsigned associat,\n        unsigned set,\n        unsigned col,\n        bool valid,\n        bool dirty,\n        uint32_t tag,\n        const uint32_t *data,\n        bool write);\n\nprivate:\n    unsigned rows, columns;\n    unsigned tag, row, col;\n    unsigned s_tag, s_row, s_col;\n    unsigned width;\n};\n\nclass CacheViewBlock : public QGraphicsObject {\n    Q_OBJECT\npublic:\n    CacheViewBlock(const machine::Cache *cache, unsigned block, bool last);\n    ~CacheViewBlock() override;\n\n    [[nodiscard]] QRectF boundingRect() const override;\n\n    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;\n\nprivate slots:\n    virtual void cache_update(\n        unsigned associat,\n        unsigned set,\n        unsigned col,\n        bool valid,\n        bool dirty,\n        uint32_t tag,\n        const uint32_t *data,\n        bool write);\n\nprivate:\n    const Endian simulated_machine_endian;\n    bool islast;\n    unsigned block;\n    unsigned rows, columns;\n    QGraphicsSimpleTextItem **validity, **dirty, **tag, ***data;\n    unsigned curr_row;\n    bool last_highlighted;\n    unsigned last_set;\n    unsigned last_col;\n};\n\nclass CacheViewScene : public QGraphicsScene {\n    Q_OBJECT\npublic:\n    explicit CacheViewScene(const machine::Cache *cache);\n    ~CacheViewScene() override;\n\nprivate:\n    unsigned associativity;\n    CacheViewBlock **block;\n    CacheAddressBlock *ablock;\n};\n\n#endif // CACHEVIEW_H\n"
  },
  {
    "path": "src/gui/windows/coreview/components/cache.cpp",
    "content": "#include \"cache.h\"\n\nCache::Cache(\n    const machine::Cache *cache,\n    svgscene::SimpleTextItem *hit_text,\n    svgscene::SimpleTextItem *mis_text)\n    : QObject()\n    , hit_text(hit_text)\n    , mis_text(mis_text) {\n    connect(cache, &machine::Cache::hit_update, this, &Cache::hit_update);\n    connect(cache, &machine::Cache::miss_update, this, &Cache::miss_update);\n}\nvoid Cache::hit_update(unsigned value) {\n    hit_text->setText(QString::number(value));\n}\nvoid Cache::miss_update(unsigned int value) {\n    mis_text->setText(QString::number(value));\n}\n"
  },
  {
    "path": "src/gui/windows/coreview/components/cache.h",
    "content": "#ifndef QTRVSIM_CACHE_H\n#define QTRVSIM_CACHE_H\n\n#include <QObject>\n#include <memory/cache/cache.h>\n#include <svgscene/components/simpletextitem.h>\n#include <svgscene/utils/memory_ownership.h>\n\nclass Cache : public QObject {\npublic:\n    Cache(\n        const machine::Cache *cache,\n        svgscene::SimpleTextItem *hit_text,\n        svgscene::SimpleTextItem *mis_text);\n\nprotected slots:\n    void hit_update(unsigned value);\n    void miss_update(unsigned value);\n\nprotected:\n    BORROWED svgscene::SimpleTextItem *hit_text;\n    BORROWED svgscene::SimpleTextItem *mis_text;\n};\n\n#endif // QTRVSIM_CACHE_H\n"
  },
  {
    "path": "src/gui/windows/coreview/components/value_handlers.cpp",
    "content": "#include \"value_handlers.h\"\n\n#include <QGraphicsPathItem>\n\nusing svgscene::SimpleTextItem;\n\nconst QString BoolValue::COMPONENT_NAME = QStringLiteral(\"bool-value\");\nconst QString PCValue::COMPONENT_NAME = QStringLiteral(\"pc-value\");\nconst QString RegValue::COMPONENT_NAME = QStringLiteral(\"reg-value\");\nconst QString RegIdValue::COMPONENT_NAME = QStringLiteral(\"reg-id-value\");\nconst QString DebugValue::COMPONENT_NAME = QStringLiteral(\"debug-value\");\nconst QString MultiTextValue::COMPONENT_NAME = QStringLiteral(\"multi-text-value\");\nconst QString InstructionValue::COMPONENT_NAME = QStringLiteral(\"instruction-value\");\n\nBoolValue::BoolValue(SimpleTextItem *const element, const bool &data)\n    : element(element)\n    , data(data) {}\n\nvoid BoolValue::update() {\n    element->setText(data ? QStringLiteral(\"1\") : QStringLiteral(\"0\"));\n}\n\nPCValue::PCValue(SimpleTextItem *element, const machine::Address &data)\n    : element(element)\n    , data(data) {}\n\nPCValue::PCValue(const PCValue &other)\n    : QObject(other.parent())\n    , element(other.element)\n    , data(other.data) {}\n\nvoid PCValue::clicked() {\n    emit jump_to_pc(data);\n}\n\nvoid PCValue::update() {\n    element->setText(QString(\"0x%1\").arg(data.get_raw(), 8, 16, QChar('0')));\n}\n\nRegValue::RegValue(SimpleTextItem *element, const machine::RegisterValue &data)\n    : element(element)\n    , data(data) {}\n\nvoid RegValue::update() {\n    element->setText(QString(\"%1\").arg(data.as_u32(), 8, 16, QChar('0')));\n}\n\nRegIdValue::RegIdValue(svgscene::SimpleTextItem *element, const machine::RegisterId &data)\n    : element(element)\n    , data(data) {}\n\nvoid RegIdValue::update() {\n    element->setText(QString(\"%1\").arg(int(data), 2, 10, QChar('0')));\n}\n\nDebugValue::DebugValue(SimpleTextItem *element, const unsigned int &data)\n    : element(element)\n    , data(data) {}\n\nvoid DebugValue::update() {\n    element->setText(QString(\"%1\").arg(data, 0, 10, QChar(' ')));\n}\nMultiTextValue::MultiTextValue(SimpleTextItem *const element, Data data)\n    : element(element)\n    , current_text_index(data.first)\n    , text_table(data.second)\n    , originalBrush(element->brush()) {}\n\nvoid MultiTextValue::update() {\n    if (current_text_index != 0) {\n        // Highlight non-default value.\n        element->setBrush(Qt::red);\n    } else {\n        element->setBrush(originalBrush);\n    }\n    element->setText(text_table.at(current_text_index));\n}\n\nInstructionValue::InstructionValue(SimpleTextItem *const element, Data data)\n    : element(element)\n    , instruction_data(data.first)\n    , address_data(data.second) {}\n\nvoid InstructionValue::update() {\n    element->setText(instruction_data.to_str(address_data));\n}\n"
  },
  {
    "path": "src/gui/windows/coreview/components/value_handlers.h",
    "content": "/**\n * Components defined in here update the GUI placeholders with up to date\n * values that is read from provided source.\n *\n * Components accept different types and produce different formatting.\n *\n * @file\n */\n#ifndef QTRVSIM_VALUE_HANDLERS_H\n#define QTRVSIM_VALUE_HANDLERS_H\n\n#include <QList>\n#include <instruction.h>\n#include <machine/memory/address.h>\n#include <machine/register_value.h>\n#include <machine/registers.h>\n#include <svgscene/components/simpletextitem.h>\n#include <svgscene/utils/memory_ownership.h>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\nclass BoolValue {\npublic:\n    BoolValue(svgscene::SimpleTextItem *element, const bool &data);\n    void update();\n    static const QString COMPONENT_NAME;\n\nprivate:\n    BORROWED svgscene::SimpleTextItem *const element;\n    const bool &data;\n};\n\nclass PCValue : public QObject {\n    Q_OBJECT\n\npublic slots:\n    void clicked();\nsignals:\n    void jump_to_pc(machine::Address pc_value);\n\npublic:\n    PCValue(svgscene::SimpleTextItem *element, const machine::Address &data);\n    PCValue(const PCValue &);\n    void update();\n    static const QString COMPONENT_NAME;\n\nprivate:\n    BORROWED svgscene::SimpleTextItem *const element;\n    const machine::Address &data;\n};\n\nclass RegValue {\npublic:\n    RegValue(svgscene::SimpleTextItem *element, const machine::RegisterValue &data);\n    void update();\n    static const QString COMPONENT_NAME;\n\nprivate:\n    BORROWED svgscene::SimpleTextItem *const element;\n    const machine::RegisterValue &data;\n};\n\nclass RegIdValue {\npublic:\n    RegIdValue(svgscene::SimpleTextItem *element, const machine::RegisterId &data);\n    void update();\n    static const QString COMPONENT_NAME;\n\nprivate:\n    BORROWED svgscene::SimpleTextItem *const element;\n    const machine::RegisterId &data;\n};\n\nclass DebugValue {\npublic:\n    DebugValue(svgscene::SimpleTextItem *element, const unsigned int &data);\n    void update();\n    static const QString COMPONENT_NAME;\n\nprivate:\n    BORROWED svgscene::SimpleTextItem *const element;\n    const unsigned &data;\n};\n\nclass MultiTextValue {\n    using Source = const std::unordered_map<unsigned, QString> &;\n    using Data = std::pair<const unsigned int &, Source>;\n\npublic:\n    MultiTextValue(svgscene::SimpleTextItem *element, Data data);\n    void update();\n    static const QString COMPONENT_NAME;\n\nprivate:\n    BORROWED svgscene::SimpleTextItem *const element;\n    const unsigned &current_text_index;\n    Source &text_table;\n    QBrush originalBrush;\n};\n\nclass InstructionValue {\n    using Data = std::pair<const machine::Instruction &, const machine::Address &>;\n\npublic:\n    InstructionValue(svgscene::SimpleTextItem *element, Data data);\n    void update();\n    static const QString COMPONENT_NAME;\n\nprivate:\n    BORROWED svgscene::SimpleTextItem *const element;\n    const machine::Instruction &instruction_data;\n    const machine::Address &address_data;\n};\n\ntemplate<typename SOURCE>\nclass Multiplexer {\npublic:\n    Multiplexer(\n        std::vector<BORROWED QGraphicsPathItem *> connections,\n        const SOURCE &active_connection)\n        : connections(std::move(connections))\n        , active_connection(active_connection)\n        , current_active_connection(0) {\n        // Hide all but first\n        for (size_t i = 1; i < this->connections.size(); ++i) {\n            this->connections.at(i)->hide();\n        }\n    }\n    void update() {\n        if (current_active_connection != active_connection) {\n            connections.at(static_cast<unsigned>(current_active_connection))->hide();\n            connections.at(static_cast<unsigned>(active_connection))->show();\n            current_active_connection = active_connection;\n        }\n    }\n\nprivate:\n    const std::vector<BORROWED QGraphicsPathItem *> connections;\n    const SOURCE &active_connection;\n    SOURCE current_active_connection;\n};\n\n#endif // QTRVSIM_VALUE_HANDLERS_H\n"
  },
  {
    "path": "src/gui/windows/coreview/data.h",
    "content": "/**\n * This file contains maps with data for core visualization.\n * - Tables of strings.\n * - Maps of string hook used in SVG files onto lenses into the core state\n *   struct. (see `common/type_utils/lens`)\n *\n * @file\n */\n#ifndef QTRVSIM_DATA_H\n#define QTRVSIM_DATA_H\n\n#include \"common/type_utils/lens.h\"\n\n#include <machine/core/core_state.h>\n\nusing std::pair;\nusing std::unordered_map;\nusing std::vector;\nclass CoreViewScene;\nusing machine::Address;\nusing machine::CoreState;\nusing machine::Instruction;\nusing machine::RegisterId;\nusing machine::RegisterValue;\n\nstatic const std::unordered_map<unsigned, QString> EXCEPTION_NAME_TABLE = {\n    { machine::EXCAUSE_NONE, QStringLiteral(\"NONE\") },\n    { machine::EXCAUSE_INSN_FAULT, QStringLiteral(\"I_FAULT\") },\n    { machine::EXCAUSE_INSN_ILLEGAL, QStringLiteral(\"I_ILLEGAL\") },\n    { machine::EXCAUSE_BREAK, QStringLiteral(\"BREAK\") },\n    { machine::EXCAUSE_LOAD_MISALIGNED, QStringLiteral(\"L_MALIGN\") },\n    { machine::EXCAUSE_LOAD_FAULT, QStringLiteral(\"L_FAULT\") },\n    { machine::EXCAUSE_STORE_MISALIGNED, QStringLiteral(\"S_MALIGN\") },\n    { machine::EXCAUSE_STORE_FAULT, QStringLiteral(\"S_FAULT\") },\n    { machine::EXCAUSE_ECALL_U, QStringLiteral(\"ECALL_U\") },\n    { machine::EXCAUSE_ECALL_S, QStringLiteral(\"ECALL_S\") },\n    { machine::EXCAUSE_RESERVED_10, QStringLiteral(\"RES_10\") },\n    { machine::EXCAUSE_ECALL_M, QStringLiteral(\"ECALL_M\") },\n    { machine::EXCAUSE_INSN_PAGE_FAULT, QStringLiteral(\"I_PGFAULT\") },\n    { machine::EXCAUSE_LOAD_PAGE_FAULT, QStringLiteral(\"L_PGFAULT\") },\n    { machine::EXCAUSE_RESERVED_14, QStringLiteral(\"RES_14\") },\n    { machine::EXCAUSE_STORE_PAGE_FAULT, QStringLiteral(\"S_PGFAULT\") },\n    // Simulator specific exception cause codes, alliases\n    { machine::EXCAUSE_HWBREAK, QStringLiteral(\"HWBREAK\") },\n    { machine::EXCAUSE_ECALL_ANY, QStringLiteral(\"ECALL\") },\n    { machine::EXCAUSE_INT, QStringLiteral(\"INT\") },\n};\n\nstatic const std::unordered_map<unsigned, QString> STALL_TEXT_TABLE = {\n    { 0, QStringLiteral(\"NORMAL\") },\n    { 1, QStringLiteral(\"STALL\") },\n    { 2, QStringLiteral(\"FORWARD\") },\n};\n\nstatic const std::unordered_map<unsigned, QString> PRIVILEGE_TEXT_TABLE = {\n    { static_cast<unsigned>(machine::CSR::PrivilegeLevel::UNPRIVILEGED), QStringLiteral(\"UNPRIV\") },\n    { static_cast<unsigned>(machine::CSR::PrivilegeLevel::SUPERVISOR), QStringLiteral(\"SUPERV\") },\n    { static_cast<unsigned>(machine::CSR::PrivilegeLevel::HYPERVISOR), QStringLiteral(\"HYPERV\") },\n    { static_cast<unsigned>(machine::CSR::PrivilegeLevel::MACHINE), QStringLiteral(\"MACHINE\") },\n};\n\n/**\n * Link targets available for use in the SVG.\n *\n * EXAMPLE:\n * ```svg\n *  <a xlink:href=\"#registers\">\n *    <text>Registers</text>\n *  </a>\n * ```\n */\nstatic const unordered_map<QString, void (::CoreViewScene::*)()> HYPERLINK_TARGETS {\n    { \"#focus_pc\", &CoreViewScene::request_jump_to_program_counter_wrapper },\n    { \"#registers\", &CoreViewScene::request_registers },\n    { \"#cache_data\", &CoreViewScene::request_cache_data },\n    { \"#cache_program\", &CoreViewScene::request_cache_program },\n    { \"#data_memory\", &CoreViewScene::request_data_memory },\n    { \"#peripherals\", &CoreViewScene::request_peripherals },\n    { \"#program_memory\", &CoreViewScene::request_program_memory },\n    { \"#terminal\", &CoreViewScene::request_terminal },\n};\n\nusing MultiTextData = pair<const unsigned &, const std::unordered_map<unsigned, QString> &>;\n\n/**\n * Maps SVG usable value names to lenses (lazy references) to fields, where thy\n * can be\n * retrieved.\n */\nconst struct {\n    const unordered_map<QStringView, Lens<CoreState, bool>> BOOL {\n        { QStringLiteral(\"decode-RegWrite\"), LENS(CoreState, pipeline.decode.result.regwrite) },\n        { QStringLiteral(\"decode-MemToReg\"), LENS(CoreState, pipeline.decode.result.memread) },\n        { QStringLiteral(\"decode-MemWrite\"), LENS(CoreState, pipeline.decode.result.memwrite) },\n        { QStringLiteral(\"decode-MemRead\"), LENS(CoreState, pipeline.decode.result.memread) },\n        { QStringLiteral(\"decode-BranchBxx\"), LENS(CoreState, pipeline.decode.result.branch_bxx) },\n        { QStringLiteral(\"decode-BranchJal\"), LENS(CoreState, pipeline.decode.result.branch_jal) },\n        { QStringLiteral(\"decode-BranchJalr\"),\n          LENS(CoreState, pipeline.decode.result.branch_jalr) },\n        { QStringLiteral(\"decode-BranchVal\"), LENS(CoreState, pipeline.decode.result.branch_val) },\n        { QStringLiteral(\"decode-AluMul\"), LENS(CoreState, pipeline.decode.internal.alu_mul) },\n        { QStringLiteral(\"decode-AluSrc\"), LENS(CoreState, pipeline.decode.result.alusrc) },\n        { QStringLiteral(\"decode-AuiPC\"), LENS(CoreState, pipeline.decode.result.alu_pc) },\n        { QStringLiteral(\"exec-RegWrite\"), LENS(CoreState, pipeline.execute.result.regwrite) },\n        { QStringLiteral(\"exec-MemToReg\"), LENS(CoreState, pipeline.execute.result.memread) },\n        { QStringLiteral(\"exec-MemWrite\"), LENS(CoreState, pipeline.execute.result.memwrite) },\n        { QStringLiteral(\"exec-MemRead\"), LENS(CoreState, pipeline.execute.result.memread) },\n        { QStringLiteral(\"exec-BranchBxx\"), LENS(CoreState, pipeline.execute.internal.branch_bxx) },\n        { QStringLiteral(\"exec-BranchJal\"), LENS(CoreState, pipeline.execute.result.branch_jal) },\n        { QStringLiteral(\"exec-BranchJalr\"), LENS(CoreState, pipeline.execute.result.branch_jalr) },\n        { QStringLiteral(\"exec-BranchVal\"), LENS(CoreState, pipeline.execute.result.branch_val) },\n        { QStringLiteral(\"exec-AluMul\"), LENS(CoreState, pipeline.execute.internal.alu_mul) },\n        { QStringLiteral(\"exec-AluSrc\"), LENS(CoreState, pipeline.execute.internal.alu_src) },\n        { QStringLiteral(\"exec-AuiPC\"), LENS(CoreState, pipeline.execute.internal.alu_pc) },\n        { QStringLiteral(\"exec-AluZero\"), LENS(CoreState, pipeline.execute.result.alu_zero) },\n        { QStringLiteral(\"mem-RegWrite\"), LENS(CoreState, pipeline.memory.result.regwrite) },\n        { QStringLiteral(\"mem-MemToReg\"), LENS(CoreState, pipeline.memory.result.memtoreg) },\n        { QStringLiteral(\"mem-MemWrite\"), LENS(CoreState, pipeline.memory.internal.memwrite) },\n        { QStringLiteral(\"mem-MemRead\"), LENS(CoreState, pipeline.memory.internal.memread) },\n        { QStringLiteral(\"mem-BranchOutcome\"),\n          LENS(CoreState, pipeline.memory.internal.branch_outcome) },\n        { QStringLiteral(\"mem-BranchJalx\"), LENS(CoreState, pipeline.memory.internal.branch_jalx) },\n        { QStringLiteral(\"mem-BranchJalr\"), LENS(CoreState, pipeline.memory.internal.branch_jalr) },\n        { QStringLiteral(\"wb-RegWrite\"), LENS(CoreState, pipeline.writeback.internal.regwrite) },\n        { QStringLiteral(\"wb-MemToReg\"), LENS(CoreState, pipeline.writeback.internal.memtoreg) },\n    };\n    const unordered_map<QStringView, Lens<CoreState, RegisterValue>> REG {\n        { QStringLiteral(\"alu-res\"), LENS(CoreState, pipeline.execute.result.alu_val) },\n        { QStringLiteral(\"alu-src1\"), LENS(CoreState, pipeline.execute.internal.alu_src1) },\n        { QStringLiteral(\"alu-src2\"), LENS(CoreState, pipeline.execute.internal.alu_src2) },\n        { QStringLiteral(\"decode-imm\"), LENS(CoreState, pipeline.decode.result.immediate_val) },\n        { QStringLiteral(\"exec-imm\"), LENS(CoreState, pipeline.execute.result.immediate_val) },\n        { QStringLiteral(\"decode-inst-bus\"), LENS(CoreState, pipeline.decode.internal.inst_bus) },\n        { QStringLiteral(\"mem-write-val\"),\n          LENS(CoreState, pipeline.memory.internal.mem_write_val) },\n        { QStringLiteral(\"mem-write-addr\"), LENS(CoreState, pipeline.memory.internal.mem_addr) },\n        { QStringLiteral(\"mem-read-val\"), LENS(CoreState, pipeline.memory.internal.mem_read_val) },\n        { QStringLiteral(\"decode-rs1\"), LENS(CoreState, pipeline.decode.result.val_rs) },\n        { QStringLiteral(\"decode-rs2\"), LENS(CoreState, pipeline.decode.result.val_rt) },\n        { QStringLiteral(\"exec-rs1\"), LENS(CoreState, pipeline.execute.internal.rs) },\n        { QStringLiteral(\"exec-rs2\"), LENS(CoreState, pipeline.execute.internal.rt) },\n        { QStringLiteral(\"wb\"), LENS(CoreState, pipeline.writeback.internal.value) },\n    };\n    const unordered_map<QStringView, Lens<CoreState, machine::RegisterId>> REG_ID {\n        { QStringLiteral(\"decode-rd\"), LENS(CoreState, pipeline.decode.result.num_rd) },\n        { QStringLiteral(\"exec-rd\"), LENS(CoreState, pipeline.execute.result.num_rd) },\n        { QStringLiteral(\"mem-rd\"), LENS(CoreState, pipeline.memory.result.num_rd) },\n        { QStringLiteral(\"wb-rd\"), LENS(CoreState, pipeline.writeback.internal.num_rd) },\n        { QStringLiteral(\"rs1\"), LENS(CoreState, pipeline.decode.result.num_rs) },\n        { QStringLiteral(\"rs2\"), LENS(CoreState, pipeline.decode.result.num_rt) },\n    };\n    const unordered_map<QStringView, Lens<CoreState, unsigned>> DEBUG_VAL {\n        { QStringLiteral(\"CycleCount\"), LENS(CoreState, cycle_count) },\n        { QStringLiteral(\"StallCount\"), LENS(CoreState, stall_count) },\n        { QStringLiteral(\"decode-AluControl\"),\n          LENS(CoreState, pipeline.decode.internal.alu_op_num) },\n        { QStringLiteral(\"exec-AluControl\"),\n          LENS(CoreState, pipeline.execute.internal.alu_op_num) },\n        { QStringLiteral(\"exec-ForwardA\"),\n          LENS(CoreState, pipeline.execute.internal.forward_from_rs1_num) },\n        { QStringLiteral(\"exec-ForwardB\"),\n          LENS(CoreState, pipeline.execute.internal.forward_from_rs2_num) },\n    };\n    const unordered_map<QStringView, Lens<CoreState, Address>> PC {\n        { QStringLiteral(\"fetch-pc\"), LENS(CoreState, pipeline.fetch.result.inst_addr) },\n    };\n\n#define MULTITEXT_LENS(INDEX, TABLE)                                                               \\\n    [](const CoreState &base) -> MultiTextData { return { base.INDEX, TABLE }; }\n\n    const unordered_map<\n        QStringView,\n        LensPair<CoreState, unsigned, const std::unordered_map<unsigned, QString>>>\n        MULTI_TEXT {\n            { QStringLiteral(\"fetch-exception\"),\n              MULTITEXT_LENS(pipeline.fetch.internal.excause_num, EXCEPTION_NAME_TABLE) },\n            { QStringLiteral(\"decode-exception\"),\n              MULTITEXT_LENS(pipeline.decode.internal.excause_num, EXCEPTION_NAME_TABLE) },\n            { QStringLiteral(\"execute-exception\"),\n              MULTITEXT_LENS(pipeline.execute.internal.excause_num, EXCEPTION_NAME_TABLE) },\n            { QStringLiteral(\"memory-exception\"),\n              MULTITEXT_LENS(pipeline.memory.internal.excause_num, EXCEPTION_NAME_TABLE) },\n            { QStringLiteral(\"hazard\"),\n              MULTITEXT_LENS(pipeline.execute.internal.stall_status, STALL_TEXT_TABLE) },\n            { QStringLiteral(\"Privilege\"),\n              MULTITEXT_LENS(current_privilege_u, PRIVILEGE_TEXT_TABLE) },\n        };\n\n    const unordered_map<QStringView, LensPair<CoreState, Instruction, Address>> INSTRUCTION {\n        { QStringLiteral(\"fetch\"),\n          LENS_PAIR(CoreState, pipeline.fetch.result.inst, pipeline.fetch.result.inst_addr) },\n        { QStringLiteral(\"decode\"),\n          LENS_PAIR(CoreState, pipeline.decode.result.inst, pipeline.decode.result.inst_addr) },\n        { QStringLiteral(\"exec\"),\n          LENS_PAIR(CoreState, pipeline.execute.result.inst, pipeline.execute.result.inst_addr) },\n        { QStringLiteral(\"mem\"),\n          LENS_PAIR(CoreState, pipeline.memory.result.inst, pipeline.memory.result.inst_addr) },\n        { QStringLiteral(\"wb\"),\n          { LENS_PAIR(\n              CoreState,\n              pipeline.writeback.internal.inst,\n              pipeline.writeback.internal.inst_addr) } },\n    };\n} VALUE_SOURCE_NAME_MAPS;\n\n#endif // QTRVSIM_DATA_H\n"
  },
  {
    "path": "src/gui/windows/coreview/scene.cpp",
    "content": "#include \"scene.h\"\n\n#include \"common/logging.h\"\n#include \"data.h\"\n#include \"machine/core.h\"\n\n#include <svgscene/components/hyperlinkitem.h>\n#include <svgscene/components/simpletextitem.h>\n#include <svgscene/svghandler.h>\n#include <unordered_map>\n#include <vector>\n\nusing std::unordered_map;\nusing std::vector;\nusing svgscene::HyperlinkItem;\nusing svgscene::SimpleTextItem;\nusing svgscene::SvgDocument;\nusing svgscene::SvgDomTree;\n\nLOG_CATEGORY(\"gui.coreview\");\n\nCoreViewScene::CoreViewScene(machine::Machine *machine, const QString &core_svg_scheme_name)\n    : SvgGraphicsScene()\n    , program_counter_value((VALUE_SOURCE_NAME_MAPS.PC.at(QStringLiteral(\"fetch-pc\")))(\n          machine->core()->get_state())) {\n    SvgDocument document\n        = svgscene::parseFromFileName(this, QString(\":/core/%1.svg\").arg(core_svg_scheme_name));\n\n    for (auto hyperlink_tree : document.getRoot().findAll<HyperlinkItem>()) {\n        this->install_hyperlink(hyperlink_tree.getElement());\n    }\n\n    /*\n     * TODO:\n     *      Components not implemented:\n     *      - colored frames on special values\n     */\n\n    const machine::CoreState &core_state = machine->core()->get_state();\n\n    // Find all components in the DOM tree and install controllers for them.\n    for (auto component : document.getRoot().findAll(\"data-component\")) {\n        QStringView component_name = component.getAttrValueOr(\"data-component\");\n        if (component_name.isEmpty()) { continue; }\n        // This switch is performance optimization.\n        // Single char lookup will usually give only one match, outperforming\n        // a hashtable, which has to check the key in the end as well hut\n        // hashing is more complex than single char lookup.\n        switch (component_name.at(0).toLatin1()) {\n        case 'b': {\n            if (component_name == BoolValue::COMPONENT_NAME) {\n                install_value(\n                    values.bool_values, VALUE_SOURCE_NAME_MAPS.BOOL, component, core_state);\n            }\n            break;\n        }\n        case 'd': {\n            if (component_name == DebugValue::COMPONENT_NAME) {\n                install_value(\n                    values.debug_values, VALUE_SOURCE_NAME_MAPS.DEBUG_VAL, component, core_state);\n            } else if (component_name == QStringLiteral(\"data-cache\")) {\n                if (machine->config().cache_data().enabled()) {\n                    auto texts = component.findAll<SimpleTextItem>();\n                    // Diagrams.net dow not allow me, to put there some marks.\n                    // :(\n                    auto miss = texts.takeLast().getElement();\n                    auto hit = texts.takeLast().getElement();\n                    data_cache.reset(new Cache(machine->cache_data(), hit, miss));\n                } else {\n                    component.getElement()->hide();\n                }\n            }\n            break;\n        }\n        case 'i': {\n            if (component_name == InstructionValue::COMPONENT_NAME) {\n                install_value(\n                    values.instruction_values, VALUE_SOURCE_NAME_MAPS.INSTRUCTION, component,\n                    core_state);\n            }\n            break;\n        }\n        case 'm': {\n            if (component_name == MultiTextValue::COMPONENT_NAME) {\n                install_value(\n                    values.multi_text_values, VALUE_SOURCE_NAME_MAPS.MULTI_TEXT, component,\n                    core_state);\n            } else if (component_name == QStringLiteral(\"mux2\")) {\n                const QString &source_name = component.getAttrValueOr(\"data-source\");\n                // Draw.io does not allow tagging the paths, to I use this style identification\n                // hack.\n                auto conn_trees = component.findAll<QGraphicsPathItem>(\"stroke-linecap\", \"round\");\n                if (conn_trees.size() != 2) {\n                    WARN(\n                        \"Mux2 does not have 2 connections found %zi (source: \\\"%s\\\").\",\n                        conn_trees.size(), qPrintable(source_name));\n                    break;\n                }\n                std::vector<QGraphicsPathItem *> connections;\n                connections.reserve(conn_trees.size());\n                std::transform(\n                    conn_trees.begin(), conn_trees.end(), std::back_inserter(connections),\n                    [](SvgDomTree<QGraphicsPathItem> &e) { return e.getElement(); });\n                try {\n                    const bool &source = VALUE_SOURCE_NAME_MAPS.BOOL.at(source_name)(core_state);\n                    values.mux2_values.emplace_back(std::move(connections), source);\n                } catch (std::out_of_range &e) {\n                    WARN(\n                        \"Source for mux2 value not found (source: \\\"%s\\\").\",\n                        qPrintable(source_name));\n                }\n            } else if (component_name == QStringLiteral(\"mux3\")) {\n                const QString &source_name = component.getAttrValueOr(\"data-source\");\n                // Draw.io does not allow tagging the paths, to I use this style identification\n                // hack.\n                auto conn_trees = component.findAll<QGraphicsPathItem>(\"stroke-linecap\", \"round\");\n                if (conn_trees.size() != 3) {\n                    WARN(\n                        \"Mux3 does not have 3 connections found %lld (source: \\\"%s\\\").\",\n                        conn_trees.size(), qPrintable(source_name));\n                    break;\n                }\n                std::vector<QGraphicsPathItem *> connections;\n                connections.reserve(conn_trees.size());\n                std::transform(\n                    conn_trees.begin(), conn_trees.end(), std::back_inserter(connections),\n                    [](SvgDomTree<QGraphicsPathItem> &e) { return e.getElement(); });\n                try {\n                    const unsigned &source\n                        = VALUE_SOURCE_NAME_MAPS.DEBUG_VAL.at(source_name)(core_state);\n                    values.mux3_values.emplace_back(std::move(connections), source);\n                } catch (std::out_of_range &e) {\n                    WARN(\n                        \"Source for mux3 value not found (source: \\\"%s\\\").\",\n                        qPrintable(source_name));\n                }\n            }\n            break;\n        }\n        case 'p': {\n            if (component_name == PCValue::COMPONENT_NAME) {\n                install_value(values.pc_values, VALUE_SOURCE_NAME_MAPS.PC, component, core_state);\n            } else if (component_name == QStringLiteral(\"program-cache\")) {\n                if (machine->config().cache_program().enabled()) {\n                    auto texts = component.findAll<SimpleTextItem>();\n                    // Diagrams.net does not allow me, to put there some\n                    // marks. :(\n                    auto miss = texts.takeLast().getElement();\n                    auto hit = texts.takeLast().getElement();\n                    program_cache.reset(new Cache(machine->cache_program(), hit, miss));\n                } else {\n                    component.getElement()->hide();\n                }\n            }\n            break;\n        }\n        case 'r': {\n            if (component_name == RegValue::COMPONENT_NAME) {\n                install_value(values.reg_values, VALUE_SOURCE_NAME_MAPS.REG, component, core_state);\n            } else if (component_name == RegIdValue::COMPONENT_NAME) {\n                install_value(\n                    values.reg_id_values, VALUE_SOURCE_NAME_MAPS.REG_ID, component, core_state);\n            }\n            break;\n        }\n        }\n    }\n\n    if (machine->config().hazard_unit() == machine::MachineConfig::HU_NONE) {\n        // Hazard unit conditional hide\n        for (auto elem_tree : document.getRoot().findAll(\"data-tags\", \"hazardunit\")) {\n            elem_tree.getElement()->hide();\n        }\n    }\n\n    update_values(); // Set to initial value - most often zero.\n\n    // Update coreview with each core step.\n    connect(machine->core(), &machine::Core::step_done, this, &CoreViewScene::update_values);\n}\n\nCoreViewScene::~CoreViewScene() = default;\n\nvoid CoreViewScene::install_hyperlink(svgscene::HyperlinkItem *element) const {\n    if (element->getTargetName() == \"#\") {\n        DEBUG(\"Skipping NOP hyperlink.\");\n        return;\n    }\n    try {\n        connect(\n            element, &svgscene::HyperlinkItem::triggered, this,\n            HYPERLINK_TARGETS.at(element->getTargetName()));\n        DEBUG(\"Registered hyperlink to target %s\", qPrintable(element->getTargetName()));\n    } catch (std::out_of_range &) {\n        WARN(\n            \"Registering hyperlink without valid target (href: \\\"%s\\\").\",\n            qPrintable(element->getTargetName()));\n    }\n}\ntemplate<typename T_handler, typename T_lens, typename T>\nvoid CoreViewScene::install_value(\n    vector<T_handler> &handler_list,\n    const unordered_map<QStringView, T_lens> &value_source_name_map,\n    SvgDomTree<T> component,\n    const CoreState &core_state) {\n    SimpleTextItem *text_element = component.template find<SimpleTextItem>().getElement();\n    const QString &source_name = component.getAttrValueOr(\"data-source\");\n    try {\n        handler_list.emplace_back(text_element, value_source_name_map.at(source_name)(core_state));\n\n        DEBUG(\n            \"Installing value %s with source %s.\", qPrintable(T_handler::COMPONENT_NAME),\n            qPrintable(source_name));\n    } catch (std::out_of_range &) {\n        WARN(\n            \"Source for %s value not found (source: \\\"%s\\\").\", typeid(T).name(),\n            qPrintable(source_name));\n    }\n}\n\ntemplate<typename T>\nvoid CoreViewScene::update_value_list(std::vector<T> &value_list) {\n    DEBUG(\"Calling full update of %s...\", typeid(T).name());\n    for (auto &value_handler : value_list) {\n        value_handler.update();\n    }\n}\n\nvoid CoreViewScene::update_values() {\n    update_value_list(values.bool_values);\n    update_value_list(values.debug_values);\n    update_value_list(values.reg_values);\n    update_value_list(values.reg_id_values);\n    update_value_list(values.pc_values);\n    update_value_list(values.multi_text_values);\n    update_value_list(values.instruction_values);\n    update_value_list(values.mux2_values);\n    update_value_list(values.mux3_values);\n}\n\nvoid CoreViewScene::request_jump_to_program_counter_wrapper() {\n    emit request_jump_to_program_counter(program_counter_value);\n}\n\nCoreViewSceneSimple::CoreViewSceneSimple(machine::Machine *machine)\n    : CoreViewScene(machine, \"simple\") {}\n\nCoreViewScenePipelined::CoreViewScenePipelined(machine::Machine *machine)\n    : CoreViewScene(\n          machine,\n          (machine->config().hazard_unit() == machine::MachineConfig::HU_STALL_FORWARD)\n              ? \"forwarding\"\n              : \"pipeline\") {}\n"
  },
  {
    "path": "src/gui/windows/coreview/scene.h",
    "content": "#ifndef QTRVSIM_SCENE_H\n#define QTRVSIM_SCENE_H\n\n#include \"./components/cache.h\"\n#include \"./components/value_handlers.h\"\n#include \"common/polyfills/qstring_hash.h\"\n#include \"graphicsview.h\"\n\n#include <QGraphicsScene>\n#include <QGraphicsView>\n#include <QSignalMapper>\n#include <machine/machine.h>\n#include <svgscene/components/hyperlinkitem.h>\n#include <svgscene/components/simpletextitem.h>\n#include <svgscene/svggraphicsscene.h>\n#include <unordered_map>\n\nclass CoreViewScene : public svgscene::SvgGraphicsScene {\n    Q_OBJECT\n\npublic:\n    CoreViewScene(machine::Machine *machine, const QString &core_svg_scheme_name);\n    ~CoreViewScene() override;\n\n    /** Hyperlink handler which automatically uses current PC value via this object */\n    void request_jump_to_program_counter_wrapper();\n\nsignals:\n    /* Hyperlink handlers propagated to the main window. */\n    void request_registers();\n    void request_data_memory();\n    void request_program_memory();\n    void request_jump_to_program_counter(machine::Address addr);\n    void request_cache_program();\n    void request_cache_data();\n    void request_peripherals();\n    void request_terminal();\n\npublic slots:\n    /**\n     * Update all installed dynamic values.\n     *\n     * @see install_value\n     */\n    void update_values();\n\nprotected:\n    /**\n     * Lookup link target and connect element one of `request_` slots.\n     * @param element   the clickable element\n     */\n    void install_hyperlink(svgscene::HyperlinkItem *element) const;\n\n    /**\n     * Create update handler for dynamic value in core view.\n     *\n     * @tparam T_handler              one of classes in\n     *                                `coreview/components/numeric_value.h`\n     * @tparam T                      type of value to update from\n     * @param handler_list            list, where the created handler will be\n     *                                stored\n     * @param value_source_name_map   maps of load_sections_indexes names used in svg to\n     *                                references in the state struct\n     * @param component               text element that will be updated\n     * @param source_name             name of data source, see\n     *                                value_source_name_map\n     */\n    template<typename T_handler, typename T_lens, typename T>\n    void install_value(\n        std::vector<T_handler> &handler_list,\n        const std::unordered_map<QStringView, T_lens> &value_source_name_map,\n        svgscene::SvgDomTree<T> component,\n        const machine::CoreState &core_state);\n\n    /**\n     * Update all value components of given type.\n     *\n     * @tparam T          type of component\n     * @param value_list  list of components\n     * @see               install_value\n     */\n    template<typename T>\n    void update_value_list(std::vector<T> &value_list);\n\nprotected:\n    /**\n     * Lists of dynamic values from svg that should updated each cycle.\n     */\n    struct {\n        std::vector<BoolValue> bool_values;\n        std::vector<RegValue> reg_values;\n        std::vector<RegIdValue> reg_id_values;\n        std::vector<DebugValue> debug_values;\n        std::vector<PCValue> pc_values;\n        std::vector<MultiTextValue> multi_text_values;\n        std::vector<InstructionValue> instruction_values;\n        std::vector<Multiplexer<bool>> mux2_values;\n        std::vector<Multiplexer<unsigned>> mux3_values;\n    } values;\n\n    Box<Cache> program_cache;\n    Box<Cache> data_cache;\n\n    /** Reference to current PC value to be used to focus PC in program memory on lick */\n    const machine::Address &program_counter_value;\n};\n\nclass CoreViewSceneSimple : public CoreViewScene {\npublic:\n    explicit CoreViewSceneSimple(machine::Machine *machine);\n};\n\nclass CoreViewScenePipelined : public CoreViewScene {\npublic:\n    explicit CoreViewScenePipelined(machine::Machine *machine);\n};\n\n#endif\n"
  },
  {
    "path": "src/gui/windows/coreview/schemas/schemas.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/core\">\n        <file>simple.svg</file>\n        <file>pipeline.svg</file>\n        <file>forwarding.svg</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "src/gui/windows/csr/csrdock.cpp",
    "content": "#include \"csrdock.h\"\n\n#include \"csr/controlstate.h\"\n\nCsrDock::CsrDock(QWidget *parent)\n    : QDockWidget(parent)\n    , xlen(machine::Xlen::_32)\n    , csr_handle(nullptr) {\n    scrollarea = new QScrollArea(this);\n    scrollarea->setWidgetResizable(true);\n    widg = new StaticTable(scrollarea);\n\n    for (size_t i = 0; i < machine::CSR::REGISTERS.size(); i++) {\n        auto &desc = machine::CSR::REGISTERS.at(i);\n        csr_view[i] = new QLabel(sizeHintText(), widg);\n        csr_view[i]->setTextFormat(Qt::PlainText);\n        csr_view[i]->setFixedSize(csr_view[i]->sizeHint());\n        csr_view[i]->setText(\"\");\n        csr_view[i]->setTextInteractionFlags(Qt::TextSelectableByMouse);\n        auto desc_label = new QLabel(QString(desc.name), widg);\n        desc_label->setToolTip(\n            (QString(\"%0 (0x%1)\").arg(desc.description).arg(desc.address.data, 0, 16)));\n        widg->addRow({ desc_label, csr_view[i] });\n        csr_highlighted[i] = false;\n    }\n    scrollarea->setWidget(widg);\n\n    setWidget(scrollarea);\n    setObjectName(\"Control and Status Registers\");\n    setWindowTitle(\"Control and Status Registers\");\n\n    pal_normal = QPalette(csr_view[1]->palette());\n    pal_updated = QPalette(csr_view[1]->palette());\n    pal_read = QPalette(csr_view[1]->palette());\n    pal_normal.setColor(QPalette::WindowText, QColor(0, 0, 0));\n    pal_updated.setColor(QPalette::WindowText, QColor(240, 0, 0));\n    pal_read.setColor(QPalette::WindowText, QColor(0, 0, 240));\n    csr_highlighted_any = false;\n}\nvoid CsrDock::setup(machine::Machine *machine) {\n    if (machine == nullptr) {\n        // Reset data\n        for (auto &i : csr_view) {\n            i->setText(\"\");\n        }\n        return;\n    }\n\n    // if xlen changes adjust space to show full value\n    if (xlen != machine->config().get_simulated_xlen()) {\n        xlen = machine->config().get_simulated_xlen();\n        auto *dumy_data_label = new QLabel(sizeHintText(), widg);\n        for (size_t i = 0; i < machine::CSR::REGISTERS.size(); i++) {\n            csr_view[i]->setFixedSize(dumy_data_label->sizeHint());\n        }\n        delete dumy_data_label;\n    }\n\n    csr_handle = machine->control_state();\n    reload();\n    connect(csr_handle, &machine::CSR::ControlState::write_signal, this, &CsrDock::csr_changed);\n    connect(csr_handle, &machine::CSR::ControlState::read_signal, this, &CsrDock::csr_read);\n    connect(machine, &machine::Machine::tick, this, &CsrDock::clear_highlights);\n}\n\nconst char *CsrDock::sizeHintText() {\n    if (xlen == machine::Xlen::_64)\n        return \"0x0000000000000000\";\n    else\n        return \"0x00000000\";\n}\n\nvoid CsrDock::reload() {\n    if (csr_handle == nullptr) { return; }\n    clear_highlights();\n    for (size_t i = 0; i < machine::CSR::REGISTERS.size(); i++) {\n        labelVal(csr_view[i], csr_handle->read_internal(i).as_xlen(xlen));\n    }\n}\n\nvoid CsrDock::csr_changed(size_t internal_reg_id, machine::RegisterValue val) {\n    if (isHidden()) { return; }\n\n    // FIXME assert takes literal\n    SANITY_ASSERT(\n        (uint)internal_reg_id < machine::CSR::REGISTERS.size(),\n        QString(\"CsrDock received signal with invalid CSR register: \")\n            + QString::number((uint)internal_reg_id));\n    labelVal(csr_view[(uint)internal_reg_id], val.as_xlen(xlen));\n    csr_view[internal_reg_id]->setPalette(pal_updated);\n    csr_highlighted[internal_reg_id] = true;\n    csr_highlighted_any = true;\n}\n\nvoid CsrDock::csr_read(size_t internal_reg_id, machine::RegisterValue val) {\n    (void)val;\n\n    if (isHidden()) { return; }\n\n    // FIXME assert takes literal\n    SANITY_ASSERT(\n        (uint)internal_reg_id < machine::CSR::REGISTERS.size(),\n        QString(\"CsrDock received signal with invalid CSR register: \")\n            + QString::number((uint)internal_reg_id));\n    if (!csr_highlighted[internal_reg_id]) { csr_view[internal_reg_id]->setPalette(pal_read); }\n    csr_highlighted[internal_reg_id] = true;\n    csr_highlighted_any = true;\n}\n\nvoid CsrDock::clear_highlights() {\n    if (!csr_highlighted_any) { return; }\n    for (size_t i = 0; i < machine::CSR::REGISTERS.size(); i++) {\n        if (csr_highlighted[i]) {\n            csr_view[i]->setPalette(pal_normal);\n            csr_highlighted[i] = false;\n        }\n    }\n    csr_highlighted_any = false;\n}\n\nvoid CsrDock::showEvent(QShowEvent *event) {\n    // Slots are inactive when this widget is hidden\n    reload();\n    QDockWidget::showEvent(event);\n}\n\nvoid CsrDock::labelVal(QLabel *label, uint64_t value) {\n    QString t = QString(\"0x\") + QString::number(value, 16);\n    label->setText(t);\n}"
  },
  {
    "path": "src/gui/windows/csr/csrdock.h",
    "content": "#ifndef CSRDOCK_H\n#define CSRDOCK_H\n\n#include \"machine/csr/controlstate.h\"\n#include \"machine/machine.h\"\n#include \"statictable.h\"\n\n#include <QDockWidget>\n#include <QFormLayout>\n#include <QLabel>\n#include <QPalette>\n#include <QPropertyAnimation>\n#include <QScrollArea>\n\nclass CsrDock : public QDockWidget {\n    Q_OBJECT\npublic:\n    explicit CsrDock(QWidget *parent);\n\n    void setup(machine::Machine *machine);\n    void reload();\n\nprivate slots:\n    void csr_changed(std::size_t internal_reg_id, machine::RegisterValue val);\n    void csr_read(std::size_t internal_reg_id, machine::RegisterValue val);\n    void clear_highlights();\n\nprivate:\n    void showEvent(QShowEvent *event) override;\n\nprivate:\n    machine::Xlen xlen;\n    // We keep this handle for batch updates when this widget was hidden.\n    const machine::CSR::ControlState *csr_handle {};\n\n    const char *sizeHintText();\n\n    QT_OWNED StaticTable *widg;\n    QT_OWNED QScrollArea *scrollarea;\n\n    std::array<QT_OWNED QLabel *, machine::CSR::REGISTERS.size()> csr_view {};\n    bool csr_highlighted[machine::CSR::REGISTERS.size()] {};\n    bool csr_highlighted_any;\n\n    QPalette pal_normal;\n    QPalette pal_updated;\n    QPalette pal_read;\n\n    static void labelVal(QLabel *label, uint64_t val);\n};\n\n#endif // CSRDOCK_H\n"
  },
  {
    "path": "src/gui/windows/editor/editordock.cpp",
    "content": "#include \"editordock.h\"\n\n#include \"common/logging.h\"\n#include \"dialogs/savechanged/savechangeddialog.h\"\n#include \"editortab.h\"\n#include \"helper/async_modal.h\"\n\n#include <QFileDialog>\n#include <QFileInfo>\n#include <QInputDialog>\n#include <QMessageBox>\n#include <qtabbar.h>\n#include <utility>\n\nLOG_CATEGORY(\"gui.editordock\");\n\n#ifdef __EMSCRIPTEN__\n    #include \"qhtml5file.h\"\n#endif\n\nint compare_filenames(const QString &filename1, const QString &filename2) {\n    QFileInfo fi1(filename1);\n    QFileInfo fi2(filename2);\n    QString canon1 = fi1.canonicalFilePath();\n    QString canon2 = fi2.canonicalFilePath();\n    if (!canon1.isEmpty() && (canon1 == canon2)) { return 2; }\n    if (filename1 == filename2) { return 1; }\n    return 0;\n}\n\nEditorDock::EditorDock(QSharedPointer<QSettings> settings, QTabWidget *parent_tabs, QWidget *parent)\n    : Super(parent)\n    , settings(std::move(settings)) {\n    {\n        auto bar = tabBar();\n        bar->setMovable(true);\n        QFont font = bar->font();\n        font.setPointSize(10);\n        font.setBold(false);\n        bar->setFont(font);\n    }\n\n    setObjectName(\"EditorDock\");\n    setTabsClosable(true);\n    connect(this, &EditorDock::tabCloseRequested, this, [this](int index) { close_tab(index); });\n\n    connect(\n        this, &EditorDock::currentChanged, parent_tabs,\n        [this, parent_tabs](int index) {\n            // Update parent title\n            if (count() == 0 || index < 0) return;\n            auto *editor = get_tab(index)->get_editor();\n            QString title = QString(\"&Editor (%1)\").arg(editor->title());\n            parent_tabs->setTabText(parent_tabs->indexOf(this), title);\n            // IMPORTANT: This repeated call solved a very annoying QT resize bug. Do not remove it!\n            parent_tabs->setTabText(parent_tabs->indexOf(this), title);\n            parent_tabs->setCurrentIndex(parent_tabs->indexOf(this));\n        },\n        Qt::QueuedConnection);\n}\n\nEditorTab *EditorDock::get_tab(int index) const {\n    return dynamic_cast<EditorTab *>(widget(index));\n}\n\nEditorTab *EditorDock::open_file(const QString &filename, bool save_as_required) {\n    auto tab = new EditorTab(line_numbers_visible, this);\n    if (tab->get_editor()->loadFile(filename)) {\n        addTab(tab, tab->title());\n        setCurrentWidget(tab);\n        if (save_as_required) tab->get_editor()->setSaveAsRequired(save_as_required);\n        return tab;\n    } else {\n        delete tab;\n        return nullptr;\n    }\n}\n\nEditorTab *EditorDock::open_file_if_not_open(const QString &filename, bool save_as_required) {\n    auto tab = find_tab_by_filename(filename);\n    if (tab == nullptr) {\n        return open_file(filename, save_as_required);\n    } else {\n        setCurrentWidget(tab);\n        return tab;\n    }\n}\n\nEditorTab *EditorDock::create_empty_tab() {\n    auto tab = new EditorTab(line_numbers_visible, this);\n    while (true) {\n        auto filename = QString(\"Unknown %1\").arg(unknown_editor_counter++);\n        if (!find_tab_id_by_filename(filename).has_value()) {\n            tab->get_editor()->setFileName(filename);\n            tab->get_editor()->setSaveAsRequired(true);\n            break;\n        }\n    }\n    addTab(tab, tab->title());\n    setCurrentWidget(tab);\n    return tab;\n}\n\nstd::optional<int> EditorDock::find_tab_id_by_filename(const QString &filename) const {\n    int best_match = 0;\n    int best_match_index = -1;\n    for (int i = 0; i < this->count(); i++) {\n        auto *editor = get_tab(i)->get_editor();\n        int match = compare_filenames(filename, editor->filename());\n        if (match == 2) { return i; }\n        if (match > best_match) {\n            best_match = match;\n            best_match_index = i;\n        }\n    }\n    if (best_match_index >= 0) { return best_match_index; }\n    return std::nullopt;\n}\n\nEditorTab *EditorDock::find_tab_by_filename(const QString &filename) const {\n    auto index = find_tab_id_by_filename(filename);\n    if (index.has_value()) {\n        return get_tab(index.value());\n    } else {\n        return nullptr;\n    }\n}\n\nSrcEditor *EditorDock::get_current_editor() const {\n    if (count() == 0) return nullptr;\n    return get_tab(currentIndex())->get_editor();\n}\n\nQStringList EditorDock::get_open_file_list() const {\n    QStringList open_src_files;\n    for (int i = 0; i < this->count(); i++) {\n        auto *editor = get_tab(i)->get_editor();\n        if (editor->filename().isEmpty()) { continue; }\n        open_src_files.append(editor->filename());\n    }\n    return open_src_files;\n}\n\nbool EditorDock::get_modified_tab_filenames(QStringList &output, bool report_unnamed) const {\n    output.clear();\n    for (int i = 0; i < this->count(); i++) {\n        auto editor = get_tab(i)->get_editor();\n        if (editor->filename().isEmpty() && !report_unnamed) { continue; }\n        if (!editor->isModified()) { continue; }\n        output.append(editor->filename());\n    }\n    return !output.empty();\n}\n\nvoid EditorDock::set_show_line_numbers(bool visible) {\n    line_numbers_visible = visible;\n    settings->setValue(\"editorShowLineNumbers\", visible);\n    for (int i = 0; i < this->count(); i++) {\n        get_tab(i)->set_show_line_number(visible);\n    }\n}\n\nvoid EditorDock::tabCountChanged() {\n    Super::tabCountChanged();\n    emit editor_available_changed(count() > 0);\n}\n\nvoid EditorDock::open_file_dialog() {\n#ifndef __EMSCRIPTEN__\n    QString file_name = QFileDialog::getOpenFileName(\n        this, tr(\"Open File\"), \"\", \"Source Files (*.asm *.S *.s *.c Makefile)\");\n\n    if (file_name.isEmpty()) { return; }\n\n    auto tab_id = find_tab_id_by_filename(file_name);\n    if (tab_id.has_value()) {\n        setCurrentIndex(tab_id.value());\n        return;\n    }\n\n    if (!open_file(file_name)) {\n        showAsyncCriticalBox(\n            this, \"Simulator Error\", tr(\"Cannot open file '%1' for reading.\").arg(file_name));\n    }\n#else\n    QHtml5File::load(\"*\", [&](const QByteArray &content, const QString &filename) {\n        auto tab = create_empty_tab();\n        tab->get_editor()->loadByteArray(content, filename);\n        setTabText(indexOf(tab), tab->get_editor()->title());\n    });\n#endif\n}\n\nvoid EditorDock::save_tab(int index) {\n    auto editor = get_tab(index)->get_editor();\n    if (editor->saveAsRequired()) { return save_tab_as(index); }\n#ifndef __EMSCRIPTEN__\n    if (!editor->saveFile()) {\n        showAsyncCriticalBox(\n            this, \"Simulator Error\", tr(\"Cannot save file '%1'.\").arg(editor->filename()));\n    }\n#else\n    QHtml5File::save(editor->document()->toPlainText().toUtf8(), editor->filename());\n    editor->setModified(false);\n#endif\n}\n\nvoid EditorDock::save_current_tab() {\n    if (count() == 0) return;\n    save_tab(currentIndex());\n}\n\nvoid EditorDock::save_tab_as(int index) {\n#ifndef __EMSCRIPTEN__\n    QFileDialog fileDialog(this, tr(\"Save as...\"));\n    fileDialog.setAcceptMode(QFileDialog::AcceptSave);\n    fileDialog.setDefaultSuffix(\"s\");\n    if (fileDialog.exec() != QDialog::Accepted) { return; }\n    const QString fn = fileDialog.selectedFiles().first();\n    auto tab = get_tab(index);\n    if (!tab->get_editor()->saveFile(fn)) {\n        showAsyncCriticalBox(this, \"Simulator Error\", tr(\"Cannot save file '%1'.\").arg(fn));\n        return;\n    }\n    setTabText(index, tab->get_editor()->title());\n    emit currentChanged(index);\n#else\n    QString filename = get_tab(index)->get_editor()->filename();\n    if (filename.isEmpty()) filename = \"unknown.s\";\n    auto *dialog = new QInputDialog(this);\n    dialog->setWindowTitle(\"Select file name\");\n    dialog->setLabelText(\"File name:\");\n    dialog->setTextValue(filename);\n    dialog->setMinimumSize(QSize(200, 100));\n    dialog->setAttribute(Qt::WA_DeleteOnClose);\n    connect(\n        dialog, &QInputDialog::textValueSelected, this,\n        [this, index](const QString &filename) { save_tab_to(index, filename); },\n        Qt::QueuedConnection);\n    dialog->open();\n#endif\n}\n\nvoid EditorDock::save_current_tab_as() {\n    if (count() == 0) return;\n    save_tab_as(currentIndex());\n}\n\nvoid EditorDock::save_tab_to(int index, const QString &filename) {\n    if (filename.isEmpty()) {\n        WARN(\"Cannot save file '%s'.\", filename.toStdString().c_str());\n        return;\n    }\n\n    auto editor = get_tab(index)->get_editor();\n    if (filename.isEmpty() || (editor == nullptr)) { return; }\n    editor->setFileName(filename);\n    if (!editor->filename().isEmpty()) { save_current_tab(); }\n}\n\nvoid EditorDock::save_current_tab_to(const QString &filename) {\n    if (count() == 0) return;\n    save_tab_to(currentIndex(), filename);\n}\n\nvoid EditorDock::close_tab(int index) {\n    auto editor = get_tab(index)->get_editor();\n    if (!editor->isModified()) {\n        close_tab_unchecked(index);\n    } else {\n        confirm_close_tab_dialog(index);\n    }\n}\n\nvoid EditorDock::close_current_tab() {\n    if (count() == 0) return;\n    close_tab(currentIndex());\n}\n\nvoid EditorDock::close_tab_by_name(QString &filename, bool ask) {\n    auto *tab = find_tab_by_filename(filename);\n    if (tab == nullptr) {\n        WARN(\"Cannot find tab for file '%s'. Unable to close it.\", filename.toStdString().c_str());\n        return;\n    }\n    if (!ask) {\n        close_tab(indexOf(tab));\n    } else {\n        confirm_close_tab_dialog(indexOf(tab));\n    }\n}\nvoid EditorDock::close_tab_unchecked(int index) {\n    auto *tab = get_tab(index);\n    removeTab(index);\n    delete tab;\n}\n\nvoid EditorDock::confirm_close_tab_dialog(int index) {\n    auto *msgbox = new QMessageBox(this);\n    msgbox->setWindowTitle(\"Close unsaved source\");\n    msgbox->setText(\"Close unsaved source.\");\n    msgbox->setInformativeText(\"Do you want to save your changes?\");\n    msgbox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);\n    msgbox->setDefaultButton(QMessageBox::Save);\n    msgbox->setMinimumSize(QSize(200, 150));\n    msgbox->setAttribute(Qt::WA_DeleteOnClose);\n    connect(\n        msgbox, &QDialog::finished, this,\n        [this, index](int result) {\n            if (result == QMessageBox::Save) {\n                save_tab(index);\n                close_tab_unchecked(index);\n            } else if (result == QMessageBox::Discard) {\n                close_tab_unchecked(index);\n            }\n        },\n        Qt::QueuedConnection);\n    msgbox->open();\n}\n\nbool EditorDock::set_cursor_to(const QString &filename, int line, int column) {\n    auto tab = (filename == \"Unknown\") ? get_tab(currentIndex()) : find_tab_by_filename(filename);\n    if (tab == nullptr) {\n        WARN(\n            \"Cannot find tab for file '%s'. Unable to set cursor.\", filename.toStdString().c_str());\n        return false;\n    }\n    setCurrentWidget(tab);\n    tab->get_editor()->setCursorTo(line, column);\n    return true;\n}\n"
  },
  {
    "path": "src/gui/windows/editor/editordock.h",
    "content": "#ifndef EDITORDOCK_H\n#define EDITORDOCK_H\n\n#include \"common/memory_ownership.h\"\n#include \"editortab.h\"\n#include \"widgets/hidingtabwidget.h\"\n\n#include <QSettings>\n#include <optional>\n\nclass EditorDock : public HidingTabWidget {\n    Q_OBJECT\n    using Super = HidingTabWidget;\n\npublic:\n    /**\n     * @param parent_tabs used to update parent tab based on current state\n     */\n    explicit EditorDock(\n        QSharedPointer<QSettings> settings,\n        QTabWidget *parent_tabs,\n        QWidget *parent = nullptr);\n    BORROWED [[nodiscard]] EditorTab *get_tab(int index) const;\n    BORROWED EditorTab *create_empty_tab();\n    BORROWED EditorTab *open_file(const QString &filename, bool save_as_required = false);\n    BORROWED EditorTab *\n    open_file_if_not_open(const QString &filename, bool save_as_required = false);\n    BORROWED [[nodiscard]] std::optional<int> find_tab_id_by_filename(const QString &filename) const;\n    BORROWED [[nodiscard]] EditorTab *find_tab_by_filename(const QString &filename) const;\n    BORROWED [[nodiscard]] SrcEditor *get_current_editor() const;\n    [[nodiscard]] QStringList get_open_file_list() const;\n    bool get_modified_tab_filenames(QStringList &output, bool report_unnamed = false) const;\n    bool set_cursor_to(const QString &filename, int line, int column);\n\nprotected:\n    void tabCountChanged() override;\n\nsignals:\n    void editor_available_changed(bool available);\n\npublic slots:\n    void set_show_line_numbers(bool visible);\n\n    void open_file_dialog();\n    void save_tab(int index);\n    void save_current_tab();\n    void save_tab_as(int index);\n    void save_current_tab_as();\n    void save_tab_to(int index, const QString &filename);\n    void save_current_tab_to(const QString &filename);\n    void close_tab(int index);\n    void close_current_tab();\n    void close_tab_by_name(QString &filename, bool ask = false);\n\nprivate:\n    void close_tab_unchecked(int index);\n    void confirm_close_tab_dialog(int index);\n\nprivate:\n    QSharedPointer<QSettings> settings;\n    bool line_numbers_visible = true;\n    size_t unknown_editor_counter = 1;\n};\n\n#endif // EDITORDOCK_H\n"
  },
  {
    "path": "src/gui/windows/editor/editortab.cpp",
    "content": "#include \"editortab.h\"\n\n#include \"srceditor.h\"\n\n#include <QtGui/qwindowdefs.h>\n#include <QtWidgets/qboxlayout.h>\n\nEditorTab::EditorTab(bool show_line_numbers, QWidget *parent)\n    : Super(parent)\n    , layout(new QVBoxLayout(this))\n    , editor(new SrcEditor(this))\n    , status_bar_layout(new QHBoxLayout())\n    , status_bar_path(new QLabel(\"Unknown\", this))\n    , status_bar_location(new QLabel(\"0:0\", this)) {\n    layout->setSpacing(0);\n    layout->setContentsMargins(0, 0, 0, 0);\n    layout->addWidget(reinterpret_cast<QWidget *>(editor));\n    status_bar_layout->setSpacing(0);\n    status_bar_layout->setContentsMargins(5, 0, 5, 0);\n    status_bar_layout->addWidget(status_bar_path);\n    status_bar_layout->addWidget(status_bar_location);\n    layout->addLayout(status_bar_layout);\n    status_bar_path->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);\n    status_bar_location->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);\n    connect(editor, &SrcEditor::cursorPositionChanged, this, [this]() {\n        auto cursor = editor->textCursor();\n        status_bar_location->setText(\n            QString(\"%1:%2\").arg(cursor.blockNumber() + 1).arg(cursor.columnNumber() + 1));\n    });\n    connect(editor, &SrcEditor::file_name_change, this, [this]() { elide_file_name(); });\n    set_show_line_number(show_line_numbers);\n}\n\nQString EditorTab::title() {\n    return editor->title();\n}\n\nvoid EditorTab::set_show_line_number(bool visible) {\n    editor->setShowLineNumbers(visible);\n}\n\nvoid EditorTab::resizeEvent(QResizeEvent *event) {\n    QWidget::resizeEvent(event);\n    elide_file_name();\n}\n\nvoid EditorTab::elide_file_name() {\n    auto filename = editor->filename().isEmpty() ? \"Unknown\" : editor->filename();\n    QFontMetrics metrics(status_bar_path->font());\n    int width\n        = status_bar_layout->geometry().width() - status_bar_location->geometry().width() - 10;\n    QString clippedText = metrics.elidedText(filename, Qt::ElideMiddle, width);\n    status_bar_path->setText(clippedText);\n}\n"
  },
  {
    "path": "src/gui/windows/editor/editortab.h",
    "content": "#ifndef EDITORTAB_H\n#define EDITORTAB_H\n\n#include \"common/memory_ownership.h\"\n#include \"srceditor.h\"\n\n#include <QtWidgets/QLabel>\n#include <QtWidgets/QWidget>\n#include <QtWidgets/qboxlayout.h>\n\nclass EditorTab : public QWidget {\n    Q_OBJECT\n    using Super = QWidget;\n\npublic:\n    explicit EditorTab(bool show_line_numbers, QWidget *parent = nullptr);\n    [[nodiscard]] SrcEditor *get_editor() const { return editor; }\n    QString title();\n\npublic slots:\n    void set_show_line_number(bool visible);\n\nprotected:\n    void resizeEvent(QResizeEvent *event) override;\n    void elide_file_name();\n\nprivate:\n    QT_OWNED QVBoxLayout *layout;\n    QT_OWNED SrcEditor *editor;\n    QT_OWNED QHBoxLayout *status_bar_layout;\n    QT_OWNED QLabel *status_bar_path;\n    QT_OWNED QLabel *status_bar_location;\n};\n\n#endif // EDITORTAB_H"
  },
  {
    "path": "src/gui/windows/editor/highlighterasm.cpp",
    "content": "/* Based on Qt example released under BSD license */\n\n#include \"highlighterasm.h\"\n\n#include \"QStringList\"\n#include \"machine/csr/controlstate.h\"\n#include \"machine/instruction.h\"\n\nHighlighterAsm::HighlighterAsm(QTextDocument *parent) : QSyntaxHighlighter(parent) {\n    HighlightingRule rule;\n\n    QTextCharFormat keywordFormat, registerFormat, singleLineCommentFormat, multiLineCommentFormat,\n        quotationFormat;\n    keywordFormat.setForeground(Qt::darkBlue);\n    keywordFormat.setFontWeight(QFont::Bold);\n    registerFormat.setFontWeight(QFont::Bold);\n    registerFormat.setForeground(Qt::darkMagenta);\n    singleLineCommentFormat.setForeground(Qt::darkGray);\n    multiLineCommentFormat.setForeground(Qt::darkGray);\n    quotationFormat.setForeground(Qt::darkGreen);\n\n    {\n        const QStringList keywordPatterns\n            = { QStringLiteral(\"\\\\.org\"),   QStringLiteral(\"\\\\.word\"), QStringLiteral(\"\\\\.text\"),\n                QStringLiteral(\"\\\\.data\"),  QStringLiteral(\"\\\\.bss\"),  QStringLiteral(\"\\\\.option\"),\n                QStringLiteral(\"\\\\.globl\"), QStringLiteral(\"\\\\.set\"),  QStringLiteral(\"\\\\.equ\"),\n                QStringLiteral(\"\\\\.end\"),   QStringLiteral(\"\\\\.ent\"),  QStringLiteral(\"\\\\.ascii\"),\n                QStringLiteral(\"\\\\.asciz\"), QStringLiteral(\"\\\\.byte\"), QStringLiteral(\"\\\\.skip\"),\n                QStringLiteral(\"\\\\.space\") };\n\n        rule.pattern = QRegularExpression(\"(\" + keywordPatterns.join('|') + \")\\\\b\");\n        rule.format = keywordFormat;\n        highlightingRules.append(rule);\n    }\n\n    {\n        QStringList inst_list;\n        machine::Instruction::append_recognized_instructions(inst_list);\n        inst_list.append(\"nop\");\n        rule.pattern = QRegularExpression(\n            QString(\"\\\\b(\" + inst_list.join('|') + \")\\\\b\"),\n            QRegularExpression::CaseInsensitiveOption);\n        rule.format = keywordFormat;\n        highlightingRules.append(rule);\n    }\n\n    {\n        QStringList reg_list;\n        machine::Instruction::append_recognized_registers(reg_list);\n        rule.pattern = QRegularExpression(\"\\\\b(\" + reg_list.join('|') + \"|x[0-9]+)\\\\b\");\n        rule.format = registerFormat;\n        highlightingRules.append(rule);\n    }\n\n    {\n        QTextCharFormat namedValueFormat;\n        namedValueFormat.setFontWeight(QFont::Bold);\n        namedValueFormat.setForeground(Qt::darkMagenta);\n\n        QString pattern = \"\\\\b(\";\n        for (const auto &reg : machine::CSR::REGISTERS) {\n            pattern.append(reg.name).append('|');\n        }\n        pattern = pattern.left(pattern.size() - 1);\n        pattern.append(\")\\\\b\");\n        rule.pattern = QRegularExpression(pattern);\n        rule.format = namedValueFormat;\n        highlightingRules.append(rule);\n    }\n\n    rule.pattern = QRegularExpression(QStringLiteral(\"(;[^\\n]*)|(#[^\\n]*)|(//[^\\n]*)\"));\n    rule.format = singleLineCommentFormat;\n    highlightingRules.append(rule);\n\n    rule.pattern = QRegularExpression(QStringLiteral(\"\\\".*\\\"\"));\n    rule.format = quotationFormat;\n    highlightingRules.append(rule);\n\n    for (auto &rule : highlightingRules) {\n        rule.pattern.optimize();\n    }\n}\n\nvoid HighlighterAsm::highlightBlock(const QString &text) {\n    for (const HighlightingRule &rule : qAsConst(highlightingRules)) {\n        auto matchIterator = rule.pattern.globalMatch(text);\n        while (matchIterator.hasNext()) {\n            QRegularExpressionMatch match = matchIterator.next();\n            setFormat(match.capturedStart(), match.capturedLength(), rule.format);\n        }\n    }\n    setCurrentBlockState(0);\n\n#if 0\n    int startIndex = 0;\n    if (previousBlockState() != 1)\n        startIndex = text.indexOf(commentStartExpression);\n\n    while (startIndex >= 0) {\n        QRegularExpressionMatch match = commentEndExpression.match(text, startIndex);\n        int endIndex = match.capturedStart();\n        int commentLength = 0;\n        if (endIndex == -1) {\n            setCurrentBlockState(1);\n            commentLength = text.length() - startIndex;\n        } else {\n            commentLength = endIndex - startIndex\n                            + match.capturedLength();\n        }\n        setFormat(startIndex, commentLength, multiLineCommentFormat);\n        startIndex = text.indexOf(commentStartExpression, startIndex + commentLength);\n    }\n#endif\n}\n"
  },
  {
    "path": "src/gui/windows/editor/highlighterasm.h",
    "content": "/* Based on Qt example released under BSD license */\n\n#ifndef HIGHLIGHTERASM_H\n#define HIGHLIGHTERASM_H\n\n#include <QRegularExpression>\n#include <QSyntaxHighlighter>\n#include <QTextCharFormat>\n\nQT_BEGIN_NAMESPACE\nclass QTextDocument;\nQT_END_NAMESPACE\n\nclass HighlighterAsm : public QSyntaxHighlighter {\n    Q_OBJECT\n\npublic:\n    explicit HighlighterAsm(QTextDocument *parent = nullptr);\n\nprotected:\n    void highlightBlock(const QString &text) override;\n\nprivate:\n    struct HighlightingRule {\n        QRegularExpression pattern;\n        QTextCharFormat format;\n    };\n    QVector<HighlightingRule> highlightingRules;\n};\n\n#endif // HIGHLIGHTERASM_H\n"
  },
  {
    "path": "src/gui/windows/editor/highlighterc.cpp",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2016 The Qt Company Ltd.\n** Contact: https://www.qt.io/licensing/\n**\n** This file is part of the examples of the Qt Toolkit.\n**\n** $QT_BEGIN_LICENSE:BSD$\n** Commercial License Usage\n** Licensees holding valid commercial Qt licenses may use this file in\n** accordance with the commercial license agreement provided with the\n** Software or, alternatively, in accordance with the terms contained in\n** a written agreement between you and The Qt Company. For licensing terms\n** and conditions see https://www.qt.io/terms-conditions. For further\n** information use the contact form at https://www.qt.io/contact-us.\n**\n** BSD License Usage\n** Alternatively, you may use this file under the terms of the BSD license\n** as follows:\n**\n** \"Redistribution and use in source and binary forms, with or without\n** modification, are permitted provided that the following conditions are\n** met:\n**   * Redistributions of source code must retain the above copyright\n**     notice, this list of conditions and the following disclaimer.\n**   * Redistributions in binary form must reproduce the above copyright\n**     notice, this list of conditions and the following disclaimer in\n**     the documentation and/or other materials provided with the\n**     distribution.\n**   * Neither the name of The Qt Company Ltd nor the names of its\n**     contributors may be used to endorse or promote products derived\n**     from this software without specific prior written permission.\n**\n**\n** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n** \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\"\n**\n** $QT_END_LICENSE$\n**\n****************************************************************************/\n\n#include \"highlighterc.h\"\n\n//! [0]\nHighlighterC::HighlighterC(QTextDocument *parent) : QSyntaxHighlighter(parent) {\n    HighlightingRule rule;\n\n    keywordFormat.setForeground(Qt::darkBlue);\n    keywordFormat.setFontWeight(QFont::Bold);\n    const QString keywordPatterns[]\n        = { QStringLiteral(\"\\\\bchar\\\\b\"),      QStringLiteral(\"\\\\bclass\\\\b\"),\n            QStringLiteral(\"\\\\bconst\\\\b\"),     QStringLiteral(\"\\\\bdouble\\\\b\"),\n            QStringLiteral(\"\\\\benum\\\\b\"),      QStringLiteral(\"\\\\bexplicit\\\\b\"),\n            QStringLiteral(\"\\\\bfriend\\\\b\"),    QStringLiteral(\"\\\\binline\\\\b\"),\n            QStringLiteral(\"\\\\bint\\\\b\"),       QStringLiteral(\"\\\\blong\\\\b\"),\n            QStringLiteral(\"\\\\bnamespace\\\\b\"), QStringLiteral(\"\\\\boperator\\\\b\"),\n            QStringLiteral(\"\\\\bprivate\\\\b\"),   QStringLiteral(\"\\\\bprotected\\\\b\"),\n            QStringLiteral(\"\\\\bpublic\\\\b\"),    QStringLiteral(\"\\\\bshort\\\\b\"),\n            QStringLiteral(\"\\\\bsignals\\\\b\"),   QStringLiteral(\"\\\\bsigned\\\\b\"),\n            QStringLiteral(\"\\\\bslots\\\\b\"),     QStringLiteral(\"\\\\bstatic\\\\b\"),\n            QStringLiteral(\"\\\\bstruct\\\\b\"),    QStringLiteral(\"\\\\btemplate\\\\b\"),\n            QStringLiteral(\"\\\\btypedef\\\\b\"),   QStringLiteral(\"\\\\btypename\\\\b\"),\n            QStringLiteral(\"\\\\bunion\\\\b\"),     QStringLiteral(\"\\\\bunsigned\\\\b\"),\n            QStringLiteral(\"\\\\bvirtual\\\\b\"),   QStringLiteral(\"\\\\bvoid\\\\b\"),\n            QStringLiteral(\"\\\\bvolatile\\\\b\"),  QStringLiteral(\"\\\\bbool\\\\b\") };\n    for (const QString &pattern : keywordPatterns) {\n        rule.pattern = QRegularExpression(pattern);\n        rule.format = keywordFormat;\n        highlightingRules.append(rule);\n        //! [0] //! [1]\n    }\n    //! [1]\n\n    //! [2]\n    classFormat.setFontWeight(QFont::Bold);\n    classFormat.setForeground(Qt::darkMagenta);\n    rule.pattern = QRegularExpression(QStringLiteral(\"\\\\bQ[A-Za-z]+\\\\b\"));\n    rule.format = classFormat;\n    highlightingRules.append(rule);\n    //! [2]\n\n    //! [3]\n    singleLineCommentFormat.setForeground(Qt::red);\n    rule.pattern = QRegularExpression(QStringLiteral(\"//[^\\n]*\"));\n    rule.format = singleLineCommentFormat;\n    highlightingRules.append(rule);\n\n    multiLineCommentFormat.setForeground(Qt::red);\n    //! [3]\n\n    //! [4]\n    quotationFormat.setForeground(Qt::darkGreen);\n    rule.pattern = QRegularExpression(QStringLiteral(\"\\\".*\\\"\"));\n    rule.format = quotationFormat;\n    highlightingRules.append(rule);\n    //! [4]\n\n    //! [5]\n    functionFormat.setFontItalic(true);\n    functionFormat.setForeground(Qt::blue);\n    rule.pattern = QRegularExpression(QStringLiteral(\"\\\\b[A-Za-z0-9_]+(?=\\\\()\"));\n    rule.format = functionFormat;\n    highlightingRules.append(rule);\n    //! [5]\n\n    //! [6]\n    commentStartExpression = QRegularExpression(QStringLiteral(\"/\\\\*\"));\n    commentEndExpression = QRegularExpression(QStringLiteral(\"\\\\*/\"));\n}\n//! [6]\n\n//! [7]\nvoid HighlighterC::highlightBlock(const QString &text) {\n#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)\n    foreach (const HighlightingRule &rule, highlightingRules)\n#else\n    for (const HighlightingRule &rule : qAsConst(highlightingRules))\n#endif\n    {\n        QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);\n        while (matchIterator.hasNext()) {\n            QRegularExpressionMatch match = matchIterator.next();\n            setFormat(match.capturedStart(), match.capturedLength(), rule.format);\n        }\n    }\n    //! [7] //! [8]\n    setCurrentBlockState(0);\n    //! [8]\n\n    //! [9]\n    int startIndex = 0;\n    if (previousBlockState() != 1) { startIndex = text.indexOf(commentStartExpression); }\n\n    //! [9] //! [10]\n    while (startIndex >= 0) {\n        //! [10] //! [11]\n        QRegularExpressionMatch match = commentEndExpression.match(text, startIndex);\n        int endIndex = match.capturedStart();\n        int commentLength = 0;\n        if (endIndex == -1) {\n            setCurrentBlockState(1);\n            commentLength = text.length() - startIndex;\n        } else {\n            commentLength = endIndex - startIndex + match.capturedLength();\n        }\n        setFormat(startIndex, commentLength, multiLineCommentFormat);\n        startIndex = text.indexOf(commentStartExpression, startIndex + commentLength);\n    }\n}\n//! [11]\n"
  },
  {
    "path": "src/gui/windows/editor/highlighterc.h",
    "content": "/****************************************************************************\n**\n** Copyright (C) 2016 The Qt Company Ltd.\n** Contact: https://www.qt.io/licensing/\n**\n** This file is part of the examples of the Qt Toolkit.\n**\n** $QT_BEGIN_LICENSE:BSD$\n** Commercial License Usage\n** Licensees holding valid commercial Qt licenses may use this file in\n** accordance with the commercial license agreement provided with the\n** Software or, alternatively, in accordance with the terms contained in\n** a written agreement between you and The Qt Company. For licensing terms\n** and conditions see https://www.qt.io/terms-conditions. For further\n** information use the contact form at https://www.qt.io/contact-us.\n**\n** BSD License Usage\n** Alternatively, you may use this file under the terms of the BSD license\n** as follows:\n**\n** \"Redistribution and use in source and binary forms, with or without\n** modification, are permitted provided that the following conditions are\n** met:\n**   * Redistributions of source code must retain the above copyright\n**     notice, this list of conditions and the following disclaimer.\n**   * Redistributions in binary form must reproduce the above copyright\n**     notice, this list of conditions and the following disclaimer in\n**     the documentation and/or other materials provided with the\n**     distribution.\n**   * Neither the name of The Qt Company Ltd nor the names of its\n**     contributors may be used to endorse or promote products derived\n**     from this software without specific prior written permission.\n**\n**\n** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n** \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\"\n**\n** $QT_END_LICENSE$\n**\n****************************************************************************/\n\n#ifndef HIGHLIGHTERC_H\n#define HIGHLIGHTERC_H\n\n#include <QRegularExpression>\n#include <QSyntaxHighlighter>\n#include <QTextCharFormat>\n\nQT_BEGIN_NAMESPACE\nclass QTextDocument;\nQT_END_NAMESPACE\n\n//! [0]\nclass HighlighterC : public QSyntaxHighlighter {\n    Q_OBJECT\n\npublic:\n    explicit HighlighterC(QTextDocument *parent = nullptr);\n\nprotected:\n    void highlightBlock(const QString &text) override;\n\nprivate:\n    struct HighlightingRule {\n        QRegularExpression pattern;\n        QTextCharFormat format;\n    };\n    QVector<HighlightingRule> highlightingRules;\n\n    QRegularExpression commentStartExpression;\n    QRegularExpression commentEndExpression;\n\n    QTextCharFormat keywordFormat;\n    QTextCharFormat classFormat;\n    QTextCharFormat singleLineCommentFormat;\n    QTextCharFormat multiLineCommentFormat;\n    QTextCharFormat quotationFormat;\n    QTextCharFormat functionFormat;\n};\n//! [0]\n\n#endif // HIGHLIGHTERC_H\n"
  },
  {
    "path": "src/gui/windows/editor/linenumberarea.cpp",
    "content": "#include \"linenumberarea.h\"\n\n#include \"srceditor.h\"\n\n#include <cmath>\n#include <qpainter.h>\n#include <qsize.h>\n\nconstexpr int RIGHT_MARGIN = 5;\nconstexpr int RIGHT_PADDING = 5;\nconstexpr int LEFT_PADDING = 5;\n\nLineNumberArea::LineNumberArea(SrcEditor *editor_) : QWidget(editor_), editor(editor_) {}\n\nQSize LineNumberArea::sizeHint() const {\n    if (!line_numbers_visible) { return { 0, 0 }; }\n\n    int digits = std::log10(std::max(1, editor->blockCount())) + 2;\n    int space = editor->fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits + LEFT_PADDING\n                + RIGHT_PADDING + RIGHT_MARGIN;\n    return { space, 0 };\n}\nvoid LineNumberArea::paintEvent(QPaintEvent *event) {\n    QPainter painter(this);\n    painter.fillRect(event->rect(), palette().base());\n    painter.drawLine(\n        event->rect().right() - RIGHT_MARGIN, 0, event->rect().right() - RIGHT_MARGIN,\n        event->rect().bottom());\n\n    QTextBlock block = editor->firstVisibleBlock();\n    int blockNumber = block.blockNumber();\n    int top\n        = qRound(editor->blockBoundingGeometry(block).translated(editor->contentOffset()).top());\n    int bottom = top + qRound(editor->blockBoundingRect(block).height());\n    while (block.isValid() && top <= event->rect().bottom()) {\n        if (block.isVisible() && bottom >= event->rect().top()) {\n            QString number = QString::number(blockNumber + 1);\n            painter.setPen(palette().windowText().color());\n            painter.drawText(\n                0, top, this->sizeHint().width() - RIGHT_PADDING - RIGHT_MARGIN,\n                editor->fontMetrics().height(), Qt::AlignRight, number);\n        }\n\n        block = block.next();\n        top = bottom;\n        bottom = top + qRound(editor->blockBoundingRect(block).height());\n        ++blockNumber;\n    }\n}\nvoid LineNumberArea::set(bool visible) {\n    QWidget::setVisible(visible);\n    line_numbers_visible = visible;\n}\n"
  },
  {
    "path": "src/gui/windows/editor/linenumberarea.h",
    "content": "#ifndef LINENUMBERAREA_H\n#define LINENUMBERAREA_H\n\n#include \"common/memory_ownership.h\"\n\n#include <QWidget>\n\nclass SrcEditor;\n\nclass LineNumberArea : public QWidget {\npublic:\n    explicit LineNumberArea(BORROWED SrcEditor *editor_);\n    void set(bool visible);\n\n    [[nodiscard]] QSize sizeHint() const override;\n\nprotected:\n    void paintEvent(QPaintEvent *event) override;\n\nprivate:\n    BORROWED SrcEditor *editor;\n    bool line_numbers_visible = false;\n};\n\n#endif // LINENUMBERAREA_H"
  },
  {
    "path": "src/gui/windows/editor/srceditor.cpp",
    "content": "#include \"srceditor.h\"\n\n#include \"common/logging.h\"\n#include \"editordock.h\"\n#include \"editortab.h\"\n#include \"linenumberarea.h\"\n#include \"windows/editor/highlighterasm.h\"\n#include \"windows/editor/highlighterc.h\"\n\n#include <QCoreApplication>\n#include <QFile>\n#include <QFileInfo>\n#include <QPainter>\n#include <QTextCursor>\n#include <QTextDocumentWriter>\n#include <qglobal.h>\n#include <qpalette.h>\n#include <qplaintextedit.h>\n#include <qstyle.h>\n\nLOG_CATEGORY(\"gui.src_editor\");\n\nSrcEditor::SrcEditor(QWidget *parent) : Super(parent), line_number_area(new LineNumberArea(this)) {\n    QFont font1;\n    saveAsRequiredFl = true;\n    font1.setFamily(\"Courier\");\n    font1.setFixedPitch(true);\n    font1.setPointSize(10);\n    setFont(font1);\n    tname = \"Unknown\";\n    highlighter.reset(new HighlighterAsm(document()));\n\n    QPalette p = palette();\n    p.setColor(QPalette::Base, Qt::white);\n    p.setColor(QPalette::Text, Qt::black);\n    p.setColor(QPalette::WindowText, Qt::darkGray);\n    setPalette(p);\n\n    // Set tab width to 4 spaces\n    setTabStopDistance(fontMetrics().horizontalAdvance(' ') * TAB_WIDTH);\n\n    connect(this, &SrcEditor::blockCountChanged, this, &SrcEditor::updateMargins);\n    connect(this, &SrcEditor::updateRequest, this, &SrcEditor::updateLineNumberArea);\n\n    // Clear error highlight on typing\n    connect(this, &SrcEditor::textChanged, [this]() { setExtraSelections({}); });\n\n    updateMargins(0);\n}\n\nQString SrcEditor::filename() const {\n    return fname;\n}\n\nQString SrcEditor::title() {\n    return tname;\n}\n\nvoid SrcEditor::setFileName(const QString &filename) {\n    QFileInfo fi(filename);\n    saveAsRequiredFl = filename.isEmpty() || filename.startsWith(\":/\");\n\n    fname = filename;\n    tname = fi.fileName();\n    if ((fi.suffix() == \"c\") || (fi.suffix() == \"C\") || (fi.suffix() == \"cpp\")\n        || ((fi.suffix() == \"c++\"))) {\n        highlighter.reset(new HighlighterC(document()));\n    } else {\n        highlighter.reset(new HighlighterAsm(document()));\n    }\n\n    emit file_name_change();\n}\n\nbool SrcEditor::loadFile(const QString &filename) {\n    QFile file(filename);\n    if (file.open(QFile::ReadOnly | QFile::Text)) {\n        setPlainText(file.readAll());\n        setFileName(filename);\n        return true;\n    } else {\n        return false;\n    }\n}\n\nbool SrcEditor::loadByteArray(const QByteArray &content, const QString &filename) {\n    setPlainText(QString::fromUtf8(content.data(), content.size()));\n    if (!filename.isEmpty()) { setFileName(filename); }\n    return true;\n}\n\nbool SrcEditor::saveFile(QString filename) {\n    if (filename.isEmpty()) { filename = this->filename(); }\n    if (filename.isEmpty()) { return false; }\n    QTextDocumentWriter writer(filename);\n    writer.setFormat(\"plaintext\");\n    bool success = writer.write(document());\n    setFileName(filename);\n    if (success) { document()->setModified(false); }\n    return success;\n}\n\nvoid SrcEditor::setCursorToLine(int ln) {\n    QTextCursor cursor(document()->findBlockByNumber(ln - 1));\n    setTextCursor(cursor);\n}\n\nvoid SrcEditor::setCursorTo(int ln, int col) {\n    QTextCursor cursor(document()->findBlockByNumber(ln - 1));\n    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, col - 1);\n    setTextCursor(cursor);\n    setFocus();\n}\n\nbool SrcEditor::isModified() const {\n    return document()->isModified();\n}\n\nvoid SrcEditor::setModified(bool val) {\n    document()->setModified(val);\n}\n\nvoid SrcEditor::setSaveAsRequired(bool val) {\n    saveAsRequiredFl = val;\n}\nbool SrcEditor::saveAsRequired() const {\n    return saveAsRequiredFl;\n}\n\nvoid SrcEditor::keyPressEvent(QKeyEvent *event) {\n    QTextCursor cursor = textCursor();\n    if (cursor.hasSelection()) {\n        switch (event->key()) {\n        case Qt::Key_Tab: {\n            indent_selection(cursor);\n            return;\n        }\n        case Qt::Key_Backtab: {\n            unindent_selection(cursor);\n            return;\n        }\n        case Qt::Key_Slash:\n            if (event->modifiers() & Qt::ControlModifier) {\n                toggle_selection_comment(cursor, is_selection_comment());\n                return;\n            }\n            break;\n        default: break;\n        }\n    }\n\n    switch (event->key()) {\n    case Qt::Key_Return: { // Keep indentation\n        QString txt = cursor.block().text();\n        QString indent;\n        for (auto ch : txt) {\n            if (ch.isSpace()) {\n                indent.append(ch);\n            } else {\n                break;\n            }\n        }\n        cursor.insertText(\"\\n\");\n        cursor.insertText(indent);\n        setTextCursor(cursor);\n        return;\n    }\n    case Qt::Key_Slash: {\n        if (event->modifiers() & Qt::ControlModifier) {\n            // Toggle comment\n            if (cursor.block().text().startsWith(\"//\")) {\n                cursor.movePosition(QTextCursor::StartOfLine);\n                cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);\n                cursor.removeSelectedText();\n            } else {\n                cursor.movePosition(QTextCursor::StartOfLine);\n                cursor.insertText(\"//\");\n            }\n            return;\n        }\n        break;\n    }\n    }\n\n    QPlainTextEdit::keyPressEvent(event);\n}\n\nvoid SrcEditor::indent_selection(QTextCursor &cursor) {\n    auto end = cursor.selectionEnd();\n    cursor.beginEditBlock();\n    cursor.setPosition(cursor.selectionStart());\n    cursor.movePosition(QTextCursor::StartOfLine);\n    while (cursor.position() < end) {\n        cursor.insertText(\"\\t\");\n        if (!cursor.movePosition(QTextCursor::Down)) break;\n    }\n    cursor.endEditBlock();\n}\n\nvoid SrcEditor::unindent_selection(QTextCursor &cursor) {\n    cursor.beginEditBlock();\n    auto end_line = document()->findBlock(cursor.selectionEnd()).blockNumber();\n    cursor.setPosition(cursor.selectionStart());\n    cursor.movePosition(QTextCursor::StartOfLine);\n    while (cursor.blockNumber() <= end_line) {\n        auto txt = cursor.block().text();\n        if (txt.isEmpty()) {\n            // Empty line, skip\n        } else if (txt.startsWith(\"\\t\")) {\n            cursor.deleteChar();\n        } else if (txt.startsWith(\" \")) {\n            // Delete at most TAB_WIDTH spaces\n            unsigned to_delete = std::min<unsigned>(txt.size(), TAB_WIDTH);\n            while (to_delete > 0 && cursor.block().text().startsWith(\" \")) {\n                cursor.deleteChar();\n                to_delete--;\n            }\n        }\n        if (!cursor.movePosition(QTextCursor::Down)) break;\n    }\n    cursor.endEditBlock();\n}\n\nbool SrcEditor::is_selection_comment() {\n    QTextCursor cursor = textCursor();\n    bool all_commented = true;\n\n    auto end_line = document()->findBlock(cursor.selectionEnd()).blockNumber();\n    while (cursor.blockNumber() <= end_line) {\n        auto txt = cursor.block().text();\n        if (!txt.startsWith(\"//\")) {\n            all_commented = false;\n            break;\n        }\n        if (!cursor.movePosition(QTextCursor::Down)) break;\n    }\n\n    return all_commented;\n}\n\nvoid SrcEditor::toggle_selection_comment(QTextCursor &cursor, bool is_comment) {\n    auto end_line = document()->findBlock(cursor.selectionEnd()).blockNumber();\n    cursor.beginEditBlock();\n    cursor.setPosition(cursor.selectionStart());\n    cursor.movePosition(QTextCursor::StartOfLine);\n    while (cursor.blockNumber() <= end_line) {\n        if (is_comment) {\n            cursor.deleteChar();\n            cursor.deleteChar();\n        } else {\n            cursor.insertText(\"//\");\n        }\n        if (!cursor.movePosition(QTextCursor::Down)) break;\n    }\n    cursor.endEditBlock();\n}\n\nvoid SrcEditor::updateMargins(int /* newBlockCount */) {\n    setViewportMargins(line_number_area->sizeHint().width(), 0, 0, 0);\n}\n\nvoid SrcEditor::updateLineNumberArea(const QRect &rect, int dy) {\n    if (dy) {\n        line_number_area->scroll(0, dy);\n    } else {\n        line_number_area->update(0, rect.y(), line_number_area->width(), rect.height());\n    }\n\n    if (rect.contains(viewport()->rect())) updateMargins(0);\n}\n\nvoid SrcEditor::resizeEvent(QResizeEvent *event) {\n    QPlainTextEdit::resizeEvent(event);\n\n    QRect cr = contentsRect();\n    line_number_area->setGeometry(\n        QRect(cr.left(), cr.top(), line_number_area->sizeHint().width(), cr.height()));\n}\n\nvoid SrcEditor::setShowLineNumbers(bool show) {\n    line_number_area->set(show);\n    updateMargins(0);\n}\n\nvoid SrcEditor::insertFromMimeData(const QMimeData *source) {\n    if (source->hasText()) { insertPlainText(source->text()); }\n}\n\nbool SrcEditor::canInsertFromMimeData(const QMimeData *source) const {\n    return source->hasText();\n}\n"
  },
  {
    "path": "src/gui/windows/editor/srceditor.h",
    "content": "#ifndef SRCEDITOR_H\n#define SRCEDITOR_H\n\n#include \"common/memory_ownership.h\"\n#include \"linenumberarea.h\"\n#include \"machine/machine.h\"\n\n#include <QMimeData>\n#include <QString>\n#include <QSyntaxHighlighter>\n#include <QTextEdit>\n#include <qplaintextedit.h>\n#include <qwidget.h>\n\nclass SrcEditor : public QPlainTextEdit {\n    Q_OBJECT\n    using Super = QPlainTextEdit;\n\npublic:\n    explicit SrcEditor(QWidget *parent);\n    [[nodiscard]] QString filename() const;\n    QString title();\n    bool loadFile(const QString &filename);\n    bool saveFile(QString filename = \"\");\n    bool loadByteArray(const QByteArray &content, const QString &filename = \"\");\n    void setCursorToLine(int ln);\n    void setCursorTo(int ln, int col);\n    void setFileName(const QString &filename);\n    [[nodiscard]] bool isModified() const;\n    void setModified(bool val);\n    void setSaveAsRequired(bool val);\n    [[nodiscard]] bool saveAsRequired() const;\n\nprotected:\n    void keyPressEvent(QKeyEvent *event) override;\n    void resizeEvent(QResizeEvent *event) override;\n    void insertFromMimeData(const QMimeData *source) override;\n    bool canInsertFromMimeData(const QMimeData *source) const override;\n\nsignals:\n    void file_name_change();\n\npublic slots:\n    void setShowLineNumbers(bool visible);\n\nprivate slots:\n    void updateMargins(int newBlockCount);\n    void updateLineNumberArea(const QRect &rect, int dy);\n\nprivate:\n    ::Box<QSyntaxHighlighter> highlighter {};\n    LineNumberArea *line_number_area;\n    bool line_numbers_visible = true;\n    QString fname;\n    QString tname;\n    bool saveAsRequiredFl {};\n    /** Width of a tab character in spaces. */\n    static constexpr unsigned TAB_WIDTH = 4;\n\n    /** Indents selected lines by one tab. */\n    void indent_selection(QTextCursor &cursor);\n\n    /** Unindents selected lines by one tab or 4 spaces from the beginning of each line.\n     * If only some lines contain less prefix whitespace, remove as much as possible to mimic\n     * VS Code behavior. */\n    void unindent_selection(QTextCursor &cursor);\n\n    /** Returns true if all lines in the selection are commented out. */\n    bool is_selection_comment();\n\n    /** Comments out all lines in the selection. */\n    void toggle_selection_comment(QTextCursor &cursor, bool is_comment);\n\n    friend class LineNumberArea;\n};\n\n#endif // SRCEDITOR_H\n"
  },
  {
    "path": "src/gui/windows/lcd/lcddisplaydock.cpp",
    "content": "#include \"lcddisplaydock.h\"\n\n#include \"lcddisplayview.h\"\n\n#include <QString>\n#include <QTextBlock>\n\nLcdDisplayDock::LcdDisplayDock(QWidget *parent, QSettings *settings) : Super(parent) {\n    (void)settings;\n    lcd_display_widget.reset(new LcdDisplayView(this));\n    auto *fill_widget = new QWidget(this);\n\n    layout = new QBoxLayout(QBoxLayout::LeftToRight, fill_widget);\n    // add spacer, then your widget, then spacer\n    layout->addItem(new QSpacerItem(0, 0));\n    layout->addWidget(lcd_display_widget.data());\n    layout->addItem(new QSpacerItem(0, 0));\n    fill_widget->setLayout(layout);\n    setWidget(fill_widget);\n\n    setObjectName(\"LCD Display\");\n    setWindowTitle(\"LCD Display\");\n}\n\nvoid LcdDisplayDock::setup(machine::LcdDisplay *lcd_display) {\n    lcd_display_widget->setup(lcd_display);\n    update_layout(width(), height());\n}\n\nvoid LcdDisplayDock::update_layout(int w, int h) {\n    // Keeping the aspect ratio based on\n    // https://stackoverflow.com/questions/30005540/keeping-the-aspect-ratio-of-a-sub-classed-qwidget-during-resize\n\n    float thisAspectRatio = (float)w / h;\n    int widgetStretch, outerStretch;\n    float arWidth = lcd_display_widget->fb_width();   // aspect ratio width\n    float arHeight = lcd_display_widget->fb_height(); // aspect ratio height\n\n    if ((arWidth == 0) || (arHeight == 0)) {\n        outerStretch = 0;\n        widgetStretch = 1;\n    } else if (thisAspectRatio > (arWidth / arHeight)) { // too wide\n        layout->setDirection(QBoxLayout::LeftToRight);\n        widgetStretch = height() * (arWidth / arHeight); // i.e., my width\n        outerStretch = (width() - widgetStretch) / 2 + 0.5;\n    } else { // too tall\n        layout->setDirection(QBoxLayout::TopToBottom);\n        widgetStretch = width() * (arHeight / arWidth); // i.e., my height\n        outerStretch = (height() - widgetStretch) / 2 + 0.5;\n    }\n\n    layout->setStretch(0, outerStretch);\n    layout->setStretch(1, widgetStretch);\n    layout->setStretch(2, outerStretch);\n}\n\nvoid LcdDisplayDock::resizeEvent(QResizeEvent *event) {\n    // Keeping the aspect ratio based on\n    // https://stackoverflow.com/questions/30005540/keeping-the-aspect-ratio-of-a-sub-classed-qwidget-during-resize\n\n    update_layout(event->size().width(), event->size().height());\n\n    Super::resizeEvent(event);\n}\n"
  },
  {
    "path": "src/gui/windows/lcd/lcddisplaydock.h",
    "content": "#ifndef LCDDISPLAYDOCK_H\n#define LCDDISPLAYDOCK_H\n\n#include \"lcddisplayview.h\"\n#include \"machine/machine.h\"\n\n#include <QBoxLayout>\n#include <QDockWidget>\n\nclass LcdDisplayDock : public QDockWidget {\n    Q_OBJECT\n\n    using Super = QDockWidget;\n\npublic:\n    LcdDisplayDock(QWidget *parent, QSettings *settings);\n    void resizeEvent(QResizeEvent *event) override;\n\n    void setup(machine::LcdDisplay *lcd_display);\n\n    // public slots:\n\nprivate:\n    void update_layout(int w, int h);\n\n    QT_OWNED QBoxLayout *layout;\n    Box<LcdDisplayView> lcd_display_widget;\n};\n\n#endif // LCDDISPLAYDOCK_H\n"
  },
  {
    "path": "src/gui/windows/lcd/lcddisplayview.cpp",
    "content": "#include \"lcddisplayview.h\"\n\n#include <QPaintEvent>\n#include <QPainter>\n#include <QStyle>\n\nLcdDisplayView::LcdDisplayView(QWidget *parent) : Super(parent) {\n    setMinimumSize(100, 100);\n    scale_x = 1.0;\n    scale_y = 1.0;\n}\n\nvoid LcdDisplayView::setup(machine::LcdDisplay *lcd_display) {\n    if (lcd_display == nullptr) { return; }\n    connect(lcd_display, &machine::LcdDisplay::pixel_update, this, &LcdDisplayView::pixel_update);\n    fb_pixels.reset(\n        new QImage(lcd_display->get_width(), lcd_display->get_height(), QImage::Format_RGB32));\n    fb_pixels->fill(qRgb(0, 0, 0));\n    update_scale();\n    update();\n}\n\nvoid LcdDisplayView::pixel_update(size_t x, size_t y, uint r, uint g, uint b) {\n    int x1, y1, x2, y2;\n    if (fb_pixels != nullptr) {\n        fb_pixels->setPixel(x, y, qRgb(r, g, b));\n        x1 = x * scale_x - 2;\n        if (x1 < 0) { x1 = 0; }\n        x2 = x * scale_x + 2;\n        if (x2 > width()) { x2 = width(); }\n        y1 = y * scale_y - 2;\n        if (y1 < 0) { y1 = 0; }\n        y2 = y * scale_y + 2;\n        if (y2 > height()) { y2 = height(); }\n        update(x1, y1, x2 - x1, y2 - y1);\n    }\n}\n\nvoid LcdDisplayView::update_scale() {\n    if (fb_pixels != nullptr) {\n        if ((fb_pixels->width() != 0) && (fb_pixels->height() != 0)) {\n            scale_x = (float)width() / fb_pixels->width();\n            scale_y = (float)height() / fb_pixels->height();\n            return;\n        }\n    }\n    scale_x = 1.0;\n    scale_y = 1.0;\n}\n\nvoid LcdDisplayView::paintEvent(QPaintEvent *event) {\n    if (fb_pixels == nullptr) { return Super::paintEvent(event); }\n    if (fb_pixels->width() == 0) { return Super::paintEvent(event); }\n\n    QPainter painter(this);\n    painter.drawImage(rect(), *fb_pixels);\n#if 0\n    painter.setPen(QPen(QColor(255, 255, 0)));\n    painter.drawLine(event->rect().topLeft(),event->rect().topRight());\n    painter.drawLine(event->rect().topLeft(),event->rect().bottomLeft());\n    painter.drawLine(event->rect().topLeft(),event->rect().bottomRight());\n#endif\n}\n\nvoid LcdDisplayView::resizeEvent(QResizeEvent *event) {\n    Super::resizeEvent(event);\n    update_scale();\n}\n\nuint LcdDisplayView::fb_width() {\n    if (fb_pixels == nullptr) { return 0; }\n    return fb_pixels->width();\n}\n\nuint LcdDisplayView::fb_height() {\n    if (fb_pixels == nullptr) { return 0; }\n    return fb_pixels->height();\n}\n"
  },
  {
    "path": "src/gui/windows/lcd/lcddisplayview.h",
    "content": "#ifndef LCDDISPLAYVIEW_H\n#define LCDDISPLAYVIEW_H\n\n#include \"common/memory_ownership.h\"\n#include \"machine/memory/backend/lcddisplay.h\"\n\n#include <QImage>\n#include <QWidget>\n\nclass LcdDisplayView : public QWidget {\n    Q_OBJECT\n\n    using Super = QWidget;\n\npublic:\n    explicit LcdDisplayView(QWidget *parent = nullptr);\n\n    void setup(machine::LcdDisplay *lcd_display);\n    uint fb_width();\n    uint fb_height();\n\npublic slots:\n    void pixel_update(std::size_t x, std::size_t y, uint r, uint g, uint b);\n\nprotected:\n    void paintEvent(QPaintEvent *event) override;\n    void resizeEvent(QResizeEvent *event) override;\n\nprivate:\n    void update_scale();\n    float scale_x;\n    float scale_y;\n    Box<QImage> fb_pixels;\n};\n\n#endif // LCDDISPLAYVIEW_H\n"
  },
  {
    "path": "src/gui/windows/memory/memorydock.cpp",
    "content": "#include \"memorydock.h\"\n\n#include \"memorymodel.h\"\n#include \"memorytableview.h\"\n#include \"ui/hexlineedit.h\"\n\n#include <QComboBox>\n#include <QHeaderView>\n#include <QVBoxLayout>\n#include <QWidget>\n\nMemoryDock::MemoryDock(QWidget *parent, QSettings *settings) : Super(parent) {\n    setObjectName(\"Memory\");\n    setWindowTitle(\"Memory\");\n\n    auto *content = new QWidget();\n\n    auto *cell_size = new QComboBox();\n    cell_size->addItem(\"Byte\", MemoryModel::CELLSIZE_BYTE);\n    cell_size->addItem(\"Half-word\", MemoryModel::CELLSIZE_HWORD);\n    cell_size->addItem(\"Word\", MemoryModel::CELLSIZE_WORD);\n    cell_size->setCurrentIndex(MemoryModel::CELLSIZE_WORD);\n\n    auto *cached_access = new QComboBox();\n    cached_access->addItem(\"Direct\", 0);\n    cached_access->addItem(\"Cached\", 1);\n    cached_access->addItem(\"As CPU (VMA)\", 2);\n\n    auto *memory_content = new MemoryTableView(nullptr, settings);\n    // memory_content->setSizePolicy();\n    auto *memory_model = new MemoryModel(this);\n    memory_content->setModel(memory_model);\n    memory_content->verticalHeader()->hide();\n    // memory_content->setHorizontalHeader(memory_model->);\n\n    auto *go_edit = new HexLineEdit(nullptr, 8, 16, \"0x\");\n\n    auto *layout_top = new QHBoxLayout;\n    layout_top->addWidget(cell_size);\n    layout_top->addWidget(cached_access);\n    auto *layout = new QVBoxLayout;\n    layout->addLayout(layout_top);\n    layout->addWidget(memory_content);\n    layout->addWidget(go_edit);\n\n    content->setLayout(layout);\n\n    setWidget(content);\n\n    connect(this, &MemoryDock::machine_setup, memory_model, &MemoryModel::setup);\n    connect(this, &MemoryDock::machine_setup, memory_model, &MemoryModel::setup);\n    connect(\n        cell_size, QOverload<int>::of(&QComboBox::currentIndexChanged), memory_content,\n        &MemoryTableView::set_cell_size);\n    connect(\n        cached_access, QOverload<int>::of(&QComboBox::currentIndexChanged), memory_model,\n        &MemoryModel::cached_access);\n    connect(\n        go_edit, &HexLineEdit::value_edit_finished, memory_content,\n        [memory_content](uint32_t value) {\n            memory_content->go_to_address(machine::Address(value));\n        });\n    connect(\n        memory_content, &MemoryTableView::address_changed, go_edit,\n        [go_edit](machine::Address addr) { go_edit->set_value(addr.get_raw()); });\n    connect(this, &MemoryDock::focus_addr, memory_content, &MemoryTableView::focus_address);\n    connect(\n        memory_model, &MemoryModel::setup_done, memory_content,\n        &MemoryTableView::recompute_columns);\n}\n\nvoid MemoryDock::setup(machine::Machine *machine) {\n    emit machine_setup(machine);\n}\n"
  },
  {
    "path": "src/gui/windows/memory/memorydock.h",
    "content": "#ifndef MEMORYDOCK_H\n#define MEMORYDOCK_H\n\n#include \"machine/machine.h\"\n#include \"machine/memory/address.h\"\n\n#include <QComboBox>\n#include <QDockWidget>\n\nclass MemoryDock : public QDockWidget {\n    Q_OBJECT\n\n    using Super = QDockWidget;\n\npublic:\n    MemoryDock(QWidget *parent, QSettings *settings);\n\n    void setup(machine::Machine *machine);\n\nsignals:\n    void machine_setup(machine::Machine *machine);\n    void focus_addr(machine::Address);\n\nprivate:\n    machine::Machine *machinePtr;\n};\n\n#endif // MEMORYDOCK_H"
  },
  {
    "path": "src/gui/windows/memory/memorymodel.cpp",
    "content": "#include \"memorymodel.h\"\n\n#include <QBrush>\n\nusing ae = machine::AccessEffects; // For enum values, the type is obvious from context.\n\nMemoryModel::MemoryModel(QObject *parent) : Super(parent), data_font(\"Monospace\") {\n    cell_size = CELLSIZE_WORD;\n    cells_per_row = 1;\n    index0_offset = machine::Address::null();\n    data_font.setStyleHint(QFont::TypeWriter);\n    machine = nullptr;\n    memory_change_counter = 0;\n    cache_data_change_counter = 0;\n    mem_access_kind = MEM_ACC_AS_CPU;\n}\n\nconst machine::FrontendMemory *MemoryModel::mem_access() const {\n    if (machine == nullptr) { return nullptr; }\n    if (machine->memory_data_bus() != nullptr) { return machine->memory_data_bus(); }\n    // Direct access to memory is not allowed, data bus must be used. At least a\n    // trivial one. If this occurred, there is a misconfigured machine.\n    throw std::logic_error(\"No memory available on machine. This is a bug, please report it.\");\n}\n\nmachine::FrontendMemory *MemoryModel::mem_access_rw() const {\n    if (machine == nullptr) { return nullptr; }\n    if (machine->memory_data_bus_rw() != nullptr) { return machine->memory_data_bus_rw(); }\n    // Direct access to memory is not allowed, data bus must be used. At least a\n    // trivial one. If this occurred, there is a misconfigured machine.\n    throw std::logic_error(\"No memory available on machine. This is u bug, please report it.\");\n}\n\nint MemoryModel::rowCount(const QModelIndex & /*parent*/) const {\n    return 750;\n}\n\nint MemoryModel::columnCount(const QModelIndex & /*parent*/) const {\n    return cells_per_row + 1;\n}\n\nQVariant MemoryModel::headerData(int section, Qt::Orientation orientation, int role) const {\n    if (orientation == Qt::Horizontal) {\n        if (role == Qt::DisplayRole) {\n            if (section == 0) {\n                return tr(\"Address\");\n            } else {\n                uint32_t addr = (section - 1) * cellSizeBytes();\n                QString ret = \"+\" + QString::number(addr, 10);\n                return ret;\n            }\n        }\n    }\n    return Super::headerData(section, orientation, role);\n}\n\nQVariant MemoryModel::data(const QModelIndex &index, int role) const {\n    if (role == Qt::DisplayRole || role == Qt::EditRole) {\n        QString s, t;\n        machine::Address address;\n        uint32_t data;\n        const machine::FrontendMemory *mem = nullptr;\n        if (!get_row_address(address, index.row())) { return QString(\"\"); }\n        if (index.column() == 0) {\n            t = QString::number(address.get_raw(), 16);\n            s.fill('0', 8 - t.count());\n            return { QString(\"0x\") + s + t };\n        }\n        if (machine == nullptr) { return QString(\"\"); }\n        bool vm_enabled = machine->config().get_vm_enabled();\n        if (!vm_enabled) {\n            mem = mem_access();\n            if ((mem_access_kind > MEM_ACC_AS_CPU) && (machine->cache_data() != nullptr)) {\n                mem = machine->cache_data();\n            }\n        } else {\n            if (mem_access_kind == MEM_ACC_PHYS_ADDR) {\n                mem = machine->get_tlb_data();\n            } else {\n                mem = mem_access_phys();\n            }\n        }\n        if (mem == nullptr) { return QString(\"\"); }\n        address += cellSizeBytes() * (index.column() - 1);\n        if (address < index0_offset) { return QString(\"\"); }\n        switch (cell_size) {\n        case CELLSIZE_BYTE: data = mem->read_u8(address, ae::INTERNAL); break;\n        case CELLSIZE_HWORD: data = mem->read_u16(address, ae::INTERNAL); break;\n        default:\n        case CELLSIZE_WORD: data = mem->read_u32(address, ae::INTERNAL); break;\n        }\n\n        t = QString::number(data, 16);\n        s.fill('0', cellSizeBytes() * 2 - t.count());\n        t = s + t;\n#if 0\n        machine::LocationStatus loc_stat = machine::LOCSTAT_NONE;\n        if (machine->cache_data() != nullptr) {\n            loc_stat = machine->cache_data()->location_status(address);\n            if (loc_stat & machine::LOCSTAT_DIRTY)\n                t += \" D\";\n            else if (loc_stat & machine::LOCSTAT_CACHED)\n                t += \" C\";\n        }\n#endif\n        return t;\n    }\n    if (role == Qt::BackgroundRole) {\n        machine::Address address;\n        if (!get_row_address(address, index.row()) || machine == nullptr || index.column() == 0) {\n            return {};\n        }\n        address += cellSizeBytes() * (index.column() - 1);\n        if (machine->cache_data() != nullptr) {\n            machine::LocationStatus loc_stat;\n            loc_stat = machine->cache_data()->location_status(address);\n            if (loc_stat & machine::LOCSTAT_DIRTY) {\n                QBrush bgd(Qt::yellow);\n                return bgd;\n            } else if (loc_stat & machine::LOCSTAT_CACHED) {\n                QBrush bgd(Qt::lightGray);\n                return bgd;\n            }\n        }\n        return {};\n    }\n    if (role == Qt::FontRole) { return data_font; }\n    return {};\n}\n\nvoid MemoryModel::setup(machine::Machine *machine) {\n    this->machine = machine;\n    if (machine != nullptr) {\n        connect(machine, &machine::Machine::post_tick, this, &MemoryModel::check_for_updates);\n    }\n    if (mem_access() != nullptr) {\n        connect(\n            mem_access(), &machine::FrontendMemory::external_change_notify, this,\n            &MemoryModel::check_for_updates);\n    }\n    emit update_all();\n    emit setup_done();\n}\n\nvoid MemoryModel::setCellsPerRow(unsigned int cells) {\n    beginResetModel();\n    cells_per_row = cells;\n    endResetModel();\n}\n\nvoid MemoryModel::set_cell_size(int index) {\n    beginResetModel();\n    cell_size = (enum MemoryCellSize)index;\n    index0_offset -= index0_offset.get_raw() % cellSizeBytes();\n    endResetModel();\n    emit cell_size_changed();\n}\n\nvoid MemoryModel::update_all() {\n    const machine::FrontendMemory *mem;\n    mem = mem_access();\n    if (mem != nullptr) {\n        memory_change_counter = mem->get_change_counter();\n        if (machine->cache_data() != nullptr) {\n            cache_data_change_counter = machine->cache_data()->get_change_counter();\n        }\n    }\n    emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));\n}\n\nvoid MemoryModel::check_for_updates() {\n    bool need_update = false;\n    const machine::FrontendMemory *mem;\n    mem = mem_access();\n    if (mem == nullptr) { return; }\n\n    if (memory_change_counter != mem->get_change_counter()) { need_update = true; }\n    if (machine->cache_data() != nullptr) {\n        if (cache_data_change_counter != machine->cache_data()->get_change_counter()) {\n            need_update = true;\n        }\n    }\n    if (!need_update) { return; }\n    update_all();\n}\n\nbool MemoryModel::adjustRowAndOffset(int &row, machine::Address address) {\n    row = rowCount() / 2;\n    address -= address.get_raw() % cellSizeBytes();\n    uint32_t row_bytes = cells_per_row * cellSizeBytes();\n    uint32_t diff = row * row_bytes;\n    if (machine::Address(diff) > address) {\n        row = address.get_raw() / row_bytes;\n        if (row == 0) {\n            index0_offset = machine::Address::null();\n        } else {\n            index0_offset = address - row * row_bytes;\n        }\n    } else {\n        index0_offset = address - diff;\n    }\n    return get_row_for_address(row, address);\n}\nvoid MemoryModel::cached_access(int cached) {\n    mem_access_kind = cached;\n    update_all();\n}\nQt::ItemFlags MemoryModel::flags(const QModelIndex &index) const {\n    if (index.column() == 0) {\n        return QAbstractTableModel::flags(index);\n    } else {\n        return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;\n    }\n}\n\nbool MemoryModel::setData(const QModelIndex &index, const QVariant &value, int role) {\n    if (role == Qt::EditRole) {\n        bool ok;\n        machine::Address address;\n        machine::FrontendMemory *mem;\n        uint32_t data = value.toString().toULong(&ok, 16);\n        if (!ok) { return false; }\n        if (!get_row_address(address, index.row())) { return false; }\n        if (index.column() == 0 || machine == nullptr) { return false; }\n        if (machine->config().get_vm_enabled()) {\n            if (mem_access_kind == MEM_ACC_PHYS_ADDR) {\n                mem = machine->get_tlb_data_rw();\n            } else {\n                mem = mem_access_phys_rw();\n            }\n        } else {\n            mem = mem_access_rw();\n            if (mem_access_kind > MEM_ACC_AS_CPU && machine->cache_data_rw()) {\n                mem = machine->cache_data_rw();\n            }\n        }\n        if (mem == nullptr) { return false; }\n        if ((mem_access_kind > MEM_ACC_AS_CPU) && (machine->cache_data_rw() != nullptr)) {\n            mem = machine->cache_data_rw();\n        }\n        address += cellSizeBytes() * (index.column() - 1);\n        switch (cell_size) {\n        case CELLSIZE_BYTE: mem->write_u8(address, data, ae::INTERNAL); break;\n        case CELLSIZE_HWORD: mem->write_u16(address, data, ae::INTERNAL); break;\n        default:\n        case CELLSIZE_WORD: mem->write_u32(address, data, ae::INTERNAL); break;\n        }\n    }\n    return true;\n}\n\nconst machine::FrontendMemory *MemoryModel::mem_access_phys() const {\n    if (!machine) return nullptr;\n    if (mem_access_kind > MEM_ACC_AS_CPU && machine->cache_data()) {\n        return machine->cache_data();\n    } else {\n        return machine->memory_data_bus();\n    }\n}\n\nmachine::FrontendMemory *MemoryModel::mem_access_phys_rw() const {\n    if (!machine) return nullptr;\n    if (mem_access_kind > MEM_ACC_AS_CPU && machine->cache_data_rw()) {\n        return machine->cache_data_rw();\n    } else {\n        return machine->memory_data_bus_rw();\n    }\n}\n"
  },
  {
    "path": "src/gui/windows/memory/memorymodel.h",
    "content": "#ifndef MEMORYMODEL_H\n#define MEMORYMODEL_H\n\n#include \"machine/machine.h\"\n\n#include <QAbstractTableModel>\n#include <QFont>\n\nclass MemoryModel : public QAbstractTableModel {\n    Q_OBJECT\n\n    using Super = QAbstractTableModel;\n\npublic:\n    enum MemoryCellSize {\n        CELLSIZE_BYTE,\n        CELLSIZE_HWORD,\n        CELLSIZE_WORD,\n    };\n\n    enum MemoryAccessAtLevel {\n        MEM_ACC_AS_CPU = 0,\n        MEM_ACC_VIRT_ADDR = 1,\n        MEM_ACC_PHYS_ADDR = 2,\n        MEM_ACC_PHYS_ADDR_SKIP_CACHES = 3,\n        MEM_ACC_AS_MACHINE = 4,\n    };\n\n    explicit MemoryModel(QObject *parent);\n    [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    [[nodiscard]] int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n    [[nodiscard]] QVariant\n    headerData(int section, Qt::Orientation orientation, int role) const override;\n    [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;\n    [[nodiscard]] Qt::ItemFlags flags(const QModelIndex &index) const override;\n    bool setData(const QModelIndex &index, const QVariant &value, int role) override;\n    bool adjustRowAndOffset(int &row, machine::Address address);\n    void update_all();\n\n    void setCellsPerRow(unsigned int cells);\n\n    [[nodiscard]] inline unsigned int cellsPerRow() const { return cells_per_row; }\n\n    [[nodiscard]] inline const QFont *getFont() const { return &data_font; }\n\n    [[nodiscard]] inline machine::Address getIndex0Offset() const { return index0_offset; }\n\n    [[nodiscard]] inline unsigned int cellSizeBytes() const {\n        switch (cell_size) {\n        case CELLSIZE_BYTE: return 1;\n        case CELLSIZE_HWORD: return 2;\n        case CELLSIZE_WORD: return 4;\n        }\n        return 0;\n    }\n    inline bool get_row_address(machine::Address &address, int row) const {\n        address = index0_offset + (row * cells_per_row * cellSizeBytes());\n        return address >= index0_offset;\n    }\n    inline bool get_row_for_address(int &row, machine::Address address) const {\n        if (address < index0_offset) {\n            row = -1;\n            return false;\n        }\n        row = (address - index0_offset) / (cells_per_row * cellSizeBytes());\n        if ((address - index0_offset > 0x80000000) || row > rowCount()) {\n            row = rowCount();\n            return false;\n        }\n        return true;\n    }\npublic slots:\n    void setup(machine::Machine *machine);\n    void set_cell_size(int index);\n    void check_for_updates();\n    void cached_access(int cached);\n\nsignals:\n    void cell_size_changed();\n    void setup_done();\n\nprivate:\n    [[nodiscard]] const machine::FrontendMemory *mem_access() const;\n    [[nodiscard]] machine::FrontendMemory *mem_access_rw() const;\n    [[nodiscard]] const machine::FrontendMemory *mem_access_phys() const;\n    [[nodiscard]] machine::FrontendMemory *mem_access_phys_rw() const;\n    enum MemoryCellSize cell_size;\n    unsigned int cells_per_row;\n    machine::Address index0_offset;\n    QFont data_font;\n    machine::Machine *machine;\n    uint32_t memory_change_counter;\n    uint32_t cache_data_change_counter;\n    int mem_access_kind;\n};\n\n#endif // MEMORYMODEL_H\n"
  },
  {
    "path": "src/gui/windows/memory/memorytableview.cpp",
    "content": "#include \"memorytableview.h\"\n\n#include \"common/polyfills/qt5/qfontmetrics.h\"\n#include \"hinttabledelegate.h\"\n#include \"memorymodel.h\"\n\n#include <QApplication>\n#include <QClipboard>\n#include <QFontMetrics>\n#include <QHeaderView>\n#include <QKeyEvent>\n#include <QScrollBar>\n#include <QtGlobal>\n\nMemoryTableView::MemoryTableView(QWidget *parent, QSettings *settings) : Super(parent) {\n    setItemDelegate(new HintTableDelegate(this));\n    connect(\n        verticalScrollBar(), &QAbstractSlider::valueChanged, this,\n        &MemoryTableView::adjust_scroll_pos_check);\n    connect(\n        this, &MemoryTableView::adjust_scroll_pos_queue, this,\n        &MemoryTableView::adjust_scroll_pos_process, Qt::QueuedConnection);\n    this->settings = settings;\n    initial_address = machine::Address(settings->value(\"DataViewAddr0\", 0).toULongLong());\n    adjust_scroll_pos_in_progress = false;\n    setTextElideMode(Qt::ElideNone);\n}\n\nvoid MemoryTableView::addr0_save_change(machine::Address val) {\n    settings->setValue(\"DataViewAddr0\", qint64(val.get_raw()));\n}\n\nvoid MemoryTableView::adjustColumnCount() {\n    auto *m = dynamic_cast<MemoryModel *>(model());\n    if (m == nullptr) { return; }\n\n    auto *delegate = dynamic_cast<HintTableDelegate *>(itemDelegate());\n    if (delegate == nullptr) { return; }\n\n    if (horizontalHeader()->count() >= 2) {\n        QModelIndex idx;\n        QFontMetrics fm(*m->getFont());\n        idx = m->index(0, 0);\n\n        QStyleOptionViewItem viewOpts;\n\n        initViewItemOption(&viewOpts);\n\n        // int width0_dh = itemDelegate(idx)->sizeHint(viewOptions(),\n        // idx).get_width() + 2;\n        int width0_dh = delegate->sizeHintForText(viewOpts, idx, \"0x00000000\").width() + 2;\n        horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);\n        horizontalHeader()->resizeSection(0, width0_dh);\n\n        idx = m->index(0, 1);\n        QString t = \"\";\n        t.fill(QChar('0'), m->cellSizeBytes() * 2);\n        int width1_dh = delegate->sizeHintForText(viewOpts, idx, t).width() + 2;\n        if (width1_dh < QFontMetrics_horizontalAdvance(fm, \"+99\")) {\n            width1_dh = QFontMetrics_horizontalAdvance(fm, \"+99\");\n        }\n        horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);\n        horizontalHeader()->resizeSection(1, width1_dh);\n\n        int w = verticalHeader()->width() + 4;\n        unsigned int cells;\n        int width0 = columnWidth(0);\n        int width1 = columnWidth(1);\n        w = width() - w - width0;\n        if (w < width1 + 4) {\n            cells = 1;\n        } else {\n            cells = w / (width1 + 4);\n        }\n        if (cells != m->cellsPerRow()) { m->setCellsPerRow(cells); }\n        for (unsigned int i = 1; i < m->cellsPerRow() + 1; i++) {\n            horizontalHeader()->setSectionResizeMode(i, QHeaderView::Fixed);\n            horizontalHeader()->resizeSection(i, width1);\n        }\n        if (!initial_address.is_null()) {\n            go_to_address(initial_address);\n            initial_address = machine::Address::null();\n        }\n    }\n}\n\nvoid MemoryTableView::recompute_columns() {\n    adjustColumnCount();\n}\n\nvoid MemoryTableView::set_cell_size(int index) {\n    machine::Address address;\n    int row;\n    bool keep_row0 = false;\n    auto *m = dynamic_cast<MemoryModel *>(model());\n    if (m != nullptr) {\n        keep_row0 = m->get_row_address(address, rowAt(0));\n        m->set_cell_size(index);\n    }\n    adjustColumnCount();\n    if (keep_row0) {\n        m->adjustRowAndOffset(row, address);\n        scrollTo(m->index(row, 0), QAbstractItemView::PositionAtTop);\n    }\n}\n\nvoid MemoryTableView::adjust_scroll_pos_check() {\n    if (!adjust_scroll_pos_in_progress) {\n        adjust_scroll_pos_in_progress = true;\n        emit adjust_scroll_pos_queue();\n    }\n}\n\nvoid MemoryTableView::adjust_scroll_pos_process() {\n    adjust_scroll_pos_in_progress = false;\n    machine::Address address;\n    auto *m = dynamic_cast<MemoryModel *>(model());\n    if (m == nullptr) { return; }\n\n    QModelIndex prev_index = currentIndex();\n    auto row_bytes = machine::Address(m->cellSizeBytes() * m->cellsPerRow());\n    machine::Address index0_offset = m->getIndex0Offset();\n\n    do {\n        int row = rowAt(0);\n        int prev_row = row;\n        if (row < m->rowCount() / 8) {\n            if ((row == 0) && (index0_offset < row_bytes) && (!index0_offset.is_null())) {\n                m->adjustRowAndOffset(row, machine::Address::null());\n            } else if (index0_offset > row_bytes) {\n                m->get_row_address(address, row);\n                m->adjustRowAndOffset(row, address);\n            } else {\n                break;\n            }\n        } else if (row > m->rowCount() - m->rowCount() / 8) {\n            m->get_row_address(address, row);\n            m->adjustRowAndOffset(row, address);\n        } else {\n            break;\n        }\n        scrollTo(m->index(row, 0), QAbstractItemView::PositionAtTop);\n        setCurrentIndex(m->index(prev_index.row() + row - prev_row, prev_index.column()));\n        emit m->update_all();\n    } while (false);\n    m->get_row_address(address, rowAt(0));\n    addr0_save_change(address);\n    emit address_changed(address);\n}\n\nvoid MemoryTableView::resizeEvent(QResizeEvent *event) {\n    auto *m = dynamic_cast<MemoryModel *>(model());\n    machine::Address address;\n    bool keep_row0 = false;\n\n    if (m != nullptr) {\n        if (initial_address.is_null()) {\n            keep_row0 = m->get_row_address(address, rowAt(0));\n        } else {\n            address = initial_address;\n        }\n    }\n    Super::resizeEvent(event);\n    adjustColumnCount();\n    if (keep_row0) {\n        initial_address = machine::Address::null();\n        go_to_address(address);\n    }\n}\n\nvoid MemoryTableView::go_to_address(machine::Address address) {\n    auto *m = dynamic_cast<MemoryModel *>(model());\n    int row;\n    if (m == nullptr) { return; }\n    m->adjustRowAndOffset(row, address);\n    scrollTo(m->index(row, 0), QAbstractItemView::PositionAtTop);\n    setCurrentIndex(m->index(row, 1));\n    addr0_save_change(address);\n    emit m->update_all();\n}\n\nvoid MemoryTableView::focus_address(machine::Address address) {\n    int row;\n    auto *m = dynamic_cast<MemoryModel *>(model());\n    if (m == nullptr) { return; }\n    if (!m->get_row_for_address(row, address)) { go_to_address(address); }\n    if (!m->get_row_for_address(row, address)) { return; }\n    setCurrentIndex(m->index(row, 1));\n}\n\nvoid MemoryTableView::keyPressEvent(QKeyEvent *event) {\n    if (event->matches(QKeySequence::Copy)) {\n        QString text;\n        QItemSelectionRange range = selectionModel()->selection().first();\n        for (auto i = range.top(); i <= range.bottom(); ++i) {\n            QStringList rowContents;\n            for (auto j = range.left(); j <= range.right(); ++j) {\n                rowContents << model()->index(i, j).data().toString();\n            }\n            text += rowContents.join(\"\\t\");\n            text += \"\\n\";\n        }\n        QApplication::clipboard()->setText(text);\n    } else {\n        Super::keyPressEvent(event);\n    }\n}\n"
  },
  {
    "path": "src/gui/windows/memory/memorytableview.h",
    "content": "#ifndef MEMORYTABLEVIEW_H\n#define MEMORYTABLEVIEW_H\n\n#include \"common/polyfills/qt5/qtableview.h\"\n#include \"machine/memory/address.h\"\n\n#include <QObject>\n#include <QSettings>\n#include <QSharedPointer>\n\nclass MemoryTableView : public Poly_QTableView {\n    Q_OBJECT\n\n    using Super = Poly_QTableView;\n\npublic:\n    MemoryTableView(QWidget *parent, QSettings *settings);\n\n    void resizeEvent(QResizeEvent *event) override;\nsignals:\n    void address_changed(machine::Address address);\n    void adjust_scroll_pos_queue();\npublic slots:\n    void set_cell_size(int index);\n    void go_to_address(machine::Address address);\n    void focus_address(machine::Address address);\n    void recompute_columns();\n\nprotected:\n    void keyPressEvent(QKeyEvent *event) override;\nprivate slots:\n    void adjust_scroll_pos_check();\n    void adjust_scroll_pos_process();\n\nprivate:\n    void addr0_save_change(machine::Address val);\n    void adjustColumnCount();\n    QSettings *settings;\n\n    machine::Address initial_address;\n    bool adjust_scroll_pos_in_progress;\n};\n\n#endif // MEMORYTABLEVIEW_H\n"
  },
  {
    "path": "src/gui/windows/messages/messagesdock.cpp",
    "content": "#include \"messagesdock.h\"\n\n#include \"assembler/messagetype.h\"\n#include \"messagesmodel.h\"\n#include \"messagesview.h\"\n#include \"ui/hexlineedit.h\"\n\n#include <QHeaderView>\n#include <QSettings>\n#include <QVBoxLayout>\n#include <QWidget>\n#include <utility>\n\nMessagesDock::MessagesDock(QWidget *parent, QSettings *settings) : Super(parent) {\n    setObjectName(\"Messages\");\n    setWindowTitle(\"Messages\");\n\n    this->settings = settings;\n\n    QWidget *content = new QWidget();\n\n    QListView *messages_content = new MessagesView(nullptr, settings);\n    MessagesModel *messages_model = new MessagesModel(this);\n    messages_content->setModel(messages_model);\n\n    QVBoxLayout *layout = new QVBoxLayout;\n    layout->addWidget(messages_content);\n\n    content->setLayout(layout);\n\n    setWidget(content);\n\n    connect(this, &MessagesDock::report_message, messages_model, &MessagesModel::insert_line);\n    connect(\n        this, &MessagesDock::pass_clear_messages, messages_model, &MessagesModel::clear_messages);\n    connect(\n        messages_content, &QAbstractItemView::activated, messages_model, &MessagesModel::activated);\n    connect(\n        messages_model, &MessagesModel::message_selected, this, &MessagesDock::message_selected);\n}\n\nvoid MessagesDock::insert_line(\n    messagetype::Type type,\n    QString file,\n    int line,\n    int column,\n    QString text,\n    QString hint) {\n    report_message(type, std::move(file), line, column, std::move(text), std::move(hint));\n}\n\nvoid MessagesDock::clear_messages() {\n    emit pass_clear_messages();\n}\n"
  },
  {
    "path": "src/gui/windows/messages/messagesdock.h",
    "content": "#ifndef MESSAGESDOCK_H\n#define MESSAGESDOCK_H\n\n#include \"messagesmodel.h\"\n\n#include <QComboBox>\n#include <QDockWidget>\n#include <QLabel>\n#include <QSettings>\n\nclass MessagesDock : public QDockWidget {\n    Q_OBJECT\n\n    using Super = QDockWidget;\n\npublic:\n    MessagesDock(QWidget *parent, QSettings *settings);\n\npublic slots:\n    void insert_line(\n        messagetype::Type type,\n        QString file,\n        int line,\n        int column,\n        QString text,\n        QString hint);\n    void clear_messages();\n\nsignals:\n    void report_message(\n        messagetype::Type type,\n        QString file,\n        int line,\n        int column,\n        QString text,\n        QString hint);\n    void pass_clear_messages();\n    void message_selected(\n        messagetype::Type type,\n        QString file,\n        int line,\n        int column,\n        QString text,\n        QString hint);\n\nprivate:\n    QSettings *settings;\n};\n\n#endif // MESSAGESDOCK_H\n"
  },
  {
    "path": "src/gui/windows/messages/messagesmodel.cpp",
    "content": "#include \"messagesmodel.h\"\n\n#include <QBrush>\n#include <utility>\n\nclass MessagesEntry {\npublic:\n    inline MessagesEntry(\n        messagetype::Type type,\n        QString file,\n        int line,\n        int column,\n        QString text,\n        QString hint) {\n        this->type = type;\n        this->file = std::move(file);\n        this->line = line;\n        this->column = column;\n        this->text = std::move(text);\n        this->hint = std::move(hint);\n    }\n    messagetype::Type type;\n    QString file;\n    int line;\n    int column;\n    QString text;\n    QString hint;\n};\n\nMessagesModel::MessagesModel(QObject *parent) : Super(parent) {}\n\nMessagesModel::~MessagesModel() {\n    clear_messages();\n}\n\nint MessagesModel::rowCount(const QModelIndex & /*parent*/) const {\n    return messages.count();\n}\n\nint MessagesModel::columnCount(const QModelIndex & /*parent*/) const {\n    return 1;\n}\n\nQVariant MessagesModel::headerData(int section, Qt::Orientation orientation, int role) const {\n    if (orientation == Qt::Horizontal) {\n        if (role == Qt::DisplayRole) {\n            switch (section) {\n            case 0: return tr(\"Type\");\n            case 1: return tr(\"Source\");\n            case 2: return tr(\"Line\");\n            case 3: return tr(\"Column\");\n            case 4: return tr(\"Text\");\n            default: return tr(\"\");\n            }\n        }\n    }\n    return Super::headerData(section, orientation, role);\n}\n\nQVariant MessagesModel::data(const QModelIndex &index, int role) const {\n    if (index.row() >= rowCount()) { return {}; }\n\n    if (role == Qt::DisplayRole || role == Qt::EditRole) {\n        MessagesEntry *ent = messages.at(index.row());\n        QString ret = \"\";\n        if (!ent->file.isEmpty()) { ret += ent->file + \":\"; }\n        if (ent->line) { ret += QString::number(ent->line) + \":\"; }\n        if (ent->column) { ret += QString::number(ent->column) + \":\"; }\n        ret += ent->text;\n        return ret;\n    }\n    if (role == Qt::BackgroundRole) {\n        MessagesEntry *ent = messages.at(index.row());\n        switch (ent->type) {\n        case messagetype::MSG_ERROR: return QBrush(QColor(255, 230, 230));\n        case messagetype::MSG_WARNING: return QBrush(QColor(255, 255, 220));\n        default: return {};\n        }\n    }\n    return {};\n}\n\nvoid MessagesModel::insert_line(\n    messagetype::Type type,\n    const QString &file,\n    int line,\n    int column,\n    const QString &text,\n    const QString &hint) {\n    beginInsertRows(QModelIndex(), rowCount(), rowCount());\n    messages.append(new MessagesEntry(type, file, line, column, text, hint));\n    endInsertRows();\n}\n\nvoid MessagesModel::clear_messages() {\n    auto row_count = rowCount();\n    if (row_count == 0) return;\n    beginRemoveRows(QModelIndex(), 0, row_count - 1);\n    while (!messages.isEmpty()) {\n        delete messages.takeFirst();\n    }\n    endRemoveRows();\n}\n\nvoid MessagesModel::activated(QModelIndex index) {\n    if (index.row() >= rowCount()) { return; }\n\n    MessagesEntry *ent = messages.at(index.row());\n    emit message_selected(ent->type, ent->file, ent->line, ent->column, ent->text, ent->hint);\n}\n"
  },
  {
    "path": "src/gui/windows/messages/messagesmodel.h",
    "content": "#ifndef MESSAGESMODEL_H\n#define MESSAGESMODEL_H\n\n#include \"assembler/messagetype.h\"\n\n#include <QAbstractListModel>\n#include <QFont>\n#include <QVector>\n\nclass MessagesEntry;\n\nclass MessagesModel : public QAbstractListModel {\n    Q_OBJECT\n\n    using Super = QAbstractListModel;\n\npublic:\n    explicit MessagesModel(QObject *parent);\n    ~MessagesModel() override;\n    [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    [[nodiscard]] int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n    [[nodiscard]] QVariant\n    headerData(int section, Qt::Orientation orientation, int role) const override;\n    [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;\n\npublic slots:\n    void insert_line(\n        messagetype::Type type,\n        const QString &file,\n        int line,\n        int column,\n        const QString &text,\n        const QString &hint);\n    void clear_messages();\n    void activated(QModelIndex index);\n\nsignals:\n    void message_selected(\n        messagetype::Type type,\n        QString file,\n        int line,\n        int column,\n        QString text,\n        QString hint);\n\nprivate:\n    QVector<MessagesEntry *> messages;\n};\n\n#endif // MESSAGESMODEL_H\n"
  },
  {
    "path": "src/gui/windows/messages/messagesview.cpp",
    "content": "#include \"messagesview.h\"\n\n#include \"messagesmodel.h\"\n\n#include <QApplication>\n#include <QClipboard>\n#include <QFontMetrics>\n#include <QHeaderView>\n#include <QKeyEvent>\n#include <QScrollBar>\n\nMessagesView::MessagesView(QWidget *parent, QSettings *settings) : Super(parent) {\n    this->settings = settings;\n}\n\nvoid MessagesView::resizeEvent(QResizeEvent *event) {\n    Super::resizeEvent(event);\n}\n\nvoid MessagesView::keyPressEvent(QKeyEvent *event) {\n    if (event->matches(QKeySequence::Copy)) {\n        QString text;\n        QItemSelectionRange range = selectionModel()->selection().first();\n        for (auto i = range.top(); i <= range.bottom(); ++i) {\n            QStringList rowContents;\n            for (auto j = range.left(); j <= range.right(); ++j) {\n                rowContents << model()->index(i, j).data().toString();\n            }\n            text += rowContents.join(\"\\t\");\n            text += \"\\n\";\n        }\n        QApplication::clipboard()->setText(text);\n    } else {\n        Super::keyPressEvent(event);\n    }\n}\n"
  },
  {
    "path": "src/gui/windows/messages/messagesview.h",
    "content": "#ifndef MESSAGESVIEW_H\n#define MESSAGESVIEW_H\n\n#include <QListView>\n#include <QObject>\n#include <QSettings>\n#include <QSharedPointer>\n\nclass MessagesView : public QListView {\n    Q_OBJECT\n\n    using Super = QListView;\n\npublic:\n    MessagesView(QWidget *parent, QSettings *settings);\n\n    void resizeEvent(QResizeEvent *event) override;\n\nprotected:\n    void keyPressEvent(QKeyEvent *event) override;\n    QSettings *settings;\n};\n\n#endif // MESSAGESVIEW_H\n"
  },
  {
    "path": "src/gui/windows/peripherals/peripheralsdock.cpp",
    "content": "#include \"peripheralsdock.h\"\n\nPeripheralsDock::PeripheralsDock(QWidget *parent, QSettings *settings) : QDockWidget(parent) {\n    (void)settings;\n    top_widget = new QWidget(this);\n    setWidget(top_widget);\n    layout_box = new QVBoxLayout(top_widget);\n    periph_view = new PeripheralsView(nullptr);\n    layout_box->addWidget(periph_view);\n\n    setObjectName(\"Peripherals\");\n    setWindowTitle(\"Peripherals\");\n}\n\nvoid PeripheralsDock::setup(const machine::PeripSpiLed *perip_spi_led) {\n    periph_view->setup(perip_spi_led);\n}\n"
  },
  {
    "path": "src/gui/windows/peripherals/peripheralsdock.h",
    "content": "#ifndef PERIPHERALSDOCK_H\n#define PERIPHERALSDOCK_H\n\n#include \"machine/machine.h\"\n#include \"machine/memory/backend/peripheral.h\"\n#include \"machine/memory/backend/peripspiled.h\"\n#include \"peripheralsview.h\"\n\n#include <QDockWidget>\n#include <QFormLayout>\n#include <QLabel>\n\nclass PeripheralsDock : public QDockWidget {\n    Q_OBJECT\npublic:\n    PeripheralsDock(QWidget *parent, QSettings *settings);\n\n    void setup(const machine::PeripSpiLed *perip_spi_led);\n\nprivate:\n    QVBoxLayout *layout_box;\n    QWidget *top_widget, *top_form {};\n    QFormLayout *layout_top_form {};\n    PeripheralsView *periph_view;\n};\n\n#endif // PERIPHERALSDOCK_H\n"
  },
  {
    "path": "src/gui/windows/peripherals/peripheralsview.cpp",
    "content": "#include \"peripheralsview.h\"\n\n#include \"ui_peripheralsview.h\"\n\nPeripheralsView::PeripheralsView(QWidget *parent) : QWidget(parent), ui(new Ui::PeripheralsView) {\n    ui->setupUi(this);\n\n    ui->dialRed->setStyleSheet(\"QDial { background-color: red }\");\n    ui->dialGreen->setStyleSheet(\"QDial { background-color: green }\");\n    ui->dialBlue->setStyleSheet(\"QDial { background-color: blue }\");\n\n    connect(ui->dialRed, &QAbstractSlider::valueChanged, ui->spinRed, &QSpinBox::setValue);\n    connect(ui->dialGreen, &QAbstractSlider::valueChanged, ui->spinGreen, &QSpinBox::setValue);\n    connect(ui->dialBlue, &QAbstractSlider::valueChanged, ui->spinBlue, &QSpinBox::setValue);\n    connect(\n        ui->spinRed, QOverload<int>::of(&QSpinBox::valueChanged), ui->dialRed,\n        &QAbstractSlider::setValue);\n    connect(\n        ui->spinGreen, QOverload<int>::of(&QSpinBox::valueChanged), ui->dialGreen,\n        &QAbstractSlider::setValue);\n    connect(\n        ui->spinBlue, QOverload<int>::of(&QSpinBox::valueChanged), ui->dialBlue,\n        &QAbstractSlider::setValue);\n}\n\nvoid PeripheralsView::setup(const machine::PeripSpiLed *perip_spi_led) {\n    int val;\n\n    connect(\n        ui->spinRed, QOverload<int>::of(&QSpinBox::valueChanged), perip_spi_led,\n        &machine::PeripSpiLed::red_knob_update);\n    connect(\n        ui->spinGreen, QOverload<int>::of(&QSpinBox::valueChanged), perip_spi_led,\n        &machine::PeripSpiLed::green_knob_update);\n    connect(\n        ui->spinBlue, QOverload<int>::of(&QSpinBox::valueChanged), perip_spi_led,\n        &machine::PeripSpiLed::blue_knob_update);\n\n    val = ui->spinRed->value();\n    ui->spinRed->setValue(val - 1);\n    ui->spinRed->setValue(val);\n\n    val = ui->spinGreen->value();\n    ui->spinGreen->setValue(val - 1);\n    ui->spinGreen->setValue(val);\n\n    val = ui->spinBlue->value();\n    ui->spinBlue->setValue(val - 1);\n    ui->spinBlue->setValue(val);\n\n    connect(\n        ui->checkRed, &QAbstractButton::clicked, perip_spi_led,\n        &machine::PeripSpiLed::red_knob_push);\n    connect(\n        ui->checkGreen, &QAbstractButton::clicked, perip_spi_led,\n        &machine::PeripSpiLed::green_knob_push);\n    connect(\n        ui->checkBlue, &QAbstractButton::clicked, perip_spi_led,\n        &machine::PeripSpiLed::blue_knob_push);\n\n    ui->checkRed->setChecked(false);\n    ui->checkGreen->setChecked(false);\n    ui->checkBlue->setChecked(false);\n\n    ui->labelRgb1->setAutoFillBackground(true);\n    ui->labelRgb2->setAutoFillBackground(true);\n\n    connect(\n        perip_spi_led, &machine::PeripSpiLed::led_line_changed, this,\n        &PeripheralsView::led_line_changed);\n    connect(\n        perip_spi_led, &machine::PeripSpiLed::led_rgb1_changed, this,\n        &PeripheralsView::led_rgb1_changed);\n    connect(\n        perip_spi_led, &machine::PeripSpiLed::led_rgb2_changed, this,\n        &PeripheralsView::led_rgb2_changed);\n\n    led_line_changed(0);\n    led_rgb1_changed(0);\n    led_rgb2_changed(0);\n}\n\nvoid PeripheralsView::led_line_changed(uint val) {\n    QString s, t;\n    s = QString::number(val, 16);\n    t.fill('0', 8 - s.count());\n    ui->lineEditHex->setText(t + s);\n    s = QString::number(val, 10);\n    ui->lineEditDec->setText(s);\n    s = QString::number(val, 2);\n    t.fill('0', 32 - s.count());\n    ui->lineEditBin->setText(t + s);\n}\n\nstatic void set_widget_background_color(QWidget *w, uint val) {\n    int r = (val >> 16) & 0xff;\n    int g = (val >> 8) & 0xff;\n    int b = (val >> 0) & 0xff;\n    QPalette::ColorRole brole = w->backgroundRole();\n    QPalette pal = w->palette();\n    pal.setColor(brole, QColor(r, g, b));\n    w->setPalette(pal);\n}\n\nvoid PeripheralsView::led_rgb1_changed(uint val) {\n    QString s, t;\n    s = QString::number(val, 16);\n    t.fill('0', 8 - s.count());\n    ui->lineEditRgb1->setText(t + s);\n\n    set_widget_background_color(ui->labelRgb1, val);\n}\n\nvoid PeripheralsView::led_rgb2_changed(uint val) {\n    QString s, t;\n    s = QString::number(val, 16);\n    t.fill('0', 8 - s.count());\n    ui->lineEditRgb2->setText(t + s);\n\n    set_widget_background_color(ui->labelRgb2, val);\n}\n"
  },
  {
    "path": "src/gui/windows/peripherals/peripheralsview.h",
    "content": "#ifndef PERIPHERALSVIEW_H\n#define PERIPHERALSVIEW_H\n\n#include \"common/memory_ownership.h\"\n#include \"machine/memory/backend/peripspiled.h\"\n#include \"ui_peripheralsview.h\"\n\n#include <QWidget>\n\nclass PeripheralsView : public QWidget {\n    Q_OBJECT\n\npublic:\n    explicit PeripheralsView(QWidget *parent = nullptr);\n\n    void setup(const machine::PeripSpiLed *perip_spi_led);\n\npublic slots:\n    void led_line_changed(uint val);\n    void led_rgb1_changed(uint val);\n    void led_rgb2_changed(uint val);\n\nprivate:\n    Box<Ui::PeripheralsView> ui {};\n};\n\n#endif // PERIPHERALSVIEW_H\n"
  },
  {
    "path": "src/gui/windows/peripherals/peripheralsview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>PeripheralsView</class>\n <widget class=\"QWidget\" name=\"PeripheralsView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>573</width>\n    <height>484</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Form</string>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout\">\n   <item row=\"0\" column=\"0\">\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n     <property name=\"sizeConstraint\">\n      <enum>QLayout::SetMaximumSize</enum>\n     </property>\n     <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_5\">\n         <item>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout_7\">\n           <item>\n            <widget class=\"QLabel\" name=\"label\">\n             <property name=\"text\">\n              <string>LED RGB 1</string>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignCenter</set>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QLabel\" name=\"labelRgb1\">\n             <property name=\"text\">\n              <string/>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignBottom|Qt::AlignHCenter</set>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QLineEdit\" name=\"lineEditRgb1\">\n             <property name=\"readOnly\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <spacer name=\"horizontalSpacer_3\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>40</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_6\">\n         <item>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout_8\">\n           <item>\n            <widget class=\"QLabel\" name=\"label_2\">\n             <property name=\"text\">\n              <string>LED RGB 2</string>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignCenter</set>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QLabel\" name=\"labelRgb2\">\n             <property name=\"text\">\n              <string/>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignBottom|Qt::AlignHCenter</set>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QLineEdit\" name=\"lineEditRgb2\">\n             <property name=\"readOnly\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </item>\n     <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n       <property name=\"sizeConstraint\">\n        <enum>QLayout::SetNoConstraint</enum>\n       </property>\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n         <item>\n          <widget class=\"QCheckBox\" name=\"checkRed\">\n           <property name=\"text\">\n            <string/>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QDial\" name=\"dialRed\">\n           <property name=\"maximum\">\n            <number>255</number>\n           </property>\n           <property name=\"wrapping\">\n            <bool>true</bool>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"label_3\">\n           <property name=\"text\">\n            <string>Red Knob</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QSpinBox\" name=\"spinRed\">\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n           <property name=\"maximum\">\n            <number>255</number>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <spacer name=\"horizontalSpacer\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>40</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n         <item>\n          <widget class=\"QCheckBox\" name=\"checkGreen\">\n           <property name=\"text\">\n            <string/>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QDial\" name=\"dialGreen\">\n           <property name=\"maximum\">\n            <number>255</number>\n           </property>\n           <property name=\"wrapping\">\n            <bool>true</bool>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"label_4\">\n           <property name=\"text\">\n            <string>Green Knob</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QSpinBox\" name=\"spinGreen\">\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n           <property name=\"maximum\">\n            <number>255</number>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <spacer name=\"horizontalSpacer_2\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>40</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n         <item>\n          <widget class=\"QCheckBox\" name=\"checkBlue\">\n           <property name=\"text\">\n            <string/>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QDial\" name=\"dialBlue\">\n           <property name=\"maximum\">\n            <number>255</number>\n           </property>\n           <property name=\"wrapping\">\n            <bool>true</bool>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"label_5\">\n           <property name=\"text\">\n            <string>Blue Knob</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QSpinBox\" name=\"spinBlue\">\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n           <property name=\"maximum\">\n            <number>255</number>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </item>\n     <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_9\">\n         <item>\n          <widget class=\"QLabel\" name=\"label_6\">\n           <property name=\"text\">\n            <string>Word hexadecimal</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignBottom|Qt::AlignHCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLineEdit\" name=\"lineEditHex\">\n           <property name=\"text\">\n            <string/>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignCenter</set>\n           </property>\n           <property name=\"readOnly\">\n            <bool>true</bool>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <spacer name=\"horizontalSpacer_4\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>40</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_10\">\n         <item>\n          <widget class=\"QLabel\" name=\"label_7\">\n           <property name=\"text\">\n            <string>Word decimal</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignBottom|Qt::AlignHCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLineEdit\" name=\"lineEditDec\">\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n           <property name=\"readOnly\">\n            <bool>true</bool>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_11\"/>\n       </item>\n      </layout>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"label_8\">\n       <property name=\"text\">\n        <string>Word binary</string>\n       </property>\n       <property name=\"alignment\">\n        <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLineEdit\" name=\"lineEditBin\">\n       <property name=\"alignment\">\n        <set>Qt::AlignCenter</set>\n       </property>\n       <property name=\"readOnly\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/gui/windows/predictor/predictor_bht_dock.cpp",
    "content": "#include \"predictor_bht_dock.h\"\n\nLOG_CATEGORY(\"gui.DockPredictorBHT\");\n\nDockPredictorBHT::DockPredictorBHT(QWidget *parent) : Super(parent) {\n    setObjectName(\"PredictorBHT\");\n    setWindowTitle(\"Predictor Branch History\");\n\n    /////////////////////////\n    // Assign layout\n\n    // Name\n    layout_type->addWidget(label_type);\n    layout_type->addWidget(label_type_value);\n\n    // Stats\n    layout_stats->addWidget(label_stats_correct_text, 0, 0);\n    layout_stats->addWidget(label_stats_correct_value, 0, 1);\n    layout_stats->addWidget(label_stats_wrong_text, 1, 0);\n    layout_stats->addWidget(label_stats_wrong_value, 1, 1);\n    layout_stats->addWidget(label_stats_accuracy_text, 2, 0);\n    layout_stats->addWidget(label_stats_accuracy_value, 2, 1);\n\n    // Main layout\n    layout_main->addLayout(layout_type);\n    layout_main->addLayout(layout_stats);\n    layout_main->addLayout(layout_event);\n    layout_main->addWidget(bht);\n\n    content->setLayout(layout_main);\n    setWidget(content);\n\n    /////////////////////////\n    // Init widget properties\n\n    // Name\n    label_type->setText(\"Predictor type:\");\n    label_type->setStyleSheet(\"font-weight: bold;\");\n    clear_name();\n\n    // Stats\n    label_stats_correct_text->setText(\"Correct predictions:\");\n    label_stats_wrong_text->setText(\"Wrong predictions:\");\n    label_stats_accuracy_text->setText(\"Accuracy:\");\n    clear_stats();\n\n    // BHT\n    bht->setRowCount(0);\n    bht->setColumnCount(5);\n    bht->setHorizontalHeaderLabels({ \"Index\", \"State\", \"Correct\", \"Incorrect\", \"Accuracy\" });\n    bht->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);\n    bht->verticalHeader()->hide();\n    bht->resizeRowsToContents();\n}\n\n// Get BHT cell item, or create new one if needed\nQTableWidgetItem *DockPredictorBHT::get_bht_cell_item(uint8_t row_index, uint8_t col_index) {\n    QTableWidgetItem *item { bht->item(row_index, col_index) };\n    if (item == nullptr) {\n        item = new QTableWidgetItem();\n        bht->setItem(row_index, col_index, item);\n        item->setTextAlignment(Qt::AlignCenter);\n        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);\n    }\n    return item;\n}\n\nvoid DockPredictorBHT::set_table_color(QColor color) {\n    for (uint16_t row_index = 0; row_index < bht->rowCount(); row_index++) {\n        for (uint16_t column_index = 0; column_index < bht->columnCount(); column_index++) {\n            get_bht_cell_item(row_index, column_index)->setBackground(QBrush(color));\n        }\n    }\n}\n\nvoid DockPredictorBHT::set_row_color(uint16_t row_index, QColor color) {\n    for (uint16_t column_index = 0; column_index < bht->columnCount(); column_index++) {\n        get_bht_cell_item(row_index, column_index)->setBackground(QBrush(color));\n    }\n}\n\nvoid DockPredictorBHT::setup(\n    const machine::BranchPredictor *branch_predictor,\n    const machine::Core *core) {\n    clear();\n\n    number_of_bhr_bits = branch_predictor->get_number_of_bhr_bits();\n    number_of_bht_bits = branch_predictor->get_number_of_bht_bits();\n    initial_state = branch_predictor->get_initial_state();\n    const machine::PredictorType predictor_type { branch_predictor->get_predictor_type() };\n    const bool is_predictor_dynamic { machine::is_predictor_type_dynamic(predictor_type) };\n    const bool is_predictor_enabled { branch_predictor->get_enabled() };\n\n    if (is_predictor_enabled) {\n        content->setDisabled(false);\n        label_type_value->setText(branch_predictor->get_predictor_name().toString());\n\n        connect(\n            branch_predictor, &machine::BranchPredictor::predictor_stats_updated, this,\n            &DockPredictorBHT::update_predictor_stats);\n        connect(\n            branch_predictor, &machine::BranchPredictor::prediction_done, this,\n            &DockPredictorBHT::show_new_prediction);\n        connect(core, &machine::Core::step_started, this, &DockPredictorBHT::reset_colors);\n\n        if (is_predictor_dynamic) {\n            bht->setDisabled(false);\n            bht->setRowCount(qPow(2, number_of_bht_bits));\n            clear_bht(initial_state);\n\n            connect(\n                branch_predictor, &machine::BranchPredictor::update_done, this,\n                &DockPredictorBHT::show_new_update);\n            connect(\n                branch_predictor, &machine::BranchPredictor::predictor_bht_row_updated, this,\n                &DockPredictorBHT::update_bht_row);\n        } else {\n            bht->setDisabled(true);\n            bht->setRowCount(0);\n        }\n    } else {\n        content->setDisabled(true);\n        label_type_value->setText(\"None\");\n    }\n}\n\nvoid DockPredictorBHT::show_new_prediction(\n    uint16_t btb_index,\n    uint16_t bht_index,\n    machine::PredictionInput input,\n    machine::BranchResult result,\n    machine::BranchType branch_type) {\n    UNUSED(btb_index);\n    UNUSED(input);\n    UNUSED(result);\n    if (branch_type == machine::BranchType::BRANCH) { set_row_color(bht_index, Q_COLOR_PREDICT); }\n}\n\nvoid DockPredictorBHT::show_new_update(\n    uint16_t btb_index,\n    uint16_t bht_index,\n    machine::PredictionFeedback feedback) {\n    UNUSED(btb_index);\n    if (feedback.branch_type == machine::BranchType::BRANCH) {\n        set_row_color(bht_index, Q_COLOR_UPDATE);\n    }\n}\n\nvoid DockPredictorBHT::update_predictor_stats(machine::PredictionStatistics stats) {\n    label_stats_correct_value->setText(QString::number(stats.correct));\n    label_stats_wrong_value->setText(QString::number(stats.wrong));\n    label_stats_accuracy_value->setText(QString::number(stats.accuracy) + \" %\");\n    if (stats.total > 0) {\n        label_stats_accuracy_value->setText(QString::number(stats.accuracy) + \" %\");\n    } else {\n        label_stats_accuracy_value->setText(\"N/A\");\n    }\n}\n\nvoid DockPredictorBHT::update_bht_row(\n    uint16_t row_index,\n    machine::BranchHistoryTableEntry bht_entry) {\n    if (row_index >= bht->rowCount()) {\n        WARN(\"BHT dock update received invalid row index: %u\", row_index);\n        return;\n    }\n\n    for (uint16_t column_index = 0; column_index < bht->columnCount(); column_index++) {\n        get_bht_cell_item(row_index, DOCK_BHT_COL_STATE)\n            ->setData(\n                Qt::DisplayRole,\n                machine::predictor_state_to_string(bht_entry.state, true).toString());\n\n        get_bht_cell_item(row_index, DOCK_BHT_COL_CORRECT)\n            ->setData(Qt::DisplayRole, QString::number(bht_entry.stats.correct));\n\n        get_bht_cell_item(row_index, DOCK_BHT_COL_INCORRECT)\n            ->setData(Qt::DisplayRole, QString::number(bht_entry.stats.wrong));\n\n        if (bht_entry.stats.total > 0) {\n            get_bht_cell_item(row_index, DOCK_BHT_COL_ACCURACY)\n                ->setData(Qt::DisplayRole, { QString::number(bht_entry.stats.accuracy) + \" %\" });\n        } else {\n            get_bht_cell_item(row_index, DOCK_BHT_COL_ACCURACY)->setData(Qt::DisplayRole, \"N/A\");\n        }\n    }\n}\n\nvoid DockPredictorBHT::reset_colors() {\n    set_table_color(Q_COLOR_DEFAULT);\n}\n\nvoid DockPredictorBHT::clear_stats() {\n    label_stats_correct_value->setText(\"0\");\n    label_stats_wrong_value->setText(\"0\");\n    label_stats_accuracy_value->setText(\"N/A\");\n}\n\nvoid DockPredictorBHT::clear_name() {\n    label_type_value->setText(\"\");\n}\n\nvoid DockPredictorBHT::clear_bht(machine::PredictorState initial_state) {\n    for (uint16_t row_index = 0; row_index < bht->rowCount(); row_index++) {\n        get_bht_cell_item(row_index, DOCK_BHT_COL_INDEX)\n            ->setData(Qt::DisplayRole, QString::number(row_index));\n\n        get_bht_cell_item(row_index, DOCK_BHT_COL_STATE)\n            ->setData(\n                Qt::DisplayRole,\n                machine::predictor_state_to_string(initial_state, true).toString());\n\n        get_bht_cell_item(row_index, DOCK_BHT_COL_CORRECT)\n            ->setData(Qt::DisplayRole, QString::number(0));\n\n        get_bht_cell_item(row_index, DOCK_BHT_COL_INCORRECT)\n            ->setData(Qt::DisplayRole, QString::number(0));\n\n        get_bht_cell_item(row_index, DOCK_BHT_COL_ACCURACY)\n            ->setData(Qt::DisplayRole, QString(\"N/A\"));\n    }\n    bht->resizeRowsToContents();\n    set_table_color(Q_COLOR_DEFAULT);\n}\n\nvoid DockPredictorBHT::clear() {\n    clear_name();\n    clear_stats();\n    clear_bht();\n}\n"
  },
  {
    "path": "src/gui/windows/predictor/predictor_bht_dock.h",
    "content": "#ifndef PREDICTOR_BHT_DOCK_H\n#define PREDICTOR_BHT_DOCK_H\n\n#include \"common/polyfills/qt5/qtableview.h\"\n#include \"machine/machine.h\"\n#include \"machine/memory/address.h\"\n#include \"machine/predictor.h\"\n#include \"machine/predictor_types.h\"\n#include \"ui/hexlineedit.h\"\n\n#include <QDockWidget>\n#include <QGridLayout>\n#include <QGroupBox>\n#include <QHBoxLayout>\n#include <QHeaderView>\n#include <QLabel>\n#include <QLineEdit>\n#include <QObject>\n#include <QSettings>\n#include <QSharedPointer>\n#include <QTableView>\n#include <QTableWidget>\n#include <QVBoxLayout>\n#include <QWidget>\n#include <QtMath>\n\n#define DOCK_BHT_COL_INDEX     0\n#define DOCK_BHT_COL_STATE     1\n#define DOCK_BHT_COL_CORRECT   2\n#define DOCK_BHT_COL_INCORRECT 3\n#define DOCK_BHT_COL_ACCURACY  4\n\n#define STYLESHEET_COLOR_DEFAULT \"background: rgb(255,255,255);\"\n#define STYLESHEET_COLOR_PREDICT \"background: rgb(255,173,173);\"\n#define STYLESHEET_COLOR_UPDATE  \"background: rgb(173,255,229);\"\n#define Q_COLOR_DEFAULT          QColor(255, 255, 255)\n#define Q_COLOR_PREDICT          QColor(255, 173, 173)\n#define Q_COLOR_UPDATE           QColor(173, 255, 229)\n\nclass DockPredictorBHT : public QDockWidget {\n    Q_OBJECT\n\n    using Super = QDockWidget;\n\npublic: // Constructors & Destructor\n    DockPredictorBHT(QWidget *parent);\n\nprivate: // Internal functions\n    QTableWidgetItem *get_bht_cell_item(uint8_t row_index, uint8_t col_index);\n    void set_table_color(QColor color);\n    void set_row_color(uint16_t row_index, QColor color);\n\npublic: // General functions\n    void setup(const machine::BranchPredictor *branch_predictor, const machine::Core *core);\n\npublic slots:\n    void show_new_prediction(\n        uint16_t btb_index,\n        uint16_t bht_index,\n        machine::PredictionInput input,\n        machine::BranchResult result,\n        machine::BranchType branch_type);\n    void\n    show_new_update(uint16_t btb_index, uint16_t bht_index, machine::PredictionFeedback feedback);\n    void update_predictor_stats(machine::PredictionStatistics stats);\n    void update_bht_row(uint16_t row_index, machine::BranchHistoryTableEntry bht_entry);\n    void reset_colors();\n    void clear_name();\n    void clear_stats();\n    void clear_bht(machine::PredictorState initial_state = machine::PredictorState::UNDEFINED);\n    void clear();\n\nprivate: // Internal variables\n    uint8_t number_of_bhr_bits { 0 };\n    uint8_t number_of_bht_bits { 0 };\n    machine::PredictorState initial_state { machine::PredictorState::UNDEFINED };\n\n    QT_OWNED QGroupBox *content { new QGroupBox() };\n\n    QT_OWNED QVBoxLayout *layout_main { new QVBoxLayout() };\n\n    // Name\n    QT_OWNED QHBoxLayout *layout_type { new QHBoxLayout() };\n    QT_OWNED QLabel *label_type { new QLabel() };\n    QT_OWNED QLabel *label_type_value { new QLabel() };\n\n    // Stats\n    QT_OWNED QGridLayout *layout_stats { new QGridLayout() };\n    QT_OWNED QLabel *label_stats_correct_text { new QLabel() };\n    QT_OWNED QLabel *label_stats_wrong_text { new QLabel() };\n    QT_OWNED QLabel *label_stats_accuracy_text { new QLabel() };\n    QT_OWNED QLabel *label_stats_correct_value { new QLabel() };\n    QT_OWNED QLabel *label_stats_wrong_value { new QLabel() };\n    QT_OWNED QLabel *label_stats_accuracy_value { new QLabel() };\n\n    // Prediction & Update\n    QT_OWNED QHBoxLayout *layout_event { new QHBoxLayout() };\n\n    // BHT\n    QT_OWNED QTableWidget *bht { new QTableWidget() };\n};\n\n#endif // PREDICTOR_BHT_DOCK_H"
  },
  {
    "path": "src/gui/windows/predictor/predictor_btb_dock.cpp",
    "content": "#include \"predictor_btb_dock.h\"\n\nLOG_CATEGORY(\"gui.DockPredictorBTB\");\n\nDockPredictorBTB::DockPredictorBTB(QWidget *parent) : Super(parent) {\n    setObjectName(\"PredictorBTB\");\n    setWindowTitle(\"Predictor Branch Target Buffer\");\n\n    /////////////////////////\n    // Assign layout\n\n    layout->addWidget(btb);\n    content->setLayout(layout);\n    setWidget(content);\n\n    /////////////////////////\n    // Init widget properties\n\n    // BTB\n    btb->setRowCount(0);\n    btb->setColumnCount(4);\n    btb->setHorizontalHeaderLabels({ \"Index\", \"Instr. Address\", \"Target Address\", \"Type\" });\n    btb->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);\n    btb->verticalHeader()->hide();\n    btb->resizeRowsToContents();\n}\n\nuint8_t DockPredictorBTB::init_number_of_bits(const uint8_t b) const {\n    if (b > BP_MAX_BTB_BITS) {\n        WARN(\"Number of BTB bits (%u) was larger than %u during init\", b, BP_MAX_BTB_BITS);\n        return BP_MAX_BTB_BITS;\n    }\n    return b;\n}\n\n// Get BTB cell item, or create new one if needed\nQTableWidgetItem *DockPredictorBTB::get_btb_cell_item(uint8_t row_index, uint8_t col_index) {\n    QTableWidgetItem *item { btb->item(row_index, col_index) };\n    if (item == nullptr) {\n        item = new QTableWidgetItem();\n        btb->setItem(row_index, col_index, item);\n        item->setTextAlignment(Qt::AlignCenter);\n        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);\n    }\n    return item;\n}\n\nvoid DockPredictorBTB::set_table_color(QColor color) {\n    for (uint16_t row_index = 0; row_index < btb->rowCount(); row_index++) {\n        for (uint16_t column_index = 0; column_index < btb->columnCount(); column_index++) {\n            get_btb_cell_item(row_index, column_index)->setBackground(QBrush(color));\n        }\n    }\n}\n\nvoid DockPredictorBTB::set_row_color(uint16_t row_index, QColor color) {\n    for (uint16_t column_index = 0; column_index < btb->columnCount(); column_index++) {\n        get_btb_cell_item(row_index, column_index)->setBackground(QBrush(color));\n    }\n}\n\nvoid DockPredictorBTB::setup(\n    const machine::BranchPredictor *branch_predictor,\n    const machine::Core *core) {\n    clear();\n\n    number_of_bits = init_number_of_bits(branch_predictor->get_number_of_btb_bits());\n    const bool is_predictor_enabled { branch_predictor->get_enabled() };\n\n    if (is_predictor_enabled) {\n        btb->setRowCount(qPow(2, number_of_bits));\n        btb->setDisabled(false);\n        clear_btb();\n\n        connect(\n            branch_predictor, &machine::BranchPredictor::btb_row_updated, this,\n            &DockPredictorBTB::update_btb_row);\n        connect(\n            branch_predictor, &machine::BranchPredictor::prediction_done, this,\n            &DockPredictorBTB::highligh_row_after_prediction);\n        connect(\n            branch_predictor, &machine::BranchPredictor::update_done, this,\n            &DockPredictorBTB::highligh_row_after_update);\n        connect(core, &machine::Core::step_started, this, &DockPredictorBTB::reset_colors);\n\n    } else {\n        btb->setRowCount(0);\n        btb->setDisabled(true);\n    }\n}\n\nvoid DockPredictorBTB::update_btb_row(\n    uint16_t row_index,\n    machine::BranchTargetBufferEntry btb_entry) {\n    if (row_index >= btb->rowCount()) {\n        WARN(\"BTB dock update received invalid row index: %u\", row_index);\n        return;\n    }\n\n    if (btb_entry.entry_valid) {\n        get_btb_cell_item(row_index, DOCK_BTB_COL_INSTR_ADDR)\n            ->setData(Qt::DisplayRole, machine::addr_to_hex_str(btb_entry.instruction_address));\n\n        get_btb_cell_item(row_index, DOCK_BTB_COL_TARGET_ADDR)\n            ->setData(Qt::DisplayRole, machine::addr_to_hex_str(btb_entry.target_address));\n\n        get_btb_cell_item(row_index, DOCK_BTB_COL_TYPE)\n            ->setData(\n                Qt::DisplayRole, machine::branch_type_to_string(btb_entry.branch_type).toString());\n    } else {\n        get_btb_cell_item(row_index, DOCK_BTB_COL_INSTR_ADDR)->setData(Qt::DisplayRole, \"\");\n\n        get_btb_cell_item(row_index, DOCK_BTB_COL_TARGET_ADDR)->setData(Qt::DisplayRole, \"\");\n\n        get_btb_cell_item(row_index, DOCK_BTB_COL_TYPE)->setData(Qt::DisplayRole, \"\");\n    }\n}\n\nvoid DockPredictorBTB::highligh_row_after_prediction(uint16_t row_index) {\n    set_row_color(row_index, Q_COLOR_PREDICT);\n}\n\nvoid DockPredictorBTB::highligh_row_after_update(uint16_t row_index) {\n    set_row_color(row_index, Q_COLOR_UPDATE);\n}\n\nvoid DockPredictorBTB::reset_colors() {\n    set_table_color(Q_COLOR_DEFAULT);\n}\n\nvoid DockPredictorBTB::clear_btb() {\n    for (uint16_t row_index = 0; row_index < btb->rowCount(); row_index++) {\n        get_btb_cell_item(row_index, DOCK_BTB_COL_INDEX)\n            ->setData(Qt::DisplayRole, QString::number(row_index));\n\n        get_btb_cell_item(row_index, DOCK_BTB_COL_INSTR_ADDR)->setData(Qt::DisplayRole, QString(\"\"));\n\n        get_btb_cell_item(row_index, DOCK_BTB_COL_TARGET_ADDR)\n            ->setData(Qt::DisplayRole, QString(\"\"));\n\n        get_btb_cell_item(row_index, DOCK_BTB_COL_TYPE)->setData(Qt::DisplayRole, QString(\"\"));\n    }\n    btb->resizeRowsToContents();\n    set_table_color(Q_COLOR_DEFAULT);\n}\n\nvoid DockPredictorBTB::clear() {\n    clear_btb();\n}"
  },
  {
    "path": "src/gui/windows/predictor/predictor_btb_dock.h",
    "content": "#ifndef PREDICTOR_BTB_DOCK_H\n#define PREDICTOR_BTB_DOCK_H\n\n#include \"common/polyfills/qt5/qtableview.h\"\n#include \"machine/machine.h\"\n#include \"machine/memory/address.h\"\n#include \"machine/predictor.h\"\n#include \"machine/predictor_types.h\"\n\n#include <QColor>\n#include <QComboBox>\n#include <QDockWidget>\n#include <QGroupBox>\n#include <QHeaderView>\n#include <QLabel>\n#include <QObject>\n#include <QSettings>\n#include <QSharedPointer>\n#include <QTableView>\n#include <QTableWidget>\n#include <QVBoxLayout>\n#include <QWidget>\n#include <QtMath>\n\n#define DOCK_BTB_COL_INDEX       0\n#define DOCK_BTB_COL_INSTR_ADDR  1\n#define DOCK_BTB_COL_TARGET_ADDR 2\n#define DOCK_BTB_COL_TYPE        3\n\n#define Q_COLOR_DEFAULT QColor(255, 255, 255)\n#define Q_COLOR_PREDICT QColor(255, 173, 173)\n#define Q_COLOR_UPDATE  QColor(173, 255, 229)\n\n// Branch Target Buffer Dock\nclass DockPredictorBTB : public QDockWidget {\n    Q_OBJECT\n\n    using Super = QDockWidget;\n\npublic: // Constructors & Destructor\n    DockPredictorBTB(QWidget *parent);\n\nprivate: // Internal functions\n    uint8_t init_number_of_bits(const uint8_t b) const;\n    QTableWidgetItem *get_btb_cell_item(uint8_t row_index, uint8_t col_index);\n    void set_table_color(QColor color);\n    void set_row_color(uint16_t row_index, QColor color);\n\npublic: // General functions\n    void setup(const machine::BranchPredictor *branch_predictor, const machine::Core *core);\n\npublic slots:\n    void update_btb_row(uint16_t row_index, machine::BranchTargetBufferEntry btb_entry);\n    void highligh_row_after_prediction(uint16_t btb_index);\n    void highligh_row_after_update(uint16_t btb_index);\n    void reset_colors();\n    void clear_btb();\n    void clear();\n\nprivate: // Internal variables\n    uint8_t number_of_bits { 0 };\n\n    QT_OWNED QGroupBox *content { new QGroupBox() };\n    QT_OWNED QVBoxLayout *layout { new QVBoxLayout() };\n    QT_OWNED QTableWidget *btb { new QTableWidget() };\n};\n\n#endif // PREDICTOR_BTB_DOCK_H"
  },
  {
    "path": "src/gui/windows/predictor/predictor_info_dock.cpp",
    "content": "#include \"predictor_info_dock.h\"\n\n#include <QApplication>\n#include <QLabel>\n#include <QObject>\n#include <QPlainTextEdit>\n#include <QPushButton>\n#include <QTextBrowser>\n#include <QVBoxLayout>\n\nLOG_CATEGORY(\"gui.DockPredictorInfo\");\n\nDockPredictorInfo::DockPredictorInfo(QWidget *parent) : Super(parent) {\n    setObjectName(\"PredictorInfo\");\n    setWindowTitle(\"Predictor Info\");\n\n    /////////////////////////\n    // Assign layout\n\n    // Stats\n    layout_stats->addWidget(label_stats_total_text, 0, 0);\n    layout_stats->addWidget(label_stats_total_value, 0, 1);\n    layout_stats->addWidget(label_stats_miss_text, 1, 0);\n    layout_stats->addWidget(label_stats_miss_value, 1, 1);\n    layout_stats->addWidget(label_stats_accuracy_text, 2, 0);\n    layout_stats->addWidget(label_stats_accuracy_value, 2, 1);\n\n    // BHR\n    layout_bhr->addWidget(label_bhr);\n    layout_bhr->addWidget(value_bhr);\n\n    // Prediction - BTB index\n    layout_event_predict_index_btb->addWidget(label_event_predict_index_btb);\n    layout_event_predict_index_btb->addWidget(value_event_predict_index_btb);\n\n    // Prediction - BHT index\n    layout_event_predict_index_bht->addWidget(label_event_predict_index_bht);\n    layout_event_predict_index_bht->addWidget(value_event_predict_index_bht);\n\n    // Prediction - Indexes\n    layout_event_predict_index->addLayout(layout_event_predict_index_btb);\n    layout_event_predict_index->addLayout(layout_event_predict_index_bht);\n\n    // Prediction\n    layout_event->addWidget(group_event_predict);\n    group_event_predict->setLayout(layout_event_predict);\n    layout_event_predict->addWidget(label_event_predict_header);\n    layout_event_predict->addWidget(label_event_predict_instruction);\n    layout_event_predict->addWidget(value_event_predict_instruction);\n    layout_event_predict->addWidget(label_event_predict_address);\n    layout_event_predict->addWidget(value_event_predict_address);\n    layout_event_predict->addLayout(layout_event_predict_index);\n    layout_event_predict->addWidget(label_event_predict_result);\n    layout_event_predict->addWidget(value_event_predict_result);\n\n    // Update - BTB index\n    layout_event_update_index_btb->addWidget(label_event_update_index_btb);\n    layout_event_update_index_btb->addWidget(value_event_update_index_btb);\n\n    // Update - BHT index\n    layout_event_update_index_bht->addWidget(label_event_update_index_bht);\n    layout_event_update_index_bht->addWidget(value_event_update_index_bht);\n\n    // Update - Indexes\n    layout_event_update_index->addLayout(layout_event_update_index_btb);\n    layout_event_update_index->addLayout(layout_event_update_index_bht);\n\n    // Update\n    layout_event->addWidget(group_event_update);\n    group_event_update->setLayout(layout_event_update);\n    layout_event_update->addWidget(label_event_update_header);\n    layout_event_update->addWidget(label_event_update_instruction);\n    layout_event_update->addWidget(value_event_update_instruction);\n    layout_event_update->addWidget(label_event_update_address);\n    layout_event_update->addWidget(value_event_update_address);\n    layout_event_update->addLayout(layout_event_update_index);\n    layout_event_update->addWidget(label_event_update_result);\n    layout_event_update->addWidget(value_event_update_result);\n\n    // Main layout\n    layout_main->addLayout(layout_stats);\n    layout_main->addLayout(layout_bhr);\n    layout_main->addLayout(layout_event);\n    layout_main->addSpacerItem(vertical_spacer);\n\n    content->setLayout(layout_main);\n    setWidget(content);\n\n    /////////////////////////\n    // Init widget properties\n\n    // Stats\n    label_stats_total_text->setText(\"Jump/Branch count:\");\n    label_stats_miss_text->setText(\"Misprediction count:\");\n    label_stats_accuracy_text->setText(\"Total accuracy:\");\n    clear_stats();\n\n    // BHR\n    label_bhr->setText(\"Branch History Register:\");\n    value_bhr->setReadOnly(true);\n    value_bhr->setAlignment(Qt::AlignCenter);\n    value_bhr->setFixedWidth(120);\n    clear_bhr();\n\n    // Prediction\n    label_event_predict_header->setText(\"Last prediction\");\n    label_event_predict_header->setStyleSheet(\"font-weight: bold;\");\n    label_event_predict_instruction->setText(\"Instruction:\");\n    label_event_predict_address->setText(\"Instruction Address:\");\n    label_event_predict_index_btb->setText(\"BTB index:\");\n    label_event_predict_index_bht->setText(\"BHT index:\");\n    label_event_predict_result->setText(\"Prediction result:\");\n    value_event_predict_instruction->setReadOnly(true);\n    value_event_predict_address->setReadOnly(true);\n    value_event_predict_index_btb->setReadOnly(true);\n    value_event_predict_index_bht->setReadOnly(true);\n    value_event_predict_result->setReadOnly(true);\n    value_event_predict_instruction->setAlignment(Qt::AlignCenter);\n    value_event_predict_address->setAlignment(Qt::AlignCenter);\n    value_event_predict_index_btb->setAlignment(Qt::AlignCenter);\n    value_event_predict_index_bht->setAlignment(Qt::AlignCenter);\n    value_event_predict_result->setAlignment(Qt::AlignCenter);\n    set_predict_widget_color(STYLESHEET_COLOR_DEFAULT);\n    clear_predict_widget();\n\n    // Update\n    label_event_update_header->setText(\"Last update\");\n    label_event_update_header->setStyleSheet(\"font-weight: bold;\");\n    label_event_update_instruction->setText(\"Instruction:\");\n    label_event_update_address->setText(\"Instruction Address:\");\n    label_event_update_index_btb->setText(\"BTB index:\");\n    label_event_update_index_bht->setText(\"BHT index:\");\n    label_event_update_result->setText(\"Branch result:\");\n    value_event_update_instruction->setReadOnly(true);\n    value_event_update_address->setReadOnly(true);\n    value_event_update_index_btb->setReadOnly(true);\n    value_event_update_index_bht->setReadOnly(true);\n    value_event_update_result->setReadOnly(true);\n    value_event_update_instruction->setAlignment(Qt::AlignCenter);\n    value_event_update_address->setAlignment(Qt::AlignCenter);\n    value_event_update_index_btb->setAlignment(Qt::AlignCenter);\n    value_event_update_index_bht->setAlignment(Qt::AlignCenter);\n    value_event_update_result->setAlignment(Qt::AlignCenter);\n    set_update_widget_color(STYLESHEET_COLOR_DEFAULT);\n    clear_update_widget();\n}\n\nvoid DockPredictorInfo::set_predict_widget_color(QString color_stylesheet) {\n    value_event_predict_instruction->setStyleSheet(color_stylesheet);\n    value_event_predict_address->setStyleSheet(color_stylesheet);\n    value_event_predict_index_btb->setStyleSheet(color_stylesheet);\n    if (is_predictor_dynamic) { value_event_predict_index_bht->setStyleSheet(color_stylesheet); }\n    value_event_predict_result->setStyleSheet(color_stylesheet);\n}\n\nvoid DockPredictorInfo::set_update_widget_color(QString color_stylesheet) {\n    value_event_update_instruction->setStyleSheet(color_stylesheet);\n    value_event_update_address->setStyleSheet(color_stylesheet);\n    value_event_update_index_btb->setStyleSheet(color_stylesheet);\n    if (is_predictor_dynamic) { value_event_update_index_bht->setStyleSheet(color_stylesheet); }\n    value_event_update_result->setStyleSheet(color_stylesheet);\n}\n\nvoid DockPredictorInfo::setup(\n    const machine::BranchPredictor *branch_predictor,\n    const machine::Core *core) {\n    clear();\n\n    number_of_bhr_bits = branch_predictor->get_number_of_bhr_bits();\n    initial_state = branch_predictor->get_initial_state();\n    const machine::PredictorType predictor_type { branch_predictor->get_predictor_type() };\n    is_predictor_dynamic = machine::is_predictor_type_dynamic(predictor_type);\n    is_predictor_enabled = branch_predictor->get_enabled();\n\n    if (is_predictor_enabled) {\n        connect(\n            branch_predictor, &machine::BranchPredictor::total_stats_updated, this,\n            &DockPredictorInfo::update_stats);\n        connect(\n            branch_predictor, &machine::BranchPredictor::prediction_done, this,\n            &DockPredictorInfo::show_new_prediction);\n        connect(core, &machine::Core::step_started, this, &DockPredictorInfo::reset_colors);\n        connect(\n            branch_predictor, &machine::BranchPredictor::update_done, this,\n            &DockPredictorInfo::show_new_update);\n\n        if (is_predictor_dynamic) {\n            connect(\n                branch_predictor, &machine::BranchPredictor::bhr_updated, this,\n                &DockPredictorInfo::update_bhr);\n        }\n    }\n\n    // Toggle BHT index display\n    if (is_predictor_dynamic) {\n        label_event_predict_index_bht->setEnabled(true);\n        value_event_predict_index_bht->setEnabled(true);\n        label_event_update_index_bht->setEnabled(true);\n        value_event_update_index_bht->setEnabled(true);\n    } else {\n        label_event_predict_index_bht->setEnabled(false);\n        value_event_predict_index_bht->setEnabled(false);\n        label_event_update_index_bht->setEnabled(false);\n        value_event_update_index_bht->setEnabled(false);\n    }\n\n    // Toggle BHR display\n    if (is_predictor_dynamic && number_of_bhr_bits > 0) {\n        label_bhr->setEnabled(true);\n        value_bhr->setEnabled(true);\n    } else {\n        label_bhr->setEnabled(false);\n        value_bhr->setEnabled(false);\n    }\n\n    // Toggle whole widget\n    if (is_predictor_enabled) {\n        content->setDisabled(false);\n    } else {\n        content->setDisabled(true);\n    }\n\n    clear_bhr();\n}\n\nvoid DockPredictorInfo::update_bhr(uint8_t number_of_bhr_bits, uint16_t register_value) {\n    if (number_of_bhr_bits > 0) {\n        QString binary_value, zero_padding;\n        binary_value = QString::number(register_value, 2);\n        zero_padding.fill('0', number_of_bhr_bits - binary_value.count());\n        value_bhr->setText(\"0b\" + zero_padding + binary_value);\n    } else {\n        value_bhr->setText(\"\");\n    }\n}\n\nvoid DockPredictorInfo::show_new_prediction(\n    uint16_t btb_index,\n    uint16_t bht_index,\n    machine::PredictionInput input,\n    machine::BranchResult result,\n    machine::BranchType branch_type) {\n    value_event_predict_instruction->setText(input.instruction.to_str());\n    value_event_predict_address->setText(addr_to_hex_str(input.instruction_address));\n    value_event_predict_index_btb->setText(QString::number(btb_index));\n    if (!is_predictor_dynamic) {\n        value_event_predict_index_bht->setText(\"\");\n    } else if (branch_type == machine::BranchType::BRANCH) {\n        value_event_predict_index_bht->setText(QString::number(bht_index));\n    } else {\n        value_event_predict_index_bht->setText(\"N/A\");\n    }\n    value_event_predict_result->setText(machine::branch_result_to_string(result).toString());\n    set_predict_widget_color(STYLESHEET_COLOR_PREDICT);\n}\n\nvoid DockPredictorInfo::show_new_update(\n    uint16_t btb_index,\n    uint16_t bht_index,\n    machine::PredictionFeedback feedback) {\n    value_event_update_instruction->setText(feedback.instruction.to_str());\n    value_event_update_address->setText(addr_to_hex_str(feedback.instruction_address));\n    value_event_update_index_btb->setText(QString::number(btb_index));\n    if (!is_predictor_dynamic) {\n        value_event_update_index_bht->setText(\"\");\n    } else if (feedback.branch_type == machine::BranchType::BRANCH) {\n        value_event_update_index_bht->setText(QString::number(bht_index));\n    } else {\n        value_event_update_index_bht->setText(\"N/A\");\n    }\n    value_event_update_result->setText(\n        machine::branch_result_to_string(feedback.result).toString());\n    set_update_widget_color(STYLESHEET_COLOR_UPDATE);\n}\n\nvoid DockPredictorInfo::update_stats(machine::PredictionStatistics stats) {\n    label_stats_total_value->setText(QString::number(stats.correct));\n    label_stats_miss_value->setText(QString::number(stats.wrong));\n    label_stats_accuracy_value->setText(QString::number(stats.accuracy) + \" %\");\n}\n\nvoid DockPredictorInfo::reset_colors() {\n    set_predict_widget_color(STYLESHEET_COLOR_DEFAULT);\n    set_update_widget_color(STYLESHEET_COLOR_DEFAULT);\n}\n\nvoid DockPredictorInfo::clear_stats() {\n    label_stats_total_value->setText(\"0\");\n    label_stats_miss_value->setText(\"0\");\n    label_stats_accuracy_value->setText(\"N/A\");\n}\n\nvoid DockPredictorInfo::clear_bhr() {\n    if (number_of_bhr_bits > 0) {\n        QString zero_padding;\n        zero_padding.fill('0', number_of_bhr_bits);\n        value_bhr->setText(\"0b\" + zero_padding);\n    } else {\n        value_bhr->setText(\"\");\n    }\n}\n\nvoid DockPredictorInfo::clear_predict_widget() {\n    value_event_predict_instruction->setText(\"\");\n    value_event_predict_address->setText(\"\");\n    value_event_predict_index_btb->setText(\"\");\n    value_event_predict_index_bht->setText(\"\");\n    value_event_predict_result->setText(\"\");\n    set_predict_widget_color(STYLESHEET_COLOR_DEFAULT);\n}\n\nvoid DockPredictorInfo::clear_update_widget() {\n    value_event_update_instruction->setText(\"\");\n    value_event_update_address->setText(\"\");\n    value_event_update_index_btb->setText(\"\");\n    value_event_update_index_bht->setText(\"\");\n    value_event_update_result->setText(\"\");\n    set_update_widget_color(STYLESHEET_COLOR_DEFAULT);\n}\n\nvoid DockPredictorInfo::clear() {\n    clear_stats();\n    clear_bhr();\n    clear_predict_widget();\n    clear_update_widget();\n}\n"
  },
  {
    "path": "src/gui/windows/predictor/predictor_info_dock.h",
    "content": "#ifndef PREDICTOR_INFO_DOCK_H\n#define PREDICTOR_INFO_DOCK_H\n\n#include \"common/polyfills/qt5/qtableview.h\"\n#include \"machine/machine.h\"\n#include \"machine/memory/address.h\"\n#include \"machine/predictor.h\"\n#include \"machine/predictor_types.h\"\n#include \"ui/hexlineedit.h\"\n\n#include <QDockWidget>\n#include <QGridLayout>\n#include <QGroupBox>\n#include <QHBoxLayout>\n#include <QHeaderView>\n#include <QLabel>\n#include <QLineEdit>\n#include <QObject>\n#include <QSettings>\n#include <QSharedPointer>\n#include <QSpacerItem>\n#include <QTableView>\n#include <QTableWidget>\n#include <QVBoxLayout>\n#include <QWidget>\n#include <QtMath>\n\n#define STYLESHEET_COLOR_DEFAULT \"background: rgb(255,255,255);\"\n#define STYLESHEET_COLOR_PREDICT \"background: rgb(255,173,173);\"\n#define STYLESHEET_COLOR_UPDATE  \"background: rgb(173,255,229);\"\n#define Q_COLOR_DEFAULT          QColor(255, 255, 255)\n#define Q_COLOR_PREDICT          QColor(255, 173, 173)\n#define Q_COLOR_UPDATE           QColor(173, 255, 229)\n\nclass DockPredictorInfo : public QDockWidget {\n    Q_OBJECT\n\n    using Super = QDockWidget;\n\npublic: // Constructors & Destructor\n    DockPredictorInfo(QWidget *parent);\n\nprivate: // Internal functions\n    void set_predict_widget_color(QString color_stylesheet);\n    void set_update_widget_color(QString color_stylesheet);\n\npublic: // General functions\n    void setup(const machine::BranchPredictor *branch_predictor, const machine::Core *core);\n\npublic slots:\n    void update_bhr(uint8_t number_of_bhr_bits, uint16_t register_value);\n    void show_new_prediction(\n        uint16_t btb_index,\n        uint16_t bht_index,\n        machine::PredictionInput input,\n        machine::BranchResult result,\n        machine::BranchType branch_type);\n    void\n    show_new_update(uint16_t btb_index, uint16_t bht_index, machine::PredictionFeedback feedback);\n    void update_stats(machine::PredictionStatistics stats);\n    void reset_colors();\n    void clear_stats();\n    void clear_bhr();\n    void clear_predict_widget();\n    void clear_update_widget();\n    void clear();\n\nprivate: // Internal variables\n    bool is_predictor_enabled { false };\n    bool is_predictor_dynamic { false };\n    uint8_t number_of_bhr_bits { 0 };\n    machine::PredictorState initial_state { machine::PredictorState::UNDEFINED };\n\n    QT_OWNED QGroupBox *content { new QGroupBox() };\n    QT_OWNED QVBoxLayout *layout_main { new QVBoxLayout() };\n    QT_OWNED QSpacerItem *vertical_spacer {\n        new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)\n    };\n\n    // Stats\n    QT_OWNED QGridLayout *layout_stats { new QGridLayout() };\n    QT_OWNED QLabel *label_stats_total_text { new QLabel() };\n    QT_OWNED QLabel *label_stats_miss_text { new QLabel() };\n    QT_OWNED QLabel *label_stats_accuracy_text { new QLabel() };\n    QT_OWNED QLabel *label_stats_total_value { new QLabel() };\n    QT_OWNED QLabel *label_stats_miss_value { new QLabel() };\n    QT_OWNED QLabel *label_stats_accuracy_value { new QLabel() };\n\n    // Prediction & Update\n    QT_OWNED QHBoxLayout *layout_event { new QHBoxLayout() };\n\n    // Prediction\n    QT_OWNED QGroupBox *group_event_predict { new QGroupBox() };\n    QT_OWNED QVBoxLayout *layout_event_predict { new QVBoxLayout() };\n    QT_OWNED QHBoxLayout *layout_event_predict_index { new QHBoxLayout() };\n    QT_OWNED QVBoxLayout *layout_event_predict_index_btb { new QVBoxLayout() };\n    QT_OWNED QVBoxLayout *layout_event_predict_index_bht { new QVBoxLayout() };\n    QT_OWNED QLabel *label_event_predict_header { new QLabel() };\n    QT_OWNED QLabel *label_event_predict_instruction { new QLabel() };\n    QT_OWNED QLabel *label_event_predict_address { new QLabel() };\n    QT_OWNED QLabel *label_event_predict_index_btb { new QLabel() };\n    QT_OWNED QLabel *label_event_predict_index_bht { new QLabel() };\n    QT_OWNED QLabel *label_event_predict_result { new QLabel() };\n    QT_OWNED QLineEdit *value_event_predict_instruction { new QLineEdit() };\n    QT_OWNED QLineEdit *value_event_predict_address { new QLineEdit() };\n    QT_OWNED QLineEdit *value_event_predict_index_btb { new QLineEdit() };\n    QT_OWNED QLineEdit *value_event_predict_index_bht { new QLineEdit() };\n    QT_OWNED QLineEdit *value_event_predict_result { new QLineEdit() };\n\n    // Update\n    QT_OWNED QGroupBox *group_event_update { new QGroupBox() };\n    QT_OWNED QVBoxLayout *layout_event_update { new QVBoxLayout() };\n    QT_OWNED QHBoxLayout *layout_event_update_index { new QHBoxLayout() };\n    QT_OWNED QVBoxLayout *layout_event_update_index_btb { new QVBoxLayout() };\n    QT_OWNED QVBoxLayout *layout_event_update_index_bht { new QVBoxLayout() };\n    QT_OWNED QLabel *label_event_update_header { new QLabel() };\n    QT_OWNED QLabel *label_event_update_instruction { new QLabel() };\n    QT_OWNED QLabel *label_event_update_address { new QLabel() };\n    QT_OWNED QLabel *label_event_update_index_btb { new QLabel() };\n    QT_OWNED QLabel *label_event_update_index_bht { new QLabel() };\n    QT_OWNED QLabel *label_event_update_result { new QLabel() };\n    QT_OWNED QLineEdit *value_event_update_instruction { new QLineEdit() };\n    QT_OWNED QLineEdit *value_event_update_address { new QLineEdit() };\n    QT_OWNED QLineEdit *value_event_update_index_btb { new QLineEdit() };\n    QT_OWNED QLineEdit *value_event_update_index_bht { new QLineEdit() };\n    QT_OWNED QLineEdit *value_event_update_result { new QLineEdit() };\n\n    // BHR\n    QT_OWNED QHBoxLayout *layout_bhr { new QHBoxLayout() };\n    QT_OWNED QLabel *label_bhr { new QLabel() };\n    QT_OWNED QLineEdit *value_bhr { new QLineEdit() };\n};\n\n#endif // PREDICTOR_INFO_DOCK_H"
  },
  {
    "path": "src/gui/windows/program/programdock.cpp",
    "content": "#include \"programdock.h\"\n\n#include \"helper/async_modal.h\"\n#include \"programmodel.h\"\n#include \"programtableview.h\"\n#include \"ui/hexlineedit.h\"\n\n#include <QComboBox>\n#include <QHeaderView>\n#include <QPushButton>\n#include <QVBoxLayout>\n#include <QWidget>\n\nProgramDock::ProgramDock(QWidget *parent, QSettings *settings) : Super(parent) {\n    setObjectName(\"Program\");\n    setWindowTitle(\"Program\");\n\n    this->settings = settings;\n    follow_source\n        = (enum FollowSource)settings->value(\"ProgramViewFollowSource\", FOLLOWSRC_FETCH).toInt();\n\n    for (auto &i : follow_addr) {\n        i = machine::Address::null();\n    }\n\n    auto *content = new QWidget();\n\n    auto *follow_inst = new QComboBox();\n    follow_inst->addItem(\"Follow none\");\n    follow_inst->addItem(\"Follow fetch\");\n    follow_inst->addItem(\"Follow decode\");\n    follow_inst->addItem(\"Follow execute\");\n    follow_inst->addItem(\"Follow memory\");\n    follow_inst->addItem(\"Follow writeback\");\n    follow_inst->setCurrentIndex((int)follow_source);\n\n    auto *program_content = new ProgramTableView(nullptr, settings);\n    // program_content->setSizePolicy();\n    auto *program_model = new ProgramModel(this);\n    program_content->setModel(program_model);\n    program_content->verticalHeader()->hide();\n    // program_content->setHorizontalHeader(program_model->);\n\n    auto *go_edit = new HexLineEdit(nullptr, 8, 16, \"0x\");\n\n    auto *layout = new QVBoxLayout;\n    layout->addWidget(follow_inst);\n    layout->addWidget(program_content);\n    layout->addWidget(go_edit);\n\n    content->setLayout(layout);\n\n    setWidget(content);\n\n    connect(this, &ProgramDock::machine_setup, program_model, &ProgramModel::setup);\n    connect(\n        go_edit, &HexLineEdit::value_edit_finished, program_content,\n        [program_content](uint32_t value) {\n            program_content->go_to_address(machine::Address(value));\n        });\n    connect(program_content, &ProgramTableView::address_changed, go_edit, &HexLineEdit::set_value);\n    connect(this, &ProgramDock::jump_to_pc, program_content, &ProgramTableView::go_to_address);\n    connect(\n        follow_inst, QOverload<int>::of(&QComboBox::currentIndexChanged), this,\n        &ProgramDock::set_follow_inst);\n    connect(this, &ProgramDock::focus_addr, program_content, &ProgramTableView::focus_address);\n    connect(\n        this, &ProgramDock::focus_addr_with_save, program_content,\n        &ProgramTableView::focus_address_with_save);\n    connect(\n        program_content, &QAbstractItemView::doubleClicked, program_model,\n        &ProgramModel::toggle_hw_break);\n    connect(\n        this, &ProgramDock::stage_addr_changed, program_model, &ProgramModel::update_stage_addr);\n    connect(program_model, &ProgramModel::report_error, this, &ProgramDock::report_error);\n    connect(this, &ProgramDock::request_update_all, program_model, &ProgramModel::update_all);\n}\n\nvoid ProgramDock::setup(machine::Machine *machine) {\n    machine::Address pc;\n    emit machine_setup(machine);\n    if (machine == nullptr) { return; }\n    pipeline_handle = &machine->core()->get_state();\n    pc = machine->registers()->read_pc();\n    for (machine::Address &address : follow_addr) {\n        address = pc;\n    }\n    update_follow_position();\n}\n\nvoid ProgramDock::showEvent(QShowEvent *event) {\n    QDockWidget::showEvent(event);\n    update_follow_position();\n    if (pipeline_handle != nullptr) { update_pipeline_addrs(*pipeline_handle); }\n}\n\nvoid ProgramDock::set_follow_inst(int follow) {\n    follow_source = (enum FollowSource)follow;\n    settings->setValue(\"ProgramViewFollowSource\", (int)follow_source);\n    update_follow_position();\n}\n\nvoid ProgramDock::update_pipeline_addrs(const machine::CoreState &s) {\n    if (isHidden()) { return; }\n    const machine::Pipeline &p = s.pipeline;\n    fetch_inst_addr(p.fetch.result.inst_addr);\n    decode_inst_addr(p.decode.result.inst_addr);\n    execute_inst_addr(p.execute.result.inst_addr);\n    memory_inst_addr(p.memory.result.inst_addr);\n    writeback_inst_addr(p.writeback.internal.inst_addr);\n}\n\nvoid ProgramDock::fetch_inst_addr(machine::Address addr) {\n    if (addr != machine::STAGEADDR_NONE) { follow_addr[FOLLOWSRC_FETCH] = addr; }\n    emit stage_addr_changed(ProgramModel::STAGEADDR_FETCH, addr);\n    if (follow_source == FOLLOWSRC_FETCH) { update_follow_position(); }\n}\n\nvoid ProgramDock::decode_inst_addr(machine::Address addr) {\n    if (addr != machine::STAGEADDR_NONE) { follow_addr[FOLLOWSRC_DECODE] = addr; }\n    emit stage_addr_changed(ProgramModel::STAGEADDR_DECODE, addr);\n    if (follow_source == FOLLOWSRC_DECODE) { update_follow_position(); }\n}\n\nvoid ProgramDock::execute_inst_addr(machine::Address addr) {\n    if (addr != machine::STAGEADDR_NONE) { follow_addr[FOLLOWSRC_EXECUTE] = addr; }\n    emit stage_addr_changed(ProgramModel::STAGEADDR_EXECUTE, addr);\n    if (follow_source == FOLLOWSRC_EXECUTE) { update_follow_position(); }\n}\n\nvoid ProgramDock::memory_inst_addr(machine::Address addr) {\n    if (addr != machine::STAGEADDR_NONE) { follow_addr[FOLLOWSRC_MEMORY] = addr; }\n    emit stage_addr_changed(ProgramModel::STAGEADDR_MEMORY, addr);\n    if (follow_source == FOLLOWSRC_MEMORY) { update_follow_position(); }\n}\n\nvoid ProgramDock::writeback_inst_addr(machine::Address addr) {\n    if (addr != machine::STAGEADDR_NONE) { follow_addr[FOLLOWSRC_WRITEBACK] = addr; }\n    emit stage_addr_changed(ProgramModel::STAGEADDR_WRITEBACK, addr);\n    if (follow_source == FOLLOWSRC_WRITEBACK) { update_follow_position(); }\n}\n\nvoid ProgramDock::update_follow_position() {\n    if (follow_source != FOLLOWSRC_NONE) { emit focus_addr(follow_addr[follow_source]); }\n}\n\nvoid ProgramDock::report_error(const QString &error) {\n    showAsyncMessageBox(this, QMessageBox::Critical, \"Simulator Error\", error);\n}\n"
  },
  {
    "path": "src/gui/windows/program/programdock.h",
    "content": "#ifndef PROGRAMDOCK_H\n#define PROGRAMDOCK_H\n\n#include \"machine/machine.h\"\n#include \"windows/peripherals/peripheralsview.h\"\n\n#include <QComboBox>\n#include <QDockWidget>\n#include <QLabel>\n\nclass ProgramDock : public QDockWidget {\n    Q_OBJECT\n\n    using Super = QDockWidget;\n\npublic:\n    ProgramDock(QWidget *parent, QSettings *settings);\n\n    void setup(machine::Machine *machine);\n\n    void showEvent(QShowEvent *event) override;\n\nsignals:\n    void machine_setup(machine::Machine *machine);\n    void jump_to_pc(machine::Address);\n    void focus_addr(machine::Address);\n    void focus_addr_with_save(machine::Address);\n    void stage_addr_changed(uint stage, machine::Address addr);\n    void request_update_all();\npublic slots:\n    void set_follow_inst(int);\n    void fetch_inst_addr(machine::Address addr);\n    void decode_inst_addr(machine::Address addr);\n    void execute_inst_addr(machine::Address addr);\n    void memory_inst_addr(machine::Address addr);\n    void writeback_inst_addr(machine::Address addr);\n    void report_error(const QString &error);\n    void update_pipeline_addrs(const machine::CoreState &p);\n\nprivate:\n    enum FollowSource {\n        FOLLOWSRC_NONE,\n        FOLLOWSRC_FETCH,\n        FOLLOWSRC_DECODE,\n        FOLLOWSRC_EXECUTE,\n        FOLLOWSRC_MEMORY,\n        FOLLOWSRC_WRITEBACK,\n        FOLLOWSRC_COUNT,\n    };\n\n    void update_follow_position();\n\n    enum FollowSource follow_source;\n    machine::Address follow_addr[FOLLOWSRC_COUNT] {};\n    QSettings *settings;\n    const machine::CoreState *pipeline_handle = nullptr;\n};\n\n#endif // PROGRAMDOCK_H\n"
  },
  {
    "path": "src/gui/windows/program/programmodel.cpp",
    "content": "#include \"programmodel.h\"\n\n#include <QtGui/qbrush.h>\n\nusing ae = machine::AccessEffects; // For enum values, the type is obvious from context.\n\nProgramModel::ProgramModel(QObject *parent) : Super(parent), data_font(\"Monospace\") {\n    index0_offset = machine::Address::null();\n    data_font.setStyleHint(QFont::TypeWriter);\n    machine = nullptr;\n    memory_change_counter = 0;\n    cache_program_change_counter = 0;\n    for (auto &i : stage_addr) {\n        i = machine::STAGEADDR_NONE;\n    }\n    stages_need_update = false;\n}\n\nconst machine::FrontendMemory *ProgramModel::mem_access() const {\n    if (machine == nullptr) { return nullptr; }\n    if (machine->memory_data_bus() != nullptr) { return machine->memory_data_bus(); }\n    throw std::logic_error(\"Use of backend memory in frontend.\"); // TODO\n    //    return machine->memory();\n}\n\nmachine::FrontendMemory *ProgramModel::mem_access_rw() const {\n    if (machine == nullptr) { return nullptr; }\n    if (machine->memory_data_bus_rw() != nullptr) { return machine->memory_data_bus_rw(); }\n    throw std::logic_error(\"Use of backend memory in frontend.\"); // TODO\n    //    return machine->memory_rw();\n}\n\nint ProgramModel::rowCount(const QModelIndex & /*parent*/) const {\n    return 750;\n}\n\nint ProgramModel::columnCount(const QModelIndex & /*parent*/) const {\n    return 4;\n}\nQVariant ProgramModel::headerData(int section, Qt::Orientation orientation, int role) const {\n    if (orientation == Qt::Horizontal) {\n        if (role == Qt::DisplayRole) {\n            switch (section) {\n            case 0: return tr(\"BP\");\n            case 1: return tr(\"Address\");\n            case 2: return tr(\"Code\");\n            case 3: return tr(\"Instruction\");\n            default: return tr(\"\");\n            }\n        }\n    }\n    return Super::headerData(section, orientation, role);\n}\n\nQVariant ProgramModel::data(const QModelIndex &index, int role) const {\n    const machine::FrontendMemory *mem;\n\n    if (role == Qt::DisplayRole || role == Qt::EditRole) {\n        QString s, t;\n        machine::Address address;\n        if (!get_row_address(address, index.row())) { return QString(\"\"); }\n\n        if (index.column() == 1) {\n            t = QString::number(address.get_raw(), 16);\n            s.fill('0', 8 - t.count());\n            return { \"0x\" + s + t };\n        }\n\n        mem = mem_access();\n        if (mem == nullptr) { return QString(\" \"); }\n\n        machine::Instruction inst(mem->read_u32(address));\n\n        switch (index.column()) {\n        case 0:\n            if (machine->is_hwbreak(address)) {\n                return QString(\"B\");\n            } else {\n                return QString(\" \");\n            }\n        case 2:\n            t = QString::number(inst.data(), 16);\n            s.fill('0', 8 - t.count());\n            return { s + t };\n        case 3: return inst.to_str(address);\n        default: return tr(\"\");\n        }\n    }\n    if (role == Qt::BackgroundRole) {\n        machine::Address address;\n        if (!get_row_address(address, index.row()) || machine == nullptr) { return {}; }\n        if (index.column() == 2 && machine->cache_program() != nullptr) {\n            machine::LocationStatus loc_stat;\n            loc_stat = machine->cache_program()->location_status(address);\n            if (loc_stat & machine::LOCSTAT_CACHED) {\n                QBrush bgd(Qt::lightGray);\n                return bgd;\n            }\n        } else if (index.column() == 0 && machine->is_hwbreak(address)) {\n            QBrush bgd(Qt::red);\n            return bgd;\n        } else if (index.column() == 3) {\n            if (address == stage_addr[STAGEADDR_WRITEBACK]) {\n                QBrush bgd(QColor(255, 173, 230));\n                return bgd;\n            } else if (address == stage_addr[STAGEADDR_MEMORY]) {\n                QBrush bgd(QColor(173, 255, 229));\n                return bgd;\n            } else if (address == stage_addr[STAGEADDR_EXECUTE]) {\n                QBrush bgd(QColor(193, 255, 173));\n                return bgd;\n            } else if (address == stage_addr[STAGEADDR_DECODE]) {\n                QBrush bgd(QColor(255, 212, 173));\n                return bgd;\n            } else if (address == stage_addr[STAGEADDR_FETCH]) {\n                QBrush bgd(QColor(255, 173, 173));\n                return bgd;\n            }\n        }\n        return {};\n    }\n    if (role == Qt::FontRole) { return data_font; }\n    if (role == Qt::TextAlignmentRole) {\n        if (index.column() == 0) { return Qt::AlignCenter; }\n        return Qt::AlignLeft;\n    }\n    return {};\n}\n\nvoid ProgramModel::setup(machine::Machine *machine) {\n    this->machine = machine;\n    for (auto &i : stage_addr) {\n        i = machine::STAGEADDR_NONE;\n    }\n    if (machine != nullptr) {\n        connect(machine, &machine::Machine::post_tick, this, &ProgramModel::check_for_updates);\n    }\n    if (mem_access() != nullptr) {\n        connect(\n            mem_access(), &machine::FrontendMemory::external_change_notify, this,\n            &ProgramModel::check_for_updates);\n    }\n    emit update_all();\n}\n\nvoid ProgramModel::update_all() {\n    const machine::FrontendMemory *mem;\n    mem = mem_access();\n    if (mem != nullptr) {\n        memory_change_counter = mem->get_change_counter();\n        if (machine->cache_program() != nullptr) {\n            cache_program_change_counter = machine->cache_program()->get_change_counter();\n        }\n    }\n    stages_need_update = false;\n    emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));\n}\n\nvoid ProgramModel::check_for_updates() {\n    bool need_update = stages_need_update;\n    const machine::FrontendMemory *mem;\n    mem = mem_access();\n    if (mem == nullptr) { return; }\n\n    if (memory_change_counter != mem->get_change_counter()) { need_update = true; }\n    if (machine->cache_data() != nullptr) {\n        if (cache_program_change_counter != machine->cache_program()->get_change_counter()) {\n            need_update = true;\n        }\n    }\n    if (!need_update) { return; }\n    update_all();\n}\n\nbool ProgramModel::adjustRowAndOffset(int &row, machine::Address address) {\n    row = rowCount() / 2;\n    address -= address.get_raw() % cellSizeBytes();\n    uint32_t row_bytes = cellSizeBytes();\n    uint32_t diff = row * row_bytes;\n    if (diff > address.get_raw()) {\n        row = address.get_raw() / row_bytes;\n        if (row == 0) {\n            index0_offset = machine::Address::null();\n        } else {\n            index0_offset = address - row * row_bytes;\n        }\n    } else {\n        index0_offset = address - diff;\n    }\n    return get_row_for_address(row, address);\n}\n\nvoid ProgramModel::toggle_hw_break(const QModelIndex &index) {\n    machine::Address address;\n    if (index.column() != 0 || machine == nullptr) { return; }\n\n    if (!get_row_address(address, index.row())) { return; }\n\n    if (machine->is_hwbreak(address)) {\n        machine->remove_hwbreak(address);\n    } else {\n        machine->insert_hwbreak(address);\n    }\n    update_all();\n}\n\nQt::ItemFlags ProgramModel::flags(const QModelIndex &index) const {\n    if (index.column() != 2 && index.column() != 3) {\n        return QAbstractTableModel::flags(index);\n    } else {\n        return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;\n    }\n}\n\nbool ProgramModel::setData(const QModelIndex &index, const QVariant &value, int role) {\n    if (role == Qt::EditRole) {\n        bool ok;\n        QString error;\n        machine::Address address;\n        uint32_t data;\n        machine::FrontendMemory *mem;\n        if (!get_row_address(address, index.row())) { return false; }\n        if (index.column() == 0 || machine == nullptr) { return false; }\n        mem = mem_access_rw();\n        if (mem == nullptr) { return false; }\n        switch (index.column()) {\n        case 2:\n            data = value.toString().toULong(&ok, 16);\n            if (!ok) { return false; }\n            mem->write_u32(address, data, ae::INTERNAL);\n            break;\n        case 3: try { machine::Instruction::code_from_string(&data, 4, value.toString(), address);\n            } catch (machine::Instruction::ParseError &e) {\n                emit report_error(tr(\"instruction 1 parse error - %2.\").arg(e.message));\n                return false;\n            }\n            mem->write_u32(address, data, ae::INTERNAL);\n            break;\n        default: return false;\n        }\n    }\n    return true;\n}\n\nvoid ProgramModel::update_stage_addr(uint stage, machine::Address addr) {\n    if (stage < STAGEADDR_COUNT) {\n        if (stage_addr[stage] != addr) {\n            stage_addr[stage] = addr;\n            stages_need_update = true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/gui/windows/program/programmodel.h",
    "content": "#ifndef PROGRAMMODEL_H\n#define PROGRAMMODEL_H\n\n#include \"machine/machine.h\"\n\n#include <QAbstractTableModel>\n#include <QFont>\n\nclass ProgramModel : public QAbstractTableModel {\n    Q_OBJECT\n\n    using Super = QAbstractTableModel;\n\npublic:\n    explicit ProgramModel(QObject *parent);\n    [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    [[nodiscard]] int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n    [[nodiscard]] QVariant\n    headerData(int section, Qt::Orientation orientation, int role) const override;\n    [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;\n    [[nodiscard]] Qt::ItemFlags flags(const QModelIndex &index) const override;\n    bool setData(const QModelIndex &index, const QVariant &value, int role) override;\n    bool adjustRowAndOffset(int &row, machine::Address address);\n\n    [[nodiscard]] inline const QFont *getFont() const { return &data_font; }\n\n    [[nodiscard]] inline machine::Address getIndex0Offset() const { return index0_offset; }\n\n    [[nodiscard]] static inline unsigned int cellSizeBytes() { return 4; }\n    inline bool get_row_address(machine::Address &address, int row) const {\n        address = index0_offset + row * cellSizeBytes();\n        return address >= index0_offset;\n    }\n    inline bool get_row_for_address(int &row, machine::Address address) const {\n        if (address < index0_offset) {\n            row = -1;\n            return false;\n        }\n        row = (address - index0_offset) / cellSizeBytes();\n        if (((address - index0_offset) > 0x80000000) || row > rowCount()) {\n            row = rowCount();\n            return false;\n        }\n        return true;\n    }\n\n    enum StageAddress {\n        STAGEADDR_FETCH,\n        STAGEADDR_DECODE,\n        STAGEADDR_EXECUTE,\n        STAGEADDR_MEMORY,\n        STAGEADDR_WRITEBACK,\n        STAGEADDR_COUNT,\n    };\n\nsignals:\n    void report_error(QString error);\npublic slots:\n    void setup(machine::Machine *machine);\n    void check_for_updates();\n    void toggle_hw_break(const QModelIndex &index);\n    void update_stage_addr(uint stage, machine::Address addr);\n    void update_all();\n\nprivate:\n    [[nodiscard]] const machine::FrontendMemory *mem_access() const;\n    [[nodiscard]] machine::FrontendMemory *mem_access_rw() const;\n    machine::Address index0_offset;\n    QFont data_font;\n    machine::Machine *machine;\n    uint32_t memory_change_counter;\n    uint32_t cache_program_change_counter;\n    machine::Address stage_addr[STAGEADDR_COUNT] {};\n    bool stages_need_update;\n};\n\n#endif // PROGRAMMODEL_H\n"
  },
  {
    "path": "src/gui/windows/program/programtableview.cpp",
    "content": "#include \"programtableview.h\"\n\n#include \"hinttabledelegate.h\"\n#include \"programmodel.h\"\n\n#include <QApplication>\n#include <QClipboard>\n#include <QHeaderView>\n#include <QKeyEvent>\n#include <QScrollBar>\n#include <QtGlobal>\n\nProgramTableView::ProgramTableView(QWidget *parent, QSettings *settings) : Super(parent) {\n    setItemDelegate(new HintTableDelegate(this));\n    connect(\n        verticalScrollBar(), &QAbstractSlider::valueChanged, this,\n        &ProgramTableView::adjust_scroll_pos_check);\n    connect(\n        this, &ProgramTableView::adjust_scroll_pos_queue, this,\n        &ProgramTableView::adjust_scroll_pos_process, Qt::QueuedConnection);\n    this->settings = settings;\n    initial_address = machine::Address(settings->value(\"ProgramViewAddr0\", 0).toULongLong());\n    adjust_scroll_pos_in_progress = false;\n    need_addr0_save = false;\n    setTextElideMode(Qt::ElideNone);\n}\n\nvoid ProgramTableView::addr0_save_change(machine::Address val) {\n    need_addr0_save = false;\n    settings->setValue(\"ProgramViewAddr0\", qint64(val.get_raw()));\n}\n\nvoid ProgramTableView::adjustColumnCount() {\n    QModelIndex idx;\n\n    auto *m = dynamic_cast<ProgramModel *>(model());\n\n    if (m == nullptr) { return; }\n\n    auto *delegate = dynamic_cast<HintTableDelegate *>(itemDelegate());\n    if (delegate == nullptr) { return; }\n\n    QStyleOptionViewItem viewOpts;\n\n    initViewItemOption(&viewOpts);\n\n    int totwidth = 0;\n    idx = m->index(0, 0);\n    auto cwidth_dh0 = delegate->sizeHintForText(viewOpts, idx, \"BP\").width() + 2;\n    horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);\n    horizontalHeader()->resizeSection(0, cwidth_dh0);\n    totwidth += cwidth_dh0;\n\n    idx = m->index(0, 1);\n    auto cwidth_dh1 = delegate->sizeHintForText(viewOpts, idx, \"0x00000000\").width() + 2;\n    horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);\n    horizontalHeader()->resizeSection(1, cwidth_dh1);\n    totwidth += cwidth_dh1;\n\n    idx = m->index(0, 2);\n    auto cwidth_dh2 = delegate->sizeHintForText(viewOpts, idx, \"00000000\").width() + 2;\n    horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);\n    horizontalHeader()->resizeSection(2, cwidth_dh2);\n    totwidth += cwidth_dh2;\n\n    horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);\n    idx = m->index(0, 3);\n    totwidth += delegate->sizeHintForText(viewOpts, idx, \"BEQ $18, $17, 0x00000258\").width() + 2;\n    totwidth += verticalHeader()->width();\n    setColumnHidden(2, totwidth > width());\n    setColumnHidden(1, totwidth - cwidth_dh2 > width());\n    setColumnHidden(0, totwidth - cwidth_dh2 - cwidth_dh1 > width());\n\n    if (!initial_address.is_null()) {\n        go_to_address(initial_address);\n        initial_address = machine::Address::null();\n    }\n}\n\nvoid ProgramTableView::adjust_scroll_pos_check() {\n    if (!adjust_scroll_pos_in_progress) {\n        adjust_scroll_pos_in_progress = true;\n        emit adjust_scroll_pos_queue();\n    }\n}\n\nvoid ProgramTableView::adjust_scroll_pos_process() {\n    adjust_scroll_pos_in_progress = false;\n    machine::Address address;\n    auto *m = dynamic_cast<ProgramModel *>(model());\n    if (m == nullptr) { return; }\n\n    QModelIndex prev_index = currentIndex();\n    auto row_bytes = machine::Address(ProgramModel::cellSizeBytes());\n    machine::Address index0_offset = m->getIndex0Offset();\n\n    do {\n        int row = rowAt(0);\n        int prev_row = row;\n        if (row < m->rowCount() / 8) {\n            if ((row == 0) && (index0_offset < row_bytes) && (!index0_offset.is_null())) {\n                m->adjustRowAndOffset(row, machine::Address::null());\n            } else if (index0_offset >= row_bytes) {\n                m->get_row_address(address, row);\n                m->adjustRowAndOffset(row, address);\n            } else {\n                break;\n            }\n        } else if (row > m->rowCount() - m->rowCount() / 8) {\n            m->get_row_address(address, row);\n            m->adjustRowAndOffset(row, address);\n        } else {\n            break;\n        }\n        scrollTo(m->index(row, 0), QAbstractItemView::PositionAtTop);\n        setCurrentIndex(m->index(prev_index.row() + row - prev_row, prev_index.column()));\n        emit m->update_all();\n    } while (false);\n    m->get_row_address(address, rowAt(0));\n    if (need_addr0_save) { addr0_save_change(address); }\n    emit address_changed(address.get_raw());\n}\n\nvoid ProgramTableView::resizeEvent(QResizeEvent *event) {\n    auto *m = dynamic_cast<ProgramModel *>(model());\n    machine::Address address;\n    bool keep_row0 = false;\n\n    if (m != nullptr) {\n        if (initial_address.is_null()) {\n            keep_row0 = m->get_row_address(address, rowAt(0));\n        } else {\n            address = initial_address;\n        }\n    }\n    Super::resizeEvent(event);\n    adjustColumnCount();\n    if (keep_row0) {\n        initial_address = machine::Address::null();\n        go_to_address(address);\n    }\n}\n\nvoid ProgramTableView::go_to_address_priv(machine::Address address) {\n    auto *m = dynamic_cast<ProgramModel *>(model());\n    int row;\n    if (m == nullptr) { return; }\n    m->adjustRowAndOffset(row, address);\n    scrollTo(m->index(row, 0), QAbstractItemView::PositionAtTop);\n    setCurrentIndex(m->index(row, 1));\n    if (need_addr0_save) { addr0_save_change(address); }\n    emit m->update_all();\n}\n\nvoid ProgramTableView::go_to_address(machine::Address address) {\n    need_addr0_save = true;\n    go_to_address_priv(address);\n}\n\nvoid ProgramTableView::focus_address(machine::Address address) {\n    int row;\n    auto *m = dynamic_cast<ProgramModel *>(model());\n    if (m == nullptr) { return; }\n    if (!m->get_row_for_address(row, address)) { go_to_address_priv(address); }\n    if (!m->get_row_for_address(row, address)) { return; }\n    setCurrentIndex(m->index(row, 3));\n}\n\nvoid ProgramTableView::focus_address_with_save(machine::Address address) {\n    need_addr0_save = true;\n    focus_address(address);\n}\n\nvoid ProgramTableView::keyPressEvent(QKeyEvent *event) {\n    if (event->matches(QKeySequence::Copy)) {\n        QString text;\n        QItemSelectionRange range = selectionModel()->selection().first();\n        for (auto i = range.top(); i <= range.bottom(); ++i) {\n            QStringList rowContents;\n            for (auto j = range.left(); j <= range.right(); ++j) {\n                rowContents << model()->index(i, j).data().toString();\n            }\n            text += rowContents.join(\"\\t\");\n            text += \"\\n\";\n        }\n        QApplication::clipboard()->setText(text);\n    } else {\n        Super::keyPressEvent(event);\n    }\n}\n"
  },
  {
    "path": "src/gui/windows/program/programtableview.h",
    "content": "#ifndef PROGRAMTABLEVIEW_H\n#define PROGRAMTABLEVIEW_H\n\n#include \"common/polyfills/qt5/qtableview.h\"\n#include \"machine/memory/address.h\"\n\n#include <QObject>\n#include <QSettings>\n#include <QSharedPointer>\n\nclass ProgramTableView : public Poly_QTableView {\n    Q_OBJECT\n\n    using Super = Poly_QTableView;\n\npublic:\n    ProgramTableView(QWidget *parent, QSettings *settings);\n\n    void resizeEvent(QResizeEvent *event) override;\nsignals:\n    void address_changed(uint32_t address);\n    void adjust_scroll_pos_queue();\npublic slots:\n    void go_to_address(machine::Address address);\n    void focus_address(machine::Address address);\n    void focus_address_with_save(machine::Address address);\n\nprotected:\n    void keyPressEvent(QKeyEvent *event) override;\nprivate slots:\n    void adjust_scroll_pos_check();\n    void adjust_scroll_pos_process();\n\nprivate:\n    void go_to_address_priv(machine::Address address);\n    void addr0_save_change(machine::Address val);\n    void adjustColumnCount();\n    QSettings *settings;\n\n    machine::Address initial_address;\n    bool adjust_scroll_pos_in_progress;\n    bool need_addr0_save;\n};\n\n#endif // PROGRAMTABLEVIEW_H\n"
  },
  {
    "path": "src/gui/windows/registers/registersdock.cpp",
    "content": "#include \"registersdock.h\"\n\n#include \"machine/instruction.h\"\n\nRegistersDock::RegistersDock(QWidget *parent, machine::Xlen xlen)\n    : QDockWidget(parent)\n    , xlen(xlen)\n    , scroll_area(new QScrollArea(this))\n    , table_widget(new StaticTable(scroll_area.data()))\n    , pal_normal(createPalette(QColor(0, 0, 0)))\n    , pal_updated(createPalette(QColor(240, 0, 0)))\n    , pal_read(createPalette(QColor(0, 0, 240))) {\n    scroll_area->setWidgetResizable(true);\n    gp_highlighted.reset();\n\n    for (size_t i = 0; i < gp.size(); i++) {\n        gp[i] = addRegisterLabel(QString(\"x%1/%2\").arg(i).arg(machine::Rv_regnames[i]));\n    }\n    pc = addRegisterLabel(\"pc\");\n\n    scroll_area->setWidget(table_widget.data());\n    setWidget(scroll_area.data());\n\n    setObjectName(\"Registers\");\n    setWindowTitle(\"Registers\");\n}\n\nconst char *RegistersDock::sizeHintText() {\n    if (xlen == machine::Xlen::_64)\n        return \"0x0000000000000000\";\n    else\n        return \"0x00000000\";\n}\n\nQLabel *RegistersDock::addRegisterLabel(const QString &title) {\n    auto *data_label = new QLabel(sizeHintText(), table_widget.data());\n    data_label->setTextFormat(Qt::PlainText);\n    data_label->setFixedSize(data_label->sizeHint());\n    data_label->setText(\"\");\n    data_label->setPalette(pal_normal);\n    data_label->setTextInteractionFlags(Qt::TextSelectableByMouse);\n\n    auto *title_label = new QLabel(title, table_widget.data());\n    title_label->setPalette(pal_normal);\n\n    // Add row take ownership of the labels.\n    table_widget->addRow({ OWNED title_label, OWNED data_label });\n\n    return BORROWED data_label;\n}\n\nvoid RegistersDock::connectToMachine(machine::Machine *machine) {\n    if (machine == nullptr) {\n        // Reset data\n        pc->setText(\"\");\n        for (auto &i : gp) {\n            i->setText(\"\");\n        }\n        return;\n    }\n\n    regs_handle = machine->registers();\n\n    // if xlen changes adjust space to show full value\n    if (xlen != machine->config().get_simulated_xlen()) {\n        xlen = machine->config().get_simulated_xlen();\n        auto *dumy_data_label = new QLabel(sizeHintText(), table_widget.data());\n        for (auto &i : gp) {\n            i->setFixedSize(dumy_data_label->sizeHint());\n        }\n        pc->setFixedSize(dumy_data_label->sizeHint());\n        delete dumy_data_label;\n    }\n\n    if (regs_handle == nullptr) { return; }\n\n    reload();\n\n    connect(regs_handle, &machine::Registers::pc_update, this, &RegistersDock::pc_changed);\n    connect(regs_handle, &machine::Registers::gp_update, this, &RegistersDock::gp_changed);\n    connect(regs_handle, &machine::Registers::gp_read, this, &RegistersDock::gp_read);\n    connect(machine, &machine::Machine::tick, this, &RegistersDock::clear_highlights);\n}\n\nvoid RegistersDock::showEvent(QShowEvent *event) {\n    reload();\n    QDockWidget::showEvent(event);\n}\n\nvoid RegistersDock::pc_changed(machine::Address val) {\n    if (isHidden()) { return; }\n    setRegisterValueToLabel(pc, val.get_raw());\n}\n\nvoid RegistersDock::gp_changed(machine::RegisterId i, machine::RegisterValue val) {\n    if (isHidden()) { return; }\n\n    setRegisterValueToLabel(gp[i], val);\n    gp[i]->setPalette(pal_updated);\n    gp_highlighted[i] = true;\n}\n\nvoid RegistersDock::gp_read(machine::RegisterId i, machine::RegisterValue val) {\n    Q_UNUSED(val)\n\n    if (isHidden()) { return; }\n\n    if (!(gp_highlighted[i])) {\n        gp[i]->setPalette(pal_read);\n        gp_highlighted[i] = true;\n    }\n}\n\nvoid RegistersDock::clear_highlights() {\n    if (gp_highlighted.any()) {\n        for (size_t i = 0; i < gp.size(); i++) {\n            if (gp_highlighted[i]) { gp[i]->setPalette(pal_normal); }\n        }\n    }\n    gp_highlighted.reset();\n}\nvoid RegistersDock::reload() {\n    if (regs_handle == nullptr) { return; }\n    setRegisterValueToLabel(pc, regs_handle->read_pc().get_raw());\n    for (size_t i = 0; i < gp.size(); i++) {\n        setRegisterValueToLabel(gp[i], regs_handle->read_gp_internal(i));\n    }\n    clear_highlights();\n}\n\nvoid RegistersDock::setRegisterValueToLabel(QLabel *label, machine::RegisterValue value) {\n    label->setText(QString(\"0x%1\").arg(value.as_xlen(xlen), 0, 16));\n}\n\nQPalette RegistersDock::createPalette(const QColor &color) const {\n    QPalette palette = this->palette();\n    palette.setColor(QPalette::WindowText, color);\n    return palette;\n}\n"
  },
  {
    "path": "src/gui/windows/registers/registersdock.h",
    "content": "#ifndef REGISTERSDOCK_H\n#define REGISTERSDOCK_H\n\n#include \"machine/machine.h\"\n#include \"statictable.h\"\n\n#include <QDockWidget>\n#include <QLabel>\n#include <QPalette>\n#include <QScrollArea>\n#include <bitset>\n\nusing std::array;\nusing std::bitset;\n\n/**\n * NOTE: RV64 ready\n */\nclass RegistersDock final : public QDockWidget {\n    Q_OBJECT\npublic:\n    explicit RegistersDock(QWidget *parent, machine::Xlen xlen);\n\n    void connectToMachine(machine::Machine *machine);\n\n    void showEvent(QShowEvent *event) override;\n\nprivate slots:\n    void pc_changed(machine::Address val);\n    void gp_changed(machine::RegisterId i, machine::RegisterValue val);\n    void gp_read(machine::RegisterId i, machine::RegisterValue val);\n    void clear_highlights();\n\nprivate:\n    // Do full update of all registers. Clear all highlights.\n    void reload();\n\n    machine::Xlen xlen;\n    // Used for batch updates when registers are shown.\n    const machine::Registers *regs_handle {};\n\n    const char *sizeHintText();\n\n    Box<QScrollArea> scroll_area;\n    Box<StaticTable> table_widget;\n\n    BORROWED QLabel *pc {};\n    array<BORROWED QLabel *, machine::REGISTER_COUNT> gp {};\n\n    bitset<machine::REGISTER_COUNT> gp_highlighted { false };\n\n    QPalette pal_normal;\n    QPalette pal_updated;\n    QPalette pal_read;\n\nprivate:\n    void setRegisterValueToLabel(QLabel *label, machine::RegisterValue value);\n    BORROWED QLabel *addRegisterLabel(const QString &title);\n    [[nodiscard]] QPalette createPalette(const QColor &color) const;\n};\n\n#endif // REGISTERSDOCK_H\n"
  },
  {
    "path": "src/gui/windows/terminal/terminaldock.cpp",
    "content": "#include \"terminaldock.h\"\n\n#include \"machine/memory/backend/serialport.h\"\n\n#include <QString>\n#include <QTextBlock>\n#include <QTextCursor>\n\nTerminalDock::TerminalDock(QWidget *parent, QSettings *settings) : QDockWidget(parent) {\n    (void)settings;\n    top_widget = new QWidget(this);\n    setWidget(top_widget);\n    layout_box = new QVBoxLayout(top_widget);\n\n    terminal_text = new QTextEdit(top_widget);\n    terminal_text->setMinimumSize(30, 30);\n    layout_box->addWidget(terminal_text);\n    append_cursor.reset(new QTextCursor(terminal_text->document()));\n    layout_bottom_box = new QHBoxLayout();\n    layout_bottom_box->addWidget(new QLabel(\"Input:\"));\n    input_edit = new QLineEdit();\n    layout_bottom_box->addWidget(input_edit);\n    layout_box->addLayout(layout_bottom_box);\n    // insert newline on enter (it will be displayed as space)\n    connect(input_edit, &QLineEdit::returnPressed, [this]() {\n        input_edit->setText(input_edit->text() + '\\n');\n    });\n\n    setObjectName(\"Terminal\");\n    setWindowTitle(\"Terminal\");\n}\n\nvoid TerminalDock::setup(machine::SerialPort *ser_port) {\n    if (ser_port == nullptr) { return; }\n    connect(\n        ser_port, &machine::SerialPort::tx_byte, this,\n        QOverload<unsigned int>::of(&TerminalDock::tx_byte));\n    connect(ser_port, &machine::SerialPort::rx_byte_pool, this, &TerminalDock::rx_byte_pool);\n    connect(input_edit, &QLineEdit::textChanged, ser_port, &machine::SerialPort::rx_queue_check);\n}\n\nvoid TerminalDock::tx_byte(unsigned int data) {\n    bool at_end = terminal_text->textCursor().atEnd();\n    if (data == '\\n') {\n        append_cursor->insertBlock();\n    } else {\n        append_cursor->insertText(QString(QChar(data)));\n    }\n    if (at_end) {\n        QTextCursor cursor = QTextCursor(terminal_text->document());\n        cursor.movePosition(QTextCursor::End);\n        terminal_text->setTextCursor(cursor);\n    }\n}\n\nvoid TerminalDock::tx_byte(int fd, unsigned int data) {\n    (void)fd;\n    tx_byte(data);\n}\n\nvoid TerminalDock::rx_byte_pool(int fd, unsigned int &data, bool &available) {\n    (void)fd;\n    QString str = input_edit->text();\n    available = false;\n    if (str.count() > 0) {\n        data = str[0].toLatin1();\n        input_edit->setText(str.remove(0, 1));\n        available = true;\n    }\n}\n"
  },
  {
    "path": "src/gui/windows/terminal/terminaldock.h",
    "content": "#ifndef TERMINALDOCK_H\n#define TERMINALDOCK_H\n\n#include \"machine/machine.h\"\n\n#include <QDockWidget>\n#include <QFormLayout>\n#include <QLabel>\n#include <QLineEdit>\n#include <QTextCursor>\n#include <QTextEdit>\n\nclass TerminalDock : public QDockWidget {\n    Q_OBJECT\npublic:\n    TerminalDock(QWidget *parent, QSettings *settings);\n\n    void setup(machine::SerialPort *ser_port);\n\npublic slots:\n    void tx_byte(unsigned int data);\n    void tx_byte(int fd, unsigned int data);\n    void rx_byte_pool(int fd, unsigned int &data, bool &available);\n\nprivate:\n    QVBoxLayout *layout_box;\n    QHBoxLayout *layout_bottom_box;\n    QWidget *top_widget, *top_form {};\n    QFormLayout *layout_top_form {};\n    QTextEdit *terminal_text;\n    Box<QTextCursor> append_cursor;\n    QLineEdit *input_edit;\n};\n\n#endif // TERMINALDOCK_H\n"
  },
  {
    "path": "src/gui/windows/tlb/tlbdock.cpp",
    "content": "#include \"tlbdock.h\"\n\nTLBDock::TLBDock(QWidget *parent, const QString &type)\n    : QDockWidget(parent)\n    , tlbscene(nullptr)\n    , connected_tlb(nullptr) {\n    top_widget = new QWidget(this);\n    setWidget(top_widget);\n    layout_box = new QVBoxLayout(top_widget);\n\n    top_form = new QWidget(top_widget);\n    top_form->setVisible(false);\n    layout_box->addWidget(top_form);\n    layout_top_form = new QFormLayout(top_form);\n\n    l_hit = new QLabel(\"0\", top_form);\n    l_hit->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Hit:\", l_hit);\n\n    l_miss = new QLabel(\"0\", top_form);\n    l_miss->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Miss:\", l_miss);\n\n    l_m_reads = new QLabel(\"0\", top_form);\n    l_m_reads->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Memory reads:\", l_m_reads);\n\n    l_m_writes = new QLabel(\"0\", top_form);\n    l_m_writes->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Memory writes:\", l_m_writes);\n\n    l_stalled = new QLabel(\"0\", top_form);\n    l_stalled->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Memory stall cycles:\", l_stalled);\n\n    l_hit_rate = new QLabel(\"0.000%\", top_form);\n    l_hit_rate->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Hit rate:\", l_hit_rate);\n\n    l_speed = new QLabel(\"100%\", top_form);\n    l_speed->setTextFormat(Qt::PlainText);\n    layout_top_form->addRow(\"Improved speed:\", l_speed);\n\n    graphicsview = new GraphicsView(top_widget);\n    graphicsview->setVisible(false);\n    layout_box->addWidget(graphicsview);\n    tlbscene = nullptr;\n\n    no_tlb = new QLabel(\"No \" + type + \" TLB configured\", top_widget);\n    layout_box->addWidget(no_tlb);\n\n    setObjectName(type + \"TLB\");\n    setWindowTitle(type + \" TLB\");\n}\n\nvoid TLBDock::setup(machine::TLB *tlb) {\n    memory_reads = 0;\n    memory_writes = 0;\n    hit = 0;\n    miss = 0;\n    stalled = 0;\n    speed_improv = 0.0;\n    hit_rate = 0.0;\n\n    l_hit->setText(\"0\");\n    l_miss->setText(\"0\");\n    l_stalled->setText(\"0\");\n    l_m_reads->setText(\"0\");\n    l_m_writes->setText(\"0\");\n    l_hit_rate->setText(\"0.000%\");\n    l_speed->setText(\"100%\");\n\n    if (tlb != nullptr) {\n        connect(tlb, &machine::TLB::hit_update, this, &TLBDock::hit_update, Qt::UniqueConnection);\n        connect(tlb, &machine::TLB::miss_update, this, &TLBDock::miss_update, Qt::UniqueConnection);\n        connect(\n            tlb, &machine::TLB::statistics_update, this, &TLBDock::statistics_update,\n            Qt::UniqueConnection);\n        connect(\n            tlb, &machine::TLB::memory_reads_update, this, &TLBDock::memory_reads_update,\n            Qt::UniqueConnection);\n        connect(\n            tlb, &machine::TLB::memory_writes_update, this, &TLBDock::memory_writes_update,\n            Qt::UniqueConnection);\n    }\n    connected_tlb = const_cast<machine::TLB *>(tlb);\n    top_form->setVisible(tlb != nullptr);\n    no_tlb->setVisible(tlb == nullptr);\n\n    delete tlbscene;\n    tlbscene = nullptr;\n    if (tlb != nullptr) {\n        tlbscene = new TLBViewScene(tlb);\n        graphicsview->setScene(tlbscene);\n    } else {\n        graphicsview->setScene(nullptr);\n    }\n    graphicsview->setVisible(tlb != nullptr);\n}\n\nvoid TLBDock::paintEvent(QPaintEvent *event) {\n    l_stalled->setText(QString::number(stalled));\n    l_hit_rate->setText(QString::number(hit_rate, 'f', 3) + QString(\"%\"));\n    l_speed->setText(QString::number(speed_improv, 'f', 0) + QString(\"%\"));\n    l_hit->setText(QString::number(hit));\n    l_miss->setText(QString::number(miss));\n    l_m_reads->setText(QString::number(memory_reads));\n    l_m_writes->setText(QString::number(memory_writes));\n    QDockWidget::paintEvent(event);\n}\n\nvoid TLBDock::hit_update(unsigned val) {\n    hit = val;\n    update();\n}\n\nvoid TLBDock::miss_update(unsigned val) {\n    miss = val;\n    update();\n}\n\nvoid TLBDock::statistics_update(unsigned stalled_cycles, double speed_improv_v, double hit_rate_v) {\n    stalled = stalled_cycles;\n    speed_improv = speed_improv_v;\n    hit_rate = hit_rate_v;\n    update();\n}\n\nvoid TLBDock::memory_reads_update(unsigned val) {\n    memory_reads = val;\n    l_m_reads->setText(QString::number(memory_reads));\n    update();\n}\n\nvoid TLBDock::memory_writes_update(unsigned val) {\n    memory_writes = val;\n    l_m_writes->setText(QString::number(memory_writes));\n    update();\n}\n"
  },
  {
    "path": "src/gui/windows/tlb/tlbdock.h",
    "content": "#ifndef TLBDOCK_H\n#define TLBDOCK_H\n\n#include \"graphicsview.h\"\n#include \"machine/machine.h\"\n#include \"tlbview.h\"\n\n#include <QDockWidget>\n#include <QFormLayout>\n#include <QLabel>\n#include <QPointer>\n\nclass TLBDock : public QDockWidget {\n    Q_OBJECT\npublic:\n    TLBDock(QWidget *parent, const QString &type);\n\n    void setup(machine::TLB *tlb);\n\n    void paintEvent(QPaintEvent *event) override;\n\nprivate slots:\n    void hit_update(unsigned val);\n    void miss_update(unsigned val);\n    void statistics_update(unsigned stalled_cycles, double speed_improv, double hit_rate);\n    void memory_reads_update(unsigned val);\n    void memory_writes_update(unsigned val);\n\nprivate:\n    QVBoxLayout *layout_box;\n    QWidget *top_widget;\n    QWidget *top_form;\n    QFormLayout *layout_top_form;\n\n    QLabel *l_hit;\n    QLabel *l_miss;\n    QLabel *l_stalled;\n    QLabel *l_speed;\n    QLabel *l_hit_rate;\n    QLabel *no_tlb;\n    QLabel *l_m_reads;\n    QLabel *l_m_writes;\n\n    GraphicsView *graphicsview;\n    TLBViewScene *tlbscene;\n\n    unsigned memory_reads = 0;\n    unsigned memory_writes = 0;\n    unsigned hit = 0;\n    unsigned miss = 0;\n    unsigned stalled = 0;\n    double speed_improv = 0.0;\n    double hit_rate = 0.0;\n\n    QPointer<machine::TLB> connected_tlb;\n};\n#endif // TLBDOCK_H\n"
  },
  {
    "path": "src/gui/windows/tlb/tlbview.cpp",
    "content": "#include \"tlbview.h\"\n\n#include <QBrush>\n#include <QFont>\n#include <QPainter>\n#include <QString>\n\nstatic const int ROW_HEIGHT = 16;\nstatic const int VCOL_WIDTH = 18;\nstatic const int FIELD_WIDTH = 120;\n\nTLBAddressBlock::TLBAddressBlock(machine::TLB *tlb, unsigned width) {\n    this->width = width;\n    rows = tlb->get_config().get_tlb_num_sets();\n    s_row = rows > 1 ? (32 - __builtin_clz(rows - 1)) : 0;\n    s_tag = 32 - s_row - 2;\n    tag = 0;\n    row = 0;\n\n    connect(tlb, &machine::TLB::tlb_update, this, &TLBAddressBlock::tlb_update);\n}\n\nQRectF TLBAddressBlock::boundingRect() const {\n    return QRectF(0, 0, width, 40);\n}\n\nvoid TLBAddressBlock::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) {\n    QFont fnt;\n    fnt.setPointSize(8);\n    painter->setFont(fnt);\n\n    painter->drawText(QRectF(0, 0, width, 14), Qt::AlignCenter, \"TLB Address\");\n    painter->drawText(QRectF(5, 18, 100, 16), Qt::AlignLeft, QString(\"Set: %1\").arg(row));\n}\n\nvoid TLBAddressBlock::tlb_update(unsigned, unsigned set, bool, unsigned, quint64, quint64, bool) {\n    this->row = set;\n    update();\n}\n\n///////////////////////////////////////////\n\nTLBViewBlock::TLBViewBlock(machine::TLB *tlb, unsigned way) : tlb(tlb), way_index(way) {\n    rows = tlb->get_config().get_tlb_num_sets();\n    curr_row = 0;\n    last_set = 0;\n    last_highlighted = false;\n\n    QFont font;\n    font.setPixelSize(10);\n\n    validity.reserve(rows);\n    asid.reserve(rows);\n    vpn.reserve(rows);\n    phys.reserve(rows);\n\n    int y = 2;\n    for (unsigned i = 0; i < rows; ++i) {\n        int x = 2;\n        auto *v = new QGraphicsSimpleTextItem(\"0\", this);\n        v->setFont(font);\n        v->setPos(x, y);\n        x += VCOL_WIDTH;\n\n        auto *a = new QGraphicsSimpleTextItem(\"\", this);\n        a->setFont(font);\n        a->setPos(x, y);\n        x += 60;\n\n        auto *vpnItem = new QGraphicsSimpleTextItem(\"\", this);\n        vpnItem->setFont(font);\n        vpnItem->setPos(x, y);\n        x += FIELD_WIDTH;\n\n        auto *physItem = new QGraphicsSimpleTextItem(\"\", this);\n        physItem->setFont(font);\n        physItem->setPos(x, y);\n\n        validity.push_back(v);\n        asid.push_back(a);\n        vpn.push_back(vpnItem);\n        phys.push_back(physItem);\n\n        y += ROW_HEIGHT;\n    }\n\n    auto *l_v = new QGraphicsSimpleTextItem(\"V\", this);\n    l_v->setFont(font);\n    l_v->setPos(2, -14);\n\n    auto *l_asid = new QGraphicsSimpleTextItem(\"ASID\", this);\n    l_asid->setFont(font);\n    l_asid->setPos(2 + VCOL_WIDTH + 2, -14);\n\n    auto *l_vpn = new QGraphicsSimpleTextItem(\"VPN\", this);\n    l_vpn->setFont(font);\n    l_vpn->setPos(2 + VCOL_WIDTH + 62, -14);\n\n    auto *l_phys = new QGraphicsSimpleTextItem(\"PBASE\", this);\n    l_phys->setFont(font);\n    l_phys->setPos(2 + VCOL_WIDTH + 62 + FIELD_WIDTH, -14);\n\n    connect(tlb, &machine::TLB::tlb_update, this, &TLBViewBlock::tlb_update);\n}\n\nQRectF TLBViewBlock::boundingRect() const {\n    return QRectF(-2, -18, VCOL_WIDTH + 60 + FIELD_WIDTH + 200, rows * ROW_HEIGHT + 40);\n}\n\nvoid TLBViewBlock::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) {\n    int width = boundingRect().width();\n    painter->drawRect(0, 0, width, rows * ROW_HEIGHT);\n    for (unsigned i = 0; i <= rows; ++i) {\n        painter->drawLine(0, i * ROW_HEIGHT, width, i * ROW_HEIGHT);\n    }\n}\n\nvoid TLBViewBlock::tlb_update(\n    unsigned way,\n    unsigned set,\n    bool valid,\n    unsigned asid_v,\n    quint64 vpn_v,\n    quint64 phys_v,\n    bool write) {\n    if (way != way_index) return;\n    validity[set]->setText(valid ? \"1\" : \"0\");\n    asid[set]->setText(valid ? QString::number(asid_v) : QString());\n    vpn[set]->setText(\n        valid ? QString(\"0x%1\").arg(QString::number(vpn_v, 16).toUpper()) : QString());\n    phys[set]->setText(\n        valid ? QString(\"0x%1\").arg(QString::number(phys_v, 16).toUpper()) : QString());\n\n    if (last_highlighted) {\n        validity[last_set]->setBrush(QBrush(QColor(0, 0, 0)));\n        asid[last_set]->setBrush(QBrush(QColor(0, 0, 0)));\n        vpn[last_set]->setBrush(QBrush(QColor(0, 0, 0)));\n        phys[last_set]->setBrush(QBrush(QColor(0, 0, 0)));\n    }\n    if (valid) {\n        QColor c = write ? QColor(200, 0, 0) : QColor(0, 0, 150);\n        validity[set]->setBrush(QBrush(c));\n        asid[set]->setBrush(QBrush(c));\n        vpn[set]->setBrush(QBrush(c));\n        phys[set]->setBrush(QBrush(c));\n    }\n    last_highlighted = true;\n    last_set = set;\n}\n\n///////////////////////////////////////////\n\nTLBViewScene::TLBViewScene(machine::TLB *tlb) {\n    associativity = tlb->get_config().get_tlb_associativity();\n    block.reserve(associativity);\n    int offset = 0;\n    for (unsigned i = 0; i < associativity; ++i) {\n        auto b = std::make_unique<TLBViewBlock>(tlb, i);\n        addItem(b.get());\n        b->setPos(1, offset);\n        offset += b->boundingRect().height();\n        block.push_back(std::move(b));\n    }\n    ablock = std::make_unique<TLBAddressBlock>(\n        tlb, block.empty() ? FIELD_WIDTH : static_cast<unsigned>(block[0]->boundingRect().width()));\n    addItem(ablock.get());\n    ablock->setPos(0, -ablock->boundingRect().height() - 16);\n}\n\nTLBViewScene::~TLBViewScene() {\n    for (auto &bptr : block) {\n        if (bptr) removeItem(bptr.get());\n    }\n    block.clear();\n    if (ablock) {\n        removeItem(ablock.get());\n        ablock.reset();\n    }\n}\n"
  },
  {
    "path": "src/gui/windows/tlb/tlbview.h",
    "content": "#ifndef TLBVIEW_H\n#define TLBVIEW_H\n\n#include \"machine/memory/tlb/tlb.h\"\n#include \"svgscene/utils/memory_ownership.h\"\n\n#include <QGraphicsObject>\n#include <QGraphicsScene>\n#include <QGraphicsSimpleTextItem>\n#include <QPointer>\n\nclass TLBAddressBlock : public QGraphicsObject {\n    Q_OBJECT\npublic:\n    TLBAddressBlock(machine::TLB *tlb, unsigned width);\n    QRectF boundingRect() const override;\n    void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override;\n\npublic slots:\n    void tlb_update(\n        unsigned way,\n        unsigned set,\n        bool valid,\n        unsigned asid,\n        quint64 vpn,\n        quint64 phys,\n        bool write);\n\nprivate:\n    unsigned width;\n    unsigned rows;\n    unsigned tag, row;\n    unsigned s_row, s_tag;\n};\n\nclass TLBViewBlock : public QGraphicsObject {\n    Q_OBJECT\npublic:\n    TLBViewBlock(machine::TLB *tlb, unsigned way);\n    ~TLBViewBlock() override = default;\n    QRectF boundingRect() const override;\n    void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override;\n\npublic slots:\n    void tlb_update(\n        unsigned way,\n        unsigned set,\n        bool valid,\n        unsigned asid,\n        quint64 vpn,\n        quint64 phys,\n        bool write);\n\nprivate:\n    QPointer<machine::TLB> tlb;\n    unsigned way_index;\n    unsigned rows;\n    std::vector<BORROWED QGraphicsSimpleTextItem *> validity;\n    std::vector<BORROWED QGraphicsSimpleTextItem *> asid;\n    std::vector<BORROWED QGraphicsSimpleTextItem *> vpn;\n    std::vector<BORROWED QGraphicsSimpleTextItem *> phys;\n    unsigned curr_row;\n    unsigned last_set;\n    bool last_highlighted;\n};\n\nclass TLBViewScene : public QGraphicsScene {\npublic:\n    TLBViewScene(machine::TLB *tlb);\n    ~TLBViewScene() override;\n\nprivate:\n    unsigned associativity;\n    std::vector<std::unique_ptr<TLBViewBlock>> block;\n    std::unique_ptr<TLBAddressBlock> ablock;\n};\n\n#endif // TLBVIEW_H\n"
  },
  {
    "path": "src/machine/CMakeLists.txt",
    "content": "project(machine\n\t\tDESCRIPTION \"The actual simulator as a library. Link with an UI of your choice.\")\n\nset(CMAKE_AUTOMOC ON)\n\nset(machine_SOURCES\n\t\texecute/alu.cpp\n\t\tcsr/controlstate.cpp\n\t\tcore.cpp\n\t\tinstruction.cpp\n\t\tmachine.cpp\n\t\tmachineconfig.cpp\n\t\tmemory/backend/lcddisplay.cpp\n\t\tmemory/backend/memory.cpp\n\t\tmemory/backend/peripheral.cpp\n\t\tmemory/backend/peripspiled.cpp\n\t\tmemory/backend/serialport.cpp\n\t\tmemory/backend/aclintmtimer.cpp\n\t\tmemory/backend/aclintmswi.cpp\n\t\tmemory/backend/aclintsswi.cpp\n\t\tmemory/cache/cache.cpp\n\t\tmemory/cache/cache_policy.cpp\n\t\tmemory/frontend_memory.cpp\n\t\tmemory/memory_bus.cpp\n\t\tmemory/tlb/tlb.cpp\n\t\tmemory/tlb/tlb_policy.cpp\n\t\tmemory/virtual/page_table_walker.cpp\n\t\tprogramloader.cpp\n\t\tpredictor.cpp\n\t\tregisters.cpp\n\t\tsimulator_exception.cpp\n\t\tsymboltable.cpp\n\t\t)\n\nset(machine_HEADERS\n\t\texecute/alu.h\n\t\tcsr/controlstate.h\n\t\tcore.h\n\t\tcore/core_state.h\n\t\tcsr/address.h\n\t\tinstruction.h\n\t\tmachine.h\n\t\tmachineconfig.h\n\t\tconfig_isa.h\n\t\tmachinedefs.h\n\t\tmemory/address.h\n\t\tmemory/address_range.h\n\t\tmemory/backend/backend_memory.h\n\t\tmemory/backend/lcddisplay.h\n\t\tmemory/backend/memory.h\n\t\tmemory/backend/peripheral.h\n\t\tmemory/backend/peripspiled.h\n\t\tmemory/backend/serialport.h\n\t\tmemory/backend/aclintmtimer.h\n\t\tmemory/backend/aclintmswi.h\n\t\tmemory/backend/aclintsswi.h\n\t\tmemory/cache/cache.h\n\t\tmemory/cache/cache_policy.h\n\t\tmemory/cache/cache_types.h\n\t\tmemory/frontend_memory.h\n\t\tmemory/memory_bus.h\n\t\tmemory/memory_utils.h\n\t\tprogramloader.h\n\t\tpredictor_types.h\n\t\tpredictor.h\n\t\tpipeline.h\n\t\tregisters.h\n\t\tregister_value.h\n\t\tsimulator_exception.h\n\t\tsymboltable.h\n\t\tutils.h\n\t\texecute/alu_op.h\n\t\texecute/mul_op.h\n\t\tmemory/virtual/virtual_address.h\n\t\tmemory/tlb/tlb.h\n\t\tmemory/virtual/sv32.h\n\t\tmemory/tlb/tlb_policy.h\n\t\tmemory/virtual/page_table_walker.h\n\t\tmemory/address_with_mode.h\n)\n\n# Object library is preferred, because the library archive is never really\n# needed. This option skips the archive creation and links directly .o files.\nadd_library(machine STATIC\n\t\t${machine_SOURCES}\n\t\t${machine_HEADERS})\ntarget_link_libraries(machine\n\t\tPRIVATE ${QtLib}::Core\n\t\tPUBLIC elf++ dwarf++)\ntarget_include_directories(machine\n\t\tPUBLIC \"${PROJECT_SOURCE_DIR}/external/libelfin\")\n\nif (NOT ${WASM})\n\t# Machine tests (not available on WASM)\n\n\tadd_executable(alu_test\n\t\t\texecute/alu.test.cpp\n\t\t\texecute/alu.test.h\n\t\t\texecute/alu.cpp\n\t\t\texecute/alu.h\n\t)\n\ttarget_link_libraries(alu_test\n\t\t\tPRIVATE ${QtLib}::Core ${QtLib}::Test)\n\tadd_test(NAME alu COMMAND alu_test)\n\n\tadd_executable(registers_test\n\t\t\tregister_value.h\n\t\t\tregisters.cpp\n\t\t\tregisters.h\n\t\t\tregisters.test.cpp\n\t\t\tregisters.test.h\n\t\t\tsimulator_exception.cpp\n\t\t\tsimulator_exception.h\n\t)\n\ttarget_link_libraries(registers_test\n\t\t\tPRIVATE ${QtLib}::Core ${QtLib}::Test)\n\tadd_test(NAME registers COMMAND registers_test)\n\n\tadd_executable(memory_test\n\t\t\tmachineconfig.cpp\n\t\t\tmachineconfig.h\n\t\t\tmemory/backend/backend_memory.h\n\t\t\tmemory/backend/memory.cpp\n\t\t\tmemory/backend/memory.h\n\t\t\tmemory/backend/memory.test.cpp\n\t\t\tmemory/backend/memory.test.h\n\t\t\tmemory/frontend_memory.cpp\n\t\t\tmemory/frontend_memory.h\n\t\t\tmemory/tlb/tlb.h\n\t\t\tmemory/tlb/tlb.cpp\n\t\t\tmemory/tlb/tlb_policy.h\n\t\t\tmemory/tlb/tlb_policy.cpp\n\t\t\tmemory/virtual/page_table_walker.h\n\t\t\tmemory/virtual/page_table_walker.cpp\n\t\t\tmemory/memory_bus.cpp\n\t\t\tmemory/memory_bus.h\n\t\t\tsimulator_exception.cpp\n\t\t\tsimulator_exception.h\n\t\t\ttests/utils/integer_decomposition.h\n\t\t\t)\n\ttarget_link_libraries(memory_test\n\t\t\tPRIVATE ${QtLib}::Core ${QtLib}::Test)\n\tadd_test(NAME memory COMMAND memory_test)\n\n\tadd_executable(cache_test\n\t\t\tmachineconfig.cpp\n\t\t\tmachineconfig.h\n\t\t\tconfig_isa.h\n\t\t\tmemory/backend/backend_memory.h\n\t\t\tmemory/backend/memory.cpp\n\t\t\tmemory/backend/memory.h\n\t\t\tmemory/cache/cache.cpp\n\t\t\tmemory/cache/cache.h\n\t\t\tmemory/cache/cache.test.cpp\n\t\t\tmemory/cache/cache.test.h\n\t\t\tmemory/cache/cache_policy.cpp\n\t\t\tmemory/cache/cache_policy.h\n\t\t\tmemory/frontend_memory.cpp\n\t\t\tmemory/frontend_memory.h\n\t\t\tmemory/tlb/tlb.h\n\t\t\tmemory/tlb/tlb.cpp\n\t\t\tmemory/tlb/tlb_policy.h\n\t\t\tmemory/tlb/tlb_policy.cpp\n\t\t\tmemory/virtual/page_table_walker.h\n\t\t\tmemory/virtual/page_table_walker.cpp\n\t\t\tmemory/memory_bus.cpp\n\t\t\tmemory/memory_bus.h\n\t\t\tsimulator_exception.cpp\n\t\t\tsimulator_exception.h\n\t\t\ttests/data/cache_test_performance_data.h\n\t\t\ttests/utils/integer_decomposition.h\n\t\t\t)\n\ttarget_link_libraries(cache_test\n\t\t\tPRIVATE ${QtLib}::Core ${QtLib}::Test)\n\tadd_test(NAME cache COMMAND cache_test)\n\n\tadd_executable(instruction_test\n\t\t\tcsr/controlstate.cpp\n\t\t\tcsr/controlstate.h\n\t\t\tinstruction.cpp\n\t\t\tinstruction.h\n\t\t\tinstruction.test.cpp\n\t\t\tinstruction.test.h\n\t\t\tsimulator_exception.cpp\n\t\t\tsimulator_exception.h\n\t)\n\ttarget_link_libraries(instruction_test\n\t\t\tPRIVATE ${QtLib}::Core ${QtLib}::Test)\n\tadd_test(NAME instruction COMMAND instruction_test)\n\n\tadd_executable(program_loader_test\n\t\t\tcsr/controlstate.cpp\n\t\t\tcsr/controlstate.h\n\t\t\tinstruction.cpp\n\t\t\tinstruction.h\n\t\t\tmemory/backend/backend_memory.h\n\t\t\tmemory/backend/memory.cpp\n\t\t\tmemory/backend/memory.h\n\t\t\tprogramloader.cpp\n\t\t\tprogramloader.h\n\t\t\tprogramloader.test.cpp\n\t\t\tprogramloader.test.h\n\t\t\tsimulator_exception.cpp\n\t\t\tsimulator_exception.h\n\t\t\tsymboltable.cpp\n\t\t\tsymboltable.h\n\t)\n\ttarget_link_libraries(program_loader_test\n\t\t\tPRIVATE ${QtLib}::Core ${QtLib}::Test elf++ dwarf++)\n\ttarget_include_directories(program_loader_test\n\t\t\tPRIVATE \"${PROJECT_SOURCE_DIR}/external/libelfin\")\n\tadd_test(NAME program_loader COMMAND program_loader_test)\n\n\n\tadd_executable(core_test\n\t\t\tcsr/controlstate.cpp\n\t\t\tcsr/controlstate.h\n\t\t\tcore.cpp\n\t\t\tcore.h\n\t\t\tcore.test.cpp\n\t\t\tcore.test.h\n\t\t\texecute/alu.cpp\n\t\t\texecute/alu.h\n\t\t\tinstruction.cpp\n\t\t\tinstruction.h\n\t\t\tmemory/backend/backend_memory.h\n\t\t\tmemory/backend/memory.cpp\n\t\t\tmemory/backend/memory.h\n\t\t\tmemory/cache/cache.cpp\n\t\t\tmemory/cache/cache.h\n\t\t\tmemory/cache/cache_policy.cpp\n\t\t\tmemory/cache/cache_policy.h\n\t\t\tmemory/frontend_memory.cpp\n\t\t\tmemory/frontend_memory.h\n\t\t\tmemory/memory_bus.cpp\n\t\t\tmemory/memory_bus.h\n\t\t\tregisters.cpp\n\t\t\tregisters.h\n\t\t\tpredictor.cpp\n\t\t\tpredictor.h\n\t\t\tpredictor_types.h\n\t\t\tsimulator_exception.cpp\n\t\t\tsimulator_exception.h\n\t\t\tmachineconfig.cpp\n\t\t\t)\n\ttarget_link_libraries(core_test\n\t\t\tPRIVATE ${QtLib}::Core ${QtLib}::Test elf++ dwarf++)\n\ttarget_include_directories(core_test\n\t\t\tPRIVATE \"${PROJECT_SOURCE_DIR}/external/libelfin\")\n\tadd_test(NAME core COMMAND core_test)\n\n\tadd_custom_target(machine_unit_tests\n\t\t\tDEPENDS alu_test registers_test memory_test cache_test instruction_test program_loader_test core_test)\nendif ()\n"
  },
  {
    "path": "src/machine/bitfield.h",
    "content": "/**\n * Utility to parse and encode binary encoded fields.\n *\n * Use BitField if the field is guaranteed to be continuous.\n * Use SplitBitField if the field is not guaranteed to be continuous.\n *\n * @file\n */\n\n#ifndef QTRVSIM_BITFIELD_H\n#define QTRVSIM_BITFIELD_H\n\n#include \"common/containers/cvector.h\"\n\n#include <cstdint>\n\nstruct BitField {\n    uint8_t count;\n    uint8_t offset;\n\n    template<typename T>\n    [[nodiscard]] T decode(T val) const {\n        return (val >> offset) & (((uint64_t)1 << count) - 1);\n    }\n    template<typename T>\n    [[nodiscard]] T encode(T val) const {\n        return ((val & (((uint64_t)1 << count) - 1)) << offset);\n    }\n    [[nodiscard]] uint64_t mask() const { return (((uint64_t)1 << count) - 1) << offset; }\n};\n\ntemplate<size_t MAX_FIELD_PARTS>\nstruct SplitBitField {\n    cvector<BitField, MAX_FIELD_PARTS> fields;\n    size_t shift = 0;\n\n    [[nodiscard]] typename decltype(fields)::const_iterator begin() const {\n        return fields.cbegin();\n    }\n    [[nodiscard]] typename decltype(fields)::const_iterator end() const { return fields.cend(); }\n    [[nodiscard]] uint32_t decode(uint32_t ins) const {\n        uint32_t ret = 0;\n        size_t offset = 0;\n        for (BitField field : *this) {\n            ret |= field.decode(ins) << offset;\n            offset += field.count;\n        }\n        return ret << shift;\n    }\n    [[nodiscard]] uint32_t encode(uint32_t imm) const {\n        uint32_t ret = 0;\n        imm >>= shift;\n        for (BitField field : *this) {\n            ret |= field.encode(imm);\n            imm >>= field.count;\n        }\n        return ret;\n    }\n};\n\n#endif // QTRVSIM_BITFIELD_H\n"
  },
  {
    "path": "src/machine/config_isa.h",
    "content": "#ifndef MACHINE_CONFIG_ISA_H\n#define MACHINE_CONFIG_ISA_H\n\n#include <QMetaType>\n#include <QtGlobal>\n\nnamespace machine {\n\nstruct ConfigIsaWord {\n    constexpr ConfigIsaWord() : bits(0) {};\n    constexpr ConfigIsaWord(const quint64 &abits) : bits(abits) {};\n    constexpr ConfigIsaWord(const ConfigIsaWord &isaWord) = default;            //> Copy constructor\n    constexpr ConfigIsaWord &operator=(const ConfigIsaWord &isaWord) = default; //> Assign\n                                                                                // constructor\n\n    constexpr static ConfigIsaWord empty() { return ConfigIsaWord(); };\n\n    constexpr ConfigIsaWord &operator&=(const ConfigIsaWord &isaWord) {\n        bits &= isaWord.bits;\n        return *this;\n    }\n    constexpr ConfigIsaWord &operator|=(const ConfigIsaWord &isaWord) {\n        bits |= isaWord.bits;\n        return *this;\n    }\n    constexpr ConfigIsaWord operator~() const {\n        ConfigIsaWord ans(~bits);\n        return ans;\n    }\n    friend constexpr ConfigIsaWord operator|(ConfigIsaWord lhs, const ConfigIsaWord &rhs) {\n        lhs |= rhs; // reuse compound assignment\n        return lhs; // return the result by value (uses move constructor)\n    }\n    friend constexpr ConfigIsaWord operator&(ConfigIsaWord lhs, const ConfigIsaWord &rhs) {\n        lhs &= rhs; // reuse compound assignment\n        return lhs; // return the result by value (uses move constructor)\n    }\n    constexpr friend bool operator==(const ConfigIsaWord &lhs, const ConfigIsaWord &rhs) {\n        return lhs.bits == rhs.bits;\n    }\n\n    static constexpr ConfigIsaWord byChar(char ch) {\n        if (ch >= 'A' && ch <= 'Z')\n            ch -= 'A';\n        else if (ch >= 'a' && ch <= 'z')\n            ch -= 'a';\n        else\n            ch = 0;\n        auto abits = static_cast<typeof(bits)>(1) << ch;\n        return ConfigIsaWord(abits);\n    };\n\n    constexpr bool isEmpty() const { return bits == 0; };\n\n    constexpr bool contains(char ch) const { return !(*this & byChar(ch)).isEmpty(); };\n\n    constexpr bool contains(ConfigIsaWord &isaWord) const { return (*this & isaWord) == isaWord; };\n\n    ConfigIsaWord &modify(ConfigIsaWord &mask, ConfigIsaWord &val) {\n        (*this) &= ~mask | val;\n        (*this) |= mask & val;\n        return *this;\n    }\n\n    constexpr auto toUnsigned() const { return bits; };\n\n    quint64 bits;\n};\n\n} // namespace machine\n\nQ_DECLARE_METATYPE(machine::ConfigIsaWord)\n\n#endif // MACHINE_CONFIG_ISA_H\n"
  },
  {
    "path": "src/machine/core/core_state.h",
    "content": "#ifndef QTRVSIM_CORE_STATE_H\n#define QTRVSIM_CORE_STATE_H\n\n#include \"common/memory_ownership.h\"\n#include \"machinedefs.h\"\n#include \"memory/address_range.h\"\n#include \"pipeline.h\"\n\n#include <QMap>\n#include <cstdint>\n#include <machineconfig.h>\nusing std::uint32_t;\n\nnamespace machine {\n\nstruct CoreState {\n    Pipeline pipeline = {};\n    AddressRange LoadReservedRange;\n    uint32_t stall_count = 0;\n    uint32_t cycle_count = 0;\n    unsigned current_privilege_u = static_cast<unsigned>(CSR::PrivilegeLevel::MACHINE);\n    unsigned current_asid_u = 0u;\n\n    [[nodiscard]] CSR::PrivilegeLevel current_privilege() const noexcept {\n        return static_cast<CSR::PrivilegeLevel>(current_privilege_u);\n    }\n\n    void set_current_privilege(CSR::PrivilegeLevel p) noexcept {\n        current_privilege_u = static_cast<unsigned>(p);\n    }\n\n    [[nodiscard]] uint16_t current_asid() const noexcept {\n        return static_cast<uint16_t>(current_asid_u & 0x1FFu);\n    }\n\n    void set_current_asid(uint16_t a) noexcept {\n        current_asid_u = static_cast<unsigned>(a & 0x1FFu);\n    }\n};\n\n} // namespace machine\n#endif // QTRVSIM_CORE_STATE_H\n"
  },
  {
    "path": "src/machine/core.cpp",
    "content": "#include \"core.h\"\n\n#include \"common/logging.h\"\n#include \"execute/alu.h\"\n#include \"utils.h\"\n\n#include <cinttypes>\n\nLOG_CATEGORY(\"machine.core\");\n\nusing namespace machine;\n\nstatic InstructionFlags unsupported_inst_flags_to_check(Xlen xlen, ConfigIsaWord isa_word) {\n    unsigned flags_to_check = IMF_SUPPORTED;\n    if (xlen == Xlen::_32) flags_to_check |= IMF_RV64;\n    if (!isa_word.contains('A')) flags_to_check |= IMF_AMO;\n    if (!isa_word.contains('M')) flags_to_check |= IMF_MUL;\n    return InstructionFlags(flags_to_check);\n}\n\nCore::Core(\n    Registers *regs,\n    BranchPredictor *predictor,\n    FrontendMemory *mem_program,\n    FrontendMemory *mem_data,\n    CSR::ControlState *control_state,\n    Xlen xlen,\n    ConfigIsaWord isa_word)\n    : pc_if(state.pipeline.pc.final)\n    , if_id(state.pipeline.fetch.final)\n    , id_ex(state.pipeline.decode.final)\n    , ex_mem(state.pipeline.execute.final)\n    , mem_wb(state.pipeline.memory.final)\n    , xlen(xlen)\n    , check_inst_flags_val(IMF_SUPPORTED)\n    , check_inst_flags_mask(unsupported_inst_flags_to_check(xlen, isa_word))\n    , regs(regs)\n    , control_state(control_state)\n    , predictor(predictor)\n    , mem_data(mem_data)\n    , mem_program(mem_program)\n    , ex_handlers()\n    , ex_default_handler(new StopExceptionHandler()) {\n    stop_on_exception.fill(true);\n    step_over_exception.fill(true);\n    step_over_exception[EXCAUSE_INT] = false;\n}\n\nvoid Core::step(bool skip_break) {\n    emit step_started();\n    state.cycle_count++;\n    do_step(skip_break);\n    emit step_done(state);\n}\n\nvoid Core::reset() {\n    state.cycle_count = 0;\n    state.stall_count = 0;\n    do_reset();\n    set_current_privilege(CSR::PrivilegeLevel::MACHINE);\n}\n\nunsigned Core::get_cycle_count() const {\n    return state.cycle_count;\n}\n\nunsigned Core::get_stall_count() const {\n    return state.stall_count;\n}\n\nRegisters *Core::get_regs() const {\n    return regs;\n}\n\nCSR::ControlState *Core::get_control_state() const {\n    return control_state;\n}\n\nFrontendMemory *Core::get_mem_data() const {\n    return mem_data;\n}\n\nFrontendMemory *Core::get_mem_program() const {\n    return mem_program;\n}\n\nBranchPredictor *Core::get_predictor() const {\n    return predictor;\n}\n\nconst CoreState &Core::get_state() const {\n    return state;\n}\n\nvoid Core::insert_hwbreak(Address address) {\n    hw_breaks.insert(address, new hwBreak(address));\n}\n\nvoid Core::remove_hwbreak(Address address) {\n    hwBreak *hwbrk = hw_breaks.take(address);\n    delete hwbrk;\n}\n\nbool Core::is_hwbreak(Address address) const {\n    hwBreak *hwbrk = hw_breaks.value(address);\n    return hwbrk != nullptr;\n}\n\nvoid Core::set_stop_on_exception(enum ExceptionCause excause, bool value) {\n    stop_on_exception[excause] = value;\n}\n\nbool Core::get_stop_on_exception(enum ExceptionCause excause) const {\n    return stop_on_exception[excause];\n}\n\nvoid Core::set_step_over_exception(enum ExceptionCause excause, bool value) {\n    step_over_exception[excause] = value;\n}\n\nbool Core::get_step_over_exception(enum ExceptionCause excause) const {\n    return step_over_exception[excause];\n}\nXlen Core::get_xlen() const {\n    return xlen;\n}\n\nvoid Core::set_current_privilege(CSR::PrivilegeLevel privilege) {\n    state.set_current_privilege(privilege);\n    const unsigned PRIV_BITS = unsigned(IMF_PRIV_M) | unsigned(IMF_PRIV_H) | unsigned(IMF_PRIV_S);\n\n    InstructionFlags base_mask = InstructionFlags(unsigned(check_inst_flags_mask) & ~PRIV_BITS);\n    InstructionFlags base_val = InstructionFlags(unsigned(check_inst_flags_val) & ~PRIV_BITS);\n\n    unsigned allowed_priv = 0;\n    if (privilege >= CSR::PrivilegeLevel::SUPERVISOR) { allowed_priv |= unsigned(IMF_PRIV_S); }\n    if (privilege >= CSR::PrivilegeLevel::HYPERVISOR) { allowed_priv |= unsigned(IMF_PRIV_H); }\n    if (privilege >= CSR::PrivilegeLevel::MACHINE) { allowed_priv |= unsigned(IMF_PRIV_M); }\n    unsigned disallowed_priv = (PRIV_BITS & ~allowed_priv);\n    InstructionFlags new_mask = InstructionFlags(unsigned(base_mask) | disallowed_priv);\n    InstructionFlags new_val = base_val;\n\n    check_inst_flags_mask = new_mask;\n    check_inst_flags_val = new_val;\n}\n\nCSR::PrivilegeLevel Core::get_current_privilege() const {\n    return state.current_privilege();\n};\n\nvoid Core::register_exception_handler(ExceptionCause excause, ExceptionHandler *exhandler) {\n    if (excause == EXCAUSE_NONE) {\n        ex_default_handler.reset(exhandler);\n    } else {\n        ExceptionHandler *old = ex_handlers.take(excause);\n        delete old;\n        ex_handlers.insert(excause, exhandler);\n    }\n}\n\nbool Core::handle_exception(\n    ExceptionCause excause,\n    const Instruction &inst,\n    Address inst_addr,\n    Address next_addr,\n    Address jump_branch_pc,\n    Address mem_ref_addr) {\n    if (excause == EXCAUSE_INSN_ILLEGAL) {\n        throw SIMULATOR_EXCEPTION(\n            UnsupportedInstruction, \"Instruction with following encoding is not supported\",\n            QString::number(inst.data(), 16));\n    }\n\n    if (excause == EXCAUSE_HWBREAK) { regs->write_pc(inst_addr); }\n\n    if (control_state != nullptr) {\n        control_state->write_internal(CSR::Id::MEPC, inst_addr.get_raw());\n        control_state->update_exception_cause(excause);\n        if (control_state->read_internal(CSR::Id::MTVEC) != 0\n            && !get_step_over_exception(excause)) {\n            control_state->exception_initiate(\n                get_current_privilege(), CSR::PrivilegeLevel::MACHINE);\n            set_current_privilege(CSR::PrivilegeLevel::MACHINE);\n            regs->write_pc(control_state->exception_pc_address());\n        }\n    }\n\n    bool ret = false;\n    ExceptionHandler *exhandler = ex_handlers.value(excause, ex_default_handler.data());\n    if (exhandler != nullptr) {\n        ret = exhandler->handle_exception(\n            this, regs, excause, inst_addr, next_addr, jump_branch_pc, mem_ref_addr);\n    }\n\n    if (get_stop_on_exception(excause)) { emit stop_on_exception_reached(); }\n\n    return ret;\n}\n\nstatic int32_t amo32_operations(enum AccessControl memctl, int32_t a, int32_t b) {\n    switch (memctl) {\n    case AC_AMOSWAP32: return b;\n    case AC_AMOADD32: return a + b;\n    case AC_AMOXOR32: return a ^ b;\n    case AC_AMOAND32: return a & b;\n    case AC_AMOOR32: return a | b;\n    case AC_AMOMIN32: return a < b ? a : b;\n    case AC_AMOMAX32: return a < b ? b : a;\n    case AC_AMOMINU32: return (uint32_t)a < (uint32_t)b ? a : b;\n    case AC_AMOMAXU32: return (uint32_t)a < (uint32_t)b ? b : a;\n    default: break;\n    }\n    return 0;\n}\n\nstatic int64_t amo64_operations(enum AccessControl memctl, int64_t a, int64_t b) {\n    switch (memctl) {\n    case AC_AMOSWAP64: return b;\n    case AC_AMOADD64: return a + b;\n    case AC_AMOXOR64: return a ^ b;\n    case AC_AMOAND64: return a & b;\n    case AC_AMOOR64: return a | b;\n    case AC_AMOMIN64: return a < b ? a : b;\n    case AC_AMOMAX64: return a < b ? b : a;\n    case AC_AMOMINU64: return (uint64_t)a < (uint64_t)b ? a : b;\n    case AC_AMOMAXU64: return (uint64_t)a < (uint64_t)b ? b : a;\n    default: break;\n    }\n    return 0;\n}\n\nenum ExceptionCause Core::memory_special(\n    enum AccessControl memctl,\n    int mode,\n    bool memread,\n    bool memwrite,\n    RegisterValue &towrite_val,\n    RegisterValue rt_value,\n    Address mem_addr) {\n    Q_UNUSED(mode)\n\n    switch (memctl) {\n    case AC_CACHE_OP:\n        mem_data->sync();\n        mem_program->sync();\n        predictor->flush();\n        break;\n    case AC_LR32:\n        if (!memread) { break; }\n        state.LoadReservedRange = AddressRange(mem_addr, mem_addr + 3);\n        towrite_val = (int32_t)(mem_data->read_u32(mem_addr));\n        break;\n    case AC_SC32:\n        if (!memwrite) { break; }\n        if (state.LoadReservedRange.contains(AddressRange(mem_addr, mem_addr + 3))) {\n            mem_data->write_u32(mem_addr, rt_value.as_u32());\n            towrite_val = 0;\n        } else {\n            towrite_val = 1;\n        }\n        state.LoadReservedRange.reset();\n        break;\n    case AC_LR64:\n        if (!memread) { break; }\n        state.LoadReservedRange = AddressRange(mem_addr, mem_addr + 7);\n        towrite_val = mem_data->read_u64(mem_addr);\n        break;\n    case AC_SC64:\n        if (!memwrite) { break; }\n        if (state.LoadReservedRange.contains(AddressRange(mem_addr, mem_addr + 7))) {\n            mem_data->write_u64(mem_addr, rt_value.as_u64());\n            towrite_val = 0;\n        } else {\n            towrite_val = 1;\n        }\n        break;\n    case AC_FISRT_AMO_MODIFY32 ... AC_LAST_AMO_MODIFY32: {\n        if (!memread || !memwrite) { break; }\n        int32_t fetched_value;\n        fetched_value = (int32_t)(mem_data->read_u32(mem_addr));\n        towrite_val = amo32_operations(memctl, fetched_value, rt_value.as_u32());\n        mem_data->write_u32(mem_addr, towrite_val.as_u32());\n        towrite_val = fetched_value;\n        break;\n    }\n    case AC_FISRT_AMO_MODIFY64 ... AC_LAST_AMO_MODIFY64: {\n        if (!memread || !memwrite) { break; }\n        int64_t fetched_value;\n        fetched_value = (int64_t)(mem_data->read_u64(mem_addr));\n        towrite_val = (uint64_t)amo64_operations(memctl, fetched_value, rt_value.as_u64());\n        mem_data->write_u64(mem_addr, towrite_val.as_u64());\n        towrite_val = fetched_value;\n        break;\n    }\n    default: break;\n    }\n\n    return EXCAUSE_NONE;\n}\n\nFetchState Core::fetch(PCInterstage pc, bool skip_break) {\n    if (pc.stop_if) { return {}; }\n\n    const AddressWithMode inst_addr = AddressWithMode(regs->read_pc(), make_access_mode(state));\n    const Instruction inst(mem_program->read_u32(inst_addr));\n    ExceptionCause excause = EXCAUSE_NONE;\n\n    if (!skip_break && hw_breaks.contains(inst_addr)) { excause = EXCAUSE_HWBREAK; }\n\n    if (control_state != nullptr) { control_state->increment_internal(CSR::Id::MCYCLE, 1); }\n\n    if (control_state != nullptr && excause == EXCAUSE_NONE) {\n        if (control_state->core_interrupt_request()) { excause = EXCAUSE_INT; }\n    }\n\n    return { FetchInternalState { .fetched_value = inst.data() },\n             FetchInterstage {\n                 .inst = inst,\n                 .inst_addr = inst_addr,\n                 .next_inst_addr = inst_addr + inst.size(),\n                 .predicted_next_inst_addr = predictor->predict_next_pc_address(inst, inst_addr),\n                 .excause = excause,\n                 .is_valid = true,\n             } };\n}\n\nDecodeState Core::decode(const FetchInterstage &dt) {\n    InstructionFlags flags;\n    bool w_operation = this->xlen != Xlen::_64;\n    AluCombinedOp alu_op {};\n    AccessControl mem_ctl;\n    ExceptionCause excause = dt.excause;\n\n    dt.inst.flags_alu_op_mem_ctl(flags, alu_op, mem_ctl);\n    CSR::PrivilegeLevel inst_xret_priv = CSR::PrivilegeLevel::UNPRIVILEGED;\n    if (flags & IMF_XRET) {\n        if (flags & IMF_PRIV_M) {\n            inst_xret_priv = CSR::PrivilegeLevel::MACHINE;\n        } else if (flags & IMF_PRIV_H) {\n            inst_xret_priv = CSR::PrivilegeLevel::HYPERVISOR;\n        } else if (flags & IMF_PRIV_S) {\n            inst_xret_priv = CSR::PrivilegeLevel::SUPERVISOR;\n        } else {\n            inst_xret_priv = CSR::PrivilegeLevel::UNPRIVILEGED;\n        }\n    }\n    if ((flags ^ check_inst_flags_val) & check_inst_flags_mask) { excause = EXCAUSE_INSN_ILLEGAL; }\n\n    RegisterId num_rs = (flags & (IMF_ALU_REQ_RS | IMF_ALU_RS_ID)) ? dt.inst.rs() : 0;\n    RegisterId num_rt = (flags & IMF_ALU_REQ_RT) ? dt.inst.rt() : 0;\n    RegisterId num_rd = (flags & IMF_REGWRITE) ? dt.inst.rd() : 0;\n    // When instruction does not specify register, it is set to x0 as operations on x0 have no\n    // side effects (not even visualization).\n    RegisterValue val_rs\n        = (flags & IMF_ALU_RS_ID) ? uint64_t(size_t(num_rs)) : regs->read_gp(num_rs);\n    RegisterValue val_rt = regs->read_gp(num_rt);\n    RegisterValue immediate_val = dt.inst.immediate();\n    const bool regwrite = flags & IMF_REGWRITE;\n\n    CSR::Address csr_address = (flags & IMF_CSR) ? dt.inst.csr_address() : CSR::Address(0);\n    RegisterValue csr_read_val = ((control_state != nullptr && (flags & IMF_CSR)))\n                                     ? control_state->read(csr_address, get_current_privilege())\n                                     : 0;\n    bool csr_write = (flags & IMF_CSR) && (!(flags & IMF_CSR_TO_ALU) || (num_rs != 0));\n\n    if ((flags & IMF_EXCEPTION) && (excause == EXCAUSE_NONE)) {\n        if (flags & IMF_EBREAK) {\n            excause = EXCAUSE_BREAK;\n        } else if (flags & IMF_ECALL) {\n            excause = EXCAUSE_ECALL_M;\n            // TODO: EXCAUSE_ECALL_S, EXCAUSE_ECALL_U\n        }\n    }\n    if (flags & IMF_FORCE_W_OP) w_operation = true;\n\n    return { DecodeInternalState {\n                 .alu_op_num = static_cast<unsigned>(alu_op.alu_op),\n                 .excause_num = static_cast<unsigned>(excause),\n                 .inst_bus = dt.inst.data(),\n                 .alu_mul = bool(flags & IMF_MUL),\n             },\n             DecodeInterstage { .inst = dt.inst,\n                                .inst_addr = dt.inst_addr,\n                                .next_inst_addr = dt.next_inst_addr,\n                                .predicted_next_inst_addr = dt.predicted_next_inst_addr,\n                                .val_rs = val_rs,\n                                .val_rs_orig = val_rs,\n                                .val_rt = val_rt,\n                                .val_rt_orig = val_rt,\n                                .immediate_val = immediate_val,\n                                .csr_read_val = csr_read_val,\n                                .csr_address = csr_address,\n                                .excause = excause,\n                                .ff_rs = FORWARD_NONE,\n                                .ff_rt = FORWARD_NONE,\n                                .alu_component = (flags & IMF_AMO)   ? AluComponent::PASS\n                                                 : (flags & IMF_MUL) ? AluComponent::MUL\n                                                                     : AluComponent::ALU,\n                                .aluop = alu_op,\n                                .memctl = mem_ctl,\n                                .num_rs = num_rs,\n                                .num_rt = num_rt,\n                                .num_rd = num_rd,\n                                .memread = bool(flags & IMF_MEMREAD),\n                                .memwrite = bool(flags & IMF_MEMWRITE),\n                                .alusrc = bool(flags & IMF_ALUSRC),\n                                .regwrite = regwrite,\n                                .alu_req_rs = bool(flags & IMF_ALU_REQ_RS),\n                                .alu_req_rt = bool(flags & IMF_ALU_REQ_RT),\n                                .branch_bxx = bool(flags & IMF_BRANCH),\n                                .branch_jal = bool(flags & IMF_JUMP),\n                                .branch_val = bool(flags & IMF_BJ_NOT),\n                                .branch_jalr = bool(flags & IMF_BRANCH_JALR),\n                                .stall = false,\n                                .is_valid = dt.is_valid,\n                                .w_operation = w_operation,\n                                .alu_mod = bool(flags & IMF_ALU_MOD),\n                                .alu_pc = bool(flags & IMF_PC_TO_ALU),\n                                .csr = bool(flags & IMF_CSR),\n                                .csr_to_alu = bool(flags & IMF_CSR_TO_ALU),\n                                .csr_write = csr_write,\n                                .xret = bool(flags & IMF_XRET),\n                                .xret_privlev = inst_xret_priv,\n                                .insert_stall_before = bool(flags & IMF_CSR) } };\n}\n\nExecuteState Core::execute(const DecodeInterstage &dt) {\n    enum ExceptionCause excause = dt.excause;\n    // TODO refactor to produce multiplexor index and multiplex function\n    const RegisterValue alu_fst = [=] {\n        if (dt.alu_pc) return RegisterValue(dt.inst_addr.get_raw());\n        return dt.val_rs;\n    }();\n    const RegisterValue alu_sec = [=] {\n        if (dt.csr_to_alu) return dt.csr_read_val;\n        if (dt.alusrc) return dt.immediate_val;\n        return dt.val_rt;\n    }();\n    const RegisterValue alu_val = [=] {\n        if (excause != EXCAUSE_NONE) return RegisterValue(0);\n        return alu_combined_operate(\n            dt.aluop, dt.alu_component, dt.w_operation, dt.alu_mod, alu_fst, alu_sec);\n    }();\n    const Address branch_jal_target = dt.inst_addr + dt.immediate_val.as_i64();\n\n    const unsigned stall_status = [=] {\n        if (dt.stall) return 1;\n        if (dt.ff_rs != FORWARD_NONE || dt.ff_rt != FORWARD_NONE) return 2;\n        return 0;\n    }();\n\n    return { ExecuteInternalState {\n                 .alu_src1 = dt.val_rs,\n                 .alu_src2 = alu_sec,\n                 .immediate = dt.immediate_val,\n                 .rs = dt.val_rs_orig,\n                 .rt = dt.val_rt_orig,\n                 .stall_status = stall_status,\n                 .alu_op_num = static_cast<unsigned>(dt.aluop.alu_op),\n                 .forward_from_rs1_num = static_cast<unsigned>(dt.ff_rs),\n                 .forward_from_rs2_num = static_cast<unsigned>(dt.ff_rt),\n                 .excause_num = static_cast<unsigned>(dt.excause),\n                 .alu_src = dt.alusrc,\n                 .alu_mul = dt.alu_component == AluComponent::MUL,\n                 .branch_bxx = dt.branch_bxx,\n                 .alu_pc = dt.alu_pc,\n             },\n             ExecuteInterstage {\n                 .inst = dt.inst,\n                 .inst_addr = dt.inst_addr,\n                 .next_inst_addr = dt.next_inst_addr,\n                 .predicted_next_inst_addr = dt.predicted_next_inst_addr,\n                 .branch_jal_target = branch_jal_target,\n                 .val_rt = dt.val_rt,\n                 .alu_val = alu_val,\n                 .immediate_val = dt.immediate_val,\n                 .csr_read_val = dt.csr_read_val,\n                 .csr_address = dt.csr_address,\n                 .excause = excause,\n                 .memctl = dt.memctl,\n                 .num_rd = dt.num_rd,\n                 .memread = dt.memread,\n                 .memwrite = dt.memwrite,\n                 .regwrite = dt.regwrite,\n                 .is_valid = dt.is_valid,\n                 .branch_bxx = dt.branch_bxx,\n                 .branch_jal = dt.branch_jal,\n                 .branch_val = dt.branch_val,\n                 .branch_jalr = dt.branch_jalr,\n                 .alu_zero = alu_val == 0,\n                 .csr = dt.csr,\n                 .csr_write = dt.csr_write,\n                 .xret = dt.xret,\n                 .xret_privlev = dt.xret_privlev,\n             } };\n}\n\nMemoryState Core::memory(const ExecuteInterstage &dt) {\n    RegisterValue towrite_val = dt.alu_val;\n    auto mem_addr = AddressWithMode(get_xlen_from_reg(dt.alu_val), make_access_mode(state));\n    bool memread = dt.memread;\n    bool memwrite = dt.memwrite;\n    bool regwrite = dt.regwrite;\n    Address computed_next_inst_addr;\n\n    enum ExceptionCause excause = dt.excause;\n    if (excause == EXCAUSE_NONE) {\n        if (is_special_access(dt.memctl)) {\n            excause = memory_special(\n                dt.memctl, dt.inst.rt(), memread, memwrite, towrite_val, dt.val_rt, mem_addr);\n        } else if (is_regular_access(dt.memctl)) {\n            if (memwrite) { mem_data->write_ctl(dt.memctl, mem_addr, dt.val_rt); }\n            if (memread) { towrite_val = mem_data->read_ctl(dt.memctl, mem_addr); }\n        } else {\n            Q_ASSERT(dt.memctl == AC_NONE);\n            // AC_NONE is memory NOP\n        }\n    }\n\n    if (dt.excause != EXCAUSE_NONE) {\n        memread = false;\n        memwrite = false;\n        regwrite = false;\n    }\n\n    // Conditional branch (BXX = BEQ | BNE...) is executed and should be taken.\n    const bool branch_bxx_taken = dt.branch_bxx && (!dt.branch_val ^ !dt.alu_zero);\n    // Unconditional jump should be taken (JALX = JAL | JALR).\n    const bool branch_jalx = dt.branch_jalr || dt.branch_jal;\n\n    computed_next_inst_addr = compute_next_inst_addr(dt, branch_bxx_taken);\n\n    // Predictor update\n    if (dt.branch_jal) {\n        // JAL Jump instruction (J-type (alternative to U-type with different immediate bit order))\n        predictor->update(\n            dt.inst, dt.inst_addr, dt.branch_jal_target, BranchType::JUMP, BranchResult::TAKEN);\n    } else if (dt.branch_jalr) {\n        // JALR Jump register instruction (I-type)\n        predictor->update(\n            dt.inst, dt.inst_addr, Address(get_xlen_from_reg(dt.alu_val)), BranchType::JUMP,\n            BranchResult::TAKEN);\n    } else if (dt.branch_bxx) {\n        // BXX Conditional branch instruction (B-type (alternative to S-type with different\n        // immediate bit order))\n        predictor->update(\n            dt.inst, dt.inst_addr, dt.branch_jal_target, BranchType::BRANCH,\n            branch_bxx_taken ? BranchResult::TAKEN : BranchResult::NOT_TAKEN);\n    }\n\n    bool csr_written = false;\n    if (control_state != nullptr && dt.is_valid && dt.excause == EXCAUSE_NONE) {\n        control_state->increment_internal(CSR::Id::MINSTRET, 1);\n        if (dt.csr_write) {\n            control_state->write(dt.csr_address, dt.alu_val, get_current_privilege());\n            csr_written = true;\n        }\n        if (dt.xret) {\n            CSR::PrivilegeLevel restored\n                = control_state->exception_return(get_current_privilege(), dt.xret_privlev);\n            set_current_privilege(restored);\n            if (this->xlen == Xlen::_32)\n                computed_next_inst_addr\n                    = Address(control_state->read_internal(CSR::Id::MEPC).as_u32());\n            else\n                computed_next_inst_addr\n                    = Address(control_state->read_internal(CSR::Id::MEPC).as_u64());\n            csr_written = true;\n        }\n    }\n\n    // Predictor statistics update\n    if (computed_next_inst_addr != dt.predicted_next_inst_addr) {\n        predictor->increment_mispredictions();\n    }\n\n    return { MemoryInternalState {\n                 .mem_read_val = towrite_val,\n                 .mem_write_val = dt.val_rt,\n                 .mem_addr = dt.alu_val,\n                 .excause_num = static_cast<unsigned>(excause),\n                 .memwrite = memwrite,\n                 .memread = memread,\n                 .branch_bxx = dt.branch_bxx,\n                 .branch_jal = dt.branch_jal,\n                 // PC should be modified by branch/jump instruction.\n                 .branch_outcome = branch_bxx_taken || branch_jalx,\n                 .branch_jalx = branch_jalx,\n                 .branch_jalr = dt.branch_jalr,\n                 .xret = dt.xret,\n             },\n             MemoryInterstage {\n                 .inst = dt.inst,\n                 .inst_addr = dt.inst_addr,\n                 .next_inst_addr = dt.next_inst_addr,\n                 .predicted_next_inst_addr = dt.predicted_next_inst_addr,\n                 .computed_next_inst_addr = computed_next_inst_addr,\n                 .mem_addr = mem_addr,\n                 .towrite_val = [=]() -> RegisterValue {\n                     if (dt.csr) return dt.csr_read_val;\n                     if (dt.branch_jalr || dt.branch_jal) return dt.next_inst_addr.get_raw();\n                     return towrite_val;\n                 }(),\n                 .excause = dt.excause,\n                 .num_rd = dt.num_rd,\n                 .memtoreg = memread,\n                 .regwrite = regwrite,\n                 .is_valid = dt.is_valid,\n                 .csr_written = csr_written,\n                 .xret_privlev = dt.xret_privlev,\n             } };\n}\n\nWritebackState Core::writeback(const MemoryInterstage &dt) {\n    if (dt.regwrite) { regs->write_gp(dt.num_rd, dt.towrite_val); }\n\n    return WritebackState { WritebackInternalState {\n        .inst = (dt.excause == EXCAUSE_NONE) ? dt.inst : Instruction::NOP,\n        .inst_addr = dt.inst_addr,\n        .value = dt.towrite_val,\n        .num_rd = dt.num_rd,\n        .regwrite = dt.regwrite,\n        .memtoreg = dt.memtoreg,\n    } };\n}\n\nAddress Core::compute_next_inst_addr(const ExecuteInterstage &exec, bool branch_taken) const {\n    if (branch_taken || exec.branch_jal) { return exec.branch_jal_target; }\n    if (exec.branch_jalr) { return Address(get_xlen_from_reg(exec.alu_val)); }\n    return exec.next_inst_addr;\n}\n\nuint64_t Core::get_xlen_from_reg(RegisterValue reg) const {\n    switch (this->xlen) {\n    case Xlen::_32: return reg.as_u32();\n    case Xlen::_64: return reg.as_u64();\n    default: UNREACHABLE\n    }\n}\n\nCoreSingle::CoreSingle(\n    Registers *regs,\n    BranchPredictor *predictor,\n    FrontendMemory *mem_program,\n    FrontendMemory *mem_data,\n    CSR::ControlState *control_state,\n    Xlen xlen,\n    ConfigIsaWord isa_word)\n    : Core(regs, predictor, mem_program, mem_data, control_state, xlen, isa_word) {\n    reset();\n}\n\nvoid CoreSingle::do_step(bool skip_break) {\n    Pipeline &p = state.pipeline;\n\n    p.fetch = fetch(pc_if, skip_break);\n    p.decode = decode(p.fetch.final);\n    p.execute = execute(p.decode.final);\n    p.memory = memory(p.execute.final);\n    p.writeback = writeback(p.memory.final);\n\n    regs->write_pc(mem_wb.computed_next_inst_addr);\n\n    if (mem_wb.excause != EXCAUSE_NONE) {\n        handle_exception(\n            mem_wb.excause, mem_wb.inst, mem_wb.inst_addr, regs->read_pc(), prev_inst_addr,\n            mem_wb.mem_addr);\n        return;\n    }\n    prev_inst_addr = mem_wb.inst_addr;\n}\n\nvoid CoreSingle::do_reset() {\n    state.pipeline = {};\n    prev_inst_addr = Address::null();\n}\n\nCorePipelined::CorePipelined(\n    Registers *regs,\n    BranchPredictor *predictor,\n    FrontendMemory *mem_program,\n    FrontendMemory *mem_data,\n    CSR::ControlState *control_state,\n    Xlen xlen,\n    ConfigIsaWord isa_word,\n    MachineConfig::HazardUnit hazard_unit)\n    : Core(regs, predictor, mem_program, mem_data, control_state, xlen, isa_word) {\n    this->hazard_unit = hazard_unit;\n    reset();\n}\n\nvoid CorePipelined::do_step(bool skip_break) {\n    Pipeline &p = state.pipeline;\n\n    const Address jump_branch_pc = mem_wb.inst_addr;\n    const FetchInterstage saved_if_id = if_id;\n\n    p.writeback = writeback(mem_wb);\n    p.memory = memory(ex_mem);\n    p.execute = execute(id_ex);\n    p.decode = decode(if_id);\n    p.fetch = fetch(pc_if, skip_break);\n\n    bool exception_in_progress = mem_wb.excause != EXCAUSE_NONE;\n    if (exception_in_progress) { ex_mem.flush(); }\n    exception_in_progress |= ex_mem.excause != EXCAUSE_NONE;\n    if (exception_in_progress) { id_ex.flush(); }\n    exception_in_progress |= id_ex.excause != EXCAUSE_NONE;\n    if (exception_in_progress) { if_id.flush(); }\n\n    bool stall = false;\n    if (hazard_unit != MachineConfig::HU_NONE) { stall |= handle_data_hazards(); }\n\n    /* PC and exception pseudo stage\n     * ============================== */\n    pc_if = {};\n    if (mem_wb.excause != EXCAUSE_NONE) {\n        /* By default, execution continues with the next instruction after exception. */\n        regs->write_pc(mem_wb.computed_next_inst_addr);\n        /* Exception handler may override this behavior and change the PC (e.g. hwbreak). */\n        handle_exception(\n            mem_wb.excause, mem_wb.inst, mem_wb.inst_addr, mem_wb.computed_next_inst_addr,\n            jump_branch_pc, mem_wb.mem_addr);\n    } else if (detect_mispredicted_jump() || mem_wb.csr_written) {\n        /* If the jump was predicted incorrectly or csr register was written, we need to flush the\n         * pipeline. */\n        flush_and_continue_from_address(mem_wb.computed_next_inst_addr);\n    } else if (exception_in_progress) {\n        /* An exception is in progress which caused the pipeline before the exception to be flushed.\n         * Therefore, next pc cannot be determined from if_id (now NOP).\n         * To make the visualization cleaner we stop fetching (and PC update) until the exception\n         * is handled. */\n        pc_if.stop_if = true;\n    } else if (stall || is_stall_requested()) {\n        /* Fetch from the same PC is repeated due to stall in the pipeline. */\n        handle_stall(saved_if_id);\n    } else {\n        /* Normal execution. */\n        regs->write_pc(if_id.predicted_next_inst_addr);\n    }\n}\n\nvoid CorePipelined::flush_and_continue_from_address(Address next_pc) {\n    regs->write_pc(next_pc);\n    if_id.flush();\n    id_ex.flush();\n    ex_mem.flush();\n}\n\nvoid CorePipelined::handle_stall(const FetchInterstage &saved_if_id) {\n    /*\n     * Stall handing:\n     * - IF fetches new instruction, but it is not allowed to save into IF/ID register. This is\n     * simulated by restoring the `if_id` to its original value.\n     * - ID continues normally. On next cycle, perform the same as before as IF/ID will be\n     * unchanged.\n     * - EX is where stall is inserted by flush. The flushed instruction will be re-executed\n     * as ID repeats its execution.\n     */\n    if_id = saved_if_id;\n    id_ex.flush();\n    id_ex.stall = true; // for visualization\n    state.stall_count++;\n}\n\nbool CorePipelined::detect_mispredicted_jump() const {\n    return mem_wb.computed_next_inst_addr != mem_wb.predicted_next_inst_addr;\n}\n\nbool CorePipelined::is_stall_requested() const {\n    return id_ex.insert_stall_before && ex_mem.is_valid;\n}\n\ntemplate<typename InterstageReg>\nbool is_hazard_in_stage(const InterstageReg &interstage, const DecodeInterstage &id_ex) {\n    return (\n        interstage.regwrite && interstage.num_rd != 0\n        && ((id_ex.alu_req_rs && interstage.num_rd == id_ex.num_rs)\n            || (id_ex.alu_req_rt && interstage.num_rd == id_ex.num_rt)));\n    // Note: We make exception with $0 as that has no effect and is used in nop instruction\n}\n\nbool CorePipelined::handle_data_hazards() {\n    // Note: We make exception with $0 as that has no effect when\n    // written and is used in nop instruction\n    bool stall = false;\n\n    if (is_hazard_in_stage(mem_wb, id_ex)) {\n        if (hazard_unit == MachineConfig::HU_STALL_FORWARD) {\n            // Forward result value\n            if (id_ex.alu_req_rs && mem_wb.num_rd == id_ex.num_rs) {\n                id_ex.val_rs = mem_wb.towrite_val;\n                id_ex.ff_rs = FORWARD_FROM_W;\n            }\n            if (id_ex.alu_req_rt && mem_wb.num_rd == id_ex.num_rt) {\n                id_ex.val_rt = mem_wb.towrite_val;\n                id_ex.ff_rt = FORWARD_FROM_W;\n            }\n        } else {\n            stall = true;\n        }\n    }\n    if (is_hazard_in_stage(ex_mem, id_ex)) {\n        if (hazard_unit == MachineConfig::HU_STALL_FORWARD) {\n            if (ex_mem.memread) {\n                stall = true;\n            } else {\n                // Forward result value\n                if (id_ex.alu_req_rs && ex_mem.num_rd == id_ex.num_rs) {\n                    id_ex.val_rs = ex_mem.alu_val;\n                    id_ex.ff_rs = FORWARD_FROM_M;\n                }\n                if (id_ex.alu_req_rt && ex_mem.num_rd == id_ex.num_rt) {\n                    id_ex.val_rt = ex_mem.alu_val;\n                    id_ex.ff_rt = FORWARD_FROM_M;\n                }\n            }\n        } else {\n            stall = true;\n        }\n    }\n    return stall;\n}\n\nvoid CorePipelined::do_reset() {\n    state.pipeline = {};\n}\n\nbool StopExceptionHandler::handle_exception(\n    Core *core,\n    Registers *regs,\n    ExceptionCause excause,\n    Address inst_addr,\n    Address next_addr,\n    Address jump_branch_pc,\n    Address mem_ref_addr) {\n    Q_UNUSED(core)\n    DEBUG(\n        \"Exception cause %d instruction PC 0x%08\" PRIx64 \" next PC 0x%08\" PRIx64\n        \" jump branch PC 0x%08\" PRIx64 \"registers PC 0x%08\" PRIx64 \" mem ref 0x%08\" PRIx64,\n        excause, inst_addr.get_raw(), next_addr.get_raw(), jump_branch_pc.get_raw(),\n        regs->read_pc().get_raw(), mem_ref_addr.get_raw());\n    return true;\n}\n"
  },
  {
    "path": "src/machine/core.h",
    "content": "#ifndef CORE_H\n#define CORE_H\n\n#include \"common/memory_ownership.h\"\n#include \"core/core_state.h\"\n#include \"csr/controlstate.h\"\n#include \"instruction.h\"\n#include \"machineconfig.h\"\n#include \"memory/address.h\"\n#include \"memory/frontend_memory.h\"\n#include \"pipeline.h\"\n#include \"predictor.h\"\n#include \"register_value.h\"\n#include \"registers.h\"\n#include \"simulator_exception.h\"\n\n#include <QObject>\n\nnamespace machine {\n\nusing std::array;\n\nclass ExceptionHandler;\nclass StopExceptionHandler;\nstruct hwBreak;\n\nclass Core : public QObject {\n    Q_OBJECT\npublic:\n    Core(\n        Registers *regs,\n        BranchPredictor *predictor,\n        FrontendMemory *mem_program,\n        FrontendMemory *mem_data,\n        CSR::ControlState *control_state,\n        Xlen xlen,\n        ConfigIsaWord isa_word);\n\n    void step(bool skip_break = false);\n    void reset(); // Reset core (only core, memory and registers has to be reset separately).\n\n    unsigned get_cycle_count() const;\n    unsigned get_stall_count() const;\n\n    Registers *get_regs() const;\n    CSR::ControlState *get_control_state() const;\n    BranchPredictor *get_predictor() const;\n    FrontendMemory *get_mem_data() const;\n    FrontendMemory *get_mem_program() const;\n    const CoreState &get_state() const;\n    Xlen get_xlen() const;\n\n    void insert_hwbreak(Address address);\n    void remove_hwbreak(Address address);\n    bool is_hwbreak(Address address) const;\n    void register_exception_handler(ExceptionCause excause, ExceptionHandler *exhandler);\n    void set_stop_on_exception(enum ExceptionCause excause, bool value);\n    bool get_stop_on_exception(enum ExceptionCause excause) const;\n    void set_step_over_exception(enum ExceptionCause excause, bool value);\n    bool get_step_over_exception(enum ExceptionCause excause) const;\n    void set_current_privilege(CSR::PrivilegeLevel privilege);\n    CSR::PrivilegeLevel get_current_privilege() const;\n    static inline AccessMode make_access_mode(const CoreState &st) {\n        CSR::PrivilegeLevel priv = st.current_privilege();\n        uint16_t asid = st.current_asid();\n        bool uncached = false;\n        return AccessMode::pack(asid, priv, uncached);\n    }\n\n    /**\n     * Abstracts XLEN from code flow. XLEN core will obtain XLEN value from register value.\n     * The value will be zero extended to u64.\n     */\n    uint64_t get_xlen_from_reg(RegisterValue reg) const;\n\nprotected:\n    CoreState state {};\n\n    /**\n     * Shortcuts to interstage registers\n     * Interstage registers are stored in the core state struct in 2 copies. One (result) is the\n     * state after combinatorial logic of each stage has been applied. This is used to visualize\n     * the internal state of a stage. The should be modified ONLY by the stage logic functions. The\n     * other (final) is the one actually written to HW interstage register. Any operation within\n     * core should happen on the final registers.\n     *\n     * The bellow references provide shortcuts to the final interstage registers.\n     */\n\n    /** Reference to pseudo interstage register PC/IF inside core state. */\n    PCInterstage &pc_if;\n    /** Reference to interstage register IF/ID inside core state. */\n    FetchInterstage &if_id;\n    /** Reference to interstage register ID/EX inside core state. */\n    DecodeInterstage &id_ex;\n    /** Reference to interstage register EX/MEM inside core state. */\n    ExecuteInterstage &ex_mem;\n    /** Reference to interstage register MEM/WB inside core state. */\n    MemoryInterstage &mem_wb;\n\nsignals:\n    void stop_on_exception_reached();\n    void step_started();\n    void step_done(const CoreState &);\n\nprotected:\n    virtual void do_step(bool skip_break) = 0;\n    virtual void do_reset() = 0;\n\n    bool handle_exception(\n        ExceptionCause excause,\n        const Instruction &inst,\n        Address inst_addr,\n        Address next_addr,\n        Address jump_branch_pc,\n        Address mem_ref_addr);\n\n    const Xlen xlen;\n    InstructionFlags check_inst_flags_val;\n    InstructionFlags check_inst_flags_mask;\n    BORROWED Registers *const regs;\n    BORROWED CSR::ControlState *const control_state;\n    BORROWED BranchPredictor *const predictor;\n    BORROWED FrontendMemory *const mem_data, *const mem_program;\n\n    array<bool, EXCAUSE_COUNT> stop_on_exception {};\n    array<bool, EXCAUSE_COUNT> step_over_exception {};\n    QMap<Address, OWNED hwBreak *> hw_breaks {};\n    QMap<ExceptionCause, OWNED ExceptionHandler *> ex_handlers;\n    Box<ExceptionHandler> ex_default_handler;\n\n    FetchState fetch(PCInterstage pc, bool skip_break);\n    DecodeState decode(const FetchInterstage &);\n    static ExecuteState execute(const DecodeInterstage &);\n    MemoryState memory(const ExecuteInterstage &);\n    WritebackState writeback(const MemoryInterstage &);\n\n    /**\n     * This function computes the address, the next executed instruction should be on. The word\n     * `computed` is used in contrast with predicted value by the branch predictor.\n     */\n    Address compute_next_inst_addr(const ExecuteInterstage &exec, bool branch_taken) const;\n\n    enum ExceptionCause memory_special(\n        enum AccessControl memctl,\n        int mode,\n        bool memread,\n        bool memwrite,\n        RegisterValue &towrite_val,\n        RegisterValue rt_value,\n        Address mem_addr);\n};\n\nclass CoreSingle : public Core {\npublic:\n    CoreSingle(\n        Registers *regs,\n        BranchPredictor *predictor,\n        FrontendMemory *mem_program,\n        FrontendMemory *mem_data,\n        CSR::ControlState *control_state,\n        Xlen xlen,\n        ConfigIsaWord isa_word);\n\nprotected:\n    void do_step(bool skip_break) override;\n    void do_reset() override;\n\nprivate:\n    Address prev_inst_addr {};\n};\n\nclass CorePipelined : public Core {\npublic:\n    CorePipelined(\n        Registers *regs,\n        BranchPredictor *predictor,\n        FrontendMemory *mem_program,\n        FrontendMemory *mem_data,\n        CSR::ControlState *control_state,\n        Xlen xlen,\n        ConfigIsaWord isa_word,\n        // Default value is used to keep same interface as core single.\n        // Forward was chosen as the most conservative variant (regarding correctness).\n        MachineConfig::HazardUnit hazard_unit = MachineConfig::HazardUnit::HU_STALL_FORWARD);\n\nprotected:\n    void do_step(bool skip_break) override;\n    void do_reset() override;\n\nprivate:\n    MachineConfig::HazardUnit hazard_unit;\n\n    bool handle_data_hazards();\n    bool detect_mispredicted_jump() const;\n\n    /** Some special instruction require that all issued instructions are committed before this\n     * instruction is fetched and issued as it may rely on side-effects of uncommitted instructions.\n     * Typical examples are csr modifying instructions. */\n    bool is_stall_requested() const;\n\n    void handle_stall(const FetchInterstage &saved_if_id);\n    /**\n     * Typically, problem in execution is discovered in memory stage. This function flushed all\n     * stages containing instructions, that would not execute in a single cycle CPU and continues\n     * execution from given address.\n     *\n     * @param next_pc   address to continue execution from\n     */\n    void flush_and_continue_from_address(Address next_pc);\n};\n\nclass ExceptionHandler : public QObject {\n    Q_OBJECT\npublic:\n    virtual bool handle_exception(\n        Core *core,\n        Registers *regs,\n        ExceptionCause excause,\n        Address inst_addr,\n        Address next_addr,\n        Address jump_branch_pc,\n        Address mem_ref_addr)\n        = 0;\n};\n\nclass StopExceptionHandler : public ExceptionHandler {\n    Q_OBJECT\npublic:\n    bool handle_exception(\n        Core *core,\n        Registers *regs,\n        ExceptionCause excause,\n        Address inst_addr,\n        Address next_addr,\n        Address jump_branch_pc,\n        Address mem_ref_addr) override;\n};\n\nstruct hwBreak {\n    explicit hwBreak(Address addr) : addr(addr), flags(0), count(0) {};\n    Address addr;\n    unsigned int flags;\n    unsigned int count;\n};\n\n} // namespace machine\n\n#endif // CORE_H\n"
  },
  {
    "path": "src/machine/core.test.cpp",
    "content": "#include \"core.test.h\"\n\n#include \"machine/core.h\"\n#include \"machine/machineconfig.h\"\n#include \"machine/memory/backend/memory.h\"\n#include \"machine/memory/cache/cache.h\"\n#include \"machine/memory/memory_bus.h\"\n#include \"machine/predictor.h\"\n\n#include <QVector>\n\nusing std::vector;\n\nusing namespace machine;\n\nQ_DECLARE_METATYPE(Xlen) // NOLINT(performance-no-int-to-ptr)\n\n/**\n * Compiles program with no relocations into memory\n *\n * @param memory    memory to save program to\n * @param init_pc   address, where the compiled program will start\n * @param instructions vector of instructions\n * @return          number of instruction compiled (number of steps, that can be executed)\n */\nsize_t compile_simple_program(\n    FrontendMemory &memory,\n    Address init_pc,\n    const std::vector<QString> &instructions) {\n    Address pc = init_pc;\n    uint32_t code[2];\n    for (auto &instruction : instructions) {\n        size_t size = Instruction::code_from_string(code, 8, instruction, pc);\n        for (size_t i = 0; i < size; i += 4, pc += 4) {\n            memory.write_u32(pc, code[i]);\n        }\n    }\n    return (pc - init_pc) / 4;\n}\n\ntemplate<typename Core>\nvoid test_program_with_single_result() {\n    QFETCH(vector<QString>, instructions);\n    QFETCH(Registers, registers);\n    QFETCH(RegisterValue, x10_result);\n    QFETCH(Xlen, xlen);\n\n    Memory memory_backend(BIG);\n    TrivialBus memory(&memory_backend);\n    BranchPredictor predictor {};\n    CSR::ControlState controlst {};\n    Core core(\n        &registers, &predictor, &memory, &memory, &controlst, Xlen::_32, config_isa_word_default);\n\n    size_t instruction_count = compile_simple_program(memory, 0x200_addr, instructions);\n    if (typeid(Core) == typeid(CorePipelined)) { instruction_count += 3; } // finish pipeline\n    for (size_t i = 0; i < instruction_count; i++) {\n        core.step();\n    }\n    QCOMPARE(registers.read_gp(10).as_xlen(xlen), x10_result.as_xlen(xlen));\n}\n\nstatic void core_regs_data() {\n    QSKIP(\"Switched RV.\");\n    QTest::addColumn<Instruction>(\"i\");\n    QTest::addColumn<Registers>(\"init\");\n    QTest::addColumn<Registers>(\"res\");\n    // Note that we shouldn't be touching program counter as that is handled\n    // automatically and differs if we use pipelining\n\n    // Arithmetic instructions\n    {\n        Registers regs_init;\n        regs_init.write_gp(24, 24);\n        regs_init.write_gp(25, 12);\n        Registers regs_res(regs_init);\n        regs_res.write_gp(26, 36);\n        //        QTest::newRow(\"ADD\") << Instruction(0, 24, 25, 26, 0, 32) << regs_init <<\n        //        regs_res; QTest::newRow(\"ADDU\") << Instruction(0, 24, 25, 26, 0, 33) <<\n        //        regs_init\n        //        << regs_res; QTest::newRow(\"ADDI\") << Instruction(8, 24, 26, 12) << regs_init\n        //        << regs_res; QTest::newRow(\"ADDIU\") << Instruction(9, 24, 26, 12) << regs_init\n        //        << regs_res;\n        regs_res.write_gp(26, 12);\n        //        QTest::newRow(\"SUB\") << Instruction(0, 24, 25, 26, 0, 34) << regs_init <<\n        //        regs_res; QTest::newRow(\"SUBU\") << Instruction(0, 24, 25, 26, 0, 35) <<\n        //        regs_init\n        //        << regs_res;\n    }\n    {\n        Registers regs_init;\n        regs_init.write_gp(24, 12);\n        regs_init.write_gp(25, 24);\n        Registers regs_res(regs_init);\n        regs_res.write_gp(26, 1);\n        //        QTest::newRow(\"SLT\") << Instruction(0, 24, 25, 26, 0, 42) << regs_init <<\n        //        regs_res; QTest::newRow(\"SLTU\") << Instruction(0, 24, 25, 26, 0, 43) <<\n        //        regs_init\n        //        << regs_res; QTest::newRow(\"SLTI\") << Instruction(10, 24, 26, 24) << regs_init\n        //        << regs_res; QTest::newRow(\"SLTIU\") << Instruction(11, 24, 26, 24) <<\n        //        regs_init << regs_res;\n    }\n\n    // Shift instructions\n    {\n        Registers regs_init;\n        regs_init.write_gp(24, 0xf0);\n        regs_init.write_gp(25, 3);\n        Registers regs_res(regs_init);\n        regs_res.write_gp(26, 0x780);\n        //        QTest::newRow(\"SLL\") << Instruction(0, 0, 24, 26, 3, 0) << regs_init <<\n        //        regs_res; QTest::newRow(\"SLLV\") << Instruction(0, 25, 24, 26, 0, 4) <<\n        //        regs_init << regs_res;\n        regs_res.write_gp(26, 0x1e);\n        //        QTest::newRow(\"SLR\") << Instruction(0, 0, 24, 26, 3, 2) << regs_init <<\n        //        regs_res; QTest::newRow(\"SLRV\") << Instruction(0, 25, 24, 26, 0, 6) <<\n        //        regs_init << regs_res;\n    }\n    {\n        Registers regs_init;\n        regs_init.write_gp(24, 0x800000f0);\n        regs_init.write_gp(25, 3);\n        Registers regs_res(regs_init);\n        // Cast is needed to correctly work with any internal size of register.\n        regs_res.write_gp(26, (int32_t)0xF000001e);\n        //        QTest::newRow(\"SRA\") << Instruction(0, 0, 24, 26, 3, 3) << regs_init <<\n        //        regs_res; QTest::newRow(\"SRAV\") << Instruction(0, 25, 24, 26, 0, 7) <<\n        //        regs_init << regs_res;\n    }\n\n    // Logical instructions\n    {\n        Registers regs_init;\n        regs_init.write_gp(24, 0xf0);\n        regs_init.write_gp(25, 0xe1);\n        Registers regs_res(regs_init);\n        regs_res.write_gp(26, 0xe0);\n        //        QTest::newRow(\"AND\") << Instruction(0, 24, 25, 26, 0, 36) << regs_init <<\n        //        regs_res; QTest::newRow(\"ANDI\") << Instruction(12, 24, 26, 0xe1) << regs_init\n        //        << regs_res;\n        regs_res.write_gp(26, 0xf1);\n        //        QTest::newRow(\"OR\") << Instruction(0, 24, 25, 26, 0, 37) << regs_init <<\n        //        regs_res; QTest::newRow(\"ORI\") << Instruction(13, 24, 26, 0xe1) << regs_init\n        //        << regs_res;\n        regs_res.write_gp(26, 0x11);\n        //        QTest::newRow(\"XOR\") << Instruction(0, 24, 25, 26, 0, 38) << regs_init <<\n        //        regs_res; QTest::newRow(\"XORI\") << Instruction(14, 24, 26, 0xe1) << regs_init\n        //        << regs_res;\n        regs_res.write_gp(26, 0xffffff0e);\n        //        QTest::newRow(\"NOR\") << Instruction(0, 24, 25, 26, 0, 39) << regs_init <<\n        //        regs_res; regs_res.write_gp(26, 0xf00f0000); QTest::newRow(\"LUI\") <<\n        //        Instruction(15, 0, 26, 0xf00f) << regs_init << regs_res;\n    }\n\n    // Move instructions\n    {\n        Registers regs_init;\n        Registers regs_res(regs_init);\n        regs_res.write_gp(26, 24);\n        //        QTest::newRow(\"MFHI\") << Instruction(0, 0, 0, 26, 0, 16) << regs_init <<\n        //        regs_res;\n        regs_res.write_gp(26, 28);\n        //        QTest::newRow(\"MFLO\") << Instruction(0, 0, 0, 26, 0, 18) << regs_init <<\n        //        regs_res;\n        regs_res.write_gp(26, 0);\n        //        QTest::newRow(\"MTHI\") << Instruction(0, 27, 0, 0, 0, 17) << regs_init <<\n        //        regs_res; QTest::newRow(\"MTLO\") << Instruction(0, 28, 0, 0, 0, 19) <<\n        //        regs_init << regs_res; QTest::newRow(\"MOVZ-F\") << Instruction(0, 24, 24, 25,\n        //        0, 10) << regs_init << regs_res; QTest::newRow(\"MOVN-F\") << Instruction(0, 24,\n        //        1, 25, 0, 11) << regs_init\n        //        << regs_res;\n        regs_res.write_gp(25, 55);\n        //        QTest::newRow(\"MOVZ-T\") << Instruction(0, 24, 1, 25, 0, 10) << regs_init <<\n        //        regs_res; QTest::newRow(\"MOVN-T\") << Instruction(0, 24, 24, 25, 0, 11) <<\n        //        regs_init << regs_res;\n    }\n}\n\nvoid TestCore::singlecore_regs_data() {\n    QSKIP(\"Switched ALU to RV.\");\n    core_regs_data();\n}\n\nvoid TestCore::pipecore_regs_data() {\n    QSKIP(\"Switched ALU to RV.\");\n    core_regs_data();\n}\n\nvoid TestCore::singlecore_regs() {\n    QSKIP(\"Switched ALU to RV.\");\n    QFETCH(Instruction, i);\n    QFETCH(Registers, init);\n    QFETCH(Registers, res);\n\n    Memory mem(BIG); // Just memory (it shouldn't be used here except\n                     // instruction)\n    TrivialBus mem_frontend(&mem);\n    memory_write_u32(&mem, res.read_pc().get_raw(), i.data()); // Store single\n                                                               // instruction\n                                                               // (anything else\n                                                               // should be 0 so\n                                                               // NOP\n                                                               // effectively)\n    Memory mem_used(mem);                                      // Create memory copy\n    TrivialBus mem_used_frontend(&mem);\n\n    //    CoreSingle core(&init, &mem_used_frontend, &mem_used_frontend, true, nullptr,\n    //    Xlen::_32); core.step(); // Single step should be enought as this is risc without\n    // pipeline\n    //    core.step();\n\n    //    res.pc_inc();\n    //    res.pc_inc();        // We did single step\tso increment program counter accordingly\n    QCOMPARE(init, res); // After doing changes from initial state this should\n                         // be same state as in case of passed expected result\n}\n\nvoid TestCore::pipecore_regs() {\n    QSKIP(\"Switched ALU to RV.\");\n    QFETCH(Instruction, i);\n    QFETCH(Registers, init);\n    QFETCH(Registers, res);\n\n    Memory mem(BIG); // Just memory (it shouldn't be used here except\n                     // instruction)\n    TrivialBus mem_frontend(&mem);\n    memory_write_u32(&mem, res.read_pc().get_raw(), i.data()); // Store single\n                                                               // instruction\n                                                               // (anything else\n                                                               // should be 0 so\n                                                               // NOP\n                                                               // effectively)\n\n    Memory mem_used(mem);\n    TrivialBus mem_used_frontend(&mem_used);\n\n    res.write_pc(0x14_addr);\n\n    //    CorePipelined core(\n    //        &init, &mem_used_frontend, &mem_used_frontend, nullptr, MachineConfig::HU_NONE, 0,\n    //        nullptr, Xlen::_32);\n    //    for (int i = 0; i < 5; i++) {\n    //        core.step(); // Fire steps for five pipelines stages\n    //    }\n    //\n    //    cout << \"well:\" << init.read_gp(26) << \":\" << regs_used.read_gp(26) << endl;\n    //    QCOMPARE(init, res); // After doing changes from initial state this should\n    //    be same state as in case of passed expected result QCOMPARE(mem, mem_used); // There\n    //    should be\n    // no change in\n    // memory\n}\n\nstatic void core_jmp_data() {\n    QSKIP(\"Switched RV.\");\n    QTest::addColumn<Instruction>(\"i\");\n    QTest::addColumn<Registers>(\"regs\");\n    QTest::addColumn<uint64_t>(\"pc\");\n\n    Registers regs;\n    regs.write_gp(14, -22);\n    regs.write_gp(15, 22);\n    regs.write_gp(16, -22);\n    regs.write_gp(12, 0x80040000);\n    //    QTest::newRow(\"B\") << Instruction(4, 0, 0, 61) << regs\n    //                       << regs.read_pc().get_raw() + 4 + (61 << 2);\n    //    QTest::newRow(\"BEQ\") << Instruction(4, 14, 16, 61) << regs\n    //                         << regs.read_pc().get_raw() + 4 + (61 << 2);\n    //    QTest::newRow(\"BEQ-BACK\") << Instruction(4, 14, 16, -4) << regs\n    //                              << regs.read_pc().get_raw() + 4 - 16;\n    //    QTest::newRow(\"BNE\") << Instruction(5, 14, 15, 61) << regs\n    //                         << regs.read_pc().get_raw() + 4 + (61 << 2);\n    //    QTest::newRow(\"BGEZ\") << Instruction(1, 15, 1, 61) << regs\n    //                          << regs.read_pc().get_raw() + 4 + (61 << 2);\n    //    QTest::newRow(\"BGTZ\") << Instruction(7, 15, 0, 61) << regs\n    //                          << regs.read_pc().get_raw() + 4 + (61 << 2);\n    //    QTest::newRow(\"BLEZ\") << Instruction(6, 14, 0, 61) << regs\n    //                          << regs.read_pc().get_raw() + 4 + (61 << 2);\n    //    QTest::newRow(\"BLTZ\") << Instruction(1, 14, 0, 61) << regs\n    //                          << regs.read_pc().get_raw() + 4 + (61 << 2);\n    //    QTest::newRow(\"J\") << Instruction(2, Address(24)) << regs\n    //                       << Address(0x80000000).get_raw() + (24 << 2);\n    //    QTest::newRow(\"JR\") << Instruction(0, 12, 0, 0, 0, 8) << regs <<\n    //    Address(0x80040000).get_raw();\n}\n\nvoid TestCore::singlecore_jmp_data() {\n    QSKIP(\"Switched ALU to RV.\");\n    core_jmp_data();\n}\n\nvoid TestCore::pipecore_jmp_data() {\n    QSKIP(\"Switched ALU to RV.\");\n    core_jmp_data();\n}\n\nvoid TestCore::singlecore_jmp() {\n    QSKIP(\"Switched ALU to RV.\");\n    QFETCH(Instruction, i);\n    QFETCH(Registers, regs);\n    // QFETCH(uint64_t, pc);\n\n    Memory mem(BIG);\n    TrivialBus mem_frontend(&mem);\n    memory_write_u32(&mem, regs.read_pc().get_raw(), i.data());\n    Memory mem_used(mem);\n    TrivialBus mem_used_frontend(&mem_used);\n    Registers regs_used(regs);\n\n    //    CoreSingle core(\n    //        &regs_used, &mem_used_frontend, &mem_used_frontend, true, nullptr, Xlen::_32);\n    //    core.step();\n    //    QCOMPARE(regs.read_pc() + 4, regs_used.read_pc()); // First execute delay\n    //                                                            slot\n    //    core.step();\n    //    QCOMPARE(pc, regs_used.read_pc().get_raw()); // Now do jump\n    //\n    //    QCOMPARE(mem, mem_used);              // There should be no change in memory\n    //    regs_used.pc_abs_jmp(regs.read_pc()); // Reset program counter before we do\n    //                                               registers compare\n    //    QCOMPARE(regs, regs_used); // There should be no change in registers now\n}\n\nvoid TestCore::pipecore_jmp() {\n    QSKIP(\"Switched ALU to RV.\");\n    QFETCH(Instruction, i);\n    QFETCH(Registers, regs);\n    // QFETCH(uint64_t, pc);\n\n    Memory mem(BIG);\n    TrivialBus mem_frontend(&mem);\n    memory_write_u32(&mem, regs.read_pc().get_raw(), i.data());\n    Memory mem_used(mem);\n    TrivialBus mem_used_frontend(&mem_used);\n    Registers regs_used(regs);\n\n    //    CorePipelined core(\n    //        &regs_used, &mem_used_frontend, &mem_used_frontend, nullptr,\n    //        MachineConfig::HU_NONE, 0, nullptr, Xlen::_32);\n    //    core.step();\n    //    QCOMPARE(regs.read_pc() + 4, regs_used.read_pc()); // First just fetch\n    //    core.step();\n    //    QCOMPARE(pc, regs_used.read_pc().get_raw()); // Now do jump\n    //    for (int i = 0; i < 3; i++) {\n    //        core.step(); // Follow up with three other steps to complete pipeline to\n    //    }\n    //     be sure that instruction has no side effects\n    //\n    //    QCOMPARE(mem, mem_used);           // There should be no change in memory\n    //    regs.pc_abs_jmp(Address(pc + 12)); // Set reference pc to three more\n    //                                        instructions later (where regs_used\n    //                                        should be)\n    //    QCOMPARE(regs, regs_used);         // There should be no change in registers now\n    // (except pc)\n}\n\nstatic void core_mem_data() {\n    QTest::addColumn<Instruction>(\"i\");\n    QTest::addColumn<Registers>(\"regs_init\");\n    QTest::addColumn<Registers>(\"regs_res\");\n    QTest::addColumn<Memory>(\"mem_init\");\n    QTest::addColumn<Memory>(\"mem_res\");\n\n    // Load\n    {\n        Memory mem(BIG);\n        memory_write_u32(&mem, 0x24, 0xA3242526);\n        Registers regs;\n        regs.write_gp(1, 0x22);\n        Registers regs_res(regs);\n        // Cast to get proper sign extension.\n        regs_res.write_gp(21, (int32_t)0xFFFFFFA3);\n        //        QTest::newRow(\"LB\") << Instruction(32, 1, 21, 0x2) << regs << regs_res << mem\n        //        << mem;\n        //         Cast to get proper sign extension.\n        //        regs_res.write_gp(21, (int32_t)0xFFFFA324);\n        //        QTest::newRow(\"LH\") << Instruction(33, 1, 21, 0x2) << regs << regs_res << mem\n        //        << mem; regs_res.write_gp(21, 0xA3242526); QTest::newRow(\"LW\") <<\n        //        Instruction(35, 1, 21, 0x2) << regs << regs_res << mem << mem;\n        //        regs_res.write_gp(21, 0x000000A3); QTest::newRow(\"LBU\") << Instruction(36, 1,\n        //        21, 0x2) << regs << regs_res << mem << mem; regs_res.write_gp(21, 0x0000A324);\n        //        QTest::newRow(\"LHU\") << Instruction(37, 1, 21, 0x2) << regs << regs_res << mem\n        //        << mem;\n    }\n    // Store\n    {\n        Registers regs;\n        regs.write_gp(1, 0x22);\n        regs.write_gp(21, 0x23242526);\n        Memory mem(BIG);\n        memory_write_u8(&mem, 0x24, 0x26); // Note: store least significant byte\n        //        QTest::newRow(\"SB\") << Instruction(40, 1, 21, 0x2) << regs << regs <<\n        //        Memory(BIG)\n        //        << mem; memory_write_u16(&mem, 0x24, 0x2526); QTest::newRow(\"SH\") <<\n        //        Instruction(41, 1, 21, 0x2) << regs << regs << Memory(BIG) << mem;\n        //        memory_write_u32(&mem, 0x24, 0x23242526);\n        //        QTest::newRow(\"SW\") << Instruction(43, 1, 21, 0x2) << regs << regs <<\n        //        Memory(BIG)\n        //        << mem;\n    }\n}\n\nvoid TestCore::singlecore_mem_data() {\n    QSKIP(\"Switched ALU to RV.\");\n    core_mem_data();\n}\n\nvoid TestCore::pipecore_mem_data() {\n    QSKIP(\"Switched ALU to RV.\");\n    core_mem_data();\n}\n\nvoid TestCore::singlecore_mem() {\n    QSKIP(\"Switched ALU to RV.\");\n    QFETCH(Instruction, i);\n    QFETCH(Registers, regs_init);\n    QFETCH(Registers, regs_res);\n    QFETCH(Memory, mem_init);\n    QFETCH(Memory, mem_res);\n\n    // Write instruction to both memories\n    memory_write_u32(&mem_init, regs_init.read_pc().get_raw(), i.data());\n    memory_write_u32(&mem_res, regs_init.read_pc().get_raw(), i.data());\n\n    TrivialBus mem_init_frontend(&mem_init);\n    //    CoreSingle core(\n    //        &regs_init, &mem_init_frontend, &mem_init_frontend, true, nullptr, Xlen::_32);\n    //    core.step();\n    //    core.step();\n    //\n    //    regs_res.pc_inc();\n    //    regs_res.pc_inc();\n    //    QCOMPARE(regs_init, regs_res);\n    //    QCOMPARE(mem_init, mem_res);\n}\n\nvoid TestCore::pipecore_mem() {\n    QSKIP(\"Switched ALU to RV.\");\n    QFETCH(Instruction, i);\n    QFETCH(Registers, regs_init);\n    QFETCH(Registers, regs_res);\n    QFETCH(Memory, mem_init);\n    QFETCH(Memory, mem_res);\n\n    // Write instruction to both memories\n    memory_write_u32(&mem_init, regs_init.read_pc().get_raw(), i.data());\n    memory_write_u32(&mem_res, regs_init.read_pc().get_raw(), i.data());\n\n    TrivialBus mem_init_frontend(&mem_init);\n    //    CorePipelined core(\n    //        &regs_init, &mem_init_frontend, &mem_init_frontend, nullptr,\n    //        MachineConfig::HU_NONE, 0, nullptr, Xlen::_32);\n    //    for (int i = 0; i < 5; i++) {\n    //        core.step(); // Fire steps for five pipelines stages\n    //    }\n    //\n    //    regs_res.pc_jmp(20);\n    //    QCOMPARE(regs_init, regs_res);\n    //    QCOMPARE(mem_init, mem_res);\n}\n\n/*======================================================================*/\n\nstatic void core_alu_forward_data() {\n    QTest::addColumn<QVector<uint32_t>>(\"code\");\n    QTest::addColumn<Registers>(\"reg_init\");\n    QTest::addColumn<Registers>(\"reg_res\");\n    // Note that we shouldn't be touching program counter as that is handled\n    // automatically and differs if we use pipelining\n\n    // Test forwarding of ALU operands\n    {\n        QVector<uint32_t> code {\n            // objdump --disassembler-options=no-aliases,numeric -d test-hazards\n            // sed -n -e 's/^[ \\t]*[^ \\t]\\+:[ \\t]\\+\\([0-9a-f]\\+\\)[ \\t]\\+\\([^ \\t].*\\)$/0x\\1, \\/\\/\n            // \\2/p'\n            0x00100113, // addi     x2,x0,1\n            0x11100093, // addi     x1,x0,273\n            0x22200093, // addi     x1,x0,546\n            0x002081b3, // add      x3,x1,x2\n            0x00208233, // add      x4,x1,x2\n            0x00300113, // addi     x2,x0,3\n            0x11100093, // addi     x1,x0,273\n            0x22200093, // addi     x1,x0,546\n            0x001102b3, // add      x5,x2,x1\n            0x00110333, // add      x6,x2,x1\n            0x00000013, // addi     x0,x0,0\n            0x00000063, // beq      x0,x0,10080 <loop>\n        };\n        Registers regs_init;\n        regs_init.write_pc(0x200_addr);\n        Registers regs_res(regs_init);\n        regs_res.write_gp(1, 0x222);\n        regs_res.write_gp(2, 3);\n        regs_res.write_gp(3, 0x223);\n        regs_res.write_gp(4, 0x223);\n        regs_res.write_gp(5, 0x225);\n        regs_res.write_gp(6, 0x225);\n        regs_res.write_pc(regs_init.read_pc() + 4 * code.length() - 4);\n        QTest::newRow(\"alu_forward_1\") << code << regs_init << regs_res;\n    }\n\n    // Test forwarding in JR and JALR\n    {\n        QVector<uint32_t> code {\n            // start:\n            0x01111537, // lui      x10,0x1111\n            0x022225b7, // lui      x11,0x2222\n            0x03333637, // lui      x12,0x3333\n            0x034000ef, // jal      x1,240 <fnc_add3>\n            0x00a00733, // add      x14,x0,x10\n            0x0140006f, // jal      x0,228 <skip>\n            0x44400413, // addi     x8,x0,1092\n            0x55500913, // addi     x18,x0,1365\n            0x66600993, // addi     x19,x0,1638\n            0x77700a13, // addi     x20,x0,1911\n            // skip:\n            0x00000297, // auipc    x5,0x0\n            0x02828293, // addi     x5,x5,40 # 250 <fnc_short>\n            0x000280e7, // jalr     x1,0(x5)\n            0x2cd00513, // addi     x10,x0,717\n            0x00050493, // addi     x9,x10,0 # 1111000 <__global_pointer$+0x1108400>\n            0x01c0006f, // jal      x0,258 <test_end>\n            // fnc_add:\n            0x00b50533, // add      x10,x10,x11\n            0x00c50533, // add      x10,x10,x12\n            0x00008067, // jalr     x0,0(x1)\n            0x7f100693, // addi     x13,x0,2033\n            // fnc_short\n            0x00008067, // jalr     x0,0(x1)\n            0x7f200693, // addi     x13,x0,2034\n            // test_end:\n            0x00000013, // addi     x0,x0,0\n            0x00000013, // addi     x0,x0,0\n            // loop:\n            0x00000063, // beq      x0,x0,260 <loop>\n        };\n        Registers regs_init;\n        regs_init.write_pc(0x200_addr);\n        Registers regs_res(regs_init);\n        regs_res.write_gp(1, 0x00000234);\n        regs_res.write_gp(5, 0x00000250);\n        regs_res.write_gp(9, 0x000002cd);\n        regs_res.write_gp(10, 0x000002cd);\n        regs_res.write_gp(11, 0x02222000);\n        regs_res.write_gp(12, 0x03333000);\n        regs_res.write_gp(14, 0x06666000);\n        regs_res.write_pc(regs_init.read_pc() + 4 * code.length() - 4);\n        QTest::newRow(\"j_jal_jalr\") << code << regs_init << regs_res;\n    }\n\n    // Test multiplication and division\n    {\n        QVector<uint32_t> code {\n            0x12345137, // lui      x2,0x12345\n            0x67810113, // addi     x2,x2,1656\n            0xabcdf1b7, // lui      x3,0xabcdf\n            0xf0118193, // addi     x3,x3,-255\n            0x02310833, // mul      x16,x2,x3\n            0x023118b3, // mulh     x17,x2,x3\n            0x02310933, // mul      x18,x2,x3\n            0x023139b3, // mulhu    x19,x2,x3\n            0x0221ca33, // div      x20,x3,x2\n            0x0221eab3, // rem      x21,x3,x2\n            0x0221db33, // divu     x22,x3,x2\n            0x0221fbb3, // remu     x23,x3,x2\n            0x0000006f, // jal      x0,230 <loop>\n        };\n        Registers regs_init;\n        regs_init.write_pc(0x80020000_addr);\n        Registers regs_res(regs_init);\n        uint32_t val_a = 0x12345678;\n        uint32_t val_b = 0xabcdef01;\n        uint64_t val_u64;\n        int64_t val_s64;\n        regs_res.write_gp(2, (int32_t)val_a);\n        regs_res.write_gp(3, (int32_t)val_b);\n        val_s64 = (int64_t)(int32_t)val_a * (int32_t)val_b;\n        regs_res.write_gp(16, (int32_t)(val_s64 & 0xffffffff));\n        regs_res.write_gp(17, (int32_t)(val_s64 >> 32));\n        val_u64 = (uint64_t)val_a * val_b;\n        regs_res.write_gp(18, (int32_t)(val_u64 & 0xffffffff));\n        regs_res.write_gp(19, (int32_t)(val_u64 >> 32));\n        regs_res.write_gp(20, (int32_t)((int32_t)val_b / (int32_t)val_a));\n        regs_res.write_gp(21, (int32_t)((int32_t)val_b % (int32_t)val_a));\n        regs_res.write_gp(22, val_b / val_a);\n        regs_res.write_gp(23, val_b % val_a);\n        regs_res.write_pc(regs_init.read_pc() + 4 * code.length() - 4);\n        QTest::newRow(\"mul-div\") << code << regs_init << regs_res;\n    }\n\n    // branches\n    {\n        QVector<uint32_t> code {\n            0xfff00093, // addi     x1,x0,-1\n            0x00100113, // addi     x2,x0,1\n            0x00000193, // addi     x3,x0,0\n            0x00200213, // addi     x4,x0,2\n            0x00100293, // addi     x5,x0,1\n            0x0000ca63, // blt      x1,x0,228 <test_branch+0x18>\n            0x00000013, // addi     x0,x0,0\n            0x00000293, // addi     x5,x0,0\n            0x10018193, // addi     x3,x3,256\n            0x001181b3, // add      x3,x3,x1\n            0x00100293, // addi     x5,x0,1\n            0x00105a63, // bge      x0,x1,240 <test_branch+0x30>\n            0x00000013, // addi     x0,x0,0\n            0x00000293, // addi     x5,x0,0\n            0x01018193, // addi     x3,x3,16\n            0x001181b3, // add      x3,x3,x1\n            0x00100293, // addi     x5,x0,1\n            0x0000da63, // bge      x1,x0,258 <test_branch+0x48>\n            0x00000013, // addi     x0,x0,0\n            0x00000293, // addi     x5,x0,0\n            0x20018193, // addi     x3,x3,512\n            0x001181b3, // add      x3,x3,x1\n            0x00100293, // addi     x5,x0,1\n            0x00104a63, // blt      x0,x1,270 <test_branch+0x60>\n            0x00000013, // addi     x0,x0,0\n            0x00000293, // addi     x5,x0,0\n            0x02018193, // addi     x3,x3,32\n            0x001181b3, // add      x3,x3,x1\n            0x00100293, // addi     x5,x0,1\n            0x00209a63, // bne      x1,x2,288 <test_branch+0x78>\n            0x00000013, // addi     x0,x0,0\n            0x00000293, // addi     x5,x0,0\n            0x40018193, // addi     x3,x3,1024\n            0x001181b3, // add      x3,x3,x1\n            0x00100293, // addi     x5,x0,1\n            0x00208a63, // beq      x1,x2,2a0 <test_branch+0x90>\n            0x00000013, // addi     x0,x0,0\n            0x00000293, // addi     x5,x0,0\n            0x04018193, // addi     x3,x3,64\n            0x001181b3, // add      x3,x3,x1\n            0x00108093, // addi     x1,x1,1\n            0xf64096e3, // bne      x1,x4,210 <test_branch>\n            0x00000013, // addi     x0,x0,0\n            0x00000013, // addi     x0,x0,0\n            0x00000013, // addi     x0,x0,0\n            0x00000063, // beq      x0,x0,2b4 <loop>\n        };\n        Registers regs_init;\n        regs_init.write_pc(0x200_addr);\n        Registers regs_res(regs_init);\n        regs_res.write_gp(1, 2);\n        regs_res.write_gp(2, 1);\n        regs_res.write_gp(3, 0x8d0);\n        regs_res.write_gp(4, 2);\n        regs_res.write_gp(5, 1);\n        regs_res.write_pc(regs_init.read_pc() + 4 * code.length());\n        QTest::newRow(\"branch_conditions_test\") << code << regs_init << regs_res;\n    }\n}\n\nvoid TestCore::singlecore_alu_forward_data() {\n    core_alu_forward_data();\n}\n\nvoid TestCore::pipecore_alu_forward_data() {\n    core_alu_forward_data();\n}\n\nvoid TestCore::pipecorestall_alu_forward_data() {\n    core_alu_forward_data();\n}\n\nstatic void run_code_fragment(\n    Core &core,\n    Registers &reg_init,\n    Registers &reg_res,\n    Memory &mem_init,\n    Memory &mem_res,\n    QVector<uint32_t> &code) {\n    uint64_t addr = reg_init.read_pc().get_raw();\n\n    foreach (uint32_t i, code) {\n        memory_write_u32(&mem_init, addr, i);\n        memory_write_u32(&mem_res, addr, i);\n        addr += 4;\n    }\n\n    for (int k = 10000; k; k--) {\n        core.step(); // Single step should be enought as this is risc without\n                     // pipeline\n        if (reg_init.read_pc() == reg_res.read_pc() && k > 6) { // reached end\n                                                                // of\n                                                                // the code\n                                                                // fragment\n            k = 6; // add some cycles to finish processing\n        }\n    }\n    reg_res.write_pc(reg_init.read_pc()); // We do not compare result pc\n    QCOMPARE(reg_init, reg_res);          // After doing changes from initial state this\n                                          // should be same state as in case of passed\n                                          // expected result\n    QCOMPARE(mem_init, mem_res);          // There should be no change in memory\n}\n\nvoid TestCore::singlecore_alu_forward() {\n    QFETCH(QVector<uint32_t>, code);\n    QFETCH(Registers, reg_init);\n    QFETCH(Registers, reg_res);\n    Memory mem_init(LITTLE);\n    TrivialBus mem_init_frontend(&mem_init);\n    Memory mem_res(LITTLE);\n    TrivialBus mem_res_frontend(&mem_res);\n\n    BranchPredictor predictor {};\n    CSR::ControlState controlst {};\n\n    CoreSingle core(\n        &reg_init, &predictor, &mem_init_frontend, &mem_init_frontend, &controlst, Xlen::_32,\n        config_isa_word_default);\n    run_code_fragment(core, reg_init, reg_res, mem_init, mem_res, code);\n}\n\nvoid TestCore::pipecore_alu_forward() {\n    QFETCH(QVector<uint32_t>, code);\n    QFETCH(Registers, reg_init);\n    QFETCH(Registers, reg_res);\n    Memory mem_init(LITTLE);\n    TrivialBus mem_init_frontend(&mem_init);\n    Memory mem_res(LITTLE);\n    TrivialBus mem_res_frontend(&mem_res);\n\n    BranchPredictor predictor {};\n    CSR::ControlState controlst {};\n\n    CorePipelined core(\n        &reg_init, &predictor, &mem_init_frontend, &mem_init_frontend, &controlst, Xlen::_32,\n        config_isa_word_default, MachineConfig::HazardUnit::HU_STALL_FORWARD);\n    run_code_fragment(core, reg_init, reg_res, mem_init, mem_res, code);\n}\n\nvoid TestCore::pipecorestall_alu_forward() {\n    QFETCH(QVector<uint32_t>, code);\n    QFETCH(Registers, reg_init);\n    QFETCH(Registers, reg_res);\n    Memory mem_init(LITTLE);\n    TrivialBus mem_init_frontend(&mem_init);\n    Memory mem_res(LITTLE);\n    TrivialBus mem_res_frontend(&mem_res);\n\n    BranchPredictor predictor {};\n    CSR::ControlState controlst {};\n\n    CorePipelined core(\n        &reg_init, &predictor, &mem_init_frontend, &mem_init_frontend, &controlst, Xlen::_32,\n        config_isa_word_default, MachineConfig::HazardUnit::HU_STALL);\n    run_code_fragment(core, reg_init, reg_res, mem_init, mem_res, code);\n}\n\n/*======================================================================*/\n\nstatic void core_memory_tests_data() {\n    QTest::addColumn<QVector<uint32_t>>(\"code\");\n    QTest::addColumn<Registers>(\"reg_init\");\n    QTest::addColumn<Registers>(\"reg_res\");\n    QTest::addColumn<Memory>(\"mem_init\");\n    QTest::addColumn<Memory>(\"mem_res\");\n\n    // Test\n    {\n        QVector<uint32_t> code {\n            // objdump --disassembler-options=no-aliases,numeric -d test-hazards\n            // sed -n -e 's/^[ \\t]*[^ \\t]\\+:[ \\t]\\+\\([0-9a-f]\\+\\)[ \\t]\\+\\([^ \\t].*\\)$/0x\\1, \\/\\/\n            // \\2/p'\n\n            // _start:\n            0x40000513, // addi     x10,x0,1024\n            0x00000413, // addi     x8,x0,0\n            0x03c00493, // addi     x9,x0,60\n            0x00800933, // add      x18,x0,x8\n            // main_cycle:\n            0x04940a63, // beq      x8,x9,264 <main_cycle_end>\n            0x008502b3, // add      x5,x10,x8\n            0x0002aa03, // lw       x20,0(x5)\n            0x000409b3, // add      x19,x8,x0\n            0x00040933, // add      x18,x8,x0\n            // inner_cycle:\n            0x02990263, // beq      x18,x9,248 <inner_cycle_end>\n            0x012502b3, // add      x5,x10,x18\n            0x0002aa83, // lw       x21,0(x5)\n            0x015a22b3, // slt      x5,x20,x21\n            0x00029663, // bne      x5,x0,240 <not_minimum>\n            0x00090993, // addi     x19,x18,0\n            0x000a8a13, // addi     x20,x21,0\n            // not_minimum:\n            0x00490913, // addi     x18,x18,4\n            0xfe1ff06f, // jal      x0,224 <inner_cycle>\n            // inner_cycle_end:\n            0x008502b3, // add      x5,x10,x8\n            0x0002aa83, // lw       x21,0(x5)\n            0x0142a023, // sw       x20,0(x5)\n            0x013502b3, // add      x5,x10,x19\n            0x0152a023, // sw       x21,0(x5)\n            0x00440413, // addi     x8,x8,4\n            0xfb1ff06f, // jal      x0,210 <main_cycle>\n            // main_cycle_end:\n            0x0ff0000f, // fence    iorw,iorw\n            0x00000013, // addi     x0,x0,0  // the mai_cycle loop end has\n            0x00000013, // addi     x0,x0,0  // to be separated because else fetch\n            0x00000013, // addi     x0,x0,0  // reaches stop address prematurely\n            0x00000063, // beq      x0,x0,10080 <loop>\n        };\n        QVector<uint32_t> data_init { 5, 3, 4, 1, 15, 8, 9, 2, 10, 6, 11, 1, 6, 9, 12 };\n        QVector<uint32_t> data_res { 1, 1, 2, 3, 4, 5, 6, 6, 8, 9, 9, 10, 11, 12, 15 };\n        Registers regs_init;\n        regs_init.write_pc(0x200_addr);\n        Registers regs_res(regs_init);\n        regs_res.write_gp(5, 0x438);\n        regs_res.write_gp(8, 0x3c);\n        regs_res.write_gp(9, 0x3c);\n        regs_res.write_gp(10, 0x400);\n        regs_res.write_gp(18, 0x3c);\n        regs_res.write_gp(19, 0x38);\n        regs_res.write_gp(20, 0xf);\n        regs_res.write_gp(21, 0xf);\n        regs_res.write_pc(regs_init.read_pc() + 4 * code.length() - 4);\n        uint32_t addr;\n        Memory mem_init(LITTLE);\n        addr = 0x400;\n        foreach (uint32_t i, data_init) {\n            memory_write_u32(&mem_init, addr, i);\n            addr += 4;\n        }\n        Memory mem_res(LITTLE);\n        addr = 0x400;\n        foreach (uint32_t i, data_res) {\n            memory_write_u32(&mem_res, addr, i);\n            addr += 4;\n        }\n        QTest::newRow(\"cache_insert_sort\") << code << regs_init << regs_res << mem_init << mem_res;\n    }\n\n    // unaligned lw a lw and sw\n    {\n        QVector<uint32_t> code {\n            0xaabbdd37, // lui      x26,0xaabbd\n            0xcddd0d13, // addi     x26,x26,-803\n            0x000d0113, // addi     x2,x26,0\n            0x000d0193, // addi     x3,x26,0\n            0x000d0213, // addi     x4,x26,0\n            0x000d0293, // addi     x5,x26,0\n            0x000d0313, // addi     x6,x26,0\n            0x000d0393, // addi     x7,x26,0\n            0x000d0413, // addi     x8,x26,0\n            0x000d0493, // addi     x9,x26,0\n            0x000d0513, // addi     x10,x26,0\n            0x80020db7, // lui      x27,0x80020\n            0x100d8d93, // addi     x27,x27,256\n            0x000da103, // lw       x2,0(x27)\n            0x001da183, // lw       x3,1(x27)\n            0x002da203, // lw       x4,2(x27)\n            0x003da283, // lw       x5,3(x27)\n            0x01ada023, // sw       x26,0(x27)\n            0x01ada2a3, // sw       x26,5(x27)\n            0x01ada523, // sw       x26,10(x27)\n            0x01ada7a3, // sw       x26,15(x27)\n            0x000da503, // lw       x10,0(x27)\n            0x004da583, // lw       x11,4(x27)\n            0x008da603, // lw       x12,8(x27)\n            0x00cda683, // lw       x13,12(x27)\n            0x010da703, // lw       x14,16(x27)\n            0x014da783, // lw       x15,20(x27)\n            0x018da803, // lw       x16,24(x27)\n            0x01cda883, // lw       x17,28(x27)\n            0x020da903, // lw       x18,32(x27)\n            0x020da983, // lw       x19,32(x27)\n            0x0ff0000f, // fence    iorw,iorw\n            0x00000013, // addi     x0,x0,0\n            0x00000013, // addi     x0,x0,0\n            0xfe000ce3, // beq      x0,x0,27c <loop>\n        };\n        Registers regs_init;\n        regs_init.write_pc(0x200_addr);\n        Registers regs_res(regs_init);\n\n        regs_res.write_gp(2, (int32_t)0x04030201);\n        regs_res.write_gp(3, (int32_t)0x05040302);\n        regs_res.write_gp(4, (int32_t)0x06050403);\n        regs_res.write_gp(5, (int32_t)0x07060504);\n        regs_res.write_gp(6, (int32_t)0xaabbccdd);\n        regs_res.write_gp(7, (int32_t)0xaabbccdd);\n        regs_res.write_gp(8, (int32_t)0xaabbccdd);\n        regs_res.write_gp(9, (int32_t)0xaabbccdd);\n        regs_res.write_gp(10, (int32_t)0xaabbccdd);\n        regs_res.write_gp(11, (int32_t)0xbbccdd05);\n        regs_res.write_gp(12, (int32_t)0xccdd0aaa);\n        regs_res.write_gp(13, (int32_t)0xdd0faabb);\n        regs_res.write_gp(14, (int32_t)0x14aabbcc);\n        regs_res.write_gp(15, (int32_t)0x18171615);\n        regs_res.write_gp(16, (int32_t)0x1c1b1a19);\n        regs_res.write_gp(17, (int32_t)0x101f1e1d);\n        regs_res.write_gp(18, (int32_t)0x24232221);\n        regs_res.write_gp(19, (int32_t)0x24232221);\n        regs_res.write_gp(26, (int32_t)0xaabbccdd);\n        regs_res.write_gp(27, (int32_t)0x80020100);\n\n        uint32_t addr;\n        Memory mem_init(LITTLE);\n        addr = 0x80020100;\n        QVector<uint32_t> data_init { 0x04030201, 0x08070605, 0x0c0b0a09, 0x000f0e0d,\n                                      0x14131211, 0x18171615, 0x1c1b1a19, 0x101f1e1d,\n                                      0x24232221, 0x28272625, 0x2c2b2a29, 0x202f2e2d };\n        foreach (uint32_t i, data_init) {\n            memory_write_u32(&mem_init, addr, i);\n            addr += 4;\n        }\n        Memory mem_res(LITTLE);\n        addr = 0x80020100;\n        QVector<uint32_t> data_res { 0xaabbccdd, 0xbbccdd05, 0xccdd0aaa, 0xdd0faabb,\n                                     0x14aabbcc, 0x18171615, 0x1c1b1a19, 0x101f1e1d,\n                                     0x24232221, 0x28272625, 0x2c2b2a29, 0x202f2e2d };\n        foreach (uint32_t i, data_res) {\n            memory_write_u32(&mem_res, addr, i);\n            addr += 4;\n        }\n\n        regs_res.write_pc(regs_init.read_pc() + 4 * code.length() - 4);\n        QTest::newRow(\"lw_sw_unaligned_be\") << code << regs_init << regs_res << mem_init << mem_res;\n    }\n}\n\nvoid TestCore::singlecore_memory_tests_data() {\n    core_memory_tests_data();\n}\n\nvoid TestCore::pipecore_nc_memory_tests_data() {\n    core_memory_tests_data();\n}\n\nvoid TestCore::pipecore_wt_na_memory_tests_data() {\n    core_memory_tests_data();\n}\n\nvoid TestCore::pipecore_wt_a_memory_tests_data() {\n    core_memory_tests_data();\n}\n\nvoid TestCore::pipecore_wb_memory_tests_data() {\n    core_memory_tests_data();\n}\n\nvoid TestCore::singlecore_memory_tests() {\n    QFETCH(QVector<uint32_t>, code);\n    QFETCH(Registers, reg_init);\n    QFETCH(Registers, reg_res);\n    QFETCH(Memory, mem_init);\n    QFETCH(Memory, mem_res);\n    TrivialBus mem_init_frontend(&mem_init);\n    TrivialBus mem_res_frontend(&mem_res);\n\n    BranchPredictor predictor {};\n    CSR::ControlState controlst {};\n\n    CoreSingle core(\n        &reg_init, &predictor, &mem_init_frontend, &mem_init_frontend, &controlst, Xlen::_32,\n        config_isa_word_default);\n    run_code_fragment(core, reg_init, reg_res, mem_init, mem_res, code);\n}\n\nvoid TestCore::pipecore_nc_memory_tests() {\n    QFETCH(QVector<uint32_t>, code);\n    QFETCH(Registers, reg_init);\n    QFETCH(Registers, reg_res);\n    QFETCH(Memory, mem_init);\n    QFETCH(Memory, mem_res);\n    TrivialBus mem_init_frontend(&mem_init);\n    TrivialBus mem_res_frontend(&mem_res);\n\n    BranchPredictor predictor {};\n    CSR::ControlState controlst {};\n\n    CorePipelined core(\n        &reg_init, &predictor, &mem_init_frontend, &mem_init_frontend, &controlst, Xlen::_32,\n        config_isa_word_default);\n    run_code_fragment(core, reg_init, reg_res, mem_init, mem_res, code);\n}\n\nvoid TestCore::pipecore_wt_na_memory_tests() {\n    QFETCH(QVector<uint32_t>, code);\n    QFETCH(Registers, reg_init);\n    QFETCH(Registers, reg_res);\n    QFETCH(Memory, mem_init);\n    QFETCH(Memory, mem_res);\n    TrivialBus mem_init_frontend(&mem_init);\n    TrivialBus mem_res_frontend(&mem_res);\n    CacheConfig cache_conf;\n    cache_conf.set_enabled(true);\n    cache_conf.set_set_count(2);     // Number of sets\n    cache_conf.set_block_size(1);    // Number of blocks\n    cache_conf.set_associativity(2); // Degree of associativity\n    cache_conf.set_replacement_policy(CacheConfig::RP_LRU);\n    cache_conf.set_write_policy(CacheConfig::WP_THROUGH_NOALLOC);\n\n    Cache i_cache(&mem_init_frontend, &cache_conf);\n    Cache d_cache(&mem_init_frontend, &cache_conf);\n\n    BranchPredictor predictor {};\n    CSR::ControlState controlst {};\n\n    CorePipelined core(\n        &reg_init, &predictor, &i_cache, &d_cache, &controlst, Xlen::_32, config_isa_word_default);\n    run_code_fragment(core, reg_init, reg_res, mem_init, mem_res, code);\n}\n\nvoid TestCore::pipecore_wt_a_memory_tests() {\n    QFETCH(QVector<uint32_t>, code);\n    QFETCH(Registers, reg_init);\n    QFETCH(Registers, reg_res);\n    QFETCH(Memory, mem_init);\n    QFETCH(Memory, mem_res);\n    TrivialBus mem_init_frontend(&mem_init);\n    TrivialBus mem_res_frontend(&mem_res);\n    CacheConfig cache_conf;\n    cache_conf.set_enabled(true);\n    cache_conf.set_set_count(2);     // Number of sets\n    cache_conf.set_block_size(1);    // Number of blocks\n    cache_conf.set_associativity(2); // Degree of associativity\n    cache_conf.set_replacement_policy(CacheConfig::RP_LRU);\n    cache_conf.set_write_policy(CacheConfig::WP_THROUGH_ALLOC);\n    Cache i_cache(&mem_init_frontend, &cache_conf);\n    Cache d_cache(&mem_init_frontend, &cache_conf);\n\n    BranchPredictor predictor {};\n    CSR::ControlState controlst {};\n\n    CorePipelined core(\n        &reg_init, &predictor, &i_cache, &d_cache, &controlst, Xlen::_32, config_isa_word_default);\n    run_code_fragment(core, reg_init, reg_res, mem_init, mem_res, code);\n}\n\nvoid TestCore::pipecore_wb_memory_tests() {\n    QFETCH(QVector<uint32_t>, code);\n    QFETCH(Registers, reg_init);\n    QFETCH(Registers, reg_res);\n    QFETCH(Memory, mem_init);\n    QFETCH(Memory, mem_res);\n    TrivialBus mem_init_frontend(&mem_init);\n    TrivialBus mem_res_frontend(&mem_res);\n    CacheConfig cache_conf;\n    cache_conf.set_enabled(true);\n    cache_conf.set_set_count(4);     // Number of sets\n    cache_conf.set_block_size(2);    // Number of blocks\n    cache_conf.set_associativity(2); // Degree of associativity\n    cache_conf.set_replacement_policy(CacheConfig::RP_LRU);\n    cache_conf.set_write_policy(CacheConfig::WP_BACK);\n    Cache i_cache(&mem_init_frontend, &cache_conf);\n    Cache d_cache(&mem_init_frontend, &cache_conf);\n\n    BranchPredictor predictor {};\n    CSR::ControlState controlst {};\n\n    CorePipelined core(\n        &reg_init, &predictor, &i_cache, &d_cache, &controlst, Xlen::_32, config_isa_word_default);\n    run_code_fragment(core, reg_init, reg_res, mem_init, mem_res, code);\n}\n\nvoid extension_m_data() {\n    QTest::addColumn<vector<QString>>(\"instructions\");\n    QTest::addColumn<Registers>(\"registers\");\n    QTest::addColumn<RegisterValue>(\"x10_result\");\n    QTest::addColumn<Xlen>(\"xlen\");\n\n    Registers registers {};\n    registers.write_gp(1, 1111111);\n    registers.write_gp(2, 7);\n    QTest::addRow(\"mul\") << vector<QString> { \"mul x10, x1, x2\", \"nop\" } << registers\n                         << RegisterValue { 7777777 } << Xlen::_32;\n    registers.write_gp(1, 7777777);\n    QTest::addRow(\"div\") << vector<QString> { \"div x10, x1, x2\", \"nop\" } << registers\n                         << RegisterValue { 1111111 } << Xlen::_32;\n    registers.write_gp(2, 1000);\n    QTest::addRow(\"rem\") << vector<QString> { \"rem x10, x1, x2\", \"nop\" } << registers\n                         << RegisterValue { 777 } << Xlen::_32;\n    registers.write_gp(1, 15);\n    registers.write_gp(2, -10);\n    QTest::addRow(\"mulh 15x-10\") << vector<QString> { \"mulh x10, x1, x2\", \"nop\" } << registers\n                                 << RegisterValue { (uint64_t)0xffffffffffffffffULL } << Xlen::_32;\n    QTest::addRow(\"mulhu 15x-10\") << vector<QString> { \"mulhu x10, x1, x2\", \"nop\" } << registers\n                                  << RegisterValue { 14 } << Xlen::_32;\n    QTest::addRow(\"mulhsu 15x-10\") << vector<QString> { \"mulhsu x10, x1, x2\", \"nop\" } << registers\n                                   << RegisterValue { 14 } << Xlen::_32;\n    QTest::addRow(\"mulh -10x15\") << vector<QString> { \"mulh x10, x2, x1\", \"nop\" } << registers\n                                 << RegisterValue { (uint64_t)0xffffffffffffffffULL } << Xlen::_32;\n    QTest::addRow(\"mulhu -10x15\") << vector<QString> { \"mulhu x10, x2, x1\", \"nop\" } << registers\n                                  << RegisterValue { 14 } << Xlen::_32;\n    QTest::addRow(\"mulhsu -10x15\")\n        << vector<QString> { \"mulhsu x10, x2, x1\", \"nop\" } << registers\n        << RegisterValue { (uint64_t)0xffffffffffffffffULL } << Xlen::_32;\n    registers.write_gp(1, -1);\n    registers.write_gp(2, 0x10000);\n    QTest::addRow(\"divu\") << vector<QString> { \"divu x10, x1, x2\", \"nop\" } << registers\n                          << RegisterValue { 0xFFFF } << Xlen::_32;\n    QTest::addRow(\"remu\") << vector<QString> { \"remu x10, x1, x2\", \"nop\" } << registers\n                          << RegisterValue { 0xFFFF } << Xlen::_32;\n}\n\n// Extensions:\n// =================================================================================================\n\n// RV32M\n\nvoid TestCore::singlecore_extension_m_data() {\n    extension_m_data();\n}\n\nvoid TestCore::pipecore_extension_m_data() {\n    extension_m_data();\n}\n\nvoid TestCore::singlecore_extension_m() {\n    test_program_with_single_result<CoreSingle>();\n}\n\nvoid TestCore::pipecore_extension_m() {\n    test_program_with_single_result<CorePipelined>();\n}\n\nQTEST_APPLESS_MAIN(TestCore)\n"
  },
  {
    "path": "src/machine/core.test.h",
    "content": "#ifndef CORE_TEST_H\n#define CORE_TEST_H\n\n#include <QtTest>\n\nclass TestCore : public QObject {\n    Q_OBJECT\n\nprivate slots:\n    // Not ported to RV:\n    void singlecore_regs();\n    void singlecore_regs_data();\n    void pipecore_regs();\n    void pipecore_regs_data();\n    void singlecore_jmp();\n    void singlecore_jmp_data();\n    void pipecore_jmp();\n    void pipecore_jmp_data();\n    void singlecore_mem_data();\n    void singlecore_mem();\n    void pipecore_mem();\n    void pipecore_mem_data();\n    void singlecore_alu_forward();\n    void singlecore_alu_forward_data();\n    void pipecore_alu_forward();\n    void pipecore_alu_forward_data();\n    void pipecorestall_alu_forward();\n    void pipecorestall_alu_forward_data();\n    void singlecore_memory_tests_data();\n    void pipecore_nc_memory_tests_data();\n    void pipecore_wt_na_memory_tests_data();\n    void pipecore_wt_a_memory_tests_data();\n    void pipecore_wb_memory_tests_data();\n    void singlecore_memory_tests();\n    void pipecore_nc_memory_tests();\n    void pipecore_wt_na_memory_tests();\n    void pipecore_wt_a_memory_tests();\n    void pipecore_wb_memory_tests();\n\n    // Extensions:\n    // =============================================================================================\n\n    // RV32M\n    void singlecore_extension_m_data();\n    void pipecore_extension_m_data();\n    void singlecore_extension_m();\n    void pipecore_extension_m();\n};\n\n#endif // CORE_TEST_H\n"
  },
  {
    "path": "src/machine/csr/address.h",
    "content": "#ifndef QTRVSIM_CSR_ADDRESS_H\n#define QTRVSIM_CSR_ADDRESS_H\n\n#include \"common/math/bit_ops.h\"\n#include \"simulator_exception.h\"\n\n#include <cstdint>\n\nnamespace machine { namespace CSR {\n    /**\n     * Spec vol. 2: Table 2.1\n     */\n    enum class PrivilegeLevel {\n        UNPRIVILEGED = 0b00, //> Unprivileged and User-Level CSRs, unimplemented\n        SUPERVISOR = 0b01,   //> Supervisor-Level CSRs, unimplemented\n        HYPERVISOR = 0b10,   //> Hypervisor and VS CSRs, unimplemented\n        MACHINE = 0b11,      //> Machine-Level CSRs\n    };\n\n    struct Address {\n        constexpr explicit Address(uint16_t address) : data(address) {\n            SANITY_ASSERT(\n                address < (1 << 12), \"CSR register address is out of the ISA \"\n                                     \"specified range (12bits)\");\n        }\n\n        constexpr Address(const Address &other) = default;\n        constexpr Address &operator=(const Address &other) = default;\n\n        uint16_t data;\n\n        /*\n         * By convention, the upper 4 bits of the CSR address (csr[11:8]) are used to encode the\n         * read and write accessibility of the CSRs according to privilege level as shown in Table\n         * 2.1.\n         */\n\n        /** The top two bits (csr[11:10]) indicate whether the register is read/write\n         * (00, 01, or 10) or read-only (11).\n         */\n        [[nodiscard]] constexpr bool is_writable() const { return get_bits(data, 11, 10) != 0b11; }\n\n        /**\n         * The next two bits (csr[9:8]) encode the lowest privilege level that can access the CSR.\n         */\n        [[nodiscard]] constexpr PrivilegeLevel get_privilege_level() const {\n            return static_cast<PrivilegeLevel>(get_bits(data, 9, 8));\n        }\n\n        bool operator<(const Address &rhs) const { return data < rhs.data; }\n        bool operator>(const Address &rhs) const { return rhs < *this; }\n        bool operator<=(const Address &rhs) const { return !(rhs < *this); }\n        bool operator>=(const Address &rhs) const { return !(*this < rhs); }\n        bool operator==(const Address &rhs) const { return data == rhs.data; }\n        bool operator!=(const Address &rhs) const { return data != rhs.data; }\n    };\n\n    constexpr Address operator\"\"_csr(unsigned long long literal) {\n        return Address(literal);\n    }\n}} // namespace machine::CSR\n\ntemplate<>\nstruct std::hash<machine::CSR::Address> {\n    std::size_t operator()(machine::CSR::Address const &addr) const noexcept {\n        return std::hash<decltype(addr.data)> {}(addr.data);\n    }\n};\n\n#endif // QTRVSIM_CSR_ADDRESS_H\n"
  },
  {
    "path": "src/machine/csr/controlstate.cpp",
    "content": "#include \"controlstate.h\"\n\n#include \"common/logging.h\"\n#include \"machinedefs.h\"\n#include \"simulator_exception.h\"\n\n#include <QtAlgorithms>\n#include <cinttypes>\n\nLOG_CATEGORY(\"machine.csr.control_state\");\n\nnamespace machine { namespace CSR {\n\n    ControlState::ControlState(Xlen xlen, ConfigIsaWord isa_word) : xlen(xlen) {\n        reset();\n        uint64_t misa = read_internal(CSR::Id::MISA).as_u64();\n        misa |= isa_word.toUnsigned();\n        register_data[CSR::Id::MISA] = misa;\n    }\n\n    ControlState::ControlState(const ControlState &other)\n        : QObject(this->parent())\n        , xlen(other.xlen)\n        , register_data(other.register_data) {}\n\n    void ControlState::reset() {\n        std::transform(\n            REGISTERS.begin(), REGISTERS.end(), register_data.begin(),\n            [](const RegisterDesc &desc) { return desc.initial_value; });\n\n        uint64_t misa = read_internal(CSR::Id::MISA).as_u64();\n        misa &= 0x3fffffff;\n        if (xlen == Xlen::_32) {\n            misa |= (uint64_t)1 << 30;\n        } else if (xlen == Xlen::_64) {\n            misa |= (uint64_t)2 << 62;\n        }\n        register_data[CSR::Id::MISA] = misa;\n\n        if (xlen == Xlen::_64) {\n            write_field_raw(Field::mstatus::UXL, 2);\n            write_field_raw(Field::mstatus::SXL, 2);\n        }\n    }\n\n    size_t ControlState::get_register_internal_id(Address address) {\n        // if (address.get_privilege_level() != PrivilegeLevel::MACHINE)\n\n        try {\n            return CSR::REGISTER_MAP.at(address);\n        } catch (std::out_of_range &e) {\n            throw SIMULATOR_EXCEPTION(\n                UnsupportedInstruction,\n                QString(\"Accessed nonexistent CSR register %1\").arg(address.data), \"\");\n        }\n    }\n\n    RegisterValue ControlState::read(Address address, PrivilegeLevel current_priv) const {\n        size_t reg_id = get_register_internal_id(address);\n        PrivilegeLevel required = address.get_privilege_level();\n        if (current_priv < required) {\n            throw SIMULATOR_EXCEPTION(\n                UnsupportedInstruction,\n                QString(\"CSR address %1 not accessible at current privilege level.\")\n                    .arg(address.data),\n                \"\");\n        }\n        RegisterValue value = register_data[reg_id];\n        DEBUG(\"Read CSR[%u] == 0x%\" PRIx64, address.data, value.as_u64());\n        emit read_signal(reg_id, value);\n        return value;\n    }\n\n    void ControlState::write(Address address, RegisterValue value, PrivilegeLevel current_priv) {\n        DEBUG(\n            \"Write CSR[%u/%zu] <== 0x%zu\", address.data, get_register_internal_id(address),\n            value.as_u64());\n        // Attempts to write a read-only register also raise illegal instruction exceptions.\n        if (!address.is_writable()) {\n            throw SIMULATOR_EXCEPTION(\n                UnsupportedInstruction,\n                QString(\"CSR address %1 is not writable.\").arg(address.data), \"\");\n        }\n\n        PrivilegeLevel required = address.get_privilege_level();\n        if (current_priv < required) {\n            throw SIMULATOR_EXCEPTION(\n                UnsupportedInstruction,\n                QString(\"CSR address %1 not writable at current privilege level.\").arg(address.data),\n                \"\");\n        }\n\n        write_internal(get_register_internal_id(address), value);\n    }\n\n    void ControlState::default_wlrl_write_handler(\n        const RegisterDesc &desc,\n        RegisterValue &reg,\n        RegisterValue val) {\n        uint64_t u;\n        u = val.as_u64() & desc.write_mask.as_u64();\n        u |= reg.as_u64() & ~desc.write_mask.as_u64();\n        if (xlen == Xlen::_32) u &= 0xffffffff;\n        reg = u;\n    }\n    void ControlState::mstatus_wlrl_write_handler(\n        const RegisterDesc &desc,\n        RegisterValue &reg,\n        RegisterValue val) {\n        default_wlrl_write_handler(desc, reg, val);\n    }\n\n    void ControlState::mcycle_wlrl_write_handler(\n        const RegisterDesc &desc,\n        RegisterValue &reg,\n        RegisterValue val) {\n        Q_UNUSED(desc)\n        reg = val;\n        register_data[Id::CYCLE] = val;\n        write_signal(Id::CYCLE, register_data[Id::CYCLE]);\n    }\n\n    void ControlState::sstatus_wlrl_write_handler(\n        const RegisterDesc &desc,\n        RegisterValue &reg,\n        RegisterValue val) {\n        uint64_t s_mask\n            = Field::mstatus::SIE.mask() | Field::mstatus::SPIE.mask() | Field::mstatus::SPP.mask();\n\n        if (xlen == Xlen::_64) {\n            s_mask |= Field::mstatus::UXL.mask();\n            s_mask |= Field::mstatus::SXL.mask();\n        }\n        uint64_t write_val = val.as_u64() & desc.write_mask.as_u64();\n        uint64_t mstatus_val = register_data[Id::MSTATUS].as_u64();\n        mstatus_val = (mstatus_val & ~s_mask) | (write_val & s_mask);\n        register_data[Id::MSTATUS] = mstatus_val;\n        uint64_t new_sstatus = mstatus_val & s_mask;\n        if (xlen == Xlen::_32) new_sstatus &= 0xffffffff;\n        reg = new_sstatus;\n        emit write_signal(Id::MSTATUS, register_data[Id::MSTATUS]);\n    }\n\n    bool ControlState::operator==(const ControlState &other) const {\n        return register_data == other.register_data;\n    }\n\n    bool ControlState::operator!=(const ControlState &c) const {\n        return !this->operator==(c);\n    }\n\n    void ControlState::update_exception_cause(enum ExceptionCause excause) {\n        RegisterValue &value = register_data[Id::MCAUSE];\n        if (excause != EXCAUSE_INT) {\n            value = static_cast<unsigned>(excause);\n        } else {\n            RegisterValue mie = register_data[Id::MIE];\n            RegisterValue mip = register_data[Id::MIP];\n            int irq_to_signal = 0;\n\n            quint64 irqs = mie.as_u64() & mip.as_u64() & 0xffffffff;\n\n            if (irqs != 0) {\n                // Find the first (leas significant) set bit\n                irq_to_signal = 63 - qCountLeadingZeroBits(irqs & (~irqs + 1));\n            }\n\n            value = (uint64_t)(irq_to_signal | ((uint64_t)1 << ((xlen == Xlen::_32) ? 31 : 63)));\n        }\n        emit write_signal(Id::MCAUSE, value);\n    }\n\n    void ControlState::set_interrupt_signal(uint irq_num, bool active) {\n        if (irq_num >= 32) { return; }\n        uint64_t mask = 1 << irq_num;\n        size_t reg_id = Id::MIP;\n        RegisterValue &value = register_data[reg_id];\n        if (active) {\n            value = value.as_xlen(xlen) | mask;\n        } else {\n            value = value.as_xlen(xlen) & ~mask;\n        }\n        emit write_signal(reg_id, value);\n    }\n\n    bool ControlState::core_interrupt_request() {\n        RegisterValue mie = register_data[Id::MIE];\n        RegisterValue mip = register_data[Id::MIP];\n\n        uint64_t irqs = mie.as_u64() & mip.as_u64() & 0xffffffff;\n\n        return irqs && read_field(Field::mstatus::MIE).as_u64();\n    }\n\n    void ControlState::exception_initiate(PrivilegeLevel act_privlev, PrivilegeLevel to_privlev) {\n        size_t reg_id = Id::MSTATUS;\n        RegisterValue &reg = register_data[reg_id];\n        Q_UNUSED(act_privlev)\n        Q_UNUSED(to_privlev)\n\n        write_field(Field::mstatus::MPIE, read_field(Field::mstatus::MIE).as_u32());\n        write_field(Field::mstatus::MIE, (uint64_t)0);\n\n        write_field(Field::mstatus::MPP, static_cast<uint64_t>(act_privlev));\n\n        emit write_signal(reg_id, reg);\n    }\n\n    PrivilegeLevel ControlState::exception_return(\n        enum PrivilegeLevel act_privlev,\n        enum PrivilegeLevel xret_privlev) {\n        size_t reg_id = Id::MSTATUS;\n        RegisterValue &reg = register_data[reg_id];\n        PrivilegeLevel restored_privlev = PrivilegeLevel::MACHINE;\n        if (xret_privlev == PrivilegeLevel::MACHINE) {\n            // MRET semantics:\n            //   MIE  <- MPIE\n            //   MPIE <- 1\n            //   restored_privlev <- MPP\n            //   MPP  <- 0\n            write_field(Field::mstatus::MIE, read_field(Field::mstatus::MPIE).as_u32());\n            write_field(Field::mstatus::MPIE, (uint64_t)1);\n            uint32_t raw_mpp\n                = static_cast<uint32_t>(read_field(Field::mstatus::MPP).as_u32()) & 0x3;\n            switch (raw_mpp) {\n            case 0: restored_privlev = PrivilegeLevel::UNPRIVILEGED; break;\n            case 1: restored_privlev = PrivilegeLevel::SUPERVISOR; break;\n            case 2: restored_privlev = PrivilegeLevel::HYPERVISOR; break;\n            case 3: restored_privlev = PrivilegeLevel::MACHINE; break;\n            default: restored_privlev = PrivilegeLevel::UNPRIVILEGED; break;\n            }\n            write_field(Field::mstatus::MPP, (uint64_t)0); // clear MPP per spec\n        } else if (xret_privlev == PrivilegeLevel::SUPERVISOR) {\n            // SRET semantics:\n            //   SIE  <- SPIE\n            //   SPIE <- 1\n            //   restored_privlev <- SPP\n            //   SPP  <- 0\n            write_field(Field::mstatus::SIE, read_field(Field::mstatus::SPIE).as_u32());\n            write_field(Field::mstatus::SPIE, (uint64_t)1);\n            uint32_t raw_spp\n                = static_cast<uint32_t>(read_field(Field::mstatus::SPP).as_u32()) & 0x1;\n            restored_privlev\n                = (raw_spp == 1) ? PrivilegeLevel::SUPERVISOR : PrivilegeLevel::UNPRIVILEGED;\n            write_field(Field::mstatus::SPP, (uint64_t)0);\n        } else {\n            restored_privlev = PrivilegeLevel::UNPRIVILEGED;\n        }\n\n        // If the instruction was executed in M-mode and the restored privilege is less-privileged\n        // than M, clear MPRV per the privileged spec.\n        if (act_privlev == PrivilegeLevel::MACHINE && restored_privlev != PrivilegeLevel::MACHINE) {\n            write_field(Field::mstatus::MPRV, (uint64_t)0);\n        }\n\n        emit write_signal(reg_id, reg);\n\n        return restored_privlev;\n    }\n\n    machine::Address ControlState::exception_pc_address() {\n        return machine::Address(register_data[Id::MTVEC].as_u64());\n    }\n\n    RegisterValue ControlState::read_internal(size_t internal_id) const {\n        return register_data[internal_id];\n    }\n\n    void ControlState::write_internal(size_t internal_id, RegisterValue value) {\n        RegisterDesc desc = REGISTERS[internal_id];\n        RegisterValue &reg = register_data[internal_id];\n        (this->*desc.write_handler)(desc, reg, value);\n        write_signal(internal_id, reg);\n    }\n    void ControlState::increment_internal(size_t internal_id, uint64_t amount) {\n        auto value = register_data[internal_id];\n        write_internal(internal_id, value.as_u64() + amount);\n    }\n}} // namespace machine::CSR\n"
  },
  {
    "path": "src/machine/csr/controlstate.h",
    "content": "#ifndef CONTROLSTATE_H\n#define CONTROLSTATE_H\n\n#include \"bitfield.h\"\n#include \"common/math/bit_ops.h\"\n#include \"config_isa.h\"\n#include \"csr/address.h\"\n#include \"machinedefs.h\"\n#include \"register_value.h\"\n#include \"simulator_exception.h\"\n\n#include <QObject>\n#include <QString>\n#include <cstdint>\n#include <unordered_map>\n\nnamespace machine { namespace CSR {\n    /** CSR register names mapping the registers to continuous locations in internal buffer */\n    struct Id {\n        enum IdxType {\n            // Unprivileged Counter/Timers\n            CYCLE,\n            // Machine Information Registers\n            MVENDORID,\n            MARCHID,\n            MIMPID,\n            MHARTID,\n            //            MCONFIGPTR,\n            // Machine Trap Setup\n            MSTATUS,\n            MISA,\n            //            MEDELEG,\n            //            MIDELET,\n            MIE,\n            MTVEC,\n            //            MCOUNTERN,\n            //            MSTATUSH,\n            // Machine Trap Handling\n            MSCRATCH,\n            MEPC,\n            MCAUSE,\n            MTVAL,\n            MIP,\n            MTINST,\n            MTVAL2,\n            // ...\n            MCYCLE,\n            MINSTRET,\n            // Supervisor Trap Setup\n            SSTATUS,\n            // ...\n            STVEC,\n            // ...\n            // Supervisor Trap Handling\n            SSCRATCH,\n            SEPC,\n            SCAUSE,\n            STVAL,\n            // ...\n            // Supervisor Protection and Translation\n            SATP,\n            _COUNT\n        };\n    };\n\n    struct RegisterDesc;\n\n    struct RegisterFieldDesc {\n        uint64_t decode(uint64_t val) const { return field.decode(val); }\n        uint64_t encode(uint64_t val) const { return field.encode(val); }\n        uint64_t mask() const { return field.mask(); }\n        uint64_t update(uint64_t orig, uint64_t val) const {\n            return field.encode(val) | (orig & ~mask());\n        }\n\n        const char *name = \"unknown\";\n        const Id::IdxType regId;\n        const BitField field;\n        const char *description = \"\";\n    };\n\n    /**\n     * This class provides access to state of CSR registers.\n     *\n     * Registers are externally addressed by 12bit address. To simplify simulation, all existing\n     * registers are stored in continuous array and indexed by \"internal id\" (not stable).\n     */\n    class ControlState : public QObject {\n        Q_OBJECT\n\n    public:\n        ControlState(Xlen xlen = Xlen::_32, ConfigIsaWord isa_word = 0);\n        ControlState(const ControlState &);\n\n        /** Read CSR register with ISA specified address. */\n        [[nodiscard]] RegisterValue read(Address address, PrivilegeLevel current_priv) const;\n\n        /**\n         * Read CSR register with an internal id.\n         *\n         * Internal id can be obtained from enum Id and works as an index to compacted table\n         * of existing CSR registers.\n         */\n        [[nodiscard]] RegisterValue read_internal(size_t internal_id) const;\n\n        /** Write value to CSR register by ISA specified address and receive the previous value. */\n        void write(Address address, RegisterValue value, PrivilegeLevel current_priv);\n\n        /** Used for writes occurring as a side-effect (instruction count update...) and\n         * internally by the write method. */\n        void write_internal(size_t internal_id, RegisterValue value);\n\n        /** Shorthand for counter incrementing. Counters like time might have different increment\n         * amount. */\n        void increment_internal(size_t internal_id, uint64_t amount);\n\n        /** Reset data to initial values */\n        void reset();\n\n        /** Read CSR register field */\n        RegisterValue read_field(const RegisterFieldDesc &field_desc) const {\n            return field_desc.decode(read_internal(field_desc.regId).as_u64());\n        }\n\n        /** Write CSR register field */\n        void write_field(const RegisterFieldDesc &field_desc, uint64_t value) {\n            uint64_t u = read_internal(field_desc.regId).as_u64();\n            u = field_desc.update(u, value);\n            write_internal(field_desc.regId, u);\n        }\n\n        void update_exception_cause(enum ExceptionCause excause);\n\n        bool operator==(const ControlState &other) const;\n        bool operator!=(const ControlState &c) const;\n\n        bool core_interrupt_request();\n        machine::Address exception_pc_address();\n\n    signals:\n        void write_signal(size_t internal_reg_id, RegisterValue val);\n        void read_signal(size_t internal_reg_id, RegisterValue val) const;\n\n    public slots:\n        void set_interrupt_signal(uint irq_num, bool active);\n        void exception_initiate(PrivilegeLevel act_privlev, PrivilegeLevel to_privlev);\n        PrivilegeLevel\n        exception_return(enum PrivilegeLevel act_privlev, enum PrivilegeLevel xret_privlev);\n\n    private:\n        static size_t get_register_internal_id(Address address);\n\n        /** Write CSR register field without write handler, read-only masking and signal */\n        void write_field_raw(const RegisterFieldDesc &field_desc, uint64_t value) {\n            uint64_t u = register_data[field_desc.regId].as_u64();\n            u = field_desc.update(u, value);\n            register_data[field_desc.regId] = u;\n        }\n\n        Xlen xlen = Xlen::_32; // TODO\n\n        /**\n         * Compacted table of existing CSR registers data. Each item is described by table\n         * REGISTERS at corresponding indexes.\n         */\n        std::array<RegisterValue, Id::_COUNT> register_data;\n\n    public:\n        void\n        default_wlrl_write_handler(const RegisterDesc &desc, RegisterValue &reg, RegisterValue val);\n        void\n        mstatus_wlrl_write_handler(const RegisterDesc &desc, RegisterValue &reg, RegisterValue val);\n        void\n        mcycle_wlrl_write_handler(const RegisterDesc &desc, RegisterValue &reg, RegisterValue val);\n        void\n        sstatus_wlrl_write_handler(const RegisterDesc &desc, RegisterValue &reg, RegisterValue val);\n    };\n\n    struct RegisterDesc {\n        using WriteHandlerFn = void (\n            ControlState::*)(const RegisterDesc &desc, RegisterValue &reg, RegisterValue val);\n\n        const char *name = \"unknown\";\n        Address address = Address(0);\n        const char *description = \"\";\n        RegisterValue initial_value = 0;\n        RegisterValue write_mask = (register_storage_t)0xffffffffffffffff;\n        WriteHandlerFn write_handler = &ControlState::default_wlrl_write_handler;\n        struct {\n            const RegisterFieldDesc *const *array;\n            const unsigned count;\n        } fields = { nullptr, 0 };\n    };\n\n    namespace Field {\n        namespace mstatus {\n            static constexpr RegisterFieldDesc SIE\n                = { \"SIE\", Id::MSTATUS, { 1, 1 }, \"System global interrupt-enable\" };\n            static constexpr RegisterFieldDesc MIE\n                = { \"MIE\", Id::MSTATUS, { 1, 3 }, \"Machine global interrupt-enable\" };\n            static constexpr RegisterFieldDesc SPIE\n                = { \"SPIE\", Id::MSTATUS, { 1, 5 }, \"Previous SIE before the trap\" };\n            static constexpr RegisterFieldDesc MPIE\n                = { \"MPIE\", Id::MSTATUS, { 1, 7 }, \"Previous MIE before the trap\" };\n            static constexpr RegisterFieldDesc MPRV\n                = { \"MPRV\", Id::MSTATUS, { 1, 17 }, \"Modify privilege for loads/stores/fetches\" };\n            static constexpr RegisterFieldDesc SPP\n                = { \"SPP\", Id::MSTATUS, { 1, 8 }, \"System previous privilege mode\" };\n            static constexpr RegisterFieldDesc MPP\n                = { \"MPP\", Id::MSTATUS, { 2, 11 }, \"Machine previous privilege mode\" };\n            static constexpr RegisterFieldDesc UXL\n                = { \"UXL\", Id::MSTATUS, { 2, 32 }, \"User mode XLEN (RV64 only)\" };\n            static constexpr RegisterFieldDesc SXL\n                = { \"SXL\", Id::MSTATUS, { 2, 34 }, \"Supervisor mode XLEN (RV64 only)\" };\n            static constexpr const RegisterFieldDesc *fields[]\n                = { &SIE, &MIE, &SPIE, &MPIE, &SPP, &MPP, &UXL, &SXL };\n            static constexpr unsigned count = sizeof(fields) / sizeof(fields[0]);\n        } // namespace mstatus\n        namespace satp {\n            static constexpr RegisterFieldDesc MODE\n                = { \"MODE\", Id::SATP, { 1, 31 }, \"Address translation mode\" };\n            static constexpr RegisterFieldDesc ASID\n                = { \"ASID\", Id::SATP, { 9, 22 }, \"Address-space ID\" };\n            static constexpr RegisterFieldDesc PPN\n                = { \"PPN\", Id::SATP, { 22, 0 }, \"Root page-table physical page number\" };\n            static constexpr const RegisterFieldDesc *fields[] = { &MODE, &ASID, &PPN };\n            static constexpr unsigned count = sizeof(fields) / sizeof(fields[0]);\n        } // namespace satp\n    } // namespace Field\n\n    /** Definitions of supported CSR registers */\n    inline constexpr std::array<RegisterDesc, Id::_COUNT> REGISTERS {\n        { // Unprivileged Counter/Timers\n          [Id::CYCLE] = { \"cycle\", 0xC00_csr, \"Cycle counter for RDCYCLE instruction.\", 0, 0 },\n          // Priviledged Machine Mode Registers\n          [Id::MVENDORID] = { \"mvendorid\", 0xF11_csr, \"Vendor ID.\", 0, 0 },\n          [Id::MARCHID] = { \"marchid\", 0xF12_csr, \"Architecture ID.\", 0, 0 },\n          [Id::MIMPID] = { \"mimpid\", 0xF13_csr, \"Implementation ID.\", 0, 0 },\n          [Id::MHARTID] = { \"mhardid\", 0xF14_csr, \"Hardware thread ID.\" },\n          [Id::MSTATUS] = { \"mstatus\",\n                            0x300_csr,\n                            \"Machine status register.\",\n                            0,\n                            0x007FFFEA,\n                            &ControlState::mstatus_wlrl_write_handler,\n                            { Field::mstatus::fields, Field::mstatus::count } },\n          [Id::MISA] = { \"misa\", 0x301_csr, \"Machine ISA Register.\", 0, 0 },\n          [Id::MIE] = { \"mie\", 0x304_csr, \"Machine interrupt-enable register.\", 0, 0x00ff0AAA },\n          [Id::MTVEC] = { \"mtvec\", 0x305_csr, \"Machine trap-handler base address.\" },\n          [Id::MSCRATCH] = { \"mscratch\", 0x340_csr, \"Scratch register for machine trap handlers.\" },\n          [Id::MEPC] = { \"mepc\", 0x341_csr, \"Machine exception program counter.\" },\n          [Id::MCAUSE] = { \"mcause\", 0x342_csr, \"Machine trap cause.\" },\n          [Id::MTVAL] = { \"mtval\", 0x343_csr, \"Machine bad address or instruction.\" },\n          [Id::MIP] = { \"mip\", 0x344_csr, \"Machine interrupt pending.\", 0, 0x00000222 },\n          [Id::MTINST] = { \"mtinst\", 0x34A_csr, \"Machine trap instruction (transformed).\" },\n          [Id::MTVAL2] = { \"mtval2\", 0x34B_csr, \"Machine bad guest physical address.\" },\n          // Machine Counter/Timers\n          [Id::MCYCLE]\n          = { \"mcycle\", 0xB00_csr, \"Machine cycle counter.\", 0,\n              (register_storage_t)0xffffffffffffffff, &ControlState::mcycle_wlrl_write_handler },\n          [Id::MINSTRET] = { \"minstret\", 0xB02_csr, \"Machine instructions-retired counter.\" },\n          // Supervisor-level CSRs\n          [Id::SSTATUS] = { \"sstatus\", 0x100_csr, \"Supervisor status register.\", 0, 0xffffffff,\n                            &ControlState::sstatus_wlrl_write_handler },\n          [Id::STVEC] = { \"stvec\", 0x105_csr, \"Supervisor trap-handler base address.\" },\n          [Id::SSCRATCH]\n          = { \"sscratch\", 0x140_csr, \"Scratch register for supervisor trap handlers.\" },\n          [Id::SEPC] = { \"sepc\", 0x141_csr, \"Supervisor exception program counter.\" },\n          [Id::SCAUSE] = { \"scause\", 0x142_csr, \"Supervisor trap cause.\" },\n          [Id::STVAL] = { \"stval\", 0x143_csr, \"Supervisor bad address or instruction.\" },\n          [Id::SATP] = { \"satp\",\n                         0x180_csr,\n                         \"Supervisor address translation and protection\",\n                         0,\n                         0xffffffff,\n                         &ControlState::default_wlrl_write_handler,\n                         { Field::satp::fields, Field::satp::count } } }\n    };\n\n    /** Lookup from CSR address (value used in instruction) to internal id (index in continuous\n     * memory) */\n    class RegisterMap {\n        bool initialized = false;\n        std::unordered_map<Address, size_t> map;\n\n        void init() {\n            for (size_t i = 0; i < REGISTERS.size(); i++) {\n                map.emplace(REGISTERS[i].address, i);\n            }\n            initialized = true;\n        }\n\n    public:\n        size_t at(Address address) {\n            if (!initialized) init();\n            return map.at(address);\n        }\n    };\n\n    class RegisterMapByName {\n        bool initialized = false;\n        std::unordered_map<std::string, size_t> map;\n\n        void init() {\n            for (size_t i = 0; i < REGISTERS.size(); i++) {\n                map.emplace(std::string(REGISTERS[i].name), i);\n            }\n            initialized = true;\n        }\n\n    public:\n        size_t at(std::string name) {\n            if (!initialized) init();\n            return map.at(name);\n        }\n    };\n\n    static RegisterMap REGISTER_MAP;\n    static RegisterMapByName REGISTER_MAP_BY_NAME;\n}} // namespace machine::CSR\n\nQ_DECLARE_METATYPE(machine::CSR::ControlState)\n\n#endif // CONTROLSTATE_H\n"
  },
  {
    "path": "src/machine/execute/alu.cpp",
    "content": "#include \"alu.h\"\n\n#include \"common/polyfills/mulh64.h\"\n\nnamespace machine {\n\nRegisterValue alu_combined_operate(\n    AluCombinedOp op,\n    AluComponent component,\n    bool w_operation,\n    bool modified,\n    RegisterValue a,\n    RegisterValue b) {\n    switch (component) {\n    case AluComponent::ALU:\n        return (w_operation) ? alu32_operate(op.alu_op, modified, a, b)\n                             : alu64_operate(op.alu_op, modified, a, b);\n    case AluComponent::MUL:\n        return (w_operation) ? mul32_operate(op.mul_op, a, b) : mul64_operate(op.mul_op, a, b);\n    case AluComponent::PASS: return a;\n    default: qDebug(\"ERROR, unknown alu component: %hhx\", uint8_t(component)); return 0;\n    }\n}\n\n/**\n * Shift operations are limited to shift by 31 bits.\n * Other bits of the operand may be used as flags and need to be masked out\n * before any ALU operation is performed.\n */\nconstexpr uint64_t SHIFT_MASK32 = 0b011111; // == 31\nconstexpr uint64_t SHIFT_MASK64 = 0b111111; // == 63\n\nint64_t alu64_operate(AluOp op, bool modified, RegisterValue a, RegisterValue b) {\n    uint64_t _a = a.as_u64();\n    uint64_t _b = b.as_u64();\n\n    switch (op) {\n    case AluOp::ADD: return _a + ((modified) ? -_b : _b);\n    case AluOp::SLL: return _a << (_b & SHIFT_MASK64);\n    case AluOp::SLT: return a.as_i64() < b.as_i64();\n    case AluOp::SLTU: return _a < _b;\n    case AluOp::XOR:\n        return _a ^ _b;\n        // Most compilers should calculate SRA correctly, but it is UB.\n    case AluOp::SR:\n        return (modified) ? (a.as_i64() >> (_b & SHIFT_MASK64)) : (_a >> (_b & SHIFT_MASK64));\n    case AluOp::OR: return _a | _b;\n    case AluOp::AND:\n        return ((modified) ? ~_a : _a) & _b; // Modified: clear bits of b using mask\n                                             // in a\n    default: qDebug(\"ERROR, unknown alu operation: %hhx\", uint8_t(op)); return 0;\n    }\n}\n\nint32_t alu32_operate(AluOp op, bool modified, RegisterValue a, RegisterValue b) {\n    uint32_t _a = a.as_u32();\n    uint32_t _b = b.as_u32();\n\n    switch (op) {\n    case AluOp::ADD: return _a + ((modified) ? -_b : _b);\n    case AluOp::SLL: return _a << (_b & SHIFT_MASK32);\n    case AluOp::SLT: return a.as_i32() < b.as_i32();\n    case AluOp::SLTU: return _a < _b;\n    case AluOp::XOR:\n        return _a ^ _b;\n        // Most compilers should calculate SRA correctly, but it is UB.\n    case AluOp::SR:\n        return (modified) ? (a.as_i32() >> (_b & SHIFT_MASK32)) : (_a >> (_b & SHIFT_MASK32));\n    case AluOp::OR: return _a | _b;\n    case AluOp::AND:\n        return ((modified) ? ~_a : _a) & _b; // Modified: clear bits of b using mask in a\n    default: qDebug(\"ERROR, unknown alu operation: %hhx\", uint8_t(op)); return 0;\n    }\n}\n\nint64_t mul64_operate(MulOp op, RegisterValue a, RegisterValue b) {\n    switch (op) {\n    case MulOp::MUL: return a.as_u64() * b.as_u64();\n    case MulOp::MULH: return mulh64(a.as_i64(), b.as_i64());\n    case MulOp::MULHSU: return mulhsu64(a.as_i64(), b.as_u64());\n    case MulOp::MULHU: return mulhu64(a.as_u64(), b.as_u64());\n    case MulOp::DIV:\n        if (b.as_i64() == 0) {\n            return -1; // Division by zero is defined.\n        } else if (a.as_i64() == INT64_MIN && b.as_i64() == -1) {\n            return INT64_MIN; // Overflow.\n        } else {\n            return a.as_i64() / b.as_i64();\n        }\n    case MulOp::DIVU:\n        return (b.as_u64() == 0) ? UINT64_MAX // Division by zero is defined.\n                                 : a.as_u64() / b.as_u64();\n    case MulOp::REM:\n        if (b.as_i64() == 0) {\n            return a.as_i64(); // Division by zero is defined.\n        } else if (a.as_i64() == INT64_MIN && b.as_i64() == -1) {\n            return 0; // Overflow.\n        } else {\n            return a.as_i64() % b.as_i64();\n        }\n    case MulOp::REMU:\n        return (b.as_u64() == 0) ? a.as_u64() // Division by zero reminder\n                                              // is defined.\n                                 : a.as_u64() % b.as_u64();\n    default: qDebug(\"ERROR, unknown multiplication operation: %hhx\", uint8_t(op)); return 0;\n    }\n}\n\nint32_t mul32_operate(MulOp op, RegisterValue a, RegisterValue b) {\n    switch (op) {\n    case MulOp::MUL: return a.as_u32() * b.as_u32();\n    case MulOp::MULH: return ((uint64_t)a.as_i32() * (uint64_t)b.as_i32()) >> 32;\n    case MulOp::MULHSU: return ((uint64_t)a.as_i32() * (uint64_t)b.as_u32()) >> 32;\n    case MulOp::MULHU: return ((uint64_t)a.as_u32() * (uint64_t)b.as_u32()) >> 32;\n    case MulOp::DIV:\n        if (b.as_i32() == 0) {\n            return -1; // Division by zero is defined.\n        } else if (a.as_i32() == INT32_MIN && b.as_i32() == -1) {\n            return INT32_MIN; // Overflow.\n        } else {\n            return a.as_i32() / b.as_i32();\n        }\n    case MulOp::DIVU:\n        return (b.as_u32() == 0) ? UINT32_MAX // Division by zero is defined.\n                                 : a.as_u32() / b.as_u32();\n    case MulOp::REM:\n        if (b.as_i32() == 0) {\n            return a.as_i32(); // Division by zero is defined.\n        } else if (a.as_i32() == INT32_MIN && b.as_i32() == -1) {\n            return 0; // Overflow.\n        } else {\n            return a.as_i32() % b.as_i32();\n        }\n    case MulOp::REMU:\n        return (b.as_u32() == 0) ? a.as_u32() // Division by zero reminder\n                                              // is defined.\n                                 : a.as_u32() % b.as_u32();\n    default: qDebug(\"ERROR, unknown multiplication operation: %hhx\", uint8_t(op)); return 0;\n    }\n}\n\n} // namespace machine\n"
  },
  {
    "path": "src/machine/execute/alu.h",
    "content": "#ifndef ALU_H\n#define ALU_H\n\n#include \"execute/alu_op.h\"\n#include \"execute/mul_op.h\"\n#include \"register_value.h\"\n\n#include <cstdint>\n\nnamespace machine {\n\n/**\n * Components available in combined ALU.\n */\nenum class AluComponent {\n    ALU,  //> RV32/64I\n    MUL,  //> RV32/64M\n    PASS, //> Pass operand A without change (used for AMO)\n};\n\nunion AluCombinedOp {\n    AluOp alu_op;\n    MulOp mul_op;\n};\n\n/**\n * Dispatcher for specialised ALUs\n *\n * @param op          alu and mul operands are isomorphic\n * @param component   specifies which specialization to use\n * @param w_operation word operation false=64b, true=32b\n * @param modified    see alu64/32\n * @param a           operand 1\n * @param b           operand 2\n * @return            result of specified ALU operation (always, no traps)\n */\n[[gnu::const]] RegisterValue alu_combined_operate(\n    AluCombinedOp op,\n    AluComponent component,\n    bool w_operation,\n    bool modified,\n    RegisterValue a,\n    RegisterValue b);\n\n/**\n * RV64I for OP and OP-IMM instructions\n *\n * ALU conforming to Base Integer Instruction Set, Version 2.0.\n *\n * @param op        operation specifier (funct3 in instruction)\n * @param modified  modifies behavior of ADD (to SUB) and SRL (to SRA)\n *                  encoded by bit 30 if applicable\n * @param a         operand 1\n * @param b         operand 2\n * @return          result of specified ALU operation (always, no traps)\n *                  integer type is returned to ensure correct signe extension\n *                  to arbitrary implementation of RegisterValue\n */\n[[gnu::const]] int64_t alu64_operate(AluOp op, bool modified, RegisterValue a, RegisterValue b);\n\n/**\n * RV32I for OP and OP-IMM instructions and RV64I OP-32 and OP-IMM-32\n *\n * ALU conforming to Base Integer Instruction Set, Version 2.0.\n *\n * @param op        operation specifier (funct3 in instruction)\n * @param modified  modifies behavior of ADD (to SUB) and SRL (to SRA)\n *                  encoded by bit 30 if applicable\n * @param a         operand 1\n * @param b         operand 2\n * @return          result of specified ALU operation (always, no traps)\n *                  integer type is returned to ensure correct signe extension\n *                  to arbitrary implementation of RegisterValue\n */\n[[gnu::const]] int32_t alu32_operate(AluOp op, bool modified, RegisterValue a, RegisterValue b);\n\n/**\n * RV64 \"M\" for OP instructions\n *\n * Multiplier conforming to Standard Extension for Integer Multiplication and\n * Division, Version 2.0.\n *\n * Implements operation for instructions: MUL, MUL[[S]H], DIV[U], REM[U].\n * Division by zero is defined §7.2 table 7.1 (or see implementation).\n *\n * @param op  operation specifier (funct3 in the instruction)\n * @param a   operand 1\n * @param b   operand 2\n * @return    result of specified operation (always, no traps)\n *            integer type is returned to ensure correct signe extension\n *            to arbitrary implementation of RegisterValue\n */\n[[gnu::const]] int64_t mul64_operate(MulOp op, RegisterValue a, RegisterValue b);\n\n/**\n * RV32 \"M\" for OP instructions and RV64 \"M\" for OP-32 instructions\n *\n * Multiplier conforming to Standard Extension for Integer Multiplication and\n * Division, Version 2.0.\n *\n * Implements operation for instructions:\n *    RV32: MUL, MUL[[S]H]W, DIV[U]W, REM[U]W.\n *    RV64: MULW, MUL[[S]H]W, DIV[U], REM[U].\n *\n * Division by zero is defined §7.2 table 7.1 (or see implementation).\n *\n * @param op  operation specifier (funct3 in the instruction)\n * @param a   operand 1\n * @param b   operand 2\n * @return    result of specified operation (always, no traps)\n *            integer type is returned to ensure correct signe extension\n *            to arbitrary implementation of RegisterValue\n */\n[[gnu::const]] int32_t mul32_operate(MulOp op, RegisterValue a, RegisterValue b);\n\n} // namespace machine\n\n#endif // ALU_H\n"
  },
  {
    "path": "src/machine/execute/alu.test.cpp",
    "content": "#include \"alu.h\"\n\n#include \"alu.test.h\"\n#include \"common/polyfills/mulh64.h\"\n\n#include <array>\n#include <tuple>\n\nusing namespace machine;\n\nvoid TestAlu::test_alu64_operate_data() {\n    QTest::addColumn<AluOp>(\"op\");\n    QTest::addColumn<bool>(\"modified\");\n    QTest::addColumn<int64_t>(\"operand_a\");\n    QTest::addColumn<int64_t>(\"operand_b\");\n    QTest::addColumn<int64_t>(\"result\");\n\n    QTest::addRow(\"ADD\") << AluOp::ADD << false << int64_t(0xFFFFFFFFFFFFFFFFULL) << int64_t(1)\n                         << int64_t(0);\n    QTest::addRow(\"ADD\") << AluOp::ADD << false << int64_t(123) << int64_t(123000)\n                         << int64_t(123123);\n    QTest::addRow(\"SUB\") << AluOp::ADD << true << int64_t(123123) << int64_t(123000)\n                         << int64_t(123);\n    QTest::addRow(\"SLT\") << AluOp::SLT << false << int64_t(123123) << int64_t(123000) << int64_t(0);\n    QTest::addRow(\"SLT\") << AluOp::SLT << false << int64_t(-123123) << int64_t(123000)\n                         << int64_t(1);\n    QTest::addRow(\"SLTU\") << AluOp::SLTU << false << int64_t(-123123) << int64_t(123000)\n                          << int64_t(0);\n    QTest::addRow(\"SLTU\") << AluOp::SLTU << false << int64_t(123123) << int64_t(123000000)\n                          << int64_t(1);\n\n    QTest::addRow(\"XOR\") << AluOp::XOR << false << int64_t(0xFFFFFFFF00000000ULL)\n                         << int64_t(0xFFFFFFFFFFFFFFFFULL) << int64_t(0x00000000FFFFFFFFULL);\n    QTest::addRow(\"OR\") << AluOp::OR << false << int64_t(0xFFFFFFFF00000000ULL)\n                        << int64_t(0xFFFFFFFFFFFFFFFFULL) << int64_t(0xFFFFFFFFFFFFFFFFULL);\n    QTest::addRow(\"AND\") << AluOp::AND << false << int64_t(0xFFFFFFFF00000000ULL)\n                         << int64_t(0xFFFFFFFFFFFFFFFFULL) << int64_t(0xFFFFFFFF00000000ULL);\n    QTest::addRow(\"SRL\") << AluOp::SR << false << int64_t(0xFFFFFFFF00000000ULL)\n                         << int64_t(0xFFFFFFFF00000010ULL) << int64_t(0x0000FFFFFFFF0000ULL);\n    QTest::addRow(\"SRA\") << AluOp::SR << true << int64_t(0xFFFFFFFF00000000ULL)\n                         << int64_t(0xFFFFFFFF00000010ULL) << int64_t(0xFFFFFFFFFFFF0000ULL);\n}\n\nvoid TestAlu::test_alu64_operate() {\n    QFETCH(AluOp, op);\n    QFETCH(bool, modified);\n    QFETCH(int64_t, operand_a);\n    QFETCH(int64_t, operand_b);\n    QFETCH(int64_t, result);\n\n    // Test unit itself.\n    QCOMPARE(alu64_operate(op, modified, operand_a, operand_b), result);\n    // Test that combined wrapper does not break anything.\n    QCOMPARE(\n        alu_combined_operate(\n            { .alu_op = op }, AluComponent::ALU, false, modified, operand_a, operand_b),\n        RegisterValue(result));\n}\n\nvoid TestAlu::test_alu32_operate_data() {\n    QTest::addColumn<AluOp>(\"op\");\n    QTest::addColumn<bool>(\"modified\");\n    QTest::addColumn<int32_t>(\"operand_a\");\n    QTest::addColumn<int32_t>(\"operand_b\");\n    QTest::addColumn<int32_t>(\"result\");\n\n    QTest::addRow(\"ADD\") << AluOp::ADD << false << int32_t(0xFFFFFFFF) << int32_t(1) << int32_t(0);\n    QTest::addRow(\"ADD\") << AluOp::ADD << false << int32_t(123) << int32_t(123000)\n                         << int32_t(123123);\n    QTest::addRow(\"SUB\") << AluOp::ADD << true << int32_t(123123) << int32_t(123000)\n                         << int32_t(123);\n    QTest::addRow(\"SLT\") << AluOp::SLT << false << int32_t(123123) << int32_t(123000) << int32_t(0);\n    QTest::addRow(\"SLT\") << AluOp::SLT << false << int32_t(-123123) << int32_t(123000)\n                         << int32_t(1);\n    QTest::addRow(\"SLTU\") << AluOp::SLTU << false << int32_t(-123123) << int32_t(123000)\n                          << int32_t(0);\n    QTest::addRow(\"SLTU\") << AluOp::SLTU << false << int32_t(123123) << int32_t(123000000)\n                          << int32_t(1);\n    QTest::addRow(\"XOR\") << AluOp::XOR << false << int32_t(0xFFFF0000) << int32_t(0xFFFFFFFF)\n                         << int32_t(0x0000FFFF);\n    QTest::addRow(\"OR\") << AluOp::OR << false << int32_t(0xFFFF0000) << int32_t(0xFFFFFFFF)\n                        << int32_t(0xFFFFFFFF);\n    QTest::addRow(\"AND\") << AluOp::AND << false << int32_t(0xFFFF0000) << int32_t(0xFFFFFFFF)\n                         << int32_t(0xFFFF0000);\n    QTest::addRow(\"SRL\") << AluOp::SR << false << int32_t(0xFFFF0000) << int32_t(0xFFFF0010)\n                         << int32_t(0x0000FFFF);\n    QTest::addRow(\"SRA\") << AluOp::SR << true << int32_t(0xFFFF0000) << int32_t(0xFFFF0010)\n                         << int32_t(0xFFFFFFFF);\n}\n\nvoid TestAlu::test_alu32_operate() {\n    QFETCH(AluOp, op);\n    QFETCH(bool, modified);\n    QFETCH(int32_t, operand_a);\n    QFETCH(int32_t, operand_b);\n    QFETCH(int32_t, result);\n\n    // Test unit itself.\n    QCOMPARE(alu32_operate(op, modified, operand_a, operand_b), result);\n    // Test that combined wrapper does not break anything.\n    QCOMPARE(\n        alu_combined_operate(\n            { .alu_op = op }, AluComponent::ALU, true, modified, operand_a, operand_b),\n        RegisterValue(result));\n}\n\n// TODO evaluate the results and inline as literals.\nconstexpr std::array<std::tuple<int64_t, int64_t>, 6> inputs = { {\n    { 1, 1 },\n    { 2, 1 },\n    { 2, 2 },\n    { 123456789, 666 },\n    { 123456789, -7777 },\n    { -1, 8888 },\n} };\n\nvoid TestAlu::test_mul64_operate_data() {\n    QTest::addColumn<MulOp>(\"op\");\n    QTest::addColumn<int64_t>(\"operand_a\");\n    QTest::addColumn<int64_t>(\"operand_b\");\n    QTest::addColumn<int64_t>(\"result\");\n\n    for (auto input : inputs) {\n        int64_t a = std::get<0>(input);\n        int64_t b = std::get<1>(input);\n\n        QTest::addRow(\"MUL\") << MulOp::MUL << a << b << a * b;\n        QTest::addRow(\"MULH\") << MulOp::MULH << a << b << int64_t(mulh64(a, b));\n        QTest::addRow(\"MULHU\") << MulOp::MULHU << a << b << int64_t(mulhu64(a, b));\n        QTest::addRow(\"MULHSU\") << MulOp::MULHSU << a << b << int64_t(mulhsu64(a, b));\n        QTest::addRow(\"DIV\") << MulOp::DIV << a << b << a / b;\n        QTest::addRow(\"DIVU\") << MulOp::DIVU << a << b << int64_t(uint64_t(a) / uint64_t(b));\n        QTest::addRow(\"REM\") << MulOp::REM << a << b << (a % b);\n        QTest::addRow(\"REMU\") << MulOp::REMU << a << b << int64_t(uint64_t(a) % uint64_t(b));\n    }\n\n    // Defined edge cases for division\n\n    QTest::addRow(\"division by zero signed\")\n        << MulOp::DIV << int64_t(42) << int64_t(0) << int64_t(-1);\n    QTest::addRow(\"division by zero unsigned\")\n        << MulOp::DIVU << int64_t(128) << int64_t(0) << int64_t(UINT64_MAX);\n    QTest::addRow(\"division by zero reminder signed\")\n        << MulOp::REM << int64_t(666) << int64_t(0) << int64_t(666);\n    QTest::addRow(\"division by zero reminder unsigned\")\n        << MulOp::REMU << int64_t(777) << int64_t(0) << int64_t(777);\n    QTest::addRow(\"zero division by zero signed\")\n        << MulOp::DIV << int64_t(0) << int64_t(0) << int64_t(-1);\n    QTest::addRow(\"zero division by zero unsigned\")\n        << MulOp::DIVU << int64_t(0) << int64_t(0) << int64_t(UINT64_MAX);\n    QTest::addRow(\"zero division by zero reminder signed\")\n        << MulOp::REM << int64_t(0) << int64_t(0) << (int64_t)0;\n    QTest::addRow(\"zero division by zero reminder unsigned\")\n        << MulOp::REMU << int64_t(0) << int64_t(0) << (int64_t)0;\n    QTest::addRow(\"division overflow\")\n        << MulOp::DIV << int64_t(INT64_MIN) << int64_t(-1) << INT64_MIN;\n    QTest::addRow(\"division reminder overflow\")\n        << MulOp::REM << int64_t(INT64_MAX) << int64_t(-1) << int64_t(0);\n}\n\nvoid TestAlu::test_mul64_operate() {\n    QFETCH(MulOp, op);\n    QFETCH(int64_t, operand_a);\n    QFETCH(int64_t, operand_b);\n    QFETCH(int64_t, result);\n\n    // Test unit itself.\n    QCOMPARE(mul64_operate(op, operand_a, operand_b), result);\n    // Test that combined wrapper does not break anything.\n    QCOMPARE(\n        alu_combined_operate(\n            (AluCombinedOp) { .mul_op = op }, AluComponent::MUL, false, false, operand_a,\n            operand_b),\n        RegisterValue(result));\n}\n\n/**\n * Helper function for upper bits of 32 bit multiplication.\n * Sign extension is handled on caller side.\n */\nconstexpr int32_t mulh32(uint64_t a, uint64_t b) {\n    return (a * b) >> 32;\n}\n\nvoid TestAlu::test_mul32_operate_data() {\n    QTest::addColumn<MulOp>(\"op\");\n    QTest::addColumn<int32_t>(\"operand_a\");\n    QTest::addColumn<int32_t>(\"operand_b\");\n    QTest::addColumn<int32_t>(\"result\");\n\n    for (auto input : inputs) {\n        int32_t a = std::get<0>(input);\n        int32_t b = std::get<1>(input);\n\n        QTest::addRow(\"MUL\") << MulOp::MUL << a << b << a * b;\n        QTest::addRow(\"MULH\") << MulOp::MULH << a << b << int32_t(mulh32(a, b));\n        QTest::addRow(\"MULHU\") << MulOp::MULHU << a << b\n                               << int32_t(mulh32(uint32_t(a), uint32_t(b)));\n        QTest::addRow(\"MULHSU\") << MulOp::MULHSU << a << b << int32_t(mulh32(a, uint32_t(b)));\n        QTest::addRow(\"DIV\") << MulOp::DIV << a << b << a / b;\n        QTest::addRow(\"DIVU\") << MulOp::DIVU << a << b << int32_t(uint32_t(a) / uint32_t(b));\n        QTest::addRow(\"REM\") << MulOp::REM << a << b << (a % b);\n        QTest::addRow(\"REMU\") << MulOp::REMU << a << b << int32_t(uint32_t(a) % uint32_t(b));\n    }\n\n    // Defined edge cases for division\n\n    QTest::addRow(\"division by zero signed\")\n        << MulOp::DIV << int32_t(42) << int32_t(0) << int32_t(-1);\n    QTest::addRow(\"division by zero unsigned\")\n        << MulOp::DIVU << int32_t(128) << int32_t(0) << int32_t(UINT32_MAX);\n    QTest::addRow(\"division by zero reminder signed\")\n        << MulOp::REM << int32_t(666) << int32_t(0) << int32_t(666);\n    QTest::addRow(\"division by zero reminder unsigned\")\n        << MulOp::REMU << int32_t(777) << int32_t(0) << int32_t(777);\n    QTest::addRow(\"zero division by zero signed\")\n        << MulOp::DIV << int32_t(0) << int32_t(0) << int32_t(-1);\n    QTest::addRow(\"zero division by zero unsigned\")\n        << MulOp::DIVU << int32_t(0) << int32_t(0) << int32_t(UINT32_MAX);\n    QTest::addRow(\"zero division by zero reminder signed\")\n        << MulOp::REM << int32_t(0) << int32_t(0) << (int32_t)0;\n    QTest::addRow(\"zero division by zero reminder unsigned\")\n        << MulOp::REMU << int32_t(0) << int32_t(0) << (int32_t)0;\n    QTest::addRow(\"division overflow\")\n        << MulOp::DIV << int32_t(INT32_MIN) << int32_t(-1) << INT32_MIN;\n    QTest::addRow(\"division reminder overflow\")\n        << MulOp::REM << int32_t(INT32_MAX) << int32_t(-1) << int32_t(0);\n}\nvoid TestAlu::test_mul32_operate() {\n    QFETCH(MulOp, op);\n    QFETCH(int32_t, operand_a);\n    QFETCH(int32_t, operand_b);\n    QFETCH(int32_t, result);\n\n    // Test unit itself.\n    QCOMPARE(mul32_operate(op, operand_a, operand_b), result);\n    // Test that combined wrapper does not break anything.\n    QCOMPARE(\n        alu_combined_operate(\n            (AluCombinedOp) { .mul_op = op }, AluComponent::MUL, true, false, operand_a, operand_b),\n        RegisterValue(result));\n}\n\nQTEST_APPLESS_MAIN(TestAlu)"
  },
  {
    "path": "src/machine/execute/alu.test.h",
    "content": "#ifndef ALU_TEST_H\n#define ALU_TEST_H\n\n#include <QtTest>\n\nclass TestAlu : public QObject {\n    Q_OBJECT\nprivate slots:\n    static void test_alu64_operate_data();\n    static void test_alu64_operate();\n    static void test_alu32_operate_data();\n    static void test_alu32_operate();\n    static void test_mul64_operate_data();\n    static void test_mul64_operate();\n    static void test_mul32_operate_data();\n    static void test_mul32_operate();\n};\n\n#endif // ALU_TEST_H\n"
  },
  {
    "path": "src/machine/execute/alu_op.h",
    "content": "#ifndef ALU_OP_H\n#define ALU_OP_H\n\n#include <QMetaType>\n#include <cstdint>\nusing std::uint8_t;\n\nnamespace machine {\n\nenum class AluOp : uint8_t {\n    ADD = 0b000,\n    SLL = 0b001,\n    SLT = 0b010,\n    SLTU = 0b011,\n    XOR = 0b100,\n    SR = 0b101,\n    OR = 0b110,\n    AND = 0b111,\n};\n\n}\n\nQ_DECLARE_METATYPE(machine::AluOp)\n\n#endif // ALU_OP_H\n"
  },
  {
    "path": "src/machine/execute/mul_op.h",
    "content": "#ifndef MUL_OP_H\n#define MUL_OP_H\n\n#include <QMetaType>\n\nnamespace machine {\n\nenum class MulOp : uint8_t {\n    MUL = 0b000,\n    MULH = 0b001,\n    MULHSU = 0b010,\n    MULHU = 0b011,\n    DIV = 0b100,\n    DIVU = 0b101,\n    REM = 0b110,\n    REMU = 0b111,\n};\n\n}\n\nQ_DECLARE_METATYPE(machine::MulOp)\n\n#endif // MUL_OP_H\n"
  },
  {
    "path": "src/machine/instruction.cpp",
    "content": "#include \"instruction.h\"\n\n#include \"common/logging.h\"\n#include \"common/math/bit_ops.h\"\n#include \"common/string_utils.h\"\n#include \"csr/controlstate.h\"\n#include \"simulator_exception.h\"\n#include \"utils.h\"\n\n#include <QChar>\n#include <QMultiMap>\n#include <cctype>\n#include <cinttypes>\n#include <cstring>\n#include <set>\n#include <type_traits>\n#include <utility>\n\nLOG_CATEGORY(\"machine.instruction\");\n\nusing namespace machine;\nusing std::underlying_type;\n\nnamespace machine {\n}\n\nstruct ArgumentDesc {\n    char name;\n    /**\n     * Possible values:\n     *  @val g: gp register id\n     *  @val n: numeric immediate\n     *  @val a: pc relative address offset\n     *  @val b: pc relative address offset\n     *  @val o: offset immediate\n     */\n    char kind;\n    int64_t min;\n    int64_t max;\n    BitArg arg;\n    inline ArgumentDesc(char name, char kind, int64_t min, int64_t max, BitArg arg)\n        : name(name)\n        , kind(kind)\n        , min(min)\n        , max(max)\n        , arg(arg) {}\n\n    /** Check whether given value fits into this instruction field. */\n    [[nodiscard]] constexpr bool is_value_in_field_range(RegisterValue val) const {\n        if (min < 0) {\n            return val.as_i64() <= max && val.as_i64() >= min;\n        } else {\n            return val.as_u64() <= static_cast<uint64_t>(max)\n                   && val.as_u64() >= static_cast<uint64_t>(min);\n        }\n    }\n\n    [[nodiscard]] constexpr bool is_imm() const { return kind != 'g'; }\n};\n\nstatic const ArgumentDesc arg_desc_list[] = {\n    // Destination register (rd)\n    ArgumentDesc('d', 'g', 0, 0x1f, { { { 5, 7 } }, 0 }),\n    // Source register 1 (rs1/rs)\n    ArgumentDesc('s', 'g', 0, 0x1f, { { { 5, 15 } }, 0 }),\n    // Source register 2 (rs2/rt)\n    ArgumentDesc('t', 'g', 0, 0x1f, { { { 5, 20 } }, 0 }),\n    // I-type immediate for arithmetic instructions (12bits)\n    ArgumentDesc('j', 'n', -0x800, 0x7ff, { { { 12, 20 } }, 0 }),\n    // Shift for bit shift instructions (5bits)\n    ArgumentDesc('>', 'n', 0, 0x1f, { { { 5, 20 } }, 0 }),\n    // Address offset immediate (20bits), encoded in multiples of 2 bytes\n    ArgumentDesc('a', 'a', -0x80000, 0x7ffff, { { { 10, 21 }, { 1, 20 }, { 8, 12 }, { 1, 31 } }, 1 }),\n    // U-type immediate for LUI and AUIPC (20bits)\n    ArgumentDesc('u', 'n', 0, 0xfffff000, { { { 20, 12 } }, 0 }),\n    // B-type immediate for branches (12 bits)\n    ArgumentDesc('p', 'p', -0x1000, 0x0fff, { { { 4, 8 }, { 6, 25 }, { 1, 7 }, { 1, 31 } }, 1 }),\n    // Offset immediate for load instructions (12 bits)\n    ArgumentDesc('o', 'o', -0x800, 0x7ff, { { { 12, 20 } }, 0 }),\n    // Offset immediate for store instructions (12 bits)\n    ArgumentDesc('q', 'o', -0x800, 0x7ff, { { { 5, 7 }, { 7, 25 } }, 0 }),\n    // 5-bit CSR value immediate\n    // (https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=opcodes/riscv-opc.c;h=7e95f645c5c5fe0a7c93c64c2f1719efaec67972;hb=HEAD#l928)\n    ArgumentDesc('Z', 'n', 0, 0x1f, { { { 5, 15 } }, 0 }),\n    // 12-bit CSR address\n    // (https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=opcodes/riscv-opc.c;h=7e95f645c5c5fe0a7c93c64c2f1719efaec67972;hb=HEAD#l928)\n    ArgumentDesc('E', 'E', 0, 0xfff, { { { 12, 20 } }, 0 }),\n};\n\nstatic const ArgumentDesc *arg_desc_by_code[(int)('z' + 1)];\n\nstatic bool fill_argdesbycode() {\n    for (const auto &desc : arg_desc_list) {\n        arg_desc_by_code[(uint)(unsigned char)desc.name] = &desc;\n    }\n    return true;\n}\n\nbool argdesbycode_filled = fill_argdesbycode();\n\n#define FLAGS_ALU_I (IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE | IMF_ALU_REQ_RS)\n#define FLAGS_ALU_I_LOAD                                                                           \\\n    (IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE | IMF_MEMREAD | IMF_MEM | IMF_ALU_REQ_RS)\n#define FLAGS_ALU_I_STORE                                                                          \\\n    (IMF_SUPPORTED | IMF_ALUSRC | IMF_MEMWRITE | IMF_MEM | IMF_ALU_REQ_RS | IMF_ALU_REQ_RT)\n#define FLAGS_ALU_T_R_D   (IMF_SUPPORTED | IMF_REGWRITE)\n#define FLAGS_ALU_T_R_STD (FLAGS_ALU_T_R_D | IMF_ALU_REQ_RS | IMF_ALU_REQ_RT)\n\n#define FLAGS_AMO_LOAD (FLAGS_ALU_I_LOAD | IMF_AMO)\n// FLAGS_AMO_STORE for store conditional requires IMF_MEMREAD to ensure stalling because\n// forwarding is not possible from memory stage after memory read, TODO to solve better way\n#define FLAGS_AMO_STORE  (FLAGS_ALU_I_STORE | FLAGS_ALU_T_R_D | IMF_AMO | IMF_MEMREAD)\n#define FLAGS_AMO_MODIFY (FLAGS_ALU_I_LOAD | FLAGS_AMO_STORE | IMF_AMO)\n\n#define NOALU { .alu_op = AluOp::ADD }\n#define NOMEM .mem_ctl = AC_NONE\n\n// TODO NOTE: if unknown is defined as all 0, instruction map can be significantly simplified\n//  using zero initialization.\n#define IM_UNKNOWN                                                                                 \\\n    { \"unknown\", Instruction::UNKNOWN, NOALU, NOMEM, nullptr, {}, 0, 0, { 0 }, nullptr }\n\nstruct InstructionMap {\n    const char *name;\n    Instruction::Type type = Instruction::UNKNOWN;\n    AluCombinedOp alu = { .alu_op = AluOp::ADD };\n    AccessControl mem_ctl = AC_NONE;\n    const struct InstructionMap *subclass = nullptr; // when subclass is used then flags\n                                                     // has special meaning\n    const cvector<QString, 3> args;\n    uint32_t code;\n    uint32_t mask;\n    union {\n        decltype(underlying_type<InstructionFlags>::type()) flags;\n        BitField subfield;\n    };\n    const InstructionMap *aliases = nullptr;\n};\n\n#define IT_R       Instruction::R\n#define IT_I       Instruction::I\n#define IT_S       Instruction::S\n#define IT_B       Instruction::B\n#define IT_U       Instruction::U\n#define IT_J       Instruction::J\n#define IT_AMO     Instruction::AMO\n#define IT_ZICSR   Instruction::ZICSR\n#define IT_UNKNOWN Instruction::UNKNOWN\n\n// clang-format off\n\n// alliases for instructions for internal assembler and possibly\n// disassembler if simplified format is requested.\n// They are not used during decoding and execution\n\n#define INST_ALIAS_LIST_END {.name = nullptr, .args = {}, .code = 0 , .mask = 0, .flags = 0 }\n\nstatic const struct InstructionMap inst_aliases_addi[] = {\n    { .name = \"mv\", .args = {\"d\", \"s\"}, .code = 0x13, .mask = 0x707f | (0xffful << 20), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_andi[] = {\n    { .name = \"zext.b\", .args = {\"d\", \"s\"}, .code = 0x7013 | (0xfful << 20), .mask = 0x707f | (0xffful << 20), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_addiw[] = {\n    { .name = \"sext.w\", .args = {\"d\", \"s\"}, .code = 0x1b, .mask = 0x707f | (0xffful << 20), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_xori[] = {\n    { .name = \"not\", .args = {\"d\", \"s\"}, .code = 0x4013 | (0xffful << 20), .mask = 0x707f | (0xffful << 20), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_sub[] = {\n    { .name = \"neg\", .args = {\"d\", \"t\"}, .code = 0x40000033 | (0 << 15), .mask = 0xfe00707f | (31 << 15), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_subw[] = {\n    { .name = \"negw\", .args = {\"d\", \"t\"}, .code = 0x4000003b | (0 << 15), .mask = 0xfe00707f | (31 << 15), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_sltiu[] = {\n    { .name = \"seqz\", .args = {\"d\", \"s\"}, .code = 0x3013| (1 << 20), .mask = 0x0000707f | (0xffful << 20), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_sltu[] = {\n    { .name = \"snez\", .args = {\"d\", \"t\"}, .code = 0x3033 | (0 << 15), .mask = 0xfe00707f | (31 << 15), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_slt[] = {\n    { .name = \"sltz\", .args = {\"d\", \"s\"}, .code = 0x2033 | (0 << 20), .mask = 0xfe00707f | (31 << 20), .flags = IMF_SUPPORTED },\n    { .name = \"sgtz\", .args = {\"d\", \"t\"}, .code = 0x2033 | (0 << 15), .mask = 0xfe00707f | (31 << 15), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_beq[] = { //0x00000063,0x0000707f\n    { .name = \"beqz\", .args = {\"s\", \"p\"}, .code = 0x0063 | (0 << 20), .mask = 0x707f | (31 << 20), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_bne[] = { //0x00001063, 0x0000707f\n    { .name = \"bnez\", .args = {\"s\", \"p\"}, .code = 0x1063 | (0 << 20), .mask = 0x707f | (31 << 20), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_blt[] = { //0x00004063, 0x0000707f\n    { .name = \"bltz\", .args = {\"s\", \"p\"}, .code = 0x4063 | (0 << 20), .mask = 0x707f | (31 << 20), .flags = IMF_SUPPORTED },\n    { .name = \"bgtz\", .args = {\"t\", \"p\"}, .code = 0x4063 | (0 << 15), .mask = 0x707f | (31 << 15), .flags = IMF_SUPPORTED },\n    { .name = \"bgt\", .args = {\"t\", \"s\", \"p\"}, .code = 0x4063, .mask = 0x707f, .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_bge[] = { //0x00005063,0x0000707f\n    { .name = \"bgez\", .args = {\"s\", \"p\"}, .code = 0x5063 | (0 << 20), .mask = 0x707f | (31 << 20), .flags = IMF_SUPPORTED },\n    { .name = \"ble\", .args = {\"t\", \"s\", \"p\"}, .code = 0x5063, .mask = 0x707f, .flags = IMF_SUPPORTED },\n    { .name = \"blez\", .args = {\"t\", \"p\"}, .code = 0x5063 | (0 << 15), .mask = 0x707f | (31 << 15), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_bltu[] = { //0x00006063, 0x0000707f\n    { .name = \"bgtu\", .args = {\"t\", \"s\", \"p\"}, .code = 0x6063, .mask = 0x707f, .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_bgeu[] = { //0x00007063,0x0000707f\n    { .name = \"bleu\", .args = {\"t\", \"s\", \"p\"}, .code = 0x7063, .mask = 0x707f, .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_jal[] = {\n    { .name = \"j\", .args = {\"a\"}, .code = 0x6f | (0 << 7) , .mask = 0x7f | (31 << 7), .flags = IMF_SUPPORTED },\n    { .name = \"jal\", .args = {\"a\"}, .code = 0x6f | (1 << 7) , .mask = 0x7f | (31 << 7), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_jalr[] = {\n    { .name = \"ret\", .args = {}, .code = 0x67 | (0 << 7) | (1 << 15) , .mask = 0xfffffffful, .flags = IMF_SUPPORTED },\n    { .name = \"jr\", .args = {\"s\"}, .code = 0x67 | (0 << 7) , .mask = 0x7f | (31 << 7) | (0xffful << 20), .flags = IMF_SUPPORTED },\n    { .name = \"jr\", .args = {\"o(s)\"}, .code = 0x67 | (0 << 7) , .mask = 0x7f | (31 << 7), .flags = IMF_SUPPORTED },\n    { .name = \"jalr\", .args = {\"s\"}, .code = 0x67 | (1 << 7) , .mask = 0x7f | (31 << 7) | (0xffful << 20), .flags = IMF_SUPPORTED },\n    { .name = \"jalr\", .args = {\"o(s)\"}, .code = 0x67 | (1 << 7) , .mask = 0x7f | (31 << 7), .flags = IMF_SUPPORTED },\n    { .name = \"jalr\", .args = {\"d\", \"s\", \"o\"}, .code = 0x67, .mask = 0x7f, .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_csrrw[] = {\n    { .name = \"csrw\", .args = {\"E\", \"s\"}, .code = 0x1073 | (0 << 7) | (0 << 15) , .mask = 0x707f | (31 << 7), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\nstatic const struct InstructionMap inst_aliases_csrrs[] = {\n    { .name = \"csrr\", .args = {\"d\", \"E\"}, .code = 0x2073 | (0 << 15) , .mask = 0x707f | (31 << 15), .flags = IMF_SUPPORTED },\n    INST_ALIAS_LIST_END,\n};\n\n// RV32/64A - Atomi Memory Operations\n\n#define AMO_ARGS_LOAD {\"d\", \"(s)\"}\n#define AMO_ARGS_STORE {\"d\", \"t\", \"(s)\"}\n#define AMO_ARGS_MODIFY {\"d\", \"t\", \"(s)\"}\n\n#define AMO_MAP_4ITEMS(NAME_BASE, CODE_BASE, MASK, MEM_CTL, FLAGS, ARGS) \\\n    { NAME_BASE, IT_AMO, NOALU, MEM_CTL, nullptr, ARGS , ((CODE_BASE) | 0x00000000), 0xfe00707f, { .flags = FLAGS}, nullptr}, \\\n    { NAME_BASE \".rl\", IT_AMO, NOALU, MEM_CTL, nullptr, ARGS , ((CODE_BASE) | 0x02000000), 0xfe00707f, { .flags = FLAGS}, nullptr}, \\\n    { NAME_BASE \".aq\", IT_AMO, NOALU, MEM_CTL, nullptr, ARGS , ((CODE_BASE) | 0x04000000), 0xfe00707f, { .flags = FLAGS}, nullptr}, \\\n    { NAME_BASE \".aqrl\", IT_AMO, NOALU, MEM_CTL, nullptr, ARGS , ((CODE_BASE) | 0x06000000), 0xfe00707f, { .flags = FLAGS}, nullptr}\n\nstatic const struct InstructionMap AMO_32_map[] = {\n    AMO_MAP_4ITEMS(\"amoadd.w\", 0x0000202f, 0xfe00707f, AC_AMOADD32, FLAGS_AMO_MODIFY, AMO_ARGS_MODIFY),\n    AMO_MAP_4ITEMS(\"amoswap.w\", 0x0800202f, 0xfe00707f, AC_AMOSWAP32, FLAGS_AMO_MODIFY, AMO_ARGS_MODIFY),\n    AMO_MAP_4ITEMS(\"lr.w\", 0x1000202f, 0xfff0707f, AC_LR32, FLAGS_AMO_LOAD, AMO_ARGS_LOAD),\n    AMO_MAP_4ITEMS(\"sc.w\", 0x1800202f, 0xfe00707f, AC_SC32, FLAGS_AMO_STORE, AMO_ARGS_STORE),\n    AMO_MAP_4ITEMS(\"amoxor.w\", 0x2000202f, 0xfe00707f, AC_AMOXOR32, FLAGS_AMO_MODIFY, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    AMO_MAP_4ITEMS(\"amoor.w\", 0x4000202f, 0xfe00707f, AC_AMOOR32, FLAGS_AMO_MODIFY, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    AMO_MAP_4ITEMS(\"amoand.w\", 0x6000202f, 0xfe00707f, AC_AMOAND32, FLAGS_AMO_MODIFY, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    AMO_MAP_4ITEMS(\"amomin.w\", 0x8000202f, 0xfe00707f, AC_AMOMIN32, FLAGS_AMO_MODIFY, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    AMO_MAP_4ITEMS(\"amomax.w\", 0xa000202f, 0xfe00707f, AC_AMOMAX32, FLAGS_AMO_MODIFY, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    AMO_MAP_4ITEMS(\"amominu.w\", 0xc000202f, 0xfe00707f, AC_AMOMINU32, FLAGS_AMO_MODIFY, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    AMO_MAP_4ITEMS(\"amomaxu.w\", 0xe000202f, 0xfe00707f, AC_AMOMAXU32, FLAGS_AMO_MODIFY, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n};\n\nstatic const struct InstructionMap AMO_64_map[] = {\n    AMO_MAP_4ITEMS(\"amoadd.d\", 0x0000302f, 0xfe00707f, AC_AMOADD64, FLAGS_AMO_MODIFY, AMO_ARGS_MODIFY),\n    AMO_MAP_4ITEMS(\"amoswap.d\", 0x0800302f, 0xfe00707f, AC_AMOSWAP64, FLAGS_AMO_MODIFY, AMO_ARGS_MODIFY),\n    AMO_MAP_4ITEMS(\"lr.d\", 0x1000302f, 0xfff0707f, AC_LR64, FLAGS_AMO_LOAD, AMO_ARGS_LOAD),\n    AMO_MAP_4ITEMS(\"sc.d\", 0x1800302f, 0xfe00707f, AC_SC64, FLAGS_AMO_STORE, AMO_ARGS_STORE),\n    AMO_MAP_4ITEMS(\"amoxor.d\", 0x2000302f, 0xfe00707f, AC_AMOXOR64, FLAGS_AMO_MODIFY | IMF_RV64, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    AMO_MAP_4ITEMS(\"amoor.d\", 0x4000302f, 0xfe00707f, AC_AMOOR64, FLAGS_AMO_MODIFY | IMF_RV64, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    AMO_MAP_4ITEMS(\"amoand.d\", 0x6000302f, 0xfe00707f, AC_AMOAND64, FLAGS_AMO_MODIFY | IMF_RV64, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    AMO_MAP_4ITEMS(\"amomin.d\", 0x8000302f, 0xfe00707f, AC_AMOMIN64, FLAGS_AMO_MODIFY | IMF_RV64, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    AMO_MAP_4ITEMS(\"amomax.d\", 0xa000302f, 0xfe00707f, AC_AMOMAX64, FLAGS_AMO_MODIFY | IMF_RV64, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    AMO_MAP_4ITEMS(\"amominu.d\", 0xc000302f, 0xfe00707f, AC_AMOMINU64, FLAGS_AMO_MODIFY | IMF_RV64, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    AMO_MAP_4ITEMS(\"amomaxu.d\", 0xe000302f, 0xfe00707f, AC_AMOMAXU64, FLAGS_AMO_MODIFY | IMF_RV64, AMO_ARGS_MODIFY),\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n    IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN, IM_UNKNOWN,\n};\n\nstatic const struct InstructionMap AMO_map[] = {\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    {\"amo-32\", IT_R, NOALU, NOMEM, AMO_32_map, {}, 0x0002027, 0x0000707f, { .subfield = {7, 25} }, nullptr}, // OP-32\n    {\"amo-64\", IT_R, NOALU, NOMEM, AMO_64_map, {}, 0x0003027, 0x0000707f, { .subfield = {7, 25} }, nullptr}, // OP-32\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n};\n\n#undef AMO_MAP_4ITEMS\n\nstatic const struct InstructionMap LOAD_map[] = {\n    {\"lb\",  IT_I, { .alu_op=AluOp::ADD }, AC_I8,  nullptr, {\"d\", \"o(s)\"}, 0x00000003,0x0000707f, { .flags = FLAGS_ALU_I_LOAD }, nullptr}, // LB\n    {\"lh\",  IT_I, { .alu_op=AluOp::ADD }, AC_I16, nullptr, {\"d\", \"o(s)\"}, 0x00001003,0x0000707f, { .flags = FLAGS_ALU_I_LOAD }, nullptr}, // LH\n    {\"lw\",  IT_I, { .alu_op=AluOp::ADD }, AC_I32, nullptr, {\"d\", \"o(s)\"}, 0x00002003,0x0000707f, { .flags = FLAGS_ALU_I_LOAD }, nullptr}, // LW\n    {\"ld\",  IT_I, { .alu_op=AluOp::ADD }, AC_I64, nullptr, {\"d\", \"o(s)\"}, 0x00003003,0x0000707f, { .flags = FLAGS_ALU_I_LOAD | IMF_RV64}, nullptr}, // LD\n    {\"lbu\", IT_I, { .alu_op=AluOp::ADD }, AC_U8,  nullptr, {\"d\", \"o(s)\"}, 0x00004003,0x0000707f, { .flags = FLAGS_ALU_I_LOAD }, nullptr}, // LBU\n    {\"lhu\", IT_I, { .alu_op=AluOp::ADD }, AC_U16, nullptr, {\"d\", \"o(s)\"}, 0x00005003,0x0000707f, { .flags = FLAGS_ALU_I_LOAD }, nullptr}, // LHU\n    {\"lwu\", IT_I, { .alu_op=AluOp::ADD }, AC_U32, nullptr, {\"d\", \"o(s)\"}, 0x00006003,0x0000707f, { .flags = FLAGS_ALU_I_LOAD  | IMF_RV64}, nullptr}, // LWU\n    IM_UNKNOWN,\n};\n\nstatic const struct InstructionMap SRI_map[] = { // 0xfe00707f mask changed to 0xfc00707f to support RV64I\n    {\"srli\", IT_I, { .alu_op=AluOp::SR }, NOMEM, nullptr, {\"d\", \"s\", \">\"}, 0x00005013,0xfc00707f, { .flags = FLAGS_ALU_I }, nullptr}, // SRLI\n    {\"srai\", IT_I, { .alu_op=AluOp::SR }, NOMEM, nullptr, {\"d\", \"s\", \">\"}, 0x40005013,0xfc00707f, { .flags = (FLAGS_ALU_I | IMF_ALU_MOD) }, nullptr}, // SRAI\n};\n\nstatic const struct InstructionMap OP_IMM_map[] = {\n    {\"addi\",  IT_I, { .alu_op=AluOp::ADD },  NOMEM, nullptr, {\"d\", \"s\", \"j\"}, 0x00000013,0x0000707f, { .flags = FLAGS_ALU_I }, inst_aliases_addi}, // ADDI\n    {\"slli\",  IT_I, { .alu_op=AluOp::SLL },  NOMEM, nullptr, {\"d\", \"s\", \">\"}, 0x00001013,0xfc00707f, { .flags = FLAGS_ALU_I }, nullptr}, // SLLI\n    {\"slti\",  IT_I, { .alu_op=AluOp::SLT },  NOMEM, nullptr, {\"d\", \"s\", \"j\"}, 0x00002013,0x0000707f, { .flags = FLAGS_ALU_I }, nullptr}, // SLTI\n    {\"sltiu\", IT_I, { .alu_op=AluOp::SLTU }, NOMEM, nullptr, {\"d\", \"s\", \"j\"}, 0x00003013,0x0000707f, { .flags = FLAGS_ALU_I }, inst_aliases_sltiu}, // SLTIU\n    {\"xori\",  IT_I, { .alu_op=AluOp::XOR },  NOMEM, nullptr, {\"d\", \"s\", \"j\"}, 0x00004013,0x0000707f, { .flags = FLAGS_ALU_I }, inst_aliases_xori}, // XORI\n    {\"sri\",   IT_I, NOALU,       NOMEM, SRI_map,              {}, 0x00005013, 0xbe00707f, { .subfield = {1, 30} }, nullptr}, // SRLI, SRAI\n    {\"ori\",   IT_I, { .alu_op=AluOp::OR },   NOMEM, nullptr, {\"d\", \"s\", \"j\"}, 0x00006013,0x0000707f, { .flags = FLAGS_ALU_I }, nullptr}, // ORI\n    {\"andi\",  IT_I, { .alu_op=AluOp::AND },  NOMEM, nullptr, {\"d\", \"s\", \"j\"}, 0x00007013,0x0000707f, { .flags = FLAGS_ALU_I }, inst_aliases_andi}, // ANDI\n};\n\nstatic const struct InstructionMap STORE_map[] = {\n    {\"sb\", IT_S, { .alu_op=AluOp::ADD }, AC_U8,  nullptr, {\"t\", \"q(s)\"}, 0x00000023, 0x0000707f, { .flags = FLAGS_ALU_I_STORE }, nullptr}, // SB\n    {\"sh\", IT_S, { .alu_op=AluOp::ADD }, AC_U16, nullptr, {\"t\", \"q(s)\"}, 0x00001023, 0x0000707f, { .flags = FLAGS_ALU_I_STORE }, nullptr}, // SH\n    {\"sw\", IT_S, { .alu_op=AluOp::ADD }, AC_U32, nullptr, {\"t\", \"q(s)\"}, 0x00002023, 0x0000707f, { .flags = FLAGS_ALU_I_STORE }, nullptr}, // SW\n    {\"sd\", IT_S, { .alu_op=AluOp::ADD }, AC_U64, nullptr, {\"t\", \"q(s)\"}, 0x00003023, 0x0000707f, { .flags = FLAGS_ALU_I_STORE | IMF_RV64}, nullptr}, // SD\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n};\n\nstatic const struct InstructionMap ADD_map[] = {\n    {\"add\", IT_R, { .alu_op=AluOp::ADD }, NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x00000033, 0xfe00707f, { .flags = FLAGS_ALU_T_R_STD }, nullptr},\n    {\"sub\", IT_R, { .alu_op=AluOp::ADD }, NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x40000033, 0xfe00707f, { .flags = FLAGS_ALU_T_R_STD | IMF_ALU_MOD }, inst_aliases_sub},\n};\n\nstatic const struct InstructionMap SR_map[] = {\n    {\"srl\", IT_R, { .alu_op=AluOp::SR }, NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x00005033,0xfe00707f, { .flags = FLAGS_ALU_T_R_STD }, nullptr}, // SRL\n    {\"sra\", IT_R, { .alu_op=AluOp::SR }, NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x40005033,0xfe00707f,  { .flags = FLAGS_ALU_T_R_STD | IMF_ALU_MOD }, nullptr}, // SRA\n};\n\nstatic const struct InstructionMap OP_ALU_map[] = {\n    {\"add/sub\", IT_R, NOALU,    NOMEM, ADD_map,              {}, 0x00000033, 0xbe00707f, { .subfield = {1, 30} }, nullptr},\n    {\"sll\",  IT_R, { .alu_op=AluOp::SLL },  NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x00001033, 0xfe00707f, { .flags = FLAGS_ALU_T_R_STD }, nullptr}, // SLL\n    {\"slt\",  IT_R, { .alu_op=AluOp::SLT },  NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x00002033, 0xfe00707f, { .flags = FLAGS_ALU_T_R_STD }, inst_aliases_slt}, // SLT\n    {\"sltu\", IT_R, { .alu_op=AluOp::SLTU }, NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x00003033,0xfe00707f, { .flags = FLAGS_ALU_T_R_STD }, inst_aliases_sltu}, // SLTU\n    {\"xor\",  IT_R, { .alu_op=AluOp::XOR },  NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x00004033,0xfe00707f, { .flags = FLAGS_ALU_T_R_STD }, nullptr}, // XOR\n    {\"sr\",   IT_R, NOALU,       NOMEM,  SR_map,              {}, 0x00005033, 0xbe00707f, { .subfield = {1, 30} }, nullptr}, // SRL, SRA\n    {\"or\",   IT_R, { .alu_op=AluOp::OR },   NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x00006033,0xfe00707f, { .flags = FLAGS_ALU_T_R_STD }, nullptr}, // OR\n    {\"and\",  IT_R, { .alu_op=AluOp::AND },  NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x00007033,0xfe00707f, { .flags = FLAGS_ALU_T_R_STD }, nullptr}, // AND\n};\n\n// RV32M\n#define MUL_MAP_ITEM(NAME, OP, CODE) \\\n    { NAME, IT_R, { .mul_op = (OP) }, NOMEM, nullptr, {\"d\", \"s\", \"t\"}, (0x02000033 | (CODE)), 0xfe00707f, { .flags = (FLAGS_ALU_T_R_STD | IMF_MUL) }, nullptr}\n\nstatic const struct InstructionMap OP_MUL_map[] = {\n    MUL_MAP_ITEM(\"mul\",     MulOp::MUL,     0x0000),\n    MUL_MAP_ITEM(\"mulh\",    MulOp::MULH,    0x1000),\n    MUL_MAP_ITEM(\"mulhsu\",  MulOp::MULHSU,  0x2000),\n    MUL_MAP_ITEM(\"mulhu\",   MulOp::MULHU,   0x3000),\n    MUL_MAP_ITEM(\"div\",     MulOp::DIV,     0x4000),\n    MUL_MAP_ITEM(\"divu\",    MulOp::DIVU,    0x5000),\n    MUL_MAP_ITEM(\"rem\",     MulOp::REM,     0x6000),\n    MUL_MAP_ITEM(\"remu\",    MulOp::REMU,    0x7000),\n};\n\nstatic const struct InstructionMap OP_map[] = {\n    {\"alu\", IT_R, NOALU, NOMEM, OP_ALU_map, {}, 0x00000033, 0x0000707f, { .subfield = {3, 12} }, nullptr},\n    {\"mul\", IT_R, NOALU, NOMEM, OP_MUL_map, {}, 0x02000033, 0xfc00707f, { .subfield = {3, 12} }, nullptr},\n};\n\nstatic const struct InstructionMap BRANCH_map[] = {\n    {\"beq\",  IT_B, { .alu_op=AluOp::ADD }, NOMEM,  nullptr, {\"s\", \"t\", \"p\"}, 0x00000063,0x0000707f, { .flags = IMF_SUPPORTED | IMF_BRANCH | IMF_ALU_REQ_RS | IMF_ALU_REQ_RT | IMF_ALU_MOD }, inst_aliases_beq}, // BEQ\n    {\"bne\",  IT_B, { .alu_op=AluOp::ADD }, NOMEM,  nullptr, {\"s\", \"t\", \"p\"}, 0x00001063, 0x0000707f, { .flags = IMF_SUPPORTED | IMF_BRANCH | IMF_ALU_REQ_RS | IMF_ALU_REQ_RT | IMF_ALU_MOD | IMF_BJ_NOT }, inst_aliases_bne}, // BNE\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    {\"blt\",  IT_B, { .alu_op=AluOp::SLT }, NOMEM,  nullptr, {\"s\", \"t\", \"p\"}, 0x00004063, 0x0000707f, { .flags = IMF_SUPPORTED | IMF_BRANCH | IMF_ALU_REQ_RS | IMF_ALU_REQ_RT | IMF_BJ_NOT }, inst_aliases_blt}, // BLT\n    {\"bge\",  IT_B, { .alu_op=AluOp::SLT }, NOMEM,  nullptr, {\"s\", \"t\", \"p\"}, 0x00005063,0x0000707f, { .flags = IMF_SUPPORTED | IMF_BRANCH | IMF_ALU_REQ_RS | IMF_ALU_REQ_RT }, inst_aliases_bge}, // BGE\n    {\"bltu\", IT_B, { .alu_op=AluOp::SLTU }, NOMEM, nullptr, {\"s\", \"t\", \"p\"}, 0x00006063, 0x0000707f, { .flags = IMF_SUPPORTED | IMF_BRANCH | IMF_ALU_REQ_RS | IMF_ALU_REQ_RT | IMF_BJ_NOT }, inst_aliases_bltu}, // BLTU\n    {\"bgeu\", IT_B, { .alu_op=AluOp::SLTU }, NOMEM, nullptr, {\"s\", \"t\", \"p\"}, 0x00007063,0x0000707f, { .flags = IMF_SUPPORTED | IMF_BRANCH | IMF_ALU_REQ_RS | IMF_ALU_REQ_RT }, inst_aliases_bgeu}, // BGEU\n};\n\n// Spec vol. 1: 2.8\nstatic const struct InstructionMap ENVIRONMENT_AND_BREAKPOINTS_map[] = {\n    {\"ecall\", IT_I, NOALU, NOMEM, nullptr, {}, 0x00000073, 0xffffffff, { .flags = IMF_SUPPORTED | IMF_EXCEPTION | IMF_ECALL }, nullptr},\n    {\"ebreak\", IT_I, NOALU, NOMEM, nullptr, {}, 0x00100073, 0xffffffff, { .flags = IMF_SUPPORTED | IMF_EXCEPTION | IMF_EBREAK }, nullptr},\n};\n\n// Priviledged system isntructions, only 5-bits (29:25) are decoded for now.\n// Full decode is should cover 128 entries (31:25) but we radly support hypervisor even in future\nstatic const struct InstructionMap SYSTEM_PRIV_map[] = {\n    {\"environment_and_breakpoints\", IT_I, NOALU, NOMEM, ENVIRONMENT_AND_BREAKPOINTS_map, {}, 0x00000073, 0xffffffff, { .subfield = {1, 20} }, nullptr},\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    {\"sret\", IT_I, NOALU, NOMEM, nullptr, {}, 0x10200073, 0xffffffff, { .flags = IMF_SUPPORTED | IMF_XRET | IMF_PRIV_S }, nullptr},\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    {\"mret\", IT_I, NOALU, NOMEM, nullptr, {}, 0x30200073, 0xffffffff, { .flags = IMF_SUPPORTED | IMF_XRET | IMF_PRIV_M }, nullptr},\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n};\n\n#define CSR_MAP_ITEM(NAME, SOURCE, CODE, ALU_OP,  EXTRA_FLAGS, ALIASES) \\\n    { NAME, Instruction::ZICSR, { .alu_op=AluOp::ALU_OP }, NOMEM, nullptr,  {\"d\", \"E\", SOURCE}, 0x00000073 | (CODE), 0x0000707f, { .flags = IMF_SUPPORTED | IMF_CSR | IMF_REGWRITE | IMF_ALU_REQ_RS | (EXTRA_FLAGS) }, ALIASES}\n\nstatic const struct InstructionMap SYSTEM_map[] = {\n    {\"system_priviledged\", IT_I, NOALU, NOMEM, SYSTEM_PRIV_map, {}, 0x00000073, 0xffffffff, { .subfield = {5, 25} }, nullptr},\n    CSR_MAP_ITEM(\"csrrw\", \"s\", 0x1000, ADD, 0, inst_aliases_csrrw),\n    CSR_MAP_ITEM(\"csrrs\", \"s\", 0x2000, OR, IMF_CSR_TO_ALU, inst_aliases_csrrs),\n    CSR_MAP_ITEM(\"csrrc\", \"s\", 0x3000, AND, IMF_CSR_TO_ALU | IMF_ALU_MOD, nullptr),\n    IM_UNKNOWN,\n    CSR_MAP_ITEM(\"csrrwi\", \"Z\", 0x5000, ADD, IMF_ALU_RS_ID, nullptr),\n    CSR_MAP_ITEM(\"csrrsi\", \"Z\", 0x6000, OR, IMF_ALU_RS_ID | IMF_CSR_TO_ALU, nullptr),\n    CSR_MAP_ITEM(\"csrrci\", \"Z\", 0x7000, AND, IMF_ALU_RS_ID | IMF_CSR_TO_ALU | IMF_ALU_MOD, nullptr),\n};\n\n#undef CSR_MAP_ITEM\n\nstatic const struct InstructionMap MISC_MEM_map[] = {\n    {\"fence\", IT_I, NOALU, AC_CACHE_OP, nullptr, {}, 0x0000000f, 0x0000707f, { .flags = IMF_SUPPORTED | IMF_MEM }, nullptr},\n    {\"fence.i\", IT_I, NOALU, AC_CACHE_OP, nullptr, {}, 0x000100f, 0x0000707f, { .flags = IMF_SUPPORTED | IMF_MEM }, nullptr},\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n};\n\n// RV64I specific operations\n\nstatic const struct InstructionMap SRI_32_map[] = {\n    {\"srliw\", IT_I, { .alu_op=AluOp::SR }, NOMEM, nullptr, {\"d\", \"s\", \">\"}, 0x0000501b,0xfe00707f, { .flags = FLAGS_ALU_I | IMF_FORCE_W_OP | IMF_RV64 }, nullptr}, // SRLIW\n    {\"sraiw\", IT_I, { .alu_op=AluOp::SR }, NOMEM, nullptr, {\"d\", \"s\", \">\"}, 0x4000501b,0xfe00707f, { .flags = FLAGS_ALU_I | IMF_ALU_MOD | IMF_FORCE_W_OP | IMF_RV64 }, nullptr}, // SRAIW\n};\n\nstatic const struct InstructionMap OP_IMM_32_map[] = {\n    {\"addiw\", IT_I, { .alu_op=AluOp::ADD },  NOMEM, nullptr, {\"d\", \"s\", \"j\"}, 0x0000001b,0x0000707f, { .flags = FLAGS_ALU_I | IMF_FORCE_W_OP | IMF_RV64 }, inst_aliases_addiw}, // ADDIW\n    {\"slliw\", IT_I, { .alu_op=AluOp::SLL },  NOMEM, nullptr, {\"d\", \"s\", \">\"}, 0x0000101b,0xfe00707f, { .flags = FLAGS_ALU_I | IMF_FORCE_W_OP | IMF_RV64 }, nullptr}, // SLLIW\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    {\"sriw\",   IT_I, NOALU,       NOMEM, SRI_32_map,              {}, 0x0000501b, 0xbe00707f, { .subfield = {1, 30} }, nullptr}, // SRLIW, SRAIW\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n};\n\nstatic const struct InstructionMap ADD_32_map[] = {\n    {\"addw\", IT_R, { .alu_op=AluOp::ADD }, NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x0000003b, 0xfe00707f, { .flags = FLAGS_ALU_T_R_STD | IMF_FORCE_W_OP | IMF_RV64 }, nullptr},\n    {\"subw\", IT_R, { .alu_op=AluOp::ADD }, NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x4000003b, 0xfe00707f, { .flags = FLAGS_ALU_T_R_STD | IMF_ALU_MOD | IMF_FORCE_W_OP | IMF_RV64 }, inst_aliases_subw},\n};\n\nstatic const struct InstructionMap SR_32_map[] = {\n    {\"srlw\", IT_R, { .alu_op=AluOp::SR }, NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x0000503b,0xfe00707f, { .flags = FLAGS_ALU_T_R_STD | IMF_FORCE_W_OP | IMF_RV64 }, nullptr}, // SRL\n    {\"sraw\", IT_R, { .alu_op=AluOp::SR }, NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x4000503b,0xfe00707f,  { .flags = FLAGS_ALU_T_R_STD | IMF_ALU_MOD | IMF_FORCE_W_OP | IMF_RV64 }, nullptr}, // SRA\n};\n\nstatic const struct InstructionMap OP_ALU_32_map[] = {\n    {\"addw/subw\", IT_R, NOALU,    NOMEM, ADD_32_map,              {}, 0x0000003b, 0xbe00707f, { .subfield = {1, 30} }, nullptr},\n    {\"sllw\",  IT_R, { .alu_op=AluOp::SLL },  NOMEM, nullptr, {\"d\", \"s\", \"t\"}, 0x0000103b, 0xfe00707f, { .flags = FLAGS_ALU_T_R_STD | IMF_FORCE_W_OP | IMF_RV64 }, nullptr}, // SLL\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    {\"srw\",   IT_R, NOALU,       NOMEM,  SR_32_map,              {}, 0x0000503b, 0xbe00707f, { .subfield = {1, 30} }, nullptr}, // SRL, SRA\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n};\n\n// RV64M\n#define MUL_32_MAP_ITEM(NAME, OP, CODE) \\\n    { NAME, IT_R, { .mul_op = (OP) }, NOMEM, nullptr, {\"d\", \"s\", \"t\"}, (0x0200003b | (CODE)), 0xfe00707f, { .flags = (FLAGS_ALU_T_R_STD | IMF_MUL | IMF_FORCE_W_OP | IMF_RV64 ) }, nullptr}\n\nstatic const struct InstructionMap OP_MUL_32_map[] = {\n    MUL_32_MAP_ITEM(\"mulw\",     MulOp::MUL,     0x0000),\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    MUL_32_MAP_ITEM(\"divw\",     MulOp::DIV,     0x4000),\n    MUL_32_MAP_ITEM(\"divuw\",    MulOp::DIVU,    0x5000),\n    MUL_32_MAP_ITEM(\"remw\",     MulOp::REM,     0x6000),\n    MUL_32_MAP_ITEM(\"remuw\",    MulOp::REMU,    0x7000),\n};\n\nstatic const struct InstructionMap OP_32_map[] = {\n    {\"aluw\", IT_R, NOALU, NOMEM, OP_ALU_32_map, {}, 0x00000033, 0x0000707f, { .subfield = {3, 12} }, nullptr},\n    {\"mulw\", IT_R, NOALU, NOMEM, OP_MUL_32_map, {}, 0x02000033, 0xfc00707f, { .subfield = {3, 12} }, nullptr},\n};\n\n// Full, uncomprese, instructions top level map\n\nstatic const struct InstructionMap I_inst_map[] = {\n    {\"load\", IT_I, NOALU, NOMEM, LOAD_map, {}, 0x03, 0x7f, { .subfield = {3, 12} }, nullptr}, // LOAD\n    IM_UNKNOWN, // LOAD-FP\n    IM_UNKNOWN, // custom-0\n    {\"misc-mem\", IT_I, NOALU, NOMEM, MISC_MEM_map, {}, 0x0f, 0x7f, { .subfield = {3, 12} }, nullptr}, // MISC-MEM\n    {\"op-imm\", IT_I, NOALU, NOMEM, OP_IMM_map, {}, 0x13, 0x7f, { .subfield = {3, 12} }, nullptr}, // OP-IMM\n    {\"auipc\", IT_U, { .alu_op=AluOp::ADD }, NOMEM, nullptr, {\"d\", \"u\"}, 0x17, 0x7f, { .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE | IMF_PC_TO_ALU }, nullptr}, // AUIPC\n    {\"op-imm-32\", IT_I, NOALU, NOMEM, OP_IMM_32_map, {}, 0x1b, 0x7f, { .subfield = {3, 12} }, nullptr}, // OP-IMM-32    IM_UNKNOWN, // OP-IMM-32\n    IM_UNKNOWN, // 48b\n    {\"store\", IT_I, NOALU, NOMEM, STORE_map, {}, 0x23, 0x7f, { .subfield = {3, 12} }, nullptr}, // STORE\n    IM_UNKNOWN, // STORE-FP\n    IM_UNKNOWN, // custom-1\n    {\"amo\", IT_R, NOALU, NOMEM, AMO_map, {}, 0x2f, 0x7f, { .subfield = {3, 12} }, nullptr}, // OP-32\n    {\"op\", IT_R, NOALU, NOMEM, OP_map, {}, 0x33, 0x7f, { .subfield = {1, 25} }, nullptr}, // OP\n    {\"lui\", IT_U, { .alu_op=AluOp::ADD }, NOMEM, nullptr, {\"d\", \"u\"}, 0x37, 0x7f, { .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE }, nullptr}, // LUI\n    {\"op-32\", IT_R, NOALU, NOMEM, OP_32_map, {}, 0x3b, 0x7f, { .subfield = {1, 25} }, nullptr}, // OP-32\n    IM_UNKNOWN, // 64b\n    IM_UNKNOWN, // MADD\n    IM_UNKNOWN, // MSUB\n    IM_UNKNOWN, // NMSUB\n    IM_UNKNOWN, // NMADD\n    IM_UNKNOWN, // OP-FP\n    IM_UNKNOWN, // reserved\n    IM_UNKNOWN, // custom-2/rv128\n    IM_UNKNOWN, // 48b\n    {\"branch\", IT_B, NOALU, NOMEM, BRANCH_map, {}, 0x63, 0x7f, { .subfield = {3, 12} }, nullptr}, // BRANCH\n    {\"jalr\", IT_I, { .alu_op=AluOp::ADD }, NOMEM, nullptr, {\"d\", \"o(s)\"}, 0x67, 0x7f, { .flags =\nIMF_SUPPORTED | IMF_REGWRITE | IMF_BRANCH_JALR | IMF_ALUSRC | IMF_ALU_REQ_RS }, inst_aliases_jalr}, // JALR\n    IM_UNKNOWN, // reserved\n    {\"jal\", IT_J, { .alu_op=AluOp::ADD }, NOMEM, nullptr, {\"d\", \"a\"}, 0x6f, 0x7f, { .flags =\nIMF_SUPPORTED |\nIMF_REGWRITE | IMF_JUMP | IMF_PC_TO_ALU | IMF_ALUSRC }, inst_aliases_jal}, // JAL\n    {\"system\", IT_I, NOALU, NOMEM, SYSTEM_map, {}, 0x73, 0x7f, { .subfield = {3, 12} }, nullptr}, // SYSTEM\n    IM_UNKNOWN, // reserved\n    IM_UNKNOWN, // custom-3/rv128\n    IM_UNKNOWN, // >= 80b\n};\n\nstatic const struct InstructionMap C_inst_map[] = {\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    IM_UNKNOWN,\n    {\"i\", IT_UNKNOWN, NOALU, NOMEM, I_inst_map, {}, 0x3, 0x3, { .subfield = {5, 2} }, nullptr},\n};\n\nstatic const struct InstructionMap C_inst_unknown = IM_UNKNOWN;\n\n// clang-format on\n\nconst BitField instruction_map_opcode_field = { 2, 0 };\n\nstatic inline const struct InstructionMap &InstructionMapFind(uint32_t code) {\n    const struct InstructionMap *im = &C_inst_map[instruction_map_opcode_field.decode(code)];\n    while (im->subclass != nullptr) {\n        im = &im->subclass[im->subfield.decode(code)];\n    }\n    if ((code ^ im->code) & im->mask) { return C_inst_unknown; }\n    return *im;\n}\n\nconst std::array<const QString, 36> RECOGNIZED_PSEUDOINSTRUCTIONS { \"nop\",    \"la\",     \"li\",\n                                                                    \"sext.b\", \"sext.h\", \"zext.h\",\n                                                                    \"zext.w\", \"call\",   \"tail\" };\n\nbool Instruction::symbolic_registers_enabled = false;\nconst Instruction Instruction::NOP = Instruction(0x00000013);\nconst Instruction Instruction::UNKNOWN_INST = Instruction(0x0);\n\nInstruction::Instruction() {\n    this->dt = 0;\n}\n\nInstruction::Instruction(uint32_t inst) {\n    this->dt = inst;\n}\n\nInstruction::Instruction(const Instruction &i) {\n    this->dt = i.data();\n}\n\n#define MASK(LEN, OFF) ((this->dt >> (OFF)) & ((1 << (LEN)) - 1))\n\nuint8_t Instruction::opcode() const {\n    return (uint8_t)MASK(7, 0); // Does include the 2 bits marking it's not a\n                                // 16b instruction\n}\n\nuint8_t Instruction::rs() const {\n    return (uint8_t)MASK(5, 15);\n}\n\nuint8_t Instruction::rt() const {\n    return (uint8_t)MASK(5, 20);\n}\n\nuint8_t Instruction::rd() const {\n    return (uint8_t)MASK(5, 7);\n}\n\nuint8_t Instruction::shamt() const {\n    return this->rt();\n}\n\nuint16_t Instruction::funct() const {\n    return uint16_t(MASK(7, 25) << 3 | MASK(3, 12));\n}\n\nCSR::Address Instruction::csr_address() const {\n    return CSR::Address(MASK(12, 20));\n}\n\nint32_t Instruction::immediate() const {\n    int32_t ret = 0;\n    switch (this->type()) {\n    case R: break;\n    case I: ret = extend(MASK(12, 20), 12); break;\n    case S: ret = extend(MASK(7, 25) << 5 | MASK(5, 7), 12); break;\n    case B:\n        ret = extend(MASK(4, 8) << 1 | MASK(6, 25) << 5 | MASK(1, 7) << 11 | MASK(1, 31) << 12, 13);\n        break;\n    case U: ret = extend(MASK(20, 12) << 12, 32); break;\n    case J:\n        ret = extend(\n            MASK(10, 21) << 1 | MASK(1, 20) << 11 | MASK(8, 12) << 12 | MASK(1, 31) << 20, 21);\n        break;\n    case ZICSR:\n    case AMO:\n    case UNKNOWN: break;\n    }\n    return ret;\n}\n\nAddress Instruction::address() const {\n    return Address(MASK(26, 0));\n}\n\nuint32_t Instruction::data() const {\n    return this->dt;\n}\n\nbool Instruction::imm_sign() const {\n    return this->dt >> 31;\n}\n\nenum Instruction::Type Instruction::type() const {\n    const struct InstructionMap &im = InstructionMapFind(dt);\n    return im.type;\n}\n\nenum InstructionFlags Instruction::flags() const {\n    const struct InstructionMap &im = InstructionMapFind(dt);\n    return (enum InstructionFlags)im.flags;\n}\nAluCombinedOp Instruction::alu_op() const {\n    const struct InstructionMap &im = InstructionMapFind(dt);\n    return im.alu;\n}\n\nenum AccessControl Instruction::mem_ctl() const {\n    const struct InstructionMap &im = InstructionMapFind(dt);\n    return im.mem_ctl;\n}\n\nvoid Instruction::flags_alu_op_mem_ctl(\n    InstructionFlags &flags,\n    AluCombinedOp &alu_op,\n    AccessControl &mem_ctl) const {\n    const struct InstructionMap &im = InstructionMapFind(dt);\n    flags = (enum InstructionFlags)im.flags;\n    alu_op = im.alu;\n    mem_ctl = im.mem_ctl;\n}\n\nbool Instruction::operator==(const Instruction &c) const {\n    return (this->data() == c.data());\n}\n\nbool Instruction::operator!=(const Instruction &c) const {\n    return !this->operator==(c);\n}\n\nInstruction &Instruction::operator=(const Instruction &c) {\n    if (this != &c) { this->dt = c.data(); }\n    return *this;\n}\n\nQString Instruction::to_str(Address inst_addr) const {\n    const InstructionMap &im = InstructionMapFind(dt);\n    // TODO there are exception where some fields are zero and such so we should\n    // not print them in such case\n    SANITY_ASSERT(argdesbycode_filled, QString(\"argdesbycode_filled not initialized\"));\n    QString res;\n    QString next_delim = \" \";\n    if (im.type == UNKNOWN) { return { \"unknown\" }; }\n    if (this->dt == NOP.dt) { return { \"nop\" }; }\n\n    res += im.name;\n    for (const QString &arg_string : im.args) {\n        res += next_delim;\n        next_delim = \", \";\n        for (int pos = 0; pos < arg_string.size(); pos += 1) {\n            char arg_letter = arg_string[pos].toLatin1();\n            const ArgumentDesc *arg_desc = arg_desc_by_code[(unsigned char)arg_letter];\n            if (arg_desc == nullptr) {\n                res += arg_letter;\n                continue;\n            }\n            auto field = (int32_t)arg_desc->arg.decode(this->dt);\n            if (arg_desc->min < 0) {\n                field = extend(field, [&]() {\n                    int sum = (int)arg_desc->arg.shift;\n                    for (auto chunk : arg_desc->arg) {\n                        sum += chunk.count;\n                    }\n                    return sum;\n                }());\n            }\n            switch (arg_desc->kind) {\n            case 'g': {\n                if (symbolic_registers_enabled) {\n                    res += QString(Rv_regnames[field]);\n                } else {\n                    res += \"x\" + QString::number(field);\n                }\n                break;\n            }\n            case 'p':\n            case 'a': {\n                field += (int32_t)inst_addr.get_raw();\n                res.append(str::asHex(uint32_t(field)));\n                break;\n            }\n            case 'o':\n            case 'n': {\n                if (arg_desc->min < 0) {\n                    res += QString::number((int32_t)field, 10);\n                } else {\n                    res.append(str::asHex(uint32_t(field)));\n                }\n                break;\n            }\n            case 'E': {\n                if (symbolic_registers_enabled) {\n                    try {\n                        res += CSR::REGISTERS[CSR::REGISTER_MAP.at(CSR::Address(field))].name;\n                    } catch (std::out_of_range &e) { res.append(str::asHex(field)); }\n                } else {\n                    res.append(str::asHex(field));\n                }\n                break;\n            }\n            }\n        }\n    }\n    return res;\n}\n\nQMultiMap<QString, uint32_t> str_to_instruction_code_map;\n\nstatic void instruction_from_string_build_base_aliases(\n    uint32_t base_code,\n    uint32_t base_mask,\n    const InstructionMap *ia) {\n    for (; ia->name != nullptr; ia++) {\n        if ((ia->code ^ base_code) & base_mask) {\n            ERROR(\n                \"alias code mismatch %s computed 0x%08\" PRIx32 \" (mask 0x%08\" PRIx32\n                \") found 0x%08\" PRIx32,\n                ia->name, base_code, base_mask, ia->code);\n            continue;\n        }\n        if (~ia->mask & base_mask) {\n            ERROR(\n                \"aliase code mismatch %s computed 0x%08\" PRIx32 \" (mask 0x%08\" PRIx32\n                \") found 0x%08\" PRIx32 \" with too wide mask 0x%08\" PRIx32,\n                ia->name, base_code, base_mask, ia->code, ia->mask);\n            continue;\n        }\n        bool found = false;\n        auto iter_range = str_to_instruction_code_map.equal_range(ia->name);\n        for (auto i = iter_range.first; i != iter_range.second; i += 1) {\n            if (i.value() == base_code) {\n                found = true;\n                break;\n            }\n        }\n        if (found) continue;\n\n        // store base code, the iteration over alliases is required anyway\n        str_to_instruction_code_map.insert(ia->name, base_code);\n    }\n}\n\nvoid instruction_from_string_build_base(\n    const InstructionMap *im,\n    BitField field,\n    uint32_t base_code,\n    uint32_t base_mask) {\n    uint32_t code;\n    uint8_t bits = field.count;\n    uint8_t shift = field.offset;\n\n    base_mask |= (((uint32_t)1 << bits) - 1) << shift;\n\n    for (unsigned int i = 0; i < 1U << bits; i++, im++) {\n        code = base_code | (i << shift);\n        if (im->subclass) {\n            instruction_from_string_build_base(im->subclass, im->subfield, code, base_mask);\n            continue;\n        }\n        if (!(im->flags & IMF_SUPPORTED)) { continue; }\n        if ((im->code ^ code) & base_mask) {\n            ERROR(\n                \"code mismatch %s computed 0x%08\" PRIx32 \" (mask 0x%08\" PRIx32\n                \") found 0x%08\" PRIx32,\n                im->name, code, base_mask, im->code);\n            continue;\n        }\n        if (~im->mask & base_mask) {\n            ERROR(\n                \"code mismatch %s computed 0x%08\" PRIx32 \" (mask 0x%08\" PRIx32\n                \") found 0x%08\" PRIx32 \" with too wide mask 0x%08\" PRIx32,\n                im->name, code, base_mask, im->code, im->mask);\n            continue;\n        }\n        str_to_instruction_code_map.insert(im->name, im->code);\n\n        if (im->aliases != nullptr)\n            instruction_from_string_build_base_aliases(im->code, im->mask, im->aliases);\n    }\n#if 0\n    for (auto i = str_to_instruction_code_map.begin();\n         i != str_to_instruction_code_map.end(); i++)\n        std::cout << i.key().toStdString() << ' ';\n#endif\n}\n\nvoid instruction_from_string_build_base() {\n    return instruction_from_string_build_base(C_inst_map, instruction_map_opcode_field, 0, 0);\n}\n\nstatic int parse_reg_from_string(const QString &str, uint *chars_taken = nullptr) {\n    if (str.size() < 2) { return -1; }\n    if (str.at(0) == 'x') {\n        int res = 0;\n        int ctk = 1;\n        for (; ctk < str.size(); ctk += 1) {\n            auto c = str.at(ctk);\n            if (c >= '0' && c <= '9') {\n                res *= 10;\n                res += c.unicode() - '0';\n            } else {\n                break;\n            }\n        }\n        if (ctk == 0) {\n            return -1;\n        } else {\n            *chars_taken = ctk;\n            return res;\n        }\n    } else {\n        auto data = str.toLocal8Bit();\n        int regnum = -1;\n        for (size_t i = 0; i < Rv_regnames.size(); i++) {\n            size_t len = std::strlen(Rv_regnames[i]);\n            if (size_t(data.size()) < len) continue;\n            if (std::strncmp(data.data(), Rv_regnames[i], len) == 0) {\n                *chars_taken = len;\n                regnum = (int)i;\n            }\n        }\n        return regnum;\n    }\n}\n\nconst QString reloc_operators = QStringLiteral(\"+-/*|&^~\");\nconst QString reloc_special_chars = QStringLiteral(\"()%_\");\n\n/** Takes largest sequence of valid relocation expression chars and removes whitespaces */\nstatic std::pair<QString, uint32_t> read_reloc_expression(const QString &input) {\n    QString expression;\n    uint32_t chars_taken = 0;\n    bool prev_was_operator = false;\n    bool is_modifier = false;\n    for (QChar ch : input) {\n        bool is_operator = reloc_operators.contains(ch);\n\n        if (ch == '(' && !prev_was_operator) {\n            if (is_modifier) {\n                is_modifier = false;\n            } else {\n                // This is a start of a new field.\n                break;\n            }\n        }\n\n        if (ch.isLetterOrNumber() || is_operator || reloc_special_chars.contains(ch)) {\n            expression.append(ch);\n            if (ch == '%') { is_modifier = true; }\n        } else if (!ch.isSpace()) {\n            break;\n        }\n\n        chars_taken += 1;\n        prev_was_operator = is_operator;\n    }\n    return { expression, chars_taken };\n}\n\nstatic void reloc_append(\n    RelocExpressionList *reloc,\n    const QString &fl,\n    Address inst_addr,\n    int64_t offset,\n    const ArgumentDesc *adesc,\n    uint *chars_taken = nullptr,\n    const QString &filename = \"\",\n    int line = 0,\n    Instruction::Modifier pseudo_mod = machine::Instruction::Modifier::NONE) {\n    auto [expression, chars_taken_] = read_reloc_expression(fl);\n    if (expression.size() > 0) {\n        // Do not append empty relocation expressions\n        reloc->append(new RelocExpression(\n            inst_addr, expression, offset, adesc->min, adesc->max, &adesc->arg, filename, line,\n            pseudo_mod));\n    }\n    if (chars_taken != nullptr) { *chars_taken = chars_taken_; }\n}\n\nsize_t Instruction::code_from_tokens(\n    uint32_t *code,\n    size_t buffsize,\n    TokenizedInstruction &inst,\n    RelocExpressionList *reloc,\n    bool pseudoinst_enabled) {\n    if (str_to_instruction_code_map.isEmpty()) { instruction_from_string_build_base(); }\n\n    Instruction result = base_from_tokens(inst, reloc);\n    if (result.data() != 0) {\n        if (result.size() > buffsize) {\n            // NOTE: this is bug, not user error.\n            throw ParseError(\"insufficient buffer size to write parsed instruction\");\n        }\n        *code = result.data();\n        return result.size();\n    }\n\n    if (pseudoinst_enabled) {\n        size_t pseudo_result = pseudo_from_tokens(code, buffsize, inst, reloc);\n        if (pseudo_result != 0) { return pseudo_result; }\n    }\n    throw ParseError(\"unknown instruction\");\n}\nsize_t Instruction::pseudo_from_tokens(\n    uint32_t *code,\n    size_t buffsize,\n    TokenizedInstruction &inst,\n    RelocExpressionList *reloc) {\n    constexpr Modifier UPPER = Modifier::COMPOSED_IMM_UPPER;\n    constexpr Modifier LOWER = Modifier::COMPOSED_IMM_LOWER;\n\n    if (inst.base == QLatin1String(\"nop\")) {\n        Instruction result;\n        if (!inst.fields.empty()) { throw ParseError(\"`nop` does not allow any arguments\"); }\n        result = Instruction::NOP;\n        *code = result.data();\n        return result.size();\n    }\n    if ((inst.base == QLatin1String(\"la\")) && (buffsize >= 8)) {\n        if (inst.fields.size() != 2) { throw ParseError(\"number of arguments does not match\"); }\n        *code = base_from_tokens(\n                    { \"auipc\", inst.fields, inst.address, inst.filename, inst.line }, reloc, UPPER,\n                    -inst.address.get_raw())\n                    .data();\n        code += 1;\n        inst.fields.insert(0, inst.fields.at(0));\n        *code = base_from_tokens(\n                    { \"addi\", inst.fields, inst.address + 4, inst.filename, inst.line }, reloc,\n                    LOWER, -inst.address.get_raw())\n                    .data();\n        return 8;\n    }\n\n    if ((inst.base == QLatin1String(\"li\")) && (buffsize >= 8)) {\n        if (inst.fields.size() != 2) { throw ParseError(\"number of arguments does not match\"); }\n        *code = base_from_tokens(\n                    { \"lui\", inst.fields, inst.address, inst.filename, inst.line }, reloc, UPPER)\n                    .data();\n        code += 1;\n        inst.fields.insert(0, inst.fields.at(0));\n        *code\n            = base_from_tokens(\n                  { \"addi\", inst.fields, inst.address + 4, inst.filename, inst.line }, reloc, LOWER)\n                  .data();\n        return 8;\n    }\n\n    if ((inst.base == QLatin1String(\"call\")) && (buffsize >= 8)) {\n        if (inst.fields.size() != 1) { throw ParseError(\"number of arguments does not match\"); }\n        inst.fields.insert(0, \"x6\");\n        *code = base_from_tokens(\n                    { \"auipc\", inst.fields, inst.address, inst.filename, inst.line }, reloc, UPPER,\n                    -inst.address.get_raw())\n                    .data();\n        code += 1;\n        inst.fields[0] = QString(\"x1\");\n        inst.fields[1] = QString(\"%0(x6)\").arg(inst.fields[1]);\n        *code = base_from_tokens(\n                    { \"jalr\", inst.fields, inst.address + 4, inst.filename, inst.line }, reloc,\n                    LOWER, -inst.address.get_raw())\n                    .data();\n        return 8;\n    }\n\n    if ((inst.base == QLatin1String(\"tail\")) && (buffsize >= 8)) {\n        if (inst.fields.size() != 1) { throw ParseError(\"number of arguments does not match\"); }\n        inst.fields.insert(0, \"x6\");\n        *code = base_from_tokens(\n                    { \"auipc\", inst.fields, inst.address, inst.filename, inst.line }, reloc, UPPER,\n                    -inst.address.get_raw())\n                    .data();\n        code += 1;\n        inst.fields[0] = QString(\"x0\");\n        inst.fields[1] = QString(\"%0(x6)\").arg(inst.fields[1]);\n        *code = base_from_tokens(\n                    { \"jalr\", inst.fields, inst.address + 4, inst.filename, inst.line }, reloc,\n                    LOWER, -inst.address.get_raw())\n                    .data();\n        return 8;\n    }\n\n    if (inst.base[0] == 's') {\n        if ((inst.base == QLatin1String(\"sext.b\")) && (buffsize >= 8)) {\n            if (inst.fields.size() != 2) { throw ParseError(\"number of arguments does not match\"); }\n            inst.base = \"slli\";\n            inst.fields.append(\"XLEN-8\");\n            *code = base_from_tokens(inst, reloc).data();\n            code += 1;\n            inst.base = \"srai\";\n            inst.fields[1] = inst.fields[0];\n            *code = base_from_tokens(inst, reloc).data();\n            return 8;\n        }\n        if ((inst.base == QLatin1String(\"sext.h\")) && (buffsize >= 8)) {\n            if (inst.fields.size() != 2) { throw ParseError(\"number of arguments does not match\"); }\n            inst.base = \"slli\";\n            inst.fields.append(\"XLEN-16\");\n            *code = base_from_tokens(inst, reloc).data();\n            code += 1;\n            inst.base = \"srai\";\n            inst.fields[1] = inst.fields[0];\n            *code = base_from_tokens(inst, reloc).data();\n            return 8;\n        }\n    }\n    if (inst.base[0] == 'z') {\n        if ((inst.base == QLatin1String(\"zext.h\")) && (buffsize >= 8)) {\n            if (inst.fields.size() != 2) { throw ParseError(\"number of arguments does not match\"); }\n            inst.base = \"slli\";\n            inst.fields.append(\"XLEN-16\");\n            *code = base_from_tokens(inst, reloc).data();\n            code += 1;\n            inst.base = \"srli\";\n            inst.fields[1] = inst.fields[0];\n            *code = base_from_tokens(inst, reloc).data();\n            return 8;\n        }\n        if ((inst.base == QLatin1String(\"zext.w\")) && (buffsize >= 8)) {\n            if (inst.fields.size() != 2) { throw ParseError(\"number of arguments does not match\"); }\n            inst.base = \"slli\";\n            inst.fields.append(\"XLEN-32\");\n            *code = base_from_tokens(inst, reloc).data();\n            code += 1;\n            inst.base = \"srli\";\n            inst.fields[1] = inst.fields[0];\n            *code = base_from_tokens(inst, reloc).data();\n            return 8;\n        }\n    }\n    return 0;\n}\nsize_t Instruction::partially_apply(\n    const char *base,\n    int argument_count,\n    int position,\n    const char *value,\n    uint32_t *code,\n    size_t buffsize,\n    TokenizedInstruction &inst,\n    RelocExpressionList *reloc) {\n    if (inst.fields.size() != argument_count) {\n        throw ParseError(\"number of arguments does not match\");\n    }\n    inst.base = base;\n    inst.fields.insert(position, value);\n    return code_from_tokens(code, buffsize, inst, reloc, false);\n}\n\nstatic void instruction_code_map_next_im(const InstructionMap *&im, bool &processing_aliases) {\n    if (!processing_aliases) {\n        processing_aliases = true;\n        im = im->aliases;\n    } else {\n        im++;\n        if (im->name == nullptr) im = nullptr;\n    }\n}\n\nInstruction Instruction::base_from_tokens(\n    const TokenizedInstruction &inst,\n    RelocExpressionList *reloc,\n    Modifier pseudo_mod,\n    uint64_t initial_immediate_value) {\n    int rethrow = false;\n    ParseError parse_error = ParseError(\"no match for arguments combination found\");\n    auto iter_range = str_to_instruction_code_map.equal_range(inst.base);\n    if (iter_range.first == iter_range.second) {\n        DEBUG(\"Base instruction of the name %s not found.\", qPrintable(inst.base));\n        return Instruction::UNKNOWN_INST;\n    }\n    // Process all codes associated with given instruction name and try matching the supplied\n    // instruction field tokens to fields. First matching instruction is used.\n    for (auto it = iter_range.first; it != iter_range.second; it++) {\n        uint32_t inst_code = it.value();\n        bool processing_aliases = false;\n\n        const InstructionMap *im = &InstructionMapFind(inst_code);\n        for (; im != nullptr; instruction_code_map_next_im(im, processing_aliases)) {\n            if (inst.base != im->name) continue;\n\n            try {\n                inst_code = im->code;\n\n                if (inst.fields.count() != (int)im->args.size()) {\n                    if (!rethrow) {\n                        parse_error = ParseError(\"number of arguments does not match\");\n                        rethrow = true;\n                    }\n                    continue;\n                }\n\n                for (int field_index = 0; field_index < (int)im->args.size(); field_index++) {\n                    const QString &arg = im->args[field_index];\n                    QString field_token = inst.fields[field_index];\n                    inst_code |= parse_field(\n                        field_token, arg, inst.address, reloc, inst.filename, inst.line, pseudo_mod,\n                        initial_immediate_value);\n                }\n                return Instruction(inst_code);\n            } catch (ParseError &pe) {\n                rethrow = true;\n                parse_error = pe;\n            }\n        }\n    }\n\n    if (rethrow) { throw parse_error; }\n\n    DEBUG(\n        \"Base instruction of the name %s not matched to any known base format.\",\n        qPrintable(inst.base));\n    // Another instruction format for this base may be found in pseudoinstructions.\n    return Instruction::UNKNOWN_INST;\n}\n\nuint16_t parse_csr_address(const QString &field_token, uint &chars_taken);\n\nbool parse_immediate_value(\n    const QString &field_token,\n    Address &inst_addr,\n    RelocExpressionList *reloc,\n    const QString &filename,\n    unsigned int line,\n    bool need_reloc,\n    const ArgumentDesc *adesc,\n    const Instruction::Modifier &effective_mod,\n    uint64_t &val,\n    uint &chars_taken);\n\nuint32_t Instruction::parse_field(\n    QString &field_token,\n    const QString &arg,\n    Address inst_addr,\n    RelocExpressionList *reloc,\n    const QString &filename,\n    unsigned int line,\n    Modifier pseudo_mod,\n    uint64_t initial_immediate_value) {\n    uint32_t inst_code = 0;\n    for (QChar ao : arg) {\n        bool need_reloc = false;\n        uint a = ao.toLatin1();\n        if (!a) { continue; }\n        field_token = field_token.trimmed();\n        const ArgumentDesc *adesc = arg_desc_by_code[a];\n        if (adesc == nullptr) {\n            if (!field_token.count()) { throw ParseError(\"empty argument encountered\"); }\n            if (field_token.at(0) != ao) {\n                throw ParseError(\"argument does not match instruction template\");\n            }\n            field_token = field_token.mid(1);\n            continue;\n        }\n\n        // Only apply modifier to immediate fields\n        const Modifier effective_mod = (adesc->is_imm()) ? pseudo_mod : Modifier::NONE;\n\n        uint64_t val = 0;\n        uint chars_taken = 0;\n\n        switch (adesc->kind) {\n        case 'g': val += parse_reg_from_string(field_token, &chars_taken); break;\n        case 'p':\n        case 'a': val -= inst_addr.get_raw(); FALLTROUGH\n        case 'o':\n        case 'n': {\n            val += initial_immediate_value;\n            if (!parse_immediate_value(\n                    field_token, inst_addr, reloc, filename, line, need_reloc, adesc, effective_mod,\n                    val, chars_taken)) {\n                throw ParseError(\n                    QString(\"field_token %1 is not a valid immediate value\").arg(field_token));\n            }\n            break;\n        }\n        case 'E': val = parse_csr_address(field_token, chars_taken); break;\n        }\n        if (chars_taken <= 0) { throw ParseError(\"argument parse error\"); }\n\n        if (effective_mod != Modifier::NONE) {\n            val = modify_pseudoinst_imm(effective_mod, val);\n        } else if (!adesc->is_value_in_field_range(val)) {\n            throw ParseError(\"argument range exceed\");\n        }\n\n        inst_code |= adesc->arg.encode(val);\n        field_token = field_token.mid(chars_taken);\n    }\n    if (field_token.trimmed() != \"\") { throw ParseError(\"excessive characters in argument\"); }\n    return inst_code;\n}\n\nbool parse_immediate_value(\n    const QString &field_token,\n    Address &inst_addr,\n    RelocExpressionList *reloc,\n    const QString &filename,\n    unsigned int line,\n    bool need_reloc,\n    const ArgumentDesc *adesc,\n    const Instruction::Modifier &effective_mod,\n    uint64_t &val,\n    uint &chars_taken) {\n    if (field_token.at(0).isDigit() || field_token.at(0) == '-' || (reloc == nullptr)) {\n        uint64_t num_val = 0;\n        // Qt functions are limited, toLongLong would be usable\n        // but does not return information how many characters\n        // are processed. Used solution has horrible overhead\n        // but is usable for now\n        int i;\n        char cstr[field_token.count() + 1];\n        for (i = 0; i < field_token.count(); i++) {\n            cstr[i] = field_token.at(i).toLatin1();\n        }\n        cstr[i] = 0;\n        const char *p = cstr;\n        char *r;\n        if (adesc->min < 0) {\n            num_val = strtoll(p, &r, 0);\n        } else {\n            num_val = strtoull(p, &r, 0);\n        }\n        while (*r && isspace(*r)) {\n            r++;\n        }\n        chars_taken = r - p;\n        if (*r && strchr(\"+-/*|&^~%\", *r)) {\n            need_reloc = true;\n        } else {\n            // extend signed bits\n            val += num_val;\n        }\n    } else {\n        need_reloc = true;\n    }\n    if (need_reloc && (reloc != nullptr)) {\n        reloc_append(\n            reloc, field_token, inst_addr, val, adesc, &chars_taken, filename, line, effective_mod);\n        val = 0;\n    }\n    return chars_taken != 0;\n}\n\nuint16_t parse_csr_address(const QString &field_token, uint &chars_taken) {\n    if (field_token.at(0).isLetter()) {\n        try {\n            size_t index = CSR::REGISTER_MAP_BY_NAME.at(field_token.toStdString());\n            auto &reg = CSR::REGISTERS[index];\n            chars_taken = strlen(reg.name);\n            return reg.address.data;\n        } catch (std::out_of_range &e) {\n            chars_taken = 0;\n            return 0;\n        }\n    } else {\n        char *r;\n        uint64_t val;\n        const char *str = field_token.toLocal8Bit().constData();\n        val = strtoul(str, &r, 0);\n        chars_taken = r - str;\n        return val;\n    }\n}\n\nbool Instruction::update(int64_t val, RelocExpression *relocexp) {\n    // Clear all bit of the updated argument.\n    dt &= ~relocexp->arg->encode(~0);\n    val += relocexp->offset;\n\n    if (relocexp->pseudo_mod != Modifier::NONE) {\n        val = (int64_t)modify_pseudoinst_imm(relocexp->pseudo_mod, val);\n    } else {\n        if ((val & ((1 << relocexp->arg->shift) - 1))) { return false; }\n        if (relocexp->min < 0) {\n            if (((int64_t)val < relocexp->min) || ((int64_t)val > relocexp->max)) {\n                if (((int64_t)val - 0x100000000 < relocexp->min)\n                    || ((int64_t)val - 0x100000000 > relocexp->max)) {\n                    return false;\n                }\n            }\n        } else {\n            if (((uint64_t)val < (uint64_t)relocexp->min)\n                || ((uint64_t)val > (uint64_t)relocexp->max)) {\n                return false;\n            }\n        }\n    }\n\n    dt |= relocexp->arg->encode(val);\n    return true;\n}\n\nconstexpr uint64_t Instruction::modify_pseudoinst_imm(Instruction::Modifier mod, uint64_t value) {\n    // Example: la rd, symbol -> auipc rd, symbol[31:12] + symbol[11], addi rd, rd, symbol[11:0]\n\n    switch (mod) {\n    case Modifier::NONE: return value;\n    case Modifier::COMPOSED_IMM_UPPER: return get_bits(value, 31, 12) + get_bit(value, 11);\n    case Modifier::COMPOSED_IMM_LOWER: return get_bits(value, 11, 0);\n    default: UNREACHABLE\n    }\n}\n\n// highlighter\nvoid Instruction::append_recognized_instructions(QStringList &list) {\n    if (str_to_instruction_code_map.isEmpty()) { instruction_from_string_build_base(); }\n\n    for (auto iter = str_to_instruction_code_map.keyBegin();\n         iter != str_to_instruction_code_map.keyEnd(); iter++) {\n        list.append(*iter);\n    }\n    for (const auto &str : RECOGNIZED_PSEUDOINSTRUCTIONS) {\n        list.append(str);\n    }\n}\n\nvoid Instruction::set_symbolic_registers(bool enable) {\n    symbolic_registers_enabled = enable;\n}\n\ninline int32_t Instruction::extend(uint32_t value, uint32_t used_bits) const {\n    return value | ~((value & (1 << (used_bits - 1))) - 1);\n}\n\nvoid Instruction::append_recognized_registers(QStringList &list) {\n    for (auto name : Rv_regnames) {\n        list.append(name);\n    }\n}\nuint8_t Instruction::size() const {\n    return 4;\n}\nsize_t Instruction::code_from_string(\n    uint32_t *code,\n    size_t buffsize,\n    QString str,\n    Address inst_addr,\n    RelocExpressionList *reloc,\n    const QString &filename,\n    unsigned line,\n    bool pseudoinst_enabled) {\n    auto inst = TokenizedInstruction::from_line(std::move(str), inst_addr, filename, line);\n    return Instruction::code_from_tokens(code, buffsize, inst, reloc, pseudoinst_enabled);\n}\n\nInstruction::ParseError::ParseError(QString message) : message(std::move(message)) {}\n\nTokenizedInstruction TokenizedInstruction::from_line(\n    QString line_str,\n    Address inst_addr,\n    const QString &filename,\n    unsigned line) {\n    int start = 0, end;\n    while (start < line_str.size()) {\n        if (!line_str.at(start).isSpace()) { break; }\n        start++;\n    }\n    end = start;\n    while (end < line_str.size()) {\n        if (line_str.at(end).isSpace()) { break; }\n        end++;\n    }\n    QString inst_base = line_str.mid(start, end - start).toLower();\n    if (!inst_base.size()) { throw Instruction::ParseError(\"empty instruction field\"); }\n\n    line_str = line_str.mid(end + 1).trimmed();\n    QStringList inst_fields;\n    if (line_str.size() > 0) { inst_fields = line_str.split(\",\"); }\n\n    return { inst_base, inst_fields, inst_addr, filename, line };\n}\n\nTokenizedInstruction::TokenizedInstruction(\n    QString base,\n    QStringList fields,\n    const Address &address,\n    QString filename,\n    unsigned int line)\n    : base(std::move(base))\n    , fields(std::move(fields))\n    , address(address)\n    , filename(std::move(filename))\n    , line(line) {}\n"
  },
  {
    "path": "src/machine/instruction.h",
    "content": "#ifndef INSTRUCTION_H\n#define INSTRUCTION_H\n\n#include \"bitfield.h\"\n#include \"common/containers/cvector.h\"\n#include \"csr/address.h\"\n#include \"execute/alu.h\"\n#include \"machinedefs.h\"\n\n#include <QObject>\n#include <QString>\n#include <QStringList>\n#include <QVector>\n#include <array>\n#include <utility>\n\nnamespace machine {\n\n// 4 is max number of parts in currently used instructions.\nusing BitArg = SplitBitField<4>;\n\nstatic constexpr std::array<const char *const, 32> Rv_regnames = {\n    \"zero\", \"ra\", \"sp\", \"gp\", \"tp\",  \"t0\",  \"t1\", \"t2\", \"s0\", \"s1\", \"a0\",\n    \"a1\",   \"a2\", \"a3\", \"a4\", \"a5\",  \"a6\",  \"a7\", \"s2\", \"s3\", \"s4\", \"s5\",\n    \"s6\",   \"s7\", \"s8\", \"s9\", \"s10\", \"s11\", \"t3\", \"t4\", \"t5\", \"t6\",\n};\n\nenum InstructionFlags : unsigned {\n    IMF_SUPPORTED = 1L << 0,  /**< Instruction is supported */\n    IMF_MEMWRITE = 1L << 1,   /**< Write to the memory when memory stage is reached */\n    IMF_MEMREAD = 1L << 2,    /**< Read from the memory when memory stage is reached */\n    IMF_ALUSRC = 1L << 3,     /**< The second ALU source is immediate operand */\n    IMF_REGWRITE = 1L << 4,   /**< Instruction result (ALU or memory) is written to register file */\n    IMF_MEM = 1L << 5,        /**< Instruction is memory access instruction */\n    IMF_ALU_REQ_RS = 1L << 6, /**< Execution phase/ALU requires RS value */\n    IMF_ALU_REQ_RT = 1L << 7, /**< Execution phase/ALU/mem requires RT value */\n    IMF_BRANCH = 1L << 8,  /**< Operation is conditional or unconditional branch or branch and link\n                               when PC_TO_R31 is set */\n    IMF_JUMP = 1L << 9,    /**< Jump operation - JAL, JALR */\n    IMF_BJ_NOT = 1L << 10, /**< Negate condition for branch instruction */\n    IMF_BRANCH_JALR = 1L << 11, /**< Use ALU output as branch/jump target. Used by JALR. */\n    IMF_EXCEPTION = 1L << 12,   /**< Instruction causes synchronous exception */\n    IMF_ALU_MOD = 1L << 13,     /**< ADD and right-shift modifier */\n    IMF_PC_TO_ALU = 1L << 14,   /**< PC is loaded instead of RS to ALU */\n    IMF_FORCE_W_OP = 1L << 15,  /**< Force word (32-bit) operation even for XLEN=64  */\n    IMF_ECALL = 1L << 16,       // seems easiest to encode ecall and ebreak as flags, but they might\n    IMF_EBREAK = 1L << 17,      // be moved elsewhere in case we run out of InstructionFlag space.\n    IMF_XRET = 1L << 18,        /**< Return from exception, MRET and SRET  */\n\n    // Extensions:\n    // =============================================================================================\n    // RV64/32M\n    IMF_MUL = 1L << 19, /**< Enables multiplication component of ALU. */\n    // Zicsr\n    IMF_CSR = 1L << 20,        /**< Implies csr read and write */\n    IMF_CSR_TO_ALU = 1L << 21, /**< Instruction modifies the current value */\n    IMF_ALU_RS_ID = 1L << 22,\n    // RV64/32A - Atomic Memory Operations\n    IMF_AMO = 1L << 23, /**< Instruction is AMO */\n    // TODO do we want to add those signals to the visualization?\n\n    IMF_RV64 = 1L << 24, /**< Mark instructions which are available in 64-bit mode only. */\n\n    /* Privilege requirement flags */\n    IMF_PRIV_S = 1L << 25, /**< Requires at least Supervisor privilege */\n    IMF_PRIV_H = 1L << 26, /**< Requires at least Hypervisor privilege */\n    IMF_PRIV_M = 1L << 27, /**< Requires Machine privilege */\n};\n\n/**\n * Collection of data necessary to parse instruction from tokens.\n *\n * @TODO Switch to QStringView\n */\nstruct TokenizedInstruction {\n    QString base;\n    QStringList fields;\n    Address address;\n    QString filename;\n    unsigned line;\n\npublic:\n    TokenizedInstruction(\n        QString base,\n        QStringList fields,\n        const Address &address,\n        QString filename,\n        unsigned int line);\n\n    /** Tokenize assembler line */\n    static TokenizedInstruction\n    from_line(QString line_str, Address inst_addr, const QString &filename, unsigned line);\n};\n\nstruct RelocExpression;\ntypedef QVector<RelocExpression *> RelocExpressionList;\n\nclass Instruction {\npublic:\n    Instruction();\n    explicit Instruction(uint32_t inst);\n    // Instruction(\n    //     uint8_t opcode,\n    //     uint8_t rs,\n    //     uint8_t rt,\n    //     uint8_t rd,\n    //     uint8_t shamt,\n    //     uint8_t funct); // Type R\n    // Instruction(\n    //     uint8_t opcode,\n    //     uint8_t rs,\n    //     uint8_t rt,\n    //     uint16_t immediate);                      // Type I\n    // Instruction(uint8_t opcode, Address address); // Type J\n    Instruction(const Instruction &);\n\n    static const Instruction NOP;\n    static const Instruction UNKNOWN_INST;\n\n    enum Type { R, I, S, B, U, J, ZICSR, AMO, UNKNOWN };\n\n    /** Modified encoding to enable pseudoinstructions. */\n    enum class Modifier {\n        /** Normal processing. All fields are checked for value max and min. */\n        NONE,\n        /**\n         * Encodes upper part of immediate from a pseudoinstruction.\n         *\n         * `imm = symbol[31:12] + symbol[11]`\n         * NOTE: `symbol[11]` compensates for sign extension from addition of the lower part.\n         */\n        COMPOSED_IMM_UPPER,\n        /**\n         * Encodes lower part of immediate from a pseudoinstruction.\n         *\n         * `imm = symbol[11:0]`\n         * Upper bits of immediate may are discarded.\n         */\n        COMPOSED_IMM_LOWER,\n    };\n\n    struct ParseError;\n\n    /** Returns size of instruction in bytes */\n    uint8_t size() const;\n    uint8_t opcode() const;\n    uint8_t rs() const;\n    uint8_t rt() const;\n    uint8_t rd() const;\n    uint8_t shamt() const;\n    uint16_t funct() const;\n    machine::CSR::Address csr_address() const;\n    int32_t immediate() const;\n    Address address() const;\n    uint32_t data() const;\n    bool imm_sign() const;\n    enum Type type() const;\n    enum InstructionFlags flags() const;\n    AluCombinedOp alu_op() const;\n    enum AccessControl mem_ctl() const;\n\n    void flags_alu_op_mem_ctl(\n        enum InstructionFlags &flags,\n        AluCombinedOp &alu_op,\n        enum AccessControl &mem_ctl) const;\n\n    bool operator==(const Instruction &c) const;\n    bool operator!=(const Instruction &c) const;\n    Instruction &operator=(const Instruction &c);\n\n    QString to_str(Address inst_addr = Address::null()) const;\n\n    /**\n     * Parses instruction from string containing one assembler line.\n     *\n     * @throws Instruction::ParseError if unable to parse\n     */\n    static size_t code_from_string(\n        uint32_t *code,\n        size_t buffsize,\n        QString str,\n        Address inst_addr,\n        RelocExpressionList *reloc = nullptr,\n        const QString &filename = \"\",\n        unsigned line = 0,\n        bool pseudoinst_enabled = true);\n\n    /**\n     * Parses instruction from prepare tokenized form.\n     *\n     * @throws Instruction::ParseError if unable to parse\n     */\n    static size_t code_from_tokens(\n        uint32_t *code,\n        size_t buffsize,\n        TokenizedInstruction &inst,\n        RelocExpressionList *reloc = nullptr,\n        bool pseudoinst_enabled = true);\n\n    bool update(int64_t val, RelocExpression *relocexp);\n\n    static void append_recognized_instructions(QStringList &list);\n    static void set_symbolic_registers(bool enable);\n    static void append_recognized_registers(QStringList &list);\n    static constexpr uint64_t modify_pseudoinst_imm(Modifier mod, uint64_t value);\n\nprivate:\n    uint32_t dt;\n    static bool symbolic_registers_enabled;\n\n    static Instruction base_from_tokens(\n        const TokenizedInstruction &inst,\n        RelocExpressionList *reloc,\n        Modifier pseudo_mod = Modifier::NONE,\n        uint64_t initial_immediate_value = 0);\n    inline int32_t extend(uint32_t value, uint32_t used_bits) const;\n    static uint32_t parse_field(\n        QString &field_token,\n        const QString &arg,\n        Address inst_addr,\n        RelocExpressionList *reloc,\n        const QString &filename,\n        unsigned int line,\n        Modifier pseudo_mod,\n        uint64_t initial_immediate_value);\n    static size_t partially_apply(\n        const char *base,\n        int argument_count,\n        int position,\n        const char *value,\n        uint32_t *code,\n        size_t buffsize,\n        TokenizedInstruction &inst,\n        RelocExpressionList *reloc);\n    static size_t pseudo_from_tokens(\n        uint32_t *code,\n        size_t buffsize,\n        TokenizedInstruction &inst,\n        RelocExpressionList *reloc);\n};\n\nstruct Instruction::ParseError : public std::exception {\n    QString message;\n\n    explicit ParseError(QString message);\n\n    const char *what() const noexcept override { return message.toUtf8().data(); }\n};\n\nstruct RelocExpression {\n    inline RelocExpression(\n        Address location,\n        QString expression,\n        int64_t offset,\n        int64_t min,\n        int64_t max,\n        const BitArg *arg,\n        QString filename,\n        int line,\n        Instruction::Modifier pseudo_mod = Instruction::Modifier::NONE) {\n        this->location = location;\n        this->expression = std::move(expression);\n        this->offset = offset;\n        this->min = min;\n        this->max = max;\n        this->arg = arg;\n        this->filename = std::move(filename);\n        this->line = line;\n        this->pseudo_mod = pseudo_mod;\n    }\n    Address location;\n    QString expression;\n    int64_t offset;\n    int64_t min;\n    int64_t max;\n    const BitArg *arg;\n    QString filename;\n    int line;\n    Instruction::Modifier pseudo_mod;\n};\n\n} // namespace machine\n\nQ_DECLARE_METATYPE(machine::Instruction)\n\n#endif // INSTRUCTION_H\n"
  },
  {
    "path": "src/machine/instruction.test.cpp",
    "content": "#include \"instruction.test.h\"\n\n#include \"instruction.h\"\n\nusing namespace machine;\n\n// Test that we are correctly encoding instructions in constructor\nvoid TestInstruction::instruction() {\n    QCOMPARE(Instruction(0x0), Instruction());\n    //    QCOMPARE(Instruction(0x4432146), Instruction(1, 2, 3, 4, 5, 6));\n    //    QCOMPARE(Instruction(0x4430004), Instruction(1, 2, 3, 4));\n    //    QCOMPARE(Instruction(0x4000002), Instruction(1, 2_addr));\n}\n\n// Test that we are correctly decoding instruction fields\nvoid TestInstruction::instruction_access() {\n    Instruction i(0xffffffff);\n\n    QCOMPARE(i.data(), (uint32_t)0xffffffff);\n    QCOMPARE(i.opcode(), (uint8_t)0x3f);\n    QCOMPARE(i.rs(), (uint8_t)0x1f);\n    QCOMPARE(i.rt(), (uint8_t)0x1f);\n    QCOMPARE(i.rd(), (uint8_t)0x1f);\n    QCOMPARE(i.shamt(), (uint8_t)0x1f);\n    QCOMPARE(i.funct(), (uint16_t)0x3f);\n    QCOMPARE(i.immediate(), (int32_t)0xffff);\n    QCOMPARE(i.address().get_raw(), (uint64_t)0x3ffffff);\n}\n\n// TODO test to_str\n\nQTEST_APPLESS_MAIN(TestInstruction)\n"
  },
  {
    "path": "src/machine/instruction.test.h",
    "content": "#ifndef INSTRUCTION_TEST_H\n#define INSTRUCTION_TEST_H\n\n#include <QtTest>\n\nclass TestInstruction : public QObject {\n    Q_OBJECT\n\npublic slots:\n    void instruction();\n    void instruction_access();\n};\n\n#endif // INSTRUCTION_TEST_H\n"
  },
  {
    "path": "src/machine/machine.cpp",
    "content": "#include \"machine.h\"\n\n#include \"programloader.h\"\n\n#include <QTime>\n#include <qelapsedtimer.h>\n#include <utility>\n\nusing namespace machine;\n\nMachine::Machine(MachineConfig config, bool load_symtab, bool load_executable)\n    : machine_config(std::move(config))\n    , stat(ST_READY) {\n    regs.reset(new Registers());\n    if (load_executable) {\n        ProgramLoader program(machine_config.elf());\n        this->machine_config.set_simulated_endian(program.get_endian());\n        mem_program_only.reset(new Memory(machine_config.get_simulated_endian()));\n        program.to_memory(mem_program_only.data());\n\n        if (program.get_architecture_type() == ARCH64)\n            this->machine_config.set_simulated_xlen(Xlen::_64);\n        else\n            this->machine_config.set_simulated_xlen(Xlen::_32);\n\n        if (load_symtab) { symtab.reset(program.get_symbol_table()); }\n\n        program_end = program.end();\n        if (program.get_executable_entry() != 0x0_addr) {\n            regs->write_pc(program.get_executable_entry());\n        }\n        mem.reset(new Memory(*mem_program_only));\n    } else {\n        mem.reset(new Memory(machine_config.get_simulated_endian()));\n    }\n\n    data_bus.reset(new MemoryDataBus(machine_config.get_simulated_endian()));\n    data_bus->insert_device_to_range(mem.data(), 0x00000000_addr, 0xefffffff_addr, false);\n\n    setup_serial_port();\n    setup_perip_spi_led();\n    setup_lcd_display();\n    setup_aclint_mtime();\n    setup_aclint_mswi();\n    setup_aclint_sswi();\n\n    unsigned access_time_read = machine_config.memory_access_time_read();\n    unsigned access_time_write = machine_config.memory_access_time_write();\n    unsigned access_time_burst = machine_config.memory_access_time_burst();\n    bool access_enable_burst = machine_config.memory_access_enable_burst();\n\n    cch_level2.reset(new Cache(\n        data_bus.data(), &machine_config.cache_level2(), access_time_read, access_time_write,\n        access_time_burst, access_enable_burst));\n    if (machine_config.cache_level2().enabled()) {\n        access_time_read = machine_config.memory_access_time_level2();\n        access_time_write = machine_config.memory_access_time_level2();\n        access_time_burst = 0;\n        access_enable_burst = true;\n    }\n    cch_program.reset(new Cache(\n        cch_level2.data(), &machine_config.cache_program(), access_time_read, access_time_write,\n        access_time_burst, access_enable_burst));\n    cch_data.reset(new Cache(\n        cch_level2.data(), &machine_config.cache_data(), access_time_read, access_time_write,\n        access_time_burst, access_enable_burst));\n\n    controlst.reset(\n        new CSR::ControlState(machine_config.get_simulated_xlen(), machine_config.get_isa_word()));\n\n    tlb_program.reset(new TLB(\n        cch_program.data(), PROGRAM, machine_config.access_tlb_program(),\n        machine_config.get_vm_enabled()));\n    tlb_data.reset(new TLB(\n        cch_data.data(), DATA, machine_config.access_tlb_data(), machine_config.get_vm_enabled()));\n    tlb_program->on_csr_write(CSR::Id::SATP, 0);\n    tlb_data->on_csr_write(CSR::Id::SATP, 0);\n    connect(\n        controlst.data(), &CSR::ControlState::write_signal, tlb_program.data(),\n        &machine::TLB::on_csr_write);\n    connect(\n        controlst.data(), &CSR::ControlState::write_signal, tlb_data.data(),\n        &machine::TLB::on_csr_write);\n    controlst->write_internal(CSR::Id::SATP, 0);\n\n    predictor.reset(new BranchPredictor(\n        machine_config.get_bp_enabled(), machine_config.get_bp_type(),\n        machine_config.get_bp_init_state(), machine_config.get_bp_btb_bits(),\n        machine_config.get_bp_bhr_bits(), machine_config.get_bp_bht_addr_bits()));\n\n    if (machine_config.pipelined()) {\n        cr.reset(new CorePipelined(\n            regs.data(), predictor.data(), tlb_program.data(), tlb_data.data(), controlst.data(),\n            machine_config.get_simulated_xlen(), machine_config.get_isa_word(),\n            machine_config.hazard_unit()));\n    } else {\n        cr.reset(new CoreSingle(\n            regs.data(), predictor.data(), tlb_program.data(), tlb_data.data(), controlst.data(),\n            machine_config.get_simulated_xlen(), machine_config.get_isa_word()));\n    }\n    connect(\n        this, &Machine::set_interrupt_signal, controlst.data(),\n        &CSR::ControlState::set_interrupt_signal);\n\n    run_t.reset(new QTimer(this));\n    set_speed(0); // In default run as fast as possible\n    connect(run_t.data(), &QTimer::timeout, this, &Machine::step_timer);\n\n    for (int i = 0; i < EXCAUSE_COUNT; i++) {\n        if (i != EXCAUSE_INT && i != EXCAUSE_BREAK && i != EXCAUSE_HWBREAK) {\n            set_stop_on_exception((enum ExceptionCause)i, machine_config.osemu_exception_stop());\n            set_step_over_exception((enum ExceptionCause)i, machine_config.osemu_exception_stop());\n        }\n    }\n\n    set_stop_on_exception(EXCAUSE_INT, machine_config.osemu_interrupt_stop());\n    set_step_over_exception(EXCAUSE_INT, false);\n}\nvoid Machine::setup_lcd_display() {\n    perip_lcd_display = new LcdDisplay(machine_config.get_simulated_endian());\n    memory_bus_insert_range(perip_lcd_display, 0xffe00000_addr, 0xffe4afff_addr, true);\n    if (machine_config.get_simulated_xlen() == Xlen::_64)\n        memory_bus_insert_range(\n            perip_lcd_display, 0xffffffffffe00000_addr, 0xffffffffffe4afff_addr, false);\n}\nvoid Machine::setup_perip_spi_led() {\n    perip_spi_led = new PeripSpiLed(machine_config.get_simulated_endian());\n    memory_bus_insert_range(perip_spi_led, 0xffffc100_addr, 0xffffc1ff_addr, true);\n    if (machine_config.get_simulated_xlen() == Xlen::_64)\n        memory_bus_insert_range(\n            perip_spi_led, 0xffffffffffffc100_addr, 0xffffffffffffc1ff_addr, false);\n}\nvoid Machine::setup_serial_port() {\n    ser_port = new SerialPort(machine_config.get_simulated_endian());\n    memory_bus_insert_range(ser_port, 0xffffc000_addr, 0xffffc03f_addr, true);\n    memory_bus_insert_range(ser_port, 0xffff0000_addr, 0xffff003f_addr, false);\n    if (machine_config.get_simulated_xlen() == Xlen::_64)\n        memory_bus_insert_range(ser_port, 0xffffffffffffc000_addr, 0xffffffffffffc03f_addr, false);\n    connect(ser_port, &SerialPort::signal_interrupt, this, &Machine::set_interrupt_signal);\n}\n\nvoid Machine::setup_aclint_mtime() {\n    aclint_mtimer = new aclint::AclintMtimer(machine_config.get_simulated_endian());\n    memory_bus_insert_range(\n        aclint_mtimer, 0xfffd0000_addr + aclint::CLINT_MTIMER_OFFSET,\n        0xfffd0000_addr + aclint::CLINT_MTIMER_OFFSET + aclint::CLINT_MTIMER_SIZE - 1, true);\n    if (machine_config.get_simulated_xlen() == Xlen::_64)\n        memory_bus_insert_range(\n            aclint_mtimer, 0xfffffffffffd0000_addr + aclint::CLINT_MTIMER_OFFSET,\n            0xfffffffffffd0000_addr + aclint::CLINT_MTIMER_OFFSET + aclint::CLINT_MTIMER_SIZE - 1,\n            false);\n    connect(\n        aclint_mtimer, &aclint::AclintMtimer::signal_interrupt, this,\n        &Machine::set_interrupt_signal);\n}\n\nvoid Machine::setup_aclint_mswi() {\n    aclint_mswi = new aclint::AclintMswi(machine_config.get_simulated_endian());\n    memory_bus_insert_range(\n        aclint_mswi, 0xfffd0000_addr + aclint::CLINT_MSWI_OFFSET,\n        0xfffd0000_addr + aclint::CLINT_MSWI_OFFSET + aclint::CLINT_MSWI_SIZE - 1, true);\n    if (machine_config.get_simulated_xlen() == Xlen::_64)\n        memory_bus_insert_range(\n            aclint_mswi, 0xfffffffffffd0000_addr + aclint::CLINT_MSWI_OFFSET,\n            0xfffffffffffd0000_addr + aclint::CLINT_MSWI_OFFSET + aclint::CLINT_MSWI_SIZE - 1,\n            false);\n    connect(\n        aclint_mswi, &aclint::AclintMswi::signal_interrupt, this, &Machine::set_interrupt_signal);\n}\n\nvoid Machine::setup_aclint_sswi() {\n    aclint_sswi = new aclint::AclintSswi(machine_config.get_simulated_endian());\n    memory_bus_insert_range(\n        aclint_sswi, 0xfffd0000_addr + aclint::CLINT_SSWI_OFFSET,\n        0xfffd0000_addr + aclint::CLINT_SSWI_OFFSET + aclint::CLINT_SSWI_SIZE - 1, true);\n    if (machine_config.get_simulated_xlen() == Xlen::_64)\n        memory_bus_insert_range(\n            aclint_sswi, 0xfffffffffffd0000_addr + aclint::CLINT_SSWI_OFFSET,\n            0xfffffffffffd0000_addr + aclint::CLINT_SSWI_OFFSET + aclint::CLINT_SSWI_SIZE - 1,\n            false);\n    connect(\n        aclint_sswi, &aclint::AclintSswi::signal_interrupt, this, &Machine::set_interrupt_signal);\n}\n\nMachine::~Machine() {\n    run_t.reset();\n    cr.reset();\n    controlst.reset();\n    regs.reset();\n    mem.reset();\n    cch_program.reset();\n    cch_data.reset();\n    cch_level2.reset();\n    data_bus.reset();\n    mem_program_only.reset();\n    symtab.reset();\n    predictor.reset();\n}\n\nconst MachineConfig &Machine::config() {\n    return machine_config;\n}\n\nvoid Machine::set_speed(unsigned int ips, unsigned int time_chunk_ms) {\n    this->time_chunk = time_chunk_ms;\n    run_t->setInterval(ips);\n    if (run_t->isActive()) {\n        // Clock settings changed.\n        start_core_clock();\n    }\n}\n\nconst Registers *Machine::registers() {\n    return regs.data();\n}\n\nconst CSR::ControlState *Machine::control_state() {\n    return controlst.data();\n}\n\nconst Memory *Machine::memory() {\n    return mem.data();\n}\n\nMemory *Machine::memory_rw() {\n    return mem.data();\n}\n\nconst Cache *Machine::cache_program() {\n    return cch_program.data();\n}\n\nconst Cache *Machine::cache_data() {\n    return cch_data.data();\n}\n\nconst Cache *Machine::cache_level2() {\n    return cch_level2.data();\n}\n\nconst BranchPredictor *Machine::branch_predictor() {\n    return predictor.data();\n}\n\nCache *Machine::cache_data_rw() {\n    return cch_data.data();\n}\n\nvoid Machine::cache_sync() {\n    if (!cch_program.isNull()) { cch_program->sync(); }\n    if (!cch_data.isNull()) { cch_data->sync(); }\n    if (!cch_level2.isNull()) { cch_level2->sync(); }\n}\n\nvoid Machine::tlb_sync() {\n    if (tlb_program) { tlb_program->sync(); }\n    if (tlb_data) { tlb_data->sync(); }\n}\n\nconst TLB *Machine::get_tlb_program() const {\n    return tlb_program ? &*tlb_program : nullptr;\n}\n\nconst TLB *Machine::get_tlb_data() const {\n    return tlb_data ? &*tlb_data : nullptr;\n}\n\nTLB *Machine::get_tlb_program_rw() {\n    return tlb_program ? &*tlb_program : nullptr;\n}\n\nTLB *Machine::get_tlb_data_rw() {\n    return tlb_data ? &*tlb_data : nullptr;\n}\n\nconst MemoryDataBus *Machine::memory_data_bus() {\n    return data_bus.data();\n}\n\nMemoryDataBus *Machine::memory_data_bus_rw() {\n    return data_bus.data();\n}\n\nSerialPort *Machine::serial_port() {\n    return ser_port;\n}\n\nPeripSpiLed *Machine::peripheral_spi_led() {\n    return perip_spi_led;\n}\n\nLcdDisplay *Machine::peripheral_lcd_display() {\n    return perip_lcd_display;\n}\n\nSymbolTable *Machine::symbol_table_rw(bool create) {\n    if (create && (symtab.isNull())) { symtab.reset(new SymbolTable); }\n    return symtab.data();\n}\n\nconst SymbolTable *Machine::symbol_table(bool create) {\n    return symbol_table_rw(create);\n}\n\nvoid Machine::set_symbol(\n    const QString &name,\n    uint32_t value,\n    uint32_t size,\n    unsigned char info,\n    unsigned char other) {\n    if (symtab.isNull()) { symtab.reset(new SymbolTable); }\n    symtab->set_symbol(name, value, size, info, other);\n}\n\nconst Core *Machine::core() {\n    return cr.data();\n}\n\nconst CoreSingle *Machine::core_singe() {\n    return machine_config.pipelined() ? nullptr : (const CoreSingle *)cr.data();\n}\n\nconst CorePipelined *Machine::core_pipelined() {\n    return machine_config.pipelined() ? (const CorePipelined *)cr.data() : nullptr;\n}\n\nbool Machine::executable_loaded() const {\n    return (!mem_program_only.isNull());\n}\n\nenum Machine::Status Machine::status() {\n    return stat;\n}\n\nbool Machine::exited() {\n    return stat == ST_EXIT || stat == ST_TRAPPED;\n}\n\n// We don't allow to call control methods when machine exited or if it's busy\n// We rather silently fail.\n#define CTL_GUARD                                                                                  \\\n    do {                                                                                           \\\n        if (exited() || stat == ST_BUSY) return;                                                   \\\n    } while (false)\n\nvoid Machine::play() {\n    CTL_GUARD;\n    set_status(ST_RUNNING);\n    start_core_clock();\n    step_internal(true);\n    emit play_initiated();\n}\n\nvoid Machine::pause() {\n    if (stat != ST_BUSY) { CTL_GUARD; }\n    set_status(ST_READY);\n    stop_core_clock();\n    emit play_paused();\n}\n\nvoid Machine::step_internal(bool skip_break) {\n    CTL_GUARD;\n    enum Status stat_prev = stat;\n    set_status(ST_BUSY);\n    emit tick();\n    try {\n        QElapsedTimer timer;\n        timer.start();\n        do {\n            cr->step(skip_break);\n        } while (time_chunk != 0 && stat == ST_BUSY && !skip_break\n                 && timer.elapsed() < (int)time_chunk);\n    } catch (SimulatorException &e) {\n        stop_core_clock();\n        set_status(ST_TRAPPED);\n        emit program_trap(e);\n        return;\n    }\n    if (regs->read_pc() >= program_end) {\n        stop_core_clock();\n        set_status(ST_EXIT);\n        emit program_exit();\n    } else {\n        if (stat == ST_BUSY) { set_status(stat_prev); }\n    }\n    emit post_tick();\n}\n\nvoid Machine::start_core_clock() {\n    // Handle frequency measurement.\n    last_cycle_count = cr->get_cycle_count();\n    if (run_t->interval() == 0) {\n        // The clock is not fixed, we need to measure it.\n        frequency_timer.start();\n    } else {\n        // Clocked is fixed.\n        emit report_core_frequency(1e3 / (double)run_t->interval());\n    }\n\n    run_t->start();\n}\n\nvoid Machine::stop_core_clock() {\n    run_t->stop();\n    frequency_timer.invalidate();\n    emit report_core_frequency(0.0);\n}\n\nvoid Machine::step() {\n    step_internal(true);\n}\n\nvoid Machine::step_timer() {\n    if (run_t->interval() == 0 && time_chunk == 0) {\n        // We need to amortize QTimer event loop overhead when running in max speed mode.\n        for (size_t i = 0; i < 32 && stat == ST_RUNNING; i++) {\n            step_internal();\n        }\n    } else {\n        step_internal();\n    }\n    // Compute core frequency each 0x100 cycles\n    auto total_cycle_count = cr->get_cycle_count();\n    auto cycle_count = total_cycle_count - last_cycle_count;\n    if (cycle_count >= 0x2000) {\n        double period_ns = (double)(frequency_timer.nsecsElapsed()) / cycle_count;\n        if (period_ns < 0.01) { return; }\n        last_cycle_count = cr->get_cycle_count();\n        emit frequency_timer.start();\n        report_core_frequency(1e9 / period_ns);\n    }\n}\n\nvoid Machine::restart() {\n    pause();\n    regs->reset();\n    if (!mem_program_only.isNull()) { mem->reset(*mem_program_only); }\n    cch_program->reset();\n    cch_data->reset();\n    cch_level2->reset();\n    cr->reset();\n    set_status(ST_READY);\n}\n\nvoid Machine::set_status(enum Status st) {\n    bool change = st != stat;\n    stat = st;\n    if (change) { emit status_change(st); }\n}\n\nvoid Machine::register_exception_handler(ExceptionCause excause, ExceptionHandler *exhandler) {\n    if (!cr.isNull()) { cr->register_exception_handler(excause, exhandler); }\n}\n\nbool Machine::memory_bus_insert_range(\n    BackendMemory *mem_acces,\n    Address start_addr,\n    Address last_addr,\n    bool move_ownership) {\n    if (data_bus.isNull()) { return false; }\n    return data_bus->insert_device_to_range(mem_acces, start_addr, last_addr, move_ownership);\n}\n\nvoid Machine::insert_hwbreak(Address address) {\n    if (!cr.isNull()) { cr->insert_hwbreak(address); }\n}\n\nvoid Machine::remove_hwbreak(Address address) {\n    if (!cr.isNull()) { cr->remove_hwbreak(address); }\n}\n\nbool Machine::is_hwbreak(Address address) {\n    if (!cr.isNull()) { return cr->is_hwbreak(address); }\n    return false;\n}\n\nvoid Machine::set_stop_on_exception(enum ExceptionCause excause, bool value) {\n    if (!cr.isNull()) { cr->set_stop_on_exception(excause, value); }\n}\n\nbool Machine::get_stop_on_exception(enum ExceptionCause excause) const {\n    if (!cr.isNull()) { return cr->get_stop_on_exception(excause); }\n    return false;\n}\n\nvoid Machine::set_step_over_exception(enum ExceptionCause excause, bool value) {\n    if (!cr.isNull()) { cr->set_step_over_exception(excause, value); }\n}\n\nbool Machine::get_step_over_exception(enum ExceptionCause excause) const {\n    if (!cr.isNull()) { return cr->get_step_over_exception(excause); }\n    return false;\n}\n\nenum ExceptionCause Machine::get_exception_cause() const {\n    uint32_t val;\n    if (controlst.isNull()) { return EXCAUSE_NONE; }\n    val = (controlst->read_internal(CSR::Id::MCAUSE).as_u64());\n    if (val & 0xffffffff80000000) {\n        return EXCAUSE_INT;\n    } else {\n        return (ExceptionCause)val;\n    }\n}\n"
  },
  {
    "path": "src/machine/machine.h",
    "content": "#ifndef MACHINE_H\n#define MACHINE_H\n\n#include \"core.h\"\n#include \"machineconfig.h\"\n#include \"memory/backend/aclintmswi.h\"\n#include \"memory/backend/aclintmtimer.h\"\n#include \"memory/backend/aclintsswi.h\"\n#include \"memory/backend/lcddisplay.h\"\n#include \"memory/backend/peripheral.h\"\n#include \"memory/backend/peripspiled.h\"\n#include \"memory/backend/serialport.h\"\n#include \"memory/cache/cache.h\"\n#include \"memory/memory_bus.h\"\n#include \"memory/tlb/tlb.h\"\n#include \"predictor.h\"\n#include \"registers.h\"\n#include \"simulator_exception.h\"\n#include \"symboltable.h\"\n\n#include <QObject>\n#include <QTimer>\n#include <cstdint>\n#include <optional>\n\nnamespace machine {\n\nclass Machine : public QObject {\n    Q_OBJECT\npublic:\n    explicit Machine(MachineConfig config, bool load_symtab = false, bool load_executable = true);\n    ~Machine() override;\n\n    const MachineConfig &config();\n    void set_speed(unsigned int ips, unsigned int time_chunk = 0);\n\n    const Registers *registers();\n    const CSR::ControlState *control_state();\n    const Memory *memory();\n    Memory *memory_rw();\n    const Cache *cache_program();\n    const Cache *cache_data();\n    const Cache *cache_level2();\n    const BranchPredictor *branch_predictor();\n    Cache *cache_data_rw();\n    void cache_sync();\n    const TLB *get_tlb_program() const;\n    const TLB *get_tlb_data() const;\n    TLB *get_tlb_program_rw();\n    TLB *get_tlb_data_rw();\n    void tlb_sync();\n    const MemoryDataBus *memory_data_bus();\n    MemoryDataBus *memory_data_bus_rw();\n    SerialPort *serial_port();\n    PeripSpiLed *peripheral_spi_led();\n    LcdDisplay *peripheral_lcd_display();\n    const SymbolTable *symbol_table(bool create = false);\n    SymbolTable *symbol_table_rw(bool create = false);\n    void set_symbol(\n        const QString &name,\n        uint32_t value,\n        uint32_t size,\n        unsigned char info = 0,\n        unsigned char other = 0);\n    const Core *core();\n    const CoreSingle *core_singe();\n    const CorePipelined *core_pipelined();\n    bool executable_loaded() const;\n\n    enum Status {\n        ST_READY,   // Machine is ready to be started or step to be called\n        ST_RUNNING, // Machine is running\n        ST_BUSY,    // Machine is calculating step\n        ST_EXIT,    // Machine exited\n        ST_TRAPPED  // Machine exited with failure\n    };\n    enum Status status();\n    bool exited();\n\n    void register_exception_handler(ExceptionCause excause, ExceptionHandler *exhandler);\n    bool memory_bus_insert_range(\n        BackendMemory *mem_acces,\n        Address start_addr,\n        Address last_addr,\n        bool move_ownership);\n\n    void insert_hwbreak(Address address);\n    void remove_hwbreak(Address address);\n    bool is_hwbreak(Address address);\n    void set_stop_on_exception(enum ExceptionCause excause, bool value);\n    bool get_stop_on_exception(enum ExceptionCause excause) const;\n    void set_step_over_exception(enum ExceptionCause excause, bool value);\n    bool get_step_over_exception(enum ExceptionCause excause) const;\n    enum ExceptionCause get_exception_cause() const;\n\n    Address virtual_to_physical(AddressWithMode v) {\n        if (tlb_data) {\n            return tlb_data->translate_virtual_to_physical(v);\n        } else {\n            return v;\n        }\n    }\n\npublic slots:\n    void play();\n    void pause();\n    void step();\n    void restart();\n\nsignals:\n    void program_exit();\n    void program_trap(machine::SimulatorException &e);\n    void status_change(enum machine::Machine::Status st);\n    void tick();      // Time tick\n    void post_tick(); // Emitted after tick to allow updates\n    void set_interrupt_signal(uint irq_num, bool active);\n    void play_initiated();\n    void play_paused();\n    void report_core_frequency(double);\n\nprivate slots:\n    void step_timer();\n\nprivate:\n    void step_internal(bool skip_break = false);\n\n    void start_core_clock();\n    void stop_core_clock();\n\n    MachineConfig machine_config;\n\n    Box<Registers> regs;\n    Box<Memory> mem;\n    /**\n     * Memory with loaded program only.\n     * It is not used for execution, only for quick\n     * simulation reset without repeated ELF file loading.\n     */\n    Box<Memory> mem_program_only;\n    Box<MemoryDataBus> data_bus;\n    // Peripherals are owned by data_bus\n    SerialPort *ser_port = nullptr;\n    PeripSpiLed *perip_spi_led = nullptr;\n    LcdDisplay *perip_lcd_display = nullptr;\n    aclint::AclintMtimer *aclint_mtimer = nullptr;\n    aclint::AclintMswi *aclint_mswi = nullptr;\n    aclint::AclintSswi *aclint_sswi = nullptr;\n    Box<Cache> cch_level2;\n    Box<Cache> cch_program;\n    Box<Cache> cch_data;\n    Box<TLB> tlb_data;\n    Box<TLB> tlb_program;\n    Box<CSR::ControlState> controlst;\n    Box<BranchPredictor> predictor;\n    Box<Core> cr;\n\n    Box<QTimer> run_t;\n    unsigned int time_chunk = { 0 };\n\n    // Used to monitor the real CPU frequency\n    QElapsedTimer frequency_timer;\n    uint64_t last_cycle_count = 0;\n\n    Box<SymbolTable> symtab;\n    Address program_end = 0xffff0000_addr;\n    enum Status stat = ST_READY;\n    void set_status(enum Status st);\n    void setup_serial_port();\n    void setup_perip_spi_led();\n    void setup_lcd_display();\n    void setup_aclint_mtime();\n    void setup_aclint_mswi();\n    void setup_aclint_sswi();\n};\n\n} // namespace machine\n\n#endif // MACHINE_H\n"
  },
  {
    "path": "src/machine/machineconfig.cpp",
    "content": "#include \"machineconfig.h\"\n\n#include \"common/endian.h\"\n\n#include <QMap>\n#include <utility>\n\nusing namespace machine;\n\n//////////////////////////////////////////////////////////////////////////////\n/// Default config of MachineConfig\n#define DF_PIPELINE             false\n#define DF_DELAYSLOT            true\n#define DF_HUNIT                HU_STALL_FORWARD\n#define DF_EXEC_PROTEC          false\n#define DF_WRITE_PROTEC         false\n#define DF_MEM_ACC_READ         10\n#define DF_MEM_ACC_WRITE        10\n#define DF_MEM_ACC_BURST        0\n#define DF_MEM_ACC_LEVEL2       2\n#define DF_MEM_ACC_BURST_ENABLE false\n#define DF_ELF                  QString(\"\")\n/// Default config of branch predictor\n#define DFC_BP_ENABLED       false\n#define DFC_BP_TYPE          PredictorType::SMITH_1_BIT\n#define DFC_BP_INIT_STATE    PredictorState::NOT_TAKEN\n#define DFC_BP_BTB_BITS      2\n#define DFC_BP_BHR_BITS      0\n#define DFC_BP_BHT_ADDR_BITS 2\n/// Default config of Virtual Memory\n#define DFC_VM_ENABLED false\n#define DFC_TLB_SETS   16\n#define DFC_TLB_ASSOC  1\n#define DFC_TLB_REPLAC RP_LRU\n//////////////////////////////////////////////////////////////////////////////\n/// Default config of CacheConfig\n#define DFC_EN     false\n#define DFC_SETS   1\n#define DFC_BLOCKS 1\n#define DFC_ASSOC  1\n#define DFC_REPLAC RP_RAND\n#define DFC_WRITE  WP_THROUGH_NOALLOC\n//////////////////////////////////////////////////////////////////////////////\n\nCacheConfig::CacheConfig() {\n    en = DFC_EN;\n    n_sets = DFC_SETS;\n    n_blocks = DFC_BLOCKS;\n    d_associativity = DFC_ASSOC;\n    replac_pol = DFC_REPLAC;\n    write_pol = DFC_WRITE;\n}\n\nCacheConfig::CacheConfig(const CacheConfig *cc) {\n    en = cc->enabled();\n    n_sets = cc->set_count();\n    n_blocks = cc->block_size();\n    d_associativity = cc->associativity();\n    replac_pol = cc->replacement_policy();\n    write_pol = cc->write_policy();\n}\n\n#define N(STR) (prefix + QString(STR))\n\nCacheConfig::CacheConfig(const QSettings *sts, const QString &prefix) {\n    en = sts->value(N(\"Enabled\"), DFC_EN).toBool();\n    n_sets = sts->value(N(\"Sets\"), DFC_SETS).toUInt();\n    n_blocks = sts->value(N(\"Blocks\"), DFC_BLOCKS).toUInt();\n    d_associativity = sts->value(N(\"Associativity\"), DFC_ASSOC).toUInt();\n    replac_pol = (enum ReplacementPolicy)sts->value(N(\"Replacement\"), DFC_REPLAC).toUInt();\n    write_pol = (enum WritePolicy)sts->value(N(\"Write\"), DFC_WRITE).toUInt();\n}\n\nvoid CacheConfig::store(QSettings *sts, const QString &prefix) const {\n    sts->setValue(N(\"Enabled\"), enabled());\n    sts->setValue(N(\"Sets\"), set_count());\n    sts->setValue(N(\"Blocks\"), block_size());\n    sts->setValue(N(\"Associativity\"), associativity());\n    sts->setValue(N(\"Replacement\"), (unsigned)replacement_policy());\n    sts->setValue(N(\"Write\"), (unsigned)write_policy());\n}\n\n#undef N\n\nvoid CacheConfig::preset(enum ConfigPresets p) {\n    switch (p) {\n    case CP_PIPE:\n    case CP_SINGLE_CACHE:\n        set_enabled(true);\n        set_set_count(4);\n        set_block_size(2);\n        set_associativity(2);\n        set_replacement_policy(RP_RAND);\n        set_write_policy(WP_THROUGH_NOALLOC);\n        break;\n    case CP_SINGLE:\n    case CP_PIPE_NO_HAZARD: set_enabled(false);\n    }\n}\n\nvoid CacheConfig::set_enabled(bool v) {\n    en = v;\n}\n\nvoid CacheConfig::set_set_count(unsigned v) {\n    n_sets = v > 0 ? v : 1;\n}\n\nvoid CacheConfig::set_block_size(unsigned v) {\n    n_blocks = v > 0 ? v : 1;\n}\n\nvoid CacheConfig::set_associativity(unsigned v) {\n    d_associativity = v > 0 ? v : 1;\n}\n\nvoid CacheConfig::set_replacement_policy(enum ReplacementPolicy v) {\n    replac_pol = v;\n}\n\nvoid CacheConfig::set_write_policy(enum WritePolicy v) {\n    write_pol = v;\n}\n\nbool CacheConfig::enabled() const {\n    return en;\n}\n\nunsigned CacheConfig::set_count() const {\n    return n_sets;\n}\n\nunsigned CacheConfig::block_size() const {\n    return n_blocks;\n}\n\nunsigned CacheConfig::associativity() const {\n    return d_associativity;\n}\n\nenum CacheConfig::ReplacementPolicy CacheConfig::replacement_policy() const {\n    return replac_pol;\n}\n\nenum CacheConfig::WritePolicy CacheConfig::write_policy() const {\n    return write_pol;\n}\n\nbool CacheConfig::operator==(const CacheConfig &c) const {\n#define CMP(GETTER) (GETTER)() == (c.GETTER)()\n    return CMP(enabled) && CMP(set_count) && CMP(block_size) && CMP(associativity)\n           && CMP(replacement_policy) && CMP(write_policy);\n#undef CMP\n}\n\nbool CacheConfig::operator!=(const CacheConfig &c) const {\n    return !operator==(c);\n}\n//////////////////////////////////////////////////////////////////////////////\n\nTLBConfig::TLBConfig() {\n    vm_asid = 0;\n    n_sets = DFC_TLB_SETS;\n    d_associativity = DFC_TLB_ASSOC;\n    replac_pol = (enum ReplacementPolicy)DFC_TLB_REPLAC;\n}\n\nTLBConfig::TLBConfig(const TLBConfig *tc) {\n    if (tc == nullptr) {\n        vm_asid = 0;\n        n_sets = DFC_TLB_SETS;\n        d_associativity = DFC_TLB_ASSOC;\n        replac_pol = (enum ReplacementPolicy)DFC_TLB_REPLAC;\n        return;\n    }\n    vm_asid = tc->get_vm_asid();\n    n_sets = tc->get_tlb_num_sets();\n    d_associativity = tc->get_tlb_associativity();\n    replac_pol = tc->get_tlb_replacement_policy();\n}\n\n#define N(STR) (prefix + QString(STR))\n\nTLBConfig::TLBConfig(const QSettings *sts, const QString &prefix) {\n    vm_asid = sts->value(N(\"VM_ASID\"), 0u).toUInt();\n    n_sets = sts->value(N(\"NumSets\"), DFC_TLB_SETS).toUInt();\n    d_associativity = sts->value(N(\"Associativity\"), DFC_TLB_ASSOC).toUInt();\n    replac_pol = (enum ReplacementPolicy)sts->value(N(\"Policy\"), DFC_TLB_REPLAC).toUInt();\n}\n\nvoid TLBConfig::store(QSettings *sts, const QString &prefix) const {\n    sts->setValue(N(\"VM_ASID\"), get_vm_asid());\n    sts->setValue(N(\"NumSets\"), get_tlb_num_sets());\n    sts->setValue(N(\"Associativity\"), get_tlb_associativity());\n    sts->setValue(N(\"Policy\"), (unsigned)get_tlb_replacement_policy());\n}\n\n#undef N\n\nvoid TLBConfig::preset(enum ConfigPresets p) {\n    switch (p) {\n    case CP_PIPE:\n    case CP_SINGLE_CACHE:\n    case CP_SINGLE:\n    case CP_PIPE_NO_HAZARD:\n        vm_asid = 0;\n        n_sets = DFC_TLB_SETS;\n        d_associativity = DFC_TLB_ASSOC;\n        replac_pol = (enum ReplacementPolicy)DFC_TLB_REPLAC;\n    }\n}\n\nvoid TLBConfig::set_vm_asid(uint32_t a) {\n    vm_asid = a;\n}\n\nuint32_t TLBConfig::get_vm_asid() const {\n    return vm_asid;\n}\n\nvoid TLBConfig::set_tlb_num_sets(unsigned v) {\n    n_sets = v > 0 ? v : 1;\n}\n\nvoid TLBConfig::set_tlb_associativity(unsigned v) {\n    d_associativity = v > 0 ? v : 1;\n}\n\nvoid TLBConfig::set_tlb_replacement_policy(TLBConfig::ReplacementPolicy p) {\n    replac_pol = p;\n}\n\nunsigned TLBConfig::get_tlb_num_sets() const {\n    return n_sets;\n}\n\nunsigned TLBConfig::get_tlb_associativity() const {\n    return d_associativity;\n}\n\nTLBConfig::ReplacementPolicy TLBConfig::get_tlb_replacement_policy() const {\n    return replac_pol;\n}\n\nbool TLBConfig::operator==(const TLBConfig &c) const {\n#define CMP(GETTER) (GETTER)() == (c.GETTER)()\n    return CMP(get_vm_asid) && CMP(get_tlb_num_sets) && CMP(get_tlb_associativity)\n           && CMP(get_tlb_replacement_policy);\n#undef CMP\n}\n\nbool TLBConfig::operator!=(const TLBConfig &c) const {\n    return !operator==(c);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n\nMachineConfig::MachineConfig() {\n    simulated_endian = LITTLE;\n    simulated_xlen = Xlen::_32;\n    isa_word = config_isa_word_default;\n    pipeline = DF_PIPELINE;\n    delayslot = DF_DELAYSLOT;\n    hunit = DF_HUNIT;\n    exec_protect = DF_EXEC_PROTEC;\n    write_protect = DF_WRITE_PROTEC;\n    mem_acc_read = DF_MEM_ACC_READ;\n    mem_acc_write = DF_MEM_ACC_WRITE;\n    mem_acc_burst = DF_MEM_ACC_BURST;\n    mem_acc_level2 = DF_MEM_ACC_LEVEL2;\n    mem_acc_enable_burst = DF_MEM_ACC_BURST_ENABLE;\n    osem_enable = true;\n    osem_known_syscall_stop = true;\n    osem_unknown_syscall_stop = true;\n    osem_interrupt_stop = true;\n    osem_exception_stop = true;\n    osem_fs_root = \"\";\n    res_at_compile = true;\n    elf_path = DF_ELF;\n    cch_program = CacheConfig();\n    cch_data = CacheConfig();\n    cch_level2 = CacheConfig();\n\n    // Branch predictor\n    bp_enabled = DFC_BP_ENABLED;\n    bp_type = DFC_BP_TYPE;\n    bp_init_state = DFC_BP_INIT_STATE;\n    bp_btb_bits = DFC_BP_BTB_BITS;\n    bp_bhr_bits = DFC_BP_BHR_BITS;\n    bp_bht_addr_bits = DFC_BP_BHT_ADDR_BITS;\n    bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits;\n\n    // Virtual memory\n    vm_enabled = DFC_VM_ENABLED;\n    tlb_program = TLBConfig();\n    tlb_data = TLBConfig();\n}\n\nMachineConfig::MachineConfig(const MachineConfig *config) {\n    simulated_endian = config->get_simulated_endian();\n    simulated_xlen = config->get_simulated_xlen();\n    isa_word = config->get_isa_word();\n    pipeline = config->pipelined();\n    delayslot = config->delay_slot();\n    hunit = config->hazard_unit();\n    exec_protect = config->memory_execute_protection();\n    write_protect = config->memory_write_protection();\n    mem_acc_read = config->memory_access_time_read();\n    mem_acc_write = config->memory_access_time_write();\n    mem_acc_burst = config->memory_access_time_burst();\n    mem_acc_level2 = config->memory_access_time_level2();\n    mem_acc_enable_burst = config->memory_access_enable_burst();\n    osem_enable = config->osemu_enable();\n    osem_known_syscall_stop = config->osemu_known_syscall_stop();\n    osem_unknown_syscall_stop = config->osemu_unknown_syscall_stop();\n    osem_interrupt_stop = config->osemu_interrupt_stop();\n    osem_exception_stop = config->osemu_exception_stop();\n    osem_fs_root = config->osemu_fs_root();\n    res_at_compile = config->reset_at_compile();\n    elf_path = config->elf();\n    cch_program = config->cache_program();\n    cch_data = config->cache_data();\n    cch_level2 = config->cache_level2();\n\n    // Branch predictor\n    bp_enabled = config->get_bp_enabled();\n    bp_type = config->get_bp_type();\n    bp_init_state = config->get_bp_init_state();\n    bp_btb_bits = config->get_bp_btb_bits();\n    bp_bhr_bits = config->get_bp_bhr_bits();\n    bp_bht_addr_bits = config->get_bp_bht_addr_bits();\n    bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits;\n\n    // Virtual memory\n    vm_enabled = config->get_vm_enabled();\n    tlb_program = config->tlbc_program();\n    tlb_data = config->tlbc_data();\n}\n\n#define N(STR) (prefix + QString(STR))\n\nMachineConfig::MachineConfig(const QSettings *sts, const QString &prefix) {\n    simulated_endian = LITTLE;\n    unsigned int xlen_num_bits = sts->value(N(\"XlenBits\"), 32).toUInt();\n    simulated_xlen = xlen_num_bits == 64 ? Xlen::_64 : Xlen::_32;\n    isa_word\n        = ConfigIsaWord(sts->value(N(\"IsaWord\"), config_isa_word_default.toUnsigned()).toUInt());\n    isa_word |= config_isa_word_default & config_isa_word_fixed;\n    isa_word &= config_isa_word_default | ~config_isa_word_fixed;\n    pipeline = sts->value(N(\"Pipelined\"), DF_PIPELINE).toBool();\n    delayslot = sts->value(N(\"DelaySlot\"), DF_DELAYSLOT).toBool();\n    hunit = (enum HazardUnit)sts->value(N(\"HazardUnit\"), DF_HUNIT).toUInt();\n    exec_protect = sts->value(N(\"MemoryExecuteProtection\"), DF_EXEC_PROTEC).toBool();\n    write_protect = sts->value(N(\"MemoryWriteProtection\"), DF_WRITE_PROTEC).toBool();\n    mem_acc_read = sts->value(N(\"MemoryRead\"), DF_MEM_ACC_READ).toUInt();\n    mem_acc_write = sts->value(N(\"MemoryWrite\"), DF_MEM_ACC_WRITE).toUInt();\n    mem_acc_burst = sts->value(N(\"MemoryBurst\"), DF_MEM_ACC_BURST).toUInt();\n    mem_acc_level2 = sts->value(N(\"MemoryLevel2\"), DF_MEM_ACC_LEVEL2).toUInt();\n    mem_acc_enable_burst = sts->value(N(\"MemoryBurstEnable\"), DF_MEM_ACC_BURST_ENABLE).toBool();\n    osem_enable = sts->value(N(\"OsemuEnable\"), true).toBool();\n    osem_known_syscall_stop = sts->value(N(\"OsemuKnownSyscallStop\"), true).toBool();\n    osem_unknown_syscall_stop = sts->value(N(\"OsemuUnknownSyscallStop\"), true).toBool();\n    osem_interrupt_stop = sts->value(N(\"OsemuInterruptStop\"), true).toBool();\n    osem_exception_stop = sts->value(N(\"OsemuExceptionStop\"), true).toBool();\n    osem_fs_root = sts->value(N(\"OsemuFilesystemRoot\"), \"\").toString();\n    res_at_compile = sts->value(N(\"ResetAtCompile\"), true).toBool();\n    elf_path = sts->value(N(\"Elf\"), DF_ELF).toString();\n    cch_program = CacheConfig(sts, N(\"ProgramCache_\"));\n    cch_data = CacheConfig(sts, N(\"DataCache_\"));\n    cch_level2 = CacheConfig(sts, N(\"Level2Cache_\"));\n\n    // Branch predictor\n    bp_enabled = sts->value(N(\"BranchPredictor_Enabled\"), DFC_BP_ENABLED).toBool();\n    bp_type = (PredictorType)sts->value(N(\"BranchPredictor_Type\"), (uint8_t)DFC_BP_TYPE).toUInt();\n    bp_init_state\n        = (PredictorState)sts->value(N(\"BranchPredictor_InitState\"), (uint8_t)DFC_BP_INIT_STATE)\n              .toUInt();\n    bp_btb_bits = sts->value(N(\"BranchPredictor_BitsBTB\"), DFC_BP_BTB_BITS).toUInt();\n    bp_bhr_bits = sts->value(N(\"BranchPredictor_BitsBHR\"), DFC_BP_BHR_BITS).toUInt();\n    bp_bht_addr_bits = sts->value(N(\"BranchPredictor_BitsBHTAddr\"), DFC_BP_BHT_ADDR_BITS).toUInt();\n    bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits;\n\n    // Virtual memory\n    vm_enabled = sts->value(N(\"VMEnabled\"), DFC_VM_ENABLED).toBool();\n    tlb_data = TLBConfig(sts, N(\"DataTLB_\"));\n    tlb_program = TLBConfig(sts, N(\"ProgramTLB_\"));\n}\n\nvoid MachineConfig::store(QSettings *sts, const QString &prefix) {\n    sts->setValue(N(\"XlenBits\"), get_simulated_xlen() == Xlen::_64 ? 64 : 32);\n    sts->setValue(N(\"IsaWord\"), get_isa_word().toUnsigned());\n    sts->setValue(N(\"Pipelined\"), pipelined());\n    sts->setValue(N(\"DelaySlot\"), delay_slot());\n    sts->setValue(N(\"HazardUnit\"), (unsigned)hazard_unit());\n    sts->setValue(N(\"MemoryRead\"), memory_access_time_read());\n    sts->setValue(N(\"MemoryWrite\"), memory_access_time_write());\n    sts->setValue(N(\"MemoryBurst\"), memory_access_time_burst());\n    sts->setValue(N(\"MemoryLevel2\"), memory_access_time_level2());\n    sts->setValue(N(\"MemoryBurstEnable\"), memory_access_enable_burst());\n    sts->setValue(N(\"OsemuEnable\"), osemu_enable());\n    sts->setValue(N(\"OsemuKnownSyscallStop\"), osemu_known_syscall_stop());\n    sts->setValue(N(\"OsemuUnknownSyscallStop\"), osemu_unknown_syscall_stop());\n    sts->setValue(N(\"OsemuInterruptStop\"), osemu_interrupt_stop());\n    sts->setValue(N(\"OsemuExceptionStop\"), osemu_exception_stop());\n    sts->setValue(N(\"OsemuFilesystemRoot\"), osemu_fs_root());\n    sts->setValue(N(\"ResetAtCompile\"), reset_at_compile());\n    sts->setValue(N(\"Elf\"), elf_path);\n    cch_program.store(sts, N(\"ProgramCache_\"));\n    cch_data.store(sts, N(\"DataCache_\"));\n    cch_level2.store(sts, N(\"Level2Cache_\"));\n\n    // Branch predictor\n    sts->setValue(N(\"BranchPredictor_Enabled\"), get_bp_enabled());\n    sts->setValue(N(\"BranchPredictor_Type\"), (uint8_t)get_bp_type());\n    sts->setValue(N(\"BranchPredictor_InitState\"), (uint8_t)get_bp_init_state());\n    sts->setValue(N(\"BranchPredictor_BitsBTB\"), get_bp_btb_bits());\n    sts->setValue(N(\"BranchPredictor_BitsBHR\"), get_bp_bhr_bits());\n    sts->setValue(N(\"BranchPredictor_BitsBHTAddr\"), get_bp_bht_addr_bits());\n\n    // Virtual memory\n    sts->setValue(N(\"VMEnabled\"), get_vm_enabled());\n    tlbc_program().store(sts, N(\"ProgramTLB_\"));\n    tlbc_data().store(sts, N(\"DataTLB_\"));\n}\n\n#undef N\n\nvoid MachineConfig::preset(enum ConfigPresets p) {\n    // Note: we set just a minimal subset to get preset (preserving as much of\n    // hidden configuration as possible)\n    switch (p) {\n    case CP_SINGLE:\n    case CP_SINGLE_CACHE:\n        set_pipelined(false);\n        set_delay_slot(true);\n        break;\n    case CP_PIPE_NO_HAZARD:\n        set_pipelined(true);\n        set_hazard_unit(MachineConfig::HU_NONE);\n        break;\n    case CP_PIPE:\n        set_pipelined(true);\n        set_hazard_unit(MachineConfig::HU_STALL_FORWARD);\n        break;\n    }\n    // Some common configurations\n    set_memory_execute_protection(DF_EXEC_PROTEC);\n    set_memory_write_protection(DF_WRITE_PROTEC);\n    set_memory_access_time_read(DF_MEM_ACC_READ);\n    set_memory_access_time_write(DF_MEM_ACC_WRITE);\n    set_memory_access_time_burst(DF_MEM_ACC_BURST);\n    set_memory_access_time_level2(DF_MEM_ACC_LEVEL2);\n    set_memory_access_enable_burst(DF_MEM_ACC_BURST_ENABLE);\n\n    // Branch predictor\n    set_bp_enabled(DFC_BP_ENABLED);\n    set_bp_type(DFC_BP_TYPE);\n    set_bp_init_state(DFC_BP_INIT_STATE);\n    set_bp_btb_bits(DFC_BP_BTB_BITS);\n    set_bp_bhr_bits(DFC_BP_BHR_BITS);\n    set_bp_bht_addr_bits(DFC_BP_BHT_ADDR_BITS);\n\n    access_cache_program()->preset(p);\n    access_cache_data()->preset(p);\n    access_cache_level2()->preset(p);\n\n    set_vm_enabled(DFC_VM_ENABLED);\n    access_tlb_program()->preset(p);\n    access_tlb_data()->preset(p);\n\n    set_simulated_xlen(Xlen::_32);\n    set_isa_word(config_isa_word_default);\n\n    switch (p) {\n    case CP_SINGLE:\n    case CP_SINGLE_CACHE:\n    case CP_PIPE_NO_HAZARD:\n    case CP_PIPE: access_cache_level2()->set_enabled(false); break;\n    }\n}\n\nvoid MachineConfig::set_pipelined(bool v) {\n    pipeline = v;\n}\n\nvoid MachineConfig::set_delay_slot(bool v) {\n    delayslot = v;\n}\n\nvoid MachineConfig::set_hazard_unit(enum MachineConfig::HazardUnit hu) {\n    hunit = hu;\n}\n\nbool MachineConfig::set_hazard_unit(const QString &hukind) {\n    static QMap<QString, enum HazardUnit> hukind_map = {\n        { \"none\", HU_NONE },\n        { \"stall\", HU_STALL },\n        { \"forward\", HU_STALL_FORWARD },\n        { \"stall-forward\", HU_STALL_FORWARD },\n    };\n    if (!hukind_map.contains(hukind)) { return false; }\n    set_hazard_unit(hukind_map.value(hukind));\n    return true;\n}\n\nvoid MachineConfig::set_memory_execute_protection(bool v) {\n    exec_protect = v;\n}\n\nvoid MachineConfig::set_memory_write_protection(bool v) {\n    write_protect = v;\n}\n\nvoid MachineConfig::set_memory_access_time_read(unsigned v) {\n    mem_acc_read = v;\n}\n\nvoid MachineConfig::set_memory_access_time_write(unsigned v) {\n    mem_acc_write = v;\n}\n\nvoid MachineConfig::set_memory_access_time_burst(unsigned v) {\n    mem_acc_burst = v;\n}\n\nvoid MachineConfig::set_memory_access_time_level2(unsigned v) {\n    mem_acc_level2 = v;\n}\n\nvoid MachineConfig::set_memory_access_enable_burst(bool v) {\n    mem_acc_enable_burst = v;\n}\n\nvoid MachineConfig::set_osemu_enable(bool v) {\n    osem_enable = v;\n}\n\nvoid MachineConfig::set_osemu_known_syscall_stop(bool v) {\n    osem_known_syscall_stop = v;\n}\n\nvoid MachineConfig::set_osemu_unknown_syscall_stop(bool v) {\n    osem_unknown_syscall_stop = v;\n}\n\nvoid MachineConfig::set_osemu_interrupt_stop(bool v) {\n    osem_interrupt_stop = v;\n}\n\nvoid MachineConfig::set_osemu_exception_stop(bool v) {\n    osem_exception_stop = v;\n}\n\nvoid MachineConfig::set_osemu_fs_root(QString v) {\n    osem_fs_root = std::move(v);\n}\n\nvoid MachineConfig::set_reset_at_compile(bool v) {\n    res_at_compile = v;\n}\n\nvoid MachineConfig::set_elf(QString path) {\n    elf_path = std::move(path);\n}\n\nvoid MachineConfig::set_cache_program(const CacheConfig &c) {\n    cch_program = c;\n}\n\nvoid MachineConfig::set_cache_data(const CacheConfig &c) {\n    cch_data = c;\n}\n\nvoid MachineConfig::set_cache_level2(const CacheConfig &c) {\n    cch_level2 = c;\n}\n\nvoid MachineConfig::set_simulated_endian(Endian endian) {\n    MachineConfig::simulated_endian = endian;\n}\n\nvoid MachineConfig::set_simulated_xlen(Xlen xlen) {\n    simulated_xlen = xlen;\n}\n\nvoid MachineConfig::set_isa_word(ConfigIsaWord bits) {\n    isa_word = bits | config_isa_word_fixed;\n}\nvoid MachineConfig::modify_isa_word(ConfigIsaWord mask, ConfigIsaWord val) {\n    mask &= ~config_isa_word_fixed;\n    isa_word.modify(mask, val);\n}\n\nbool MachineConfig::pipelined() const {\n    return pipeline;\n}\n\nbool MachineConfig::delay_slot() const {\n    // Delay slot is always on when pipeline is enabled\n    return pipeline || delayslot;\n}\n\nenum MachineConfig::HazardUnit MachineConfig::hazard_unit() const {\n    // Hazard unit is always off when there is no pipeline\n    return pipeline ? hunit : machine::MachineConfig::HU_NONE;\n}\n\nbool MachineConfig::memory_execute_protection() const {\n    return exec_protect;\n}\n\nbool MachineConfig::memory_write_protection() const {\n    return write_protect;\n}\n\nunsigned MachineConfig::memory_access_time_read() const {\n    return mem_acc_read > 1 ? mem_acc_read : 1;\n}\n\nunsigned MachineConfig::memory_access_time_write() const {\n    return mem_acc_write > 1 ? mem_acc_write : 1;\n}\n\nunsigned MachineConfig::memory_access_time_burst() const {\n    return mem_acc_burst;\n}\n\nunsigned MachineConfig::memory_access_time_level2() const {\n    return mem_acc_level2;\n}\n\nbool MachineConfig::memory_access_enable_burst() const {\n    return mem_acc_enable_burst;\n}\n\nbool MachineConfig::osemu_enable() const {\n    return osem_enable;\n}\nbool MachineConfig::osemu_known_syscall_stop() const {\n    return osem_known_syscall_stop;\n}\nbool MachineConfig::osemu_unknown_syscall_stop() const {\n    return osem_unknown_syscall_stop;\n}\nbool MachineConfig::osemu_interrupt_stop() const {\n    return osem_interrupt_stop;\n}\nbool MachineConfig::osemu_exception_stop() const {\n    return osem_exception_stop;\n}\n\nQString MachineConfig::osemu_fs_root() const {\n    return osem_fs_root;\n}\n\nbool MachineConfig::reset_at_compile() const {\n    return res_at_compile;\n}\n\nQString MachineConfig::elf() const {\n    return elf_path;\n}\n\nconst CacheConfig &MachineConfig::cache_program() const {\n    return cch_program;\n}\n\nconst CacheConfig &MachineConfig::cache_data() const {\n    return cch_data;\n}\n\nconst CacheConfig &MachineConfig::cache_level2() const {\n    return cch_level2;\n}\n\nCacheConfig *MachineConfig::access_cache_program() {\n    return &cch_program;\n}\n\nCacheConfig *MachineConfig::access_cache_data() {\n    return &cch_data;\n}\n\nCacheConfig *MachineConfig::access_cache_level2() {\n    return &cch_level2;\n}\n\nTLBConfig *MachineConfig::access_tlb_program() {\n    return &tlb_program;\n}\n\nTLBConfig *MachineConfig::access_tlb_data() {\n    return &tlb_data;\n}\n\nconst TLBConfig &MachineConfig::tlbc_program() const {\n    return tlb_program;\n}\n\nconst TLBConfig &MachineConfig::tlbc_data() const {\n    return tlb_data;\n}\n\nEndian MachineConfig::get_simulated_endian() const {\n    return simulated_endian;\n}\n\nXlen MachineConfig::get_simulated_xlen() const {\n    return simulated_xlen;\n}\n\nConfigIsaWord MachineConfig::get_isa_word() const {\n    return isa_word;\n}\n\nvoid MachineConfig::set_bp_enabled(bool e) {\n    bp_enabled = e;\n}\n\nvoid MachineConfig::set_bp_type(PredictorType t) {\n    bp_type = t;\n}\n\nvoid MachineConfig::set_bp_init_state(PredictorState i) {\n    bp_init_state = i;\n}\n\nvoid MachineConfig::set_bp_btb_bits(uint8_t b) {\n    bp_btb_bits = b > BP_MAX_BTB_BITS ? BP_MAX_BTB_BITS : b;\n}\n\nvoid MachineConfig::set_bp_bhr_bits(uint8_t b) {\n    bp_bhr_bits = b > BP_MAX_BHR_BITS ? BP_MAX_BHR_BITS : b;\n    bp_bht_addr_bits\n        = bp_bht_addr_bits > BP_MAX_BHT_ADDR_BITS ? BP_MAX_BHT_ADDR_BITS : bp_bht_addr_bits;\n    bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits;\n    bp_bht_bits = bp_bht_bits > BP_MAX_BHT_BITS ? BP_MAX_BHT_BITS : bp_bht_bits;\n}\n\nvoid MachineConfig::set_bp_bht_addr_bits(uint8_t b) {\n    bp_bht_addr_bits = b > BP_MAX_BHT_ADDR_BITS ? BP_MAX_BHT_ADDR_BITS : b;\n    bp_bhr_bits = bp_bhr_bits > BP_MAX_BHR_BITS ? BP_MAX_BHR_BITS : bp_bhr_bits;\n    bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits;\n    bp_bht_bits = bp_bht_bits > BP_MAX_BHT_BITS ? BP_MAX_BHT_BITS : bp_bht_bits;\n}\n\nbool MachineConfig::get_bp_enabled() const {\n    return bp_enabled;\n}\n\nPredictorType MachineConfig::get_bp_type() const {\n    return bp_type;\n}\n\nPredictorState MachineConfig::get_bp_init_state() const {\n    return bp_init_state;\n}\n\nuint8_t MachineConfig::get_bp_btb_bits() const {\n    return bp_btb_bits;\n}\n\nuint8_t MachineConfig::get_bp_bhr_bits() const {\n    return bp_bhr_bits;\n}\n\nuint8_t MachineConfig::get_bp_bht_addr_bits() const {\n    return bp_bht_addr_bits;\n}\n\nuint8_t MachineConfig::get_bp_bht_bits() const {\n    return bp_bht_bits;\n}\n\nvoid MachineConfig::set_vm_enabled(bool v) {\n    vm_enabled = v;\n}\n\nbool MachineConfig::get_vm_enabled() const {\n    return vm_enabled;\n}\n\nbool MachineConfig::operator==(const MachineConfig &c) const {\n#define CMP(GETTER) (GETTER)() == (c.GETTER)()\n    return CMP(pipelined) && CMP(delay_slot) && CMP(hazard_unit) && CMP(get_simulated_xlen)\n           && CMP(get_isa_word) && CMP(get_bp_enabled) && CMP(get_bp_type) && CMP(get_bp_init_state)\n           && CMP(get_bp_btb_bits) && CMP(get_bp_bhr_bits) && CMP(get_bp_bht_addr_bits)\n           && CMP(memory_execute_protection) && CMP(memory_write_protection)\n           && CMP(memory_access_time_read) && CMP(memory_access_time_write)\n           && CMP(memory_access_time_burst) && CMP(memory_access_time_level2)\n           && CMP(memory_access_enable_burst) && CMP(elf) && CMP(cache_program) && CMP(cache_data)\n           && CMP(cache_level2) && CMP(get_vm_enabled) && CMP(tlbc_data) && CMP(tlbc_program);\n#undef CMP\n}\n\nbool MachineConfig::operator!=(const MachineConfig &c) const {\n    return !operator==(c);\n}\n"
  },
  {
    "path": "src/machine/machineconfig.h",
    "content": "#ifndef MACHINECONFIG_H\n#define MACHINECONFIG_H\n\n#include \"common/endian.h\"\n#include \"config_isa.h\"\n#include \"predictor_types.h\"\n\n#include <QSettings>\n#include <QString>\n\nnamespace machine {\n\n/**\n * There are two primary base integer variants, RV32I and RV64I, described in Chapters 2 and 5,\n * which provide 32-bit or 64-bit address spaces respectively. We use the term XLEN to refer to\n * the width of an integer register in bits (either 32 or 64). [RISC-V Unprivileged ISA, page 4]\n *\n * `RegisterValue` type is used to abstract the size of stored value. It provides methods to get\n *  correct size. The `Core` can extract them with a special method.\n */\nenum class Xlen { _32 = 32, _64 = 64 };\n\nenum ConfigPresets {\n    CP_SINGLE,         // No pipeline cpu without cache\n    CP_SINGLE_CACHE,   // No pipeline cpu with cache\n    CP_PIPE_NO_HAZARD, // Pipelined cpu without hazard unit and without cache\n    CP_PIPE            // Full pipelined cpu\n};\n\nconstexpr ConfigIsaWord config_isa_word_default\n    = ConfigIsaWord::byChar('E') | ConfigIsaWord::byChar('I') | ConfigIsaWord::byChar('A')\n      | ConfigIsaWord::byChar('M');\n\nconstexpr ConfigIsaWord config_isa_word_fixed\n    = ConfigIsaWord::byChar('E') | ConfigIsaWord::byChar('I');\n\nclass CacheConfig {\npublic:\n    CacheConfig();\n    explicit CacheConfig(const CacheConfig *cc);\n    explicit CacheConfig(const QSettings *, const QString &prefix = \"\");\n\n    void store(QSettings *, const QString &prefix = \"\") const;\n\n    void preset(enum ConfigPresets);\n\n    enum ReplacementPolicy {\n        RP_RAND, // Random\n        RP_LRU,  // Least recently used\n        RP_LFU,  // Least frequently used\n        RP_PLRU, // Pseudo Least recently used\n        RP_NMRU  // Not most recently used\n    };\n\n    enum WritePolicy {\n        WP_THROUGH_NOALLOC, // Write through\n        WP_THROUGH_ALLOC,   // Write through\n        WP_BACK             // Write back\n    };\n\n    // If cache should be used or not\n    void set_enabled(bool);\n    void set_set_count(unsigned);     // Number of sets\n    void set_block_size(unsigned);    // Number of uint32_t in block\n    void set_associativity(unsigned); // Degree of associativity (number of\n                                      // ways)\n    void set_replacement_policy(enum ReplacementPolicy);\n    void set_write_policy(enum WritePolicy);\n\n    bool enabled() const;\n    unsigned set_count() const;\n    unsigned block_size() const;\n    unsigned associativity() const;\n    enum ReplacementPolicy replacement_policy() const;\n    enum WritePolicy write_policy() const;\n\n    bool operator==(const CacheConfig &c) const;\n    bool operator!=(const CacheConfig &c) const;\n\nprivate:\n    bool en;\n    unsigned n_sets, n_blocks, d_associativity;\n    enum ReplacementPolicy replac_pol;\n    enum WritePolicy write_pol;\n};\n\nclass TLBConfig {\npublic:\n    TLBConfig();\n    explicit TLBConfig(const TLBConfig *cc);\n    explicit TLBConfig(const QSettings *, const QString &prefix = \"\");\n\n    void store(QSettings *, const QString &prefix = \"\") const;\n\n    void preset(enum ConfigPresets);\n\n    enum VmMode { VM_BARE, VM_SV32 };\n\n    enum ReplacementPolicy {\n        RP_RAND, // Random\n        RP_LRU,  // Least recently used\n        RP_LFU,  // Least frequently used\n        RP_PLRU  // Pseudo Least recently used\n    };\n\n    // Virtual Memory\n    void set_vm_asid(uint32_t a);\n    uint32_t get_vm_asid() const;\n\n    void set_tlb_num_sets(unsigned);\n    void set_tlb_associativity(unsigned);\n    void set_tlb_replacement_policy(ReplacementPolicy);\n\n    unsigned get_tlb_num_sets() const;\n    unsigned get_tlb_associativity() const;\n    ReplacementPolicy get_tlb_replacement_policy() const;\n\n    bool operator==(const TLBConfig &c) const;\n    bool operator!=(const TLBConfig &c) const;\n\nprivate:\n    bool vm_enabled = false;\n    uint32_t vm_asid = 0;\n    unsigned n_sets = 1;\n    unsigned d_associativity = 1;\n    enum ReplacementPolicy replac_pol = RP_RAND;\n};\n\nclass MachineConfig {\npublic:\n    MachineConfig();\n    explicit MachineConfig(const MachineConfig *config);\n    explicit MachineConfig(const QSettings *, const QString &prefix = \"\");\n\n    void store(QSettings *, const QString &prefix = \"\");\n\n    void preset(enum ConfigPresets);\n\n    enum HazardUnit { HU_NONE, HU_STALL, HU_STALL_FORWARD };\n\n    // Configure if CPU is pipelined\n    // In default disabled.\n    void set_pipelined(bool);\n    // Configure if cpu should simulate delay slot in non-pipelined core\n    // In default enabled. When disabled it also automatically disables\n    // pipelining.\n    void set_delay_slot(bool);\n    // Hazard unit\n    void set_hazard_unit(enum HazardUnit);\n    bool set_hazard_unit(const QString &hukind);\n    // Protect data memory from execution. Only program sections can be\n    // executed.\n    void set_memory_execute_protection(bool);\n    // Protect program memory from accidental writes.\n    void set_memory_write_protection(bool);\n    // Set memory access times. Passed value is in cycles.\n    void set_memory_access_time_read(unsigned);\n    void set_memory_access_time_write(unsigned);\n    void set_memory_access_time_burst(unsigned);\n    void set_memory_access_time_level2(unsigned);\n    void set_memory_access_enable_burst(bool);\n    // Operating system and exceptions setup\n    void set_osemu_enable(bool);\n    void set_osemu_known_syscall_stop(bool);\n    void set_osemu_unknown_syscall_stop(bool);\n    void set_osemu_interrupt_stop(bool);\n    void set_osemu_exception_stop(bool);\n    void set_osemu_fs_root(QString v);\n    // reset machine befor internal compile/reload after external make\n    void set_reset_at_compile(bool);\n    // Set path to source elf file. This has to be set before core is\n    // initialized.\n    void set_elf(QString path);\n    // Configure cache\n    void set_cache_program(const CacheConfig &);\n    void set_cache_data(const CacheConfig &);\n    void set_cache_level2(const CacheConfig &);\n    void set_simulated_endian(Endian endian);\n    void set_simulated_xlen(Xlen xlen);\n    void set_isa_word(ConfigIsaWord bits);\n    void modify_isa_word(ConfigIsaWord mask, ConfigIsaWord val);\n\n    bool pipelined() const;\n    bool delay_slot() const;\n    enum HazardUnit hazard_unit() const;\n    bool memory_execute_protection() const;\n    bool memory_write_protection() const;\n    unsigned memory_access_time_read() const;\n    unsigned memory_access_time_write() const;\n    unsigned memory_access_time_burst() const;\n    unsigned memory_access_time_level2() const;\n    bool memory_access_enable_burst() const;\n    bool osemu_enable() const;\n    bool osemu_known_syscall_stop() const;\n    bool osemu_unknown_syscall_stop() const;\n    bool osemu_interrupt_stop() const;\n    bool osemu_exception_stop() const;\n    QString osemu_fs_root() const;\n    bool reset_at_compile() const;\n    QString elf() const;\n    const CacheConfig &cache_program() const;\n    const CacheConfig &cache_data() const;\n    const CacheConfig &cache_level2() const;\n    Endian get_simulated_endian() const;\n    Xlen get_simulated_xlen() const;\n    ConfigIsaWord get_isa_word() const;\n\n    // Virtual memory\n    void set_vm_enabled(bool v);\n    bool get_vm_enabled() const;\n    const TLBConfig &tlbc_program() const;\n    const TLBConfig &tlbc_data() const;\n\n    // Branch predictor - Setters\n    void set_bp_enabled(bool e);\n    void set_bp_type(PredictorType t);\n    void set_bp_init_state(PredictorState i);\n    void set_bp_btb_bits(uint8_t b);\n    void set_bp_bhr_bits(uint8_t b);\n    void set_bp_bht_addr_bits(uint8_t b);\n    // Branch predictor - Getters\n    bool get_bp_enabled() const;\n    PredictorType get_bp_type() const;\n    PredictorState get_bp_init_state() const;\n    uint8_t get_bp_btb_bits() const;\n    uint8_t get_bp_bhr_bits() const;\n    uint8_t get_bp_bht_addr_bits() const;\n    uint8_t get_bp_bht_bits() const;\n\n    CacheConfig *access_cache_program();\n    CacheConfig *access_cache_data();\n    CacheConfig *access_cache_level2();\n\n    TLBConfig *access_tlb_program();\n    TLBConfig *access_tlb_data();\n\n    bool operator==(const MachineConfig &c) const;\n    bool operator!=(const MachineConfig &c) const;\n\nprivate:\n    bool pipeline, delayslot;\n    enum HazardUnit hunit;\n    bool exec_protect, write_protect;\n    unsigned mem_acc_read, mem_acc_write, mem_acc_burst, mem_acc_level2;\n    bool mem_acc_enable_burst;\n    bool osem_enable, osem_known_syscall_stop, osem_unknown_syscall_stop;\n    bool osem_interrupt_stop, osem_exception_stop;\n    bool res_at_compile;\n    QString osem_fs_root;\n    QString elf_path;\n    CacheConfig cch_program, cch_data, cch_level2;\n    Endian simulated_endian;\n    Xlen simulated_xlen;\n    ConfigIsaWord isa_word;\n\n    // Branch predictor\n    bool bp_enabled;\n    PredictorType bp_type;\n    PredictorState bp_init_state;\n    uint8_t bp_btb_bits;\n    uint8_t bp_bhr_bits;\n    uint8_t bp_bht_addr_bits;\n    uint8_t bp_bht_bits; // = bp_bhr_bits + bp_bht_addr_bits\n\n    // Virtual memory\n    bool vm_enabled;\n    TLBConfig tlb_program, tlb_data;\n};\n\n} // namespace machine\n\nQ_DECLARE_METATYPE(machine::CacheConfig)\n\n#endif // MACHINECONFIG_H\n"
  },
  {
    "path": "src/machine/machinedefs.h",
    "content": "#ifndef MACHINEDEFS_H\n#define MACHINEDEFS_H\n\n#include \"memory/address.h\"\n\n#include <cstdint>\n#include <qmetatype.h>\n\nnamespace machine {\n\nenum AccessControl {\n    AC_NONE,\n    AC_I8,\n    AC_U8,\n    AC_I16,\n    AC_U16,\n    AC_I32,\n    AC_U32,\n    AC_I64,\n    AC_U64,\n    AC_LR32,\n    AC_SC32,\n    AC_AMOSWAP32,\n    AC_AMOADD32,\n    AC_AMOXOR32,\n    AC_AMOAND32,\n    AC_AMOOR32,\n    AC_AMOMIN32,\n    AC_AMOMAX32,\n    AC_AMOMINU32,\n    AC_AMOMAXU32,\n    AC_LR64,\n    AC_SC64,\n    AC_AMOSWAP64,\n    AC_AMOADD64,\n    AC_AMOXOR64,\n    AC_AMOAND64,\n    AC_AMOOR64,\n    AC_AMOMIN64,\n    AC_AMOMAX64,\n    AC_AMOMINU64,\n    AC_AMOMAXU64,\n    AC_CACHE_OP,\n};\n\nconstexpr AccessControl AC_FIRST_REGULAR = AC_I8;\nconstexpr AccessControl AC_LAST_REGULAR = AC_U64;\nconstexpr AccessControl AC_FIRST_SPECIAL = AC_LR32;\nconstexpr AccessControl AC_LAST_SPECIAL = AC_CACHE_OP;\nconstexpr AccessControl AC_FISRT_AMO_MODIFY32 = AC_AMOSWAP32;\nconstexpr AccessControl AC_LAST_AMO_MODIFY32 = AC_AMOMAXU32;\nconstexpr AccessControl AC_FISRT_AMO_MODIFY64 = AC_AMOSWAP64;\nconstexpr AccessControl AC_LAST_AMO_MODIFY64 = AC_AMOMAXU64;\n\nconstexpr bool is_regular_access(AccessControl type) {\n    return AC_FIRST_REGULAR <= type and type <= AC_LAST_REGULAR;\n}\n\nconstexpr bool is_special_access(AccessControl type) {\n    return AC_FIRST_SPECIAL <= type and type <= AC_LAST_SPECIAL;\n}\nstatic_assert(is_special_access(AC_CACHE_OP), \"\");\nstatic_assert(is_special_access((AccessControl)11), \"\");\n\nenum ExceptionCause {\n    EXCAUSE_NONE = 0, // Use zero as default value when no exception is\n    // ECAUSE_INSN_MISALIGNED - not defined for now, overlaps with EXCAUSE_NON\n    EXCAUSE_INSN_FAULT = 1,        // Instruction access fault\n    EXCAUSE_INSN_ILLEGAL = 2,      // Illegal instruction\n    EXCAUSE_BREAK = 3,             // Breakpoint\n    EXCAUSE_LOAD_MISALIGNED = 4,   // Load address misaligned\n    EXCAUSE_LOAD_FAULT = 5,        // Load access fault\n    EXCAUSE_STORE_MISALIGNED = 6,  // Store/AMO address misaligned\n    EXCAUSE_STORE_FAULT = 7,       // Store/AMO access fault\n    EXCAUSE_ECALL_U = 8,           // Environment call from U-mode\n    EXCAUSE_ECALL_S = 9,           // Environment call from S-mode\n    EXCAUSE_RESERVED_10 = 10,      // Reserved\n    EXCAUSE_ECALL_M = 11,          // Environment call from M-mode\n    EXCAUSE_INSN_PAGE_FAULT = 12,  // Instruction page fault\n    EXCAUSE_LOAD_PAGE_FAULT = 13,  // Load page fault\n    EXCAUSE_RESERVED_14 = 14,      // Reserved\n    EXCAUSE_STORE_PAGE_FAULT = 15, // Store/AMO page fault\n    // Simulator specific exception cause codes, alliases\n    EXCAUSE_HWBREAK = 16,\n    EXCAUSE_ECALL_ANY = 17, // sythetic exception to mark ECALL instruction\n    EXCAUSE_INT = 18,       // External/asynchronous interrupt, bit 32 or 63\n    EXCAUSE_COUNT = 19,\n};\n\nenum LocationStatus {\n    LOCSTAT_NONE = 0,\n    LOCSTAT_CACHED = 1 << 0,\n    LOCSTAT_DIRTY = 1 << 1,\n    LOCSTAT_READ_ONLY = 1 << 2,\n    LOCSTAT_ILLEGAL = 1 << 3,\n};\n\nconst Address STAGEADDR_NONE = 0xffffffff_addr;\n} // namespace machine\n\nQ_DECLARE_METATYPE(machine::AccessControl)\n\n#endif // MACHINEDEFS_H\n"
  },
  {
    "path": "src/machine/memory/address.h",
    "content": "#ifndef ADDRESS_H\n#define ADDRESS_H\n\n#include \"utils.h\"\n\n#include <QMetaType>\n#include <cstdint>\n\nusing std::uint64_t;\n\nnamespace machine {\n\n/**\n * Emulated physical memory address.\n *\n * It is guaranteed that all instances of this type belong to single address\n * space and therefore all can be compared fearlessly.\n * <p>\n * OPTIMIZATION NOTE: All methods are implemented in header file to support\n * inlining (as we want to use it as a primitive type) but too keep declaration\n * tidy out-of-line definitions are preferred.\n */\nclass Address {\nprivate:\n    uint64_t data; //> Real wrapped address\n\npublic:\n    constexpr explicit Address(uint64_t);\n\n    /**\n     *  Default constructor results in null pointer.\n     *  Why it exists? To make address default constructable for STL.\n     */\n    constexpr Address();\n\n    constexpr Address(const Address &address) = default;            //> Copy constructor\n    constexpr Address &operator=(const Address &address) = default; //> Assign constructor\n\n    /**\n     * Extracts raw numeric value of the address.\n     * Why it exists? Output, unsafe operations not supported by operators.\n     */\n    [[nodiscard]] constexpr uint64_t get_raw() const;\n\n    /**\n     * Explicit cast operator to underlying type.\n     * This is an alternative ti get_raw() method.\n     *\n     * NOTE: This operator is needed for correct functioning of\n     * repeat_access_until_completed function defined in memory_utils.h.\n     */\n    constexpr explicit operator uint64_t() const;\n\n    /**\n     * More expressive way to assign null.\n     * Why it exists? Equivalent of nullptr in cpp. Address(0) was ugly and too\n     * common.\n     */\n    constexpr static Address null();\n\n    /**\n     * Test for null address\n     *\n     * Why is exists? Testing for null in very often and without this method a\n     * creation of a new address object set to null would be required. That is\n     * considered too long and badly readable. For other comparison we expect a\n     * comparison with existing value to be the common case.\n     * Alternative: `address.get_raw() == 0`\n     * Alternative: `address == Address::null()`\n     */\n    [[nodiscard]] constexpr bool is_null() const;\n\n    /**\n     * Test whether address is aligned to the size of type T.\n     *\n     * Example:\n     * ```\n     *  is_aligned<uint32_t>(address) <=> address % 4 == 0\n     * ```\n     */\n    template<typename T>\n    [[nodiscard]] constexpr bool is_aligned() const;\n\n    /* Eq */\n    constexpr inline bool operator==(const Address &other) const;\n    constexpr inline bool operator!=(const Address &other) const;\n\n    /* Ord */\n    constexpr inline bool operator<(const Address &other) const;\n    constexpr inline bool operator>(const Address &other) const;\n    constexpr inline bool operator<=(const Address &other) const;\n    constexpr inline bool operator>=(const Address &other) const;\n\n    /* Offset arithmetic */\n    constexpr inline Address operator+(const uint64_t &offset) const;\n    constexpr inline Address operator-(const uint64_t &offset) const;\n    inline void operator+=(const uint64_t &offset);\n    inline void operator-=(const uint64_t &offset);\n\n    /* Bitwise arithmetic */\n    constexpr inline Address operator&(const uint64_t &mask) const;\n    constexpr inline Address operator|(const uint64_t &mask) const;\n    constexpr inline Address operator^(const uint64_t &mask) const;\n    constexpr inline Address operator>>(const uint64_t &size) const;\n    constexpr inline Address operator<<(const uint64_t &size) const;\n\n    /* Distance arithmetic */\n    constexpr inline int64_t operator-(const Address &other) const;\n};\n\nconstexpr Address operator\"\"_addr(unsigned long long literal) {\n    return Address(literal);\n}\n\nconstexpr Address::Address(uint64_t address) : data(address) {}\n\nconstexpr Address::Address() : data(0) {}\n\nconstexpr uint64_t Address::get_raw() const {\n    return data;\n}\n\nconstexpr Address::operator uint64_t() const {\n    return this->get_raw();\n}\n\nconstexpr Address Address::null() {\n    return Address(0x0);\n}\n\nconstexpr bool Address::is_null() const {\n    return this->get_raw() == 0;\n}\n\ntemplate<typename T>\nconstexpr bool Address::is_aligned() const {\n    return is_aligned_generic<typeof(this->data), T>(this->data);\n}\n\n/*\n * Equality operators\n */\n\nconstexpr bool Address::operator==(const Address &other) const {\n    return this->get_raw() == other.get_raw();\n}\n\nconstexpr bool Address::operator!=(const Address &other) const {\n    return this->get_raw() != other.get_raw();\n}\n\n/*\n * Ordering operators\n */\n\nconstexpr bool Address::operator<(const Address &other) const {\n    return this->get_raw() < other.get_raw();\n}\n\nconstexpr bool Address::operator>(const Address &other) const {\n    return this->get_raw() > other.get_raw();\n}\n\nconstexpr bool Address::operator<=(const Address &other) const {\n    return this->get_raw() <= other.get_raw();\n}\n\nconstexpr bool Address::operator>=(const Address &other) const {\n    return this->get_raw() >= other.get_raw();\n}\n\n/*\n * Offset arithmetic operators\n */\n\nconstexpr Address Address::operator+(const uint64_t &offset) const {\n    return Address(this->get_raw() + offset);\n}\n\nconstexpr Address Address::operator-(const uint64_t &offset) const {\n    return Address(this->get_raw() - offset);\n}\n\nvoid Address::operator+=(const uint64_t &offset) {\n    data += offset;\n}\n\nvoid Address::operator-=(const uint64_t &offset) {\n    data -= offset;\n}\n\n/*\n * Bitwise operators\n */\n\nconstexpr Address Address::operator&(const uint64_t &mask) const {\n    return Address(this->get_raw() & mask);\n}\n\nconstexpr Address Address::operator|(const uint64_t &mask) const {\n    return Address(this->get_raw() | mask);\n}\n\nconstexpr Address Address::operator^(const uint64_t &mask) const {\n    return Address(get_raw() ^ mask);\n}\n\nconstexpr Address Address::operator>>(const uint64_t &size) const {\n    return Address(this->get_raw() >> size);\n}\n\nconstexpr Address Address::operator<<(const uint64_t &size) const {\n    return Address(this->get_raw() << size);\n}\n\n/*\n * Distance arithmetic operators\n */\n\nconstexpr int64_t Address::operator-(const Address &other) const {\n    return this->get_raw() - other.get_raw();\n}\n\n} // namespace machine\n\nQ_DECLARE_METATYPE(machine::Address)\n\n#endif // ADDRESS_H\n"
  },
  {
    "path": "src/machine/memory/address_range.h",
    "content": "#ifndef ADDRESS_RANGE_H\n#define ADDRESS_RANGE_H\n\n#include \"memory/address.h\"\n#include \"utils.h\"\n\n#include <QMetaType>\n#include <cstdint>\n\nnamespace machine {\n\n/**\n * Physical memory range - for nou used for range reservation for AMO operations\n *\n * If the first and the last point to the same address then range represnets\n * exaclly one valid byte. If the last is smaller than the first then range\n * is empty.\n */\nclass AddressRange {\npublic:\n    Address first; //> The first valid location of the range\n    Address last;  //> The last valid location of the range\n\n    /**\n     *  Default constructor results for empty range.\n     */\n    constexpr AddressRange() : first(1), last(0) {};\n\n    constexpr AddressRange(const AddressRange &range) = default; //> Copy constructor\n    constexpr AddressRange(const Address &afirst, const Address &alast)\n        : first(afirst)\n        , last(alast) {};\n    constexpr AddressRange(const Address &asingleAddr) : first(asingleAddr), last(asingleAddr) {};\n    constexpr AddressRange &operator=(const AddressRange &address) = default; //> Assign constructor\n\n    /**\n     * Is empty range\n     */\n    constexpr bool is_empty() const { return first > last; };\n\n    /* Eq */\n    constexpr inline bool operator==(const AddressRange &other) const {\n        return (first == other.first && last == other.last) || (is_empty() && other.is_empty());\n    };\n    constexpr inline bool operator!=(const AddressRange &other) const {\n        return !((first == other.first && last == other.last) || (is_empty() && other.is_empty()));\n    };\n\n    constexpr bool within(const AddressRange &other) const {\n        return (first >= other.first) && (last <= other.last);\n    };\n\n    constexpr bool contains(const AddressRange &other) const {\n        return (first <= other.first) && (last >= other.last);\n    };\n\n    constexpr bool overlaps(const AddressRange &other) const {\n        return (first <= other.last) && (last >= other.first) && !is_empty() && !other.is_empty();\n    };\n\n    void reset() {\n        first = Address(1);\n        last = Address(0);\n    }\n};\n\n} // namespace machine\n\nQ_DECLARE_METATYPE(machine::AddressRange)\n\n#endif // ADDRESS_RANGE_H\n"
  },
  {
    "path": "src/machine/memory/address_with_mode.h",
    "content": "#ifndef ADDRESS_WITH_MODE_H\n#define ADDRESS_WITH_MODE_H\n\n#include <utility>\n#include <cstdint>\n#include <QMetaType>\n#include \"address.h\"\n#include \"csr/address.h\"\n\nnamespace machine {\n\nstruct AccessMode {\n    uint32_t token = 0;\n\n    AccessMode() = default;\n    explicit AccessMode(uint32_t t) : token(t) {}\n\n    static AccessMode pack(uint16_t asid, CSR::PrivilegeLevel priv, bool uncached = false) {\n        uint32_t t = (static_cast<uint32_t>(priv) & 0x3u)\n            | ((static_cast<uint32_t>(asid) & 0xFFFFu) << 2)\n            | (uncached ? (1u << 18) : 0u);\n        return AccessMode(t);\n    }\n    uint16_t asid() const { return static_cast<uint16_t>((token >> 2) & 0xFFFFu); }\n    CSR::PrivilegeLevel priv() const {\n        return static_cast<CSR::PrivilegeLevel>(token & 0x3u);\n    }\n    bool uncached() const { return ((token >> 18) & 0x1u) != 0; }\n    uint32_t raw() const { return token; }\n};\n\nclass AddressWithMode : public Address {\nprivate:\n    AccessMode mode;\n\npublic:\n    constexpr AddressWithMode() : Address(), mode() {}\n\n    constexpr explicit AddressWithMode(uint64_t addr, AccessMode m = {}) : Address(addr), mode(m) {}\n\n    constexpr AddressWithMode(const Address &addr, AccessMode m = {}) : Address(addr), mode(m) {}\n\n    constexpr AddressWithMode(const AddressWithMode &other) = default;\n    AddressWithMode &operator=(const AddressWithMode &other) = default;\n\n    constexpr AccessMode access_mode() const noexcept { return mode; }\n    void set_access_mode(AccessMode m) noexcept { mode = m; }\n    uint32_t access_mode_raw() const noexcept { return mode.raw(); }\n\n    constexpr bool operator==(const AddressWithMode &other) const noexcept {\n        return static_cast<const Address&>(*this) == static_cast<const Address&>(other)\n               && mode.raw() == other.mode.raw();\n    }\n    constexpr bool operator!=(const AddressWithMode &other) const noexcept { return !(*this == other); }\n\n    constexpr std::pair<Address, AccessMode> unpack() const noexcept {\n        return { Address(get_raw()), mode };\n    }\n};\n\n} // namespace machine\n\nQ_DECLARE_METATYPE(machine::AddressWithMode)\n\n#endif // ADDRESS_WITH_MODE_H\n"
  },
  {
    "path": "src/machine/memory/backend/aclintmswi.cpp",
    "content": "#include \"memory/backend/aclintmswi.h\"\n\n#include \"common/endian.h\"\n\n#include <QTimerEvent>\n\nusing ae = machine::AccessEffects; // For enum values, type is obvious from\n                                   // context.\n\nnamespace machine::aclint {\n\nAclintMswi::AclintMswi(Endian simulated_machine_endian)\n    : BackendMemory(simulated_machine_endian)\n    , mswi_irq_level(3) {\n    mswi_count = 1;\n    for (bool &i : mswi_value)\n        i = false;\n}\n\nAclintMswi::~AclintMswi() = default;\n\nbool AclintMswi::update_mswi_irq() {\n    bool active;\n\n    active = mswi_value[0];\n\n    if (active != mswi_irq_active) {\n        mswi_irq_active = active;\n        emit signal_interrupt(mswi_irq_level, active);\n    }\n    return active;\n}\n\nWriteResult\nAclintMswi::write(Offset destination, const void *source, size_t size, WriteOptions options) {\n    UNUSED(options)\n    return write_by_u32(\n        destination, source, size,\n        [&](Offset src) {\n            return byteswap_if(\n                read_reg32(src, options.type), internal_endian != simulated_machine_endian);\n        },\n        [&](Offset src, uint32_t value) {\n            return write_reg32(\n                src, byteswap_if(value, internal_endian != simulated_machine_endian));\n        });\n}\n\nReadResult\nAclintMswi::read(void *destination, Offset source, size_t size, ReadOptions options) const {\n    return read_by_u32(destination, source, size, [&](Offset src) {\n        return byteswap_if(\n            read_reg32(src, options.type), internal_endian != simulated_machine_endian);\n    });\n}\n\nuint32_t AclintMswi::read_reg32(Offset source, AccessEffects type) const {\n    Q_UNUSED(type)\n    Q_ASSERT((source & 3U) == 0); // uint32_t aligned\n\n    uint32_t value = 0;\n\n    if ((source >= ACLINT_MSWI_OFFSET) && (source < ACLINT_MSWI_OFFSET + 4 * mswi_count)) {\n        value = mswi_value[source >> 2] ? 1 : 0;\n    }\n\n    emit read_notification(source, value);\n\n    return value;\n}\n\nbool AclintMswi::write_reg32(Offset destination, uint32_t value) {\n    Q_ASSERT((destination & 3U) == 0); // uint32_t aligned\n\n    bool changed = false;\n\n    if ((destination >= ACLINT_MSWI_OFFSET)\n        && (destination < ACLINT_MSWI_OFFSET + 4 * mswi_count)) {\n        bool value_bool = value & 1;\n        changed = value_bool != mswi_value[destination >> 2];\n        mswi_value[destination >> 2] = value_bool;\n        update_mswi_irq();\n    } else {\n        printf(\"WARNING: ACLINT MSWI - read out of range (at 0x%zu).\\n\", destination);\n    }\n\n    emit write_notification(destination, value);\n\n    return changed;\n}\nLocationStatus AclintMswi::location_status(Offset offset) const {\n    if ((offset >= ACLINT_MSWI_OFFSET) && (offset < ACLINT_MSWI_OFFSET + 4 * mswi_count))\n        return LOCSTAT_NONE;\n\n    return LOCSTAT_ILLEGAL;\n}\n\n} // namespace machine::aclint\n"
  },
  {
    "path": "src/machine/memory/backend/aclintmswi.h",
    "content": "#ifndef ACLINTMSWI_H\n#define ACLINTMSWI_H\n\n#include \"common/endian.h\"\n#include \"memory/backend/backend_memory.h\"\n\n#include <QTime>\n#include <cstdint>\n\nnamespace machine::aclint {\n\nconstexpr Offset CLINT_MSWI_OFFSET = 0x0000u;\nconstexpr Offset CLINT_MSWI_SIZE = 0x4000u;\n\nconstexpr Offset ACLINT_MSWI_OFFSET = 0;\nconstexpr Offset ACLINT_MSWI_COUNT_MAX = 1;\n\n// Timer interrupts\n// mip.MTIP and mie.MTIE are bit 7\n// mip.STIP and mie.STIE are bit 5\n\n// Software interrupts\n// mip.MSIP and mie.MSIE are bit 3\n// mip.SSIP and mie.SSIE are bit 1\n\nclass AclintMswi : public BackendMemory {\n    Q_OBJECT\npublic:\n    explicit AclintMswi(Endian simulated_machine_endian);\n    ~AclintMswi() override;\n\nsignals:\n    void write_notification(Offset address, uint32_t value);\n    void read_notification(Offset address, uint32_t value) const;\n    void signal_interrupt(uint irq_level, bool active) const;\n\npublic:\n    WriteResult\n    write(Offset destination, const void *source, size_t size, WriteOptions options) override;\n\n    ReadResult\n    read(void *destination, Offset source, size_t size, ReadOptions options) const override;\n\n    [[nodiscard]] LocationStatus location_status(Offset offset) const override;\n\nprivate:\n    /** endian of internal registers of the periphery use. */\n    static constexpr Endian internal_endian = NATIVE_ENDIAN;\n\n    [[nodiscard]] uint32_t read_reg32(Offset source, AccessEffects type) const;\n    bool write_reg32(Offset destination, uint32_t value);\n\n    bool update_mswi_irq();\n\n    unsigned mswi_count;\n    bool mswi_value[ACLINT_MSWI_COUNT_MAX] {};\n\n    const uint8_t mswi_irq_level;\n    bool mswi_irq_active = false;\n};\n\n} // namespace machine::aclint\n\n#endif // ACLINTMSWI_H\n"
  },
  {
    "path": "src/machine/memory/backend/aclintmtimer.cpp",
    "content": "#include \"memory/backend/aclintmtimer.h\"\n\n#include \"common/endian.h\"\n\n#include <QTimerEvent>\n#include <common/logging.h>\n\nLOG_CATEGORY(\"machine.memory.aclintmtimer\");\n\nusing ae = machine::AccessEffects; // For enum values, type is obvious from\n                                   // context.\n\nnamespace machine::aclint {\n\nAclintMtimer::AclintMtimer(Endian simulated_machine_endian)\n    : BackendMemory(simulated_machine_endian)\n    , mtimer_irq_level(7) {\n    mtimecmp_count = 1;\n\n    for (auto &value : mtimecmp_value) {\n        value = 0;\n    }\n\n    clock.start();\n    qt_timer_id = -1;\n}\n\nAclintMtimer::~AclintMtimer() {\n    if (qt_timer_id >= 0) killTimer(qt_timer_id);\n    qt_timer_id = -1;\n}\n\nuint64_t AclintMtimer::mtime_fetch_current() const {\n    mtime_last_current_fetch = clock.elapsed() * (uint64_t)10000;\n\n    return mtime_last_current_fetch;\n}\n\nbool AclintMtimer::update_mtimer_irq() {\n    bool active;\n\n    active = mtimecmp_value[0] < mtime_last_current_fetch + mtime_user_offset;\n\n    if (active != mtimer_irq_active) {\n        mtimer_irq_active = active;\n        emit signal_interrupt(mtimer_irq_level, active);\n    }\n\n    if (active) {\n        if (qt_timer_id >= 0) killTimer(qt_timer_id);\n        qt_timer_id = -1;\n    }\n    return active;\n}\n\nvoid AclintMtimer::timerEvent(QTimerEvent *event) {\n    if (event->timerId() == qt_timer_id) {\n        if (qt_timer_id >= 0) killTimer(qt_timer_id);\n        qt_timer_id = -1;\n\n        mtime_fetch_current();\n        if (!update_mtimer_irq()) { arm_mtimer_event(); }\n    } else {\n        BackendMemory::timerEvent(event);\n    }\n}\n\nvoid AclintMtimer::arm_mtimer_event() {\n    if (qt_timer_id >= 0) killTimer(qt_timer_id);\n    qt_timer_id = -1;\n\n    uint64_t ticks_to_wait = mtimecmp_value[0] - (mtime_last_current_fetch + mtime_user_offset);\n    qt_timer_id = startTimer(ticks_to_wait / 10000);\n}\n\nWriteResult\nAclintMtimer::write(Offset destination, const void *source, size_t size, WriteOptions options) {\n    UNUSED(options)\n    return write_by_u64(\n        destination, source, size,\n        [&](Offset src) {\n            return byteswap_if(\n                read_reg64(src, options.type), internal_endian != simulated_machine_endian);\n        },\n        [&](Offset src, uint64_t value) {\n            return write_reg64(\n                src, byteswap_if(value, internal_endian != simulated_machine_endian));\n        });\n}\n\nReadResult\nAclintMtimer::read(void *destination, Offset source, size_t size, ReadOptions options) const {\n    return read_by_u64(destination, source, size, [&](Offset src) {\n        return byteswap_if(\n            read_reg64(src, options.type), internal_endian != simulated_machine_endian);\n    });\n}\n\nuint64_t AclintMtimer::read_reg64(Offset source, AccessEffects type) const {\n    Q_UNUSED(type)\n    Q_ASSERT((source & 7U) == 0); // uint64_t aligned\n\n    uint64_t value = 0;\n\n    if (source == ACLINT_MTIME_OFFSET) {\n        if (type == AccessEffects::REGULAR) mtime_fetch_current();\n        value = mtime_last_current_fetch + mtime_user_offset;\n    } else if (\n        (source >= ACLINT_MTIMECMP_OFFSET)\n        && (source < ACLINT_MTIMECMP_OFFSET + 8 * mtimecmp_count)) {\n        value = mtimecmp_value[source >> 3];\n    }\n\n    emit read_notification(source, value);\n\n    return value;\n}\n\nbool AclintMtimer::write_reg64(Offset destination, uint64_t value) {\n    Q_ASSERT((destination & 7U) == 0); // uint64_t aligned\n\n    bool changed = false;\n\n    if (destination == ACLINT_MTIME_OFFSET) {\n        mtime_fetch_current();\n        mtime_user_offset = value - mtime_last_current_fetch;\n        if (!update_mtimer_irq()) arm_mtimer_event();\n        changed = true;\n    } else if (\n        (destination >= ACLINT_MTIMECMP_OFFSET)\n        && (destination < ACLINT_MTIMECMP_OFFSET + 8 * mtimecmp_count)) {\n        changed = value != mtimecmp_value[destination >> 3];\n        mtimecmp_value[destination >> 3] = value;\n        if (!update_mtimer_irq()) arm_mtimer_event();\n    } else {\n        WARN(\"ACLINT MTIMER - read out of range (at 0x%zu).\\n\", destination);\n    }\n\n    emit write_notification(destination, value);\n\n    return changed;\n}\n\nLocationStatus AclintMtimer::location_status(Offset offset) const {\n    if ((offset >= ACLINT_MTIMECMP_OFFSET)\n        && (offset < ACLINT_MTIMECMP_OFFSET + 8 * mtimecmp_count))\n        return LOCSTAT_NONE;\n    if ((offset & ~7U) == ACLINT_MTIME_OFFSET)\n        return LOCSTAT_NONE; // LOCSTAT_NONE / LOCSTAT_READ_ONLY\n\n    return LOCSTAT_ILLEGAL;\n}\n} // namespace machine::aclint\n"
  },
  {
    "path": "src/machine/memory/backend/aclintmtimer.h",
    "content": "#ifndef ACLINTMTIMER_H\n#define ACLINTMTIMER_H\n\n#include \"common/endian.h\"\n#include \"memory/backend/backend_memory.h\"\n\n#include <QTime>\n#include <cstdint>\n#include <qelapsedtimer.h>\n\nnamespace machine { namespace aclint {\n\n    constexpr Offset CLINT_MTIMER_OFFSET = 0x4000u;\n    constexpr Offset CLINT_MTIMER_SIZE = 0x8000u;\n\n    constexpr Offset ACLINT_MTIME_OFFSET = 0x7ff8u;\n    constexpr Offset ACLINT_MTIME_SIZE = 0x8u;\n    constexpr Offset ACLINT_MTIMECMP_OFFSET = 0x0000u;\n    constexpr Offset ACLINT_MTIMECMP_SIZE = 0x7ff8u;\n    constexpr unsigned ACLINT_MTIMECMP_COUNT_MAX = 1;\n\n    // Timer interrupts\n    // mip.MTIP and mie.MTIE are bit 7\n    // mip.STIP and mie.STIE are bit 5\n\n    // Software interrupts\n    // mip.MSIP and mie.MSIE are bit 3\n    // mip.SSIP and mie.SSIE are bit 1\n\n    class AclintMtimer : public BackendMemory {\n        Q_OBJECT\n    public:\n        explicit AclintMtimer(Endian simulated_machine_endian);\n        ~AclintMtimer() override;\n\n    signals:\n        void write_notification(Offset address, uint32_t value);\n        void read_notification(Offset address, uint32_t value) const;\n        void signal_interrupt(uint irq_level, bool active) const;\n\n    public:\n        uint64_t mtime_fetch_current() const;\n\n        WriteResult\n        write(Offset destination, const void *source, size_t size, WriteOptions options) override;\n\n        ReadResult\n        read(void *destination, Offset source, size_t size, ReadOptions options) const override;\n\n        LocationStatus location_status(Offset offset) const override;\n\n    private:\n        void timerEvent(QTimerEvent *event) override;\n\n        /** endian of internal registers of the periphery use. */\n        static constexpr Endian internal_endian = NATIVE_ENDIAN;\n\n        uint64_t read_reg64(Offset source, AccessEffects type) const;\n        bool write_reg64(Offset destination, uint64_t value);\n\n        bool update_mtimer_irq();\n        void arm_mtimer_event();\n\n        unsigned mtimecmp_count;\n        uint64_t mtimecmp_value[ACLINT_MTIMECMP_COUNT_MAX] {};\n\n        QElapsedTimer clock;\n        const uint8_t mtimer_irq_level;\n        uint64_t mtime_user_offset = 0;\n        mutable uint64_t mtime_last_current_fetch = 0;\n        mutable bool mtimer_irq_active = false;\n        int qt_timer_id = -1;\n    };\n\n}} // namespace machine::aclint\n\n#endif // ACLINTMTIMER_H\n"
  },
  {
    "path": "src/machine/memory/backend/aclintsswi.cpp",
    "content": "#include \"memory/backend/aclintsswi.h\"\n\n#include \"common/endian.h\"\n\n#include <QTimerEvent>\n#include <cstdio>\n\nusing ae = machine::AccessEffects; // For enum values, type is obvious from\n                                   // context.\n\nnamespace machine { namespace aclint {\n\n    AclintSswi::AclintSswi(Endian simulated_machine_endian)\n        : BackendMemory(simulated_machine_endian)\n        , sswi_irq_level(1) {\n        sswi_count = 1;\n    }\n\n    AclintSswi::~AclintSswi() = default;\n\n    WriteResult\n    AclintSswi::write(Offset destination, const void *source, size_t size, WriteOptions options) {\n        UNUSED(options)\n        return write_by_u32(\n            destination, source, size,\n            [&](Offset src) {\n                return byteswap_if(\n                    read_reg32(src, options.type), internal_endian != simulated_machine_endian);\n            },\n            [&](Offset src, uint32_t value) {\n                return write_reg32(\n                    src, byteswap_if(value, internal_endian != simulated_machine_endian));\n            });\n    }\n\n    ReadResult\n    AclintSswi::read(void *destination, Offset source, size_t size, ReadOptions options) const {\n        return read_by_u32(destination, source, size, [&](Offset src) {\n            return byteswap_if(\n                read_reg32(src, options.type), internal_endian != simulated_machine_endian);\n        });\n    }\n\n    uint32_t AclintSswi::read_reg32(Offset source, AccessEffects type) const {\n        Q_UNUSED(type)\n        Q_ASSERT((source & 3U) == 0); // uint32_t aligned\n\n        uint32_t value = 0;\n\n        if ((source >= ACLINT_SSWI_OFFSET) && (source < ACLINT_SSWI_OFFSET + 4 * sswi_count)) {}\n\n        emit read_notification(source, value);\n\n        return value;\n    }\n\n    bool AclintSswi::write_reg32(Offset destination, uint32_t value) {\n        Q_ASSERT((destination & 3U) == 0); // uint32_t aligned\n\n        bool changed = false;\n\n        if ((destination >= ACLINT_SSWI_OFFSET)\n            && (destination < ACLINT_SSWI_OFFSET + 4 * sswi_count)) {\n            bool value_bool = value & 1;\n            if (value_bool) emit signal_interrupt(sswi_irq_level, value_bool);\n        } else {\n            printf(\"WARNING: ACLINT SSWI - read out of range (at 0x%zu).\\n\", destination);\n        }\n\n        emit write_notification(destination, value);\n\n        return changed;\n    }\n    LocationStatus AclintSswi::location_status(Offset offset) const {\n        if ((offset >= ACLINT_SSWI_OFFSET) && (offset < ACLINT_SSWI_OFFSET + 4 * sswi_count))\n            return LOCSTAT_NONE;\n\n        return LOCSTAT_ILLEGAL;\n    }\n\n}} // namespace machine::aclint\n"
  },
  {
    "path": "src/machine/memory/backend/aclintsswi.h",
    "content": "#ifndef ACLINTSSWI_H\n#define ACLINTSSWI_H\n\n#include \"common/endian.h\"\n#include \"memory/backend/backend_memory.h\"\n\n#include <QTime>\n#include <cstdint>\n\nnamespace machine::aclint {\n\nconstexpr Offset CLINT_SSWI_OFFSET = 0xc000u;\nconstexpr Offset CLINT_SSWI_SIZE = 0x4000u;\n\nconstexpr Offset ACLINT_SSWI_OFFSET = 0;\nconstexpr Offset ACLINT_SSWI_COUNT_MAX = 1;\n\n// Timer interrupts\n// mip.MTIP and mie.MTIE are bit 7\n// mip.STIP and mie.STIE are bit 5\n\n// Software interrupts\n// mip.MSIP and mie.MSIE are bit 3\n// mip.SSIP and mie.SSIE are bit 1\n\nclass AclintSswi : public BackendMemory {\n    Q_OBJECT\npublic:\n    explicit AclintSswi(Endian simulated_machine_endian);\n    ~AclintSswi() override;\n\nsignals:\n    void write_notification(Offset address, uint32_t value);\n    void read_notification(Offset address, uint32_t value) const;\n    void signal_interrupt(uint irq_level, bool active) const;\n\npublic:\n    WriteResult\n    write(Offset destination, const void *source, size_t size, WriteOptions options) override;\n\n    ReadResult\n    read(void *destination, Offset source, size_t size, ReadOptions options) const override;\n\n    [[nodiscard]] LocationStatus location_status(Offset offset) const override;\n\nprivate:\n    /** endian of internal registers of the periphery use. */\n    static constexpr Endian internal_endian = NATIVE_ENDIAN;\n\n    [[nodiscard]] uint32_t read_reg32(Offset source, AccessEffects type) const;\n    bool write_reg32(Offset destination, uint32_t value);\n\n    unsigned sswi_count;\n\n    const uint8_t sswi_irq_level;\n};\n\n} // namespace machine::aclint\n\n#endif // ACLINTSSWI_H\n"
  },
  {
    "path": "src/machine/memory/backend/backend_memory.h",
    "content": "#ifndef BACKEND_MEMORY_H\n#define BACKEND_MEMORY_H\n\n#include \"common/endian.h\"\n#include \"machinedefs.h\"\n#include \"memory/memory_utils.h\"\n\n#include <QObject>\n\n// Shortcut for enum class values, type is obvious from context.\nusing ae = machine::AccessEffects;\n\nnamespace machine {\n\n/**\n * Relative index within an instance of backend memory.\n */\ntypedef size_t Offset;\n\n/**\n * Interface for physical memory or periphery.\n * .\n * Device implementing this interface is connected to the memory system via\n * memory data bus (`memory/memory_bus.h`).\n *\n * ## ENDIAN\n * Each device is responsible to return reads and write with the correct endian.\n * This is because there are different optimal ways to perform the swapping in\n * different kind of peripheries. For example, peripheries that have only word\n * (u23) accessible registers are simple to swap.\n *\n * All backend memory devices must set the `simulated_machine_endian` in\n * `BackendMemory` parent. They should also, by convention, have a private\n * (`const` or `static constexpr`) variable `internal_endian`. It can have\n * values `LITTLE | BIG | NATIVE_ENDIAN` (note: `NATIVE_ENDIAN` is a compile\n * time constant with endian of the host machine i.e. `LITTLE | BIG`). Byteswap\n * is needed, when internal and simulated endian are mismatched.\n *\n * ### Examples of internal endian values\n * - LED diode will have `NATIVE_ENDIAN` as the rgb value needs to be valid for\n *   GUI.\n * - Memory mapped source will have runtime set endian, based on the file\n *   endian.\n * - LCD has fixed `BIG` endian, as required by the hardware.\n */\nclass BackendMemory : public QObject {\n    Q_OBJECT\n\npublic:\n    /**\n     * @param simulated_machine_endian      endian of the simulated CPU/memory\n     *                                      system\n     */\n    explicit BackendMemory(Endian simulated_machine_endian);\n\n    /**\n     * Write byte sequence to memory.\n     *\n     * @param source        pointer to array of bytes to be copied\n     * @param destination   relative index of destination to write to\n     * @param size         \tnumber of bytes to be written\n     * @return              true when memory before and after write differs\n     */\n    virtual WriteResult\n    write(Offset destination, const void *source, size_t size, WriteOptions options)\n        = 0;\n\n    /**\n     * Read sequence of bytes from memory\n     *\n     * @param source        relative index of data to be read\n     * @param destination   pointer to destination buffer\n     * @param size          number of bytes to be read\n     * @param options       additional option like debug mode, see type\n     *                      definition\n     */\n    virtual ReadResult read(void *destination, Offset source, size_t size, ReadOptions options) const\n        = 0;\n\n    /**\n     * Determine status of given address.\n     *\n     * Applicable values:\n     *  - LOCSTAT_NONE          normal RW area\n     *  - LOCSTAT_READ_ONLY     read only hw register\n     *  - LOCSTAT_ILLEGAL       address is not occupied, write will result in\n     *                          NOP, read will return constant zero.\n     */\n    [[nodiscard]] virtual enum LocationStatus location_status(Offset offset) const = 0;\n\n    /**\n     * Endian of the simulated CPU/memory system.\n     * @see BackendMemory docs\n     */\n    const Endian simulated_machine_endian;\n\nsignals:\n    /**\n     * Notify upper layer about a change in managed physical memory of periphery\n     *\n     * @param mem_access    this\n     * @param start_addr    affected area start\n     * @param last_addr     affected area end\n     * @param type          allowed side effects, see type declaration\n     */\n    void external_backend_change_notify(\n        const BackendMemory *mem_access,\n        uint32_t start_addr,\n        uint32_t last_addr,\n        AccessEffects type) const;\n};\n\ninline BackendMemory::BackendMemory(Endian simulated_machine_endian)\n    : simulated_machine_endian(simulated_machine_endian) {}\n\n} // namespace machine\n\n#endif // BACKEND_MEMORY_H\n"
  },
  {
    "path": "src/machine/memory/backend/lcddisplay.cpp",
    "content": "#include \"lcddisplay.h\"\n\n#include \"common/endian.h\"\n\n#ifdef DEBUG_LCD\n    #undef DEBUG_LCD\n    #define DEBUG_LCD true\n#else\n    #define DEBUG_LCD false\n#endif\n\nnamespace machine {\n\nLcdDisplay::LcdDisplay(Endian simulated_machine_endian)\n    : BackendMemory(simulated_machine_endian)\n    , fb_width(480)\n    , fb_height(320)\n    , fb_bits_per_pixel(16)\n    , fb_data(get_fb_size_bytes(), 0) {}\n\nLcdDisplay::~LcdDisplay() = default;\n\nWriteResult\nLcdDisplay::write(Offset destination, const void *source, size_t size, WriteOptions options) {\n    UNUSED(options)\n    return write_by_u16(\n        destination, source, size,\n        [&](Offset src) {\n            return byteswap_if(read_raw_pixel(src), internal_endian != simulated_machine_endian);\n        },\n        [&](Offset src, uint16_t value) {\n            return write_raw_pixel(\n                src, byteswap_if(value, internal_endian != simulated_machine_endian));\n        });\n}\n\nReadResult\nLcdDisplay::read(void *destination, Offset source, size_t size, ReadOptions options) const {\n    UNUSED(options)\n    return read_by_u16(destination, source, size, [&](Offset src) {\n        return byteswap_if(read_raw_pixel(src), internal_endian != simulated_machine_endian);\n    });\n}\n\nuint16_t LcdDisplay::read_raw_pixel(Offset source) const {\n    Q_ASSERT((source & 1U) == 0); // uint16_t aligned\n\n    if (source + 1 >= get_fb_size_bytes()) { return 0; }\n\n    uint16_t value;\n    memcpy(&value, &fb_data[source], sizeof(value));\n\n    // TODO Switch to if constexpr as soon as we have cpp17.\n    if (DEBUG_LCD) {\n        printf(\n            \"LcdDisplay::read_reg address 0x%08lx data 0x%08lx\\n\", (unsigned long)source,\n            (unsigned long)value);\n    }\n\n    emit read_notification(source, value);\n    return value;\n}\n\nbool LcdDisplay::write_raw_pixel(Offset destination, uint16_t value) {\n    Q_ASSERT((destination & 1U) == 0); // uint16_t aligned\n\n    if (destination + 1 >= get_fb_size_bytes()) {\n        printf(\"WARNING: LCD display - read out of range.\\n\");\n        return false;\n    }\n\n    // TODO Switch to if constexpr as soon as we have cpp17.\n    if (DEBUG_LCD) {\n        printf(\n            \"LcdDisplay::write_reg address 0x%08lx data 0x%08lx\\n\", (unsigned long)destination,\n            (unsigned long)value);\n    }\n\n    if (read_raw_pixel(destination) == value) { return false; }\n\n    memcpy(&fb_data[destination], &value, sizeof(value));\n\n    size_t x, y;\n    std::tie(x, y) = get_pixel_from_address(destination);\n\n    const uint32_t last_addr = destination + 1;\n    uint32_t pixel_addr;\n    uint16_t pixel_data;\n    uint r, g, b;\n\n    while ((pixel_addr = get_address_from_pixel(x, y)) <= last_addr) {\n        memcpy(&pixel_data, &fb_data[pixel_addr], sizeof(pixel_data));\n\n        r = ((pixel_data >> 11u) & 0x1fu) << 3u;\n        g = ((pixel_data >> 5u) & 0x3fu) << 2u;\n        b = ((pixel_data >> 0u) & 0x1fu) << 3u;\n\n        emit pixel_update(x, y, r, g, b);\n\n        if (++x >= fb_width) {\n            x = 0;\n            y++;\n        }\n    }\n\n    emit write_notification(destination, value);\n\n    return true;\n}\n\nsize_t LcdDisplay::get_address_from_pixel(size_t x, size_t y) const {\n    size_t address = y * get_fb_line_size();\n    if (fb_bits_per_pixel > 12) {\n        address += x * divide_and_ceil(fb_bits_per_pixel, 8u);\n    } else {\n        address += x * fb_bits_per_pixel / 8;\n    }\n\n    return address;\n}\n\nstd::tuple<size_t, size_t> LcdDisplay::get_pixel_from_address(size_t address) const {\n    size_t y = address / get_fb_line_size();\n    size_t x = (fb_bits_per_pixel > 12)\n                   ? (address - y * get_fb_line_size()) / ((fb_bits_per_pixel + 7) >> 3u)\n                   : (address - y * get_fb_line_size()) * 8 / fb_bits_per_pixel;\n    return std::make_tuple(x, y);\n}\n\nsize_t LcdDisplay::get_fb_line_size() const {\n    return (fb_bits_per_pixel > 12) ? ((fb_bits_per_pixel + 7) >> 3u) * fb_width\n                                    : (fb_bits_per_pixel * fb_width + 7) >> 3u;\n}\n\nsize_t LcdDisplay::get_fb_size_bytes() const {\n    return get_fb_line_size() * fb_height;\n}\nLocationStatus LcdDisplay::location_status(Offset offset) const {\n    if ((offset | ~3u) >= get_fb_size_bytes()) { return LOCSTAT_ILLEGAL; }\n    return LOCSTAT_NONE;\n}\n\n} // namespace machine\n"
  },
  {
    "path": "src/machine/memory/backend/lcddisplay.h",
    "content": "#ifndef LCDDISPLAY_H\n#define LCDDISPLAY_H\n\n#include \"common/endian.h\"\n#include \"machinedefs.h\"\n#include \"memory/backend/backend_memory.h\"\n#include \"simulator_exception.h\"\n\n#include <QMap>\n#include <QObject>\n#include <cstdint>\n\nnamespace machine {\n\nclass LcdDisplay final : public BackendMemory {\n    Q_OBJECT\npublic:\n    explicit LcdDisplay(Endian simulated_machine_endian);\n    ~LcdDisplay() override;\n\nsignals:\n    void write_notification(Offset offset, uint32_t value) const;\n    void read_notification(Offset offset, uint32_t value) const;\n    void pixel_update(size_t x, size_t y, uint r, uint g, uint b);\n\npublic:\n    WriteResult\n    write(Offset destination, const void *source, size_t size, WriteOptions options) override;\n\n    ReadResult\n    read(void *destination, Offset source, size_t size, ReadOptions options) const override;\n\n    [[nodiscard]] LocationStatus location_status(Offset offset) const override;\n\n    /**\n     * @return  framebuffer width in pixels\n     */\n    [[nodiscard]] inline constexpr size_t get_width() const { return fb_width; }\n\n    /**\n     * @return  framebuffer height in pixels\n     */\n    [[nodiscard]] inline constexpr size_t get_height() const { return fb_height; }\n\nprivate:\n    /** Endian internal registers of the periphery (framebuffer) use. */\n    static constexpr Endian internal_endian = NATIVE_ENDIAN;\n\n    /** Read HW register - allows only 32bit aligned access. */\n    [[nodiscard]] uint16_t read_raw_pixel(Offset source) const;\n\n    /** Write HW register - allows only 32bit aligned access */\n    bool write_raw_pixel(Offset destination, uint16_t value);\n\n    [[nodiscard]] size_t get_fb_line_size() const;\n    [[nodiscard]] size_t get_fb_size_bytes() const;\n    [[nodiscard]] size_t get_address_from_pixel(size_t x, size_t y) const;\n    [[nodiscard]] std::tuple<size_t, size_t> get_pixel_from_address(size_t address) const;\n\n    const size_t fb_width;  //> Width in pixels\n    const size_t fb_height; //> Height in pixels\n    const size_t fb_bits_per_pixel;\n    std::vector<byte> fb_data;\n};\n\n} // namespace machine\n\n#endif // LCDDISPLAY_H\n"
  },
  {
    "path": "src/machine/memory/backend/memory.cpp",
    "content": "#include \"memory/backend/memory.h\"\n\n#include \"common/endian.h\"\n#include \"simulator_exception.h\"\n\n#include <memory>\n\nnamespace machine {\n\nMemorySection::MemorySection(size_t length_bytes, Endian simulated_machine_endian)\n    : BackendMemory(simulated_machine_endian)\n    , dt(length_bytes, 0) {}\n\nMemorySection::MemorySection(const MemorySection &other)\n    : BackendMemory(other.simulated_machine_endian)\n    , dt(other.dt) {}\n\nWriteResult\nMemorySection::write(Offset dst_offset, const void *source, size_t size, WriteOptions options) {\n    UNUSED(options)\n\n    auto destination = static_cast<size_t>(dst_offset);\n\n    if (destination >= length()) {\n        throw SIMULATOR_EXCEPTION(\n            OutOfMemoryAccess, \"Trying to write outside of the memory section\",\n            QString(\"Accessing using offset: \") + QString::number(destination));\n    }\n\n    // Size the can be read from this section\n    const size_t available_size = std::min(destination + size, length()) - destination;\n\n    // TODO, make swap conditional for big endian machines\n    bool changed = memcmp(source, &dt[destination], available_size) != 0;\n    if (changed) { memcpy(&dt[destination], source, available_size); }\n\n    return { .n_bytes = available_size, .changed = changed };\n}\n\nReadResult\nMemorySection::read(void *destination, Offset src_offset, size_t size, ReadOptions options) const {\n    UNUSED(options)\n\n    auto source = static_cast<size_t>(src_offset);\n\n    size = std::min(source + size, length()) - source;\n\n    if (source >= length()) {\n        throw SIMULATOR_EXCEPTION(\n            OutOfMemoryAccess, \"Trying to read outside of the memory section\",\n            QString(\"Accessing using offset: \") + QString::number(source));\n    }\n\n    memcpy(destination, &dt[source], size);\n\n    return { .n_bytes = size };\n}\n\nLocationStatus MemorySection::location_status(Offset offset) const {\n    UNUSED(offset)\n    return LOCSTAT_NONE;\n}\n\nsize_t MemorySection::length() const {\n    return this->dt.size();\n}\n\nconst byte *MemorySection::data() const {\n    return this->dt.data();\n}\n\nbool MemorySection::operator==(const MemorySection &other) const {\n    return this->dt == other.dt;\n}\n\nbool MemorySection::operator!=(const MemorySection &ms) const {\n    return !this->operator==(ms);\n}\n\n// Settings sanity checks\nstatic_assert(MEMORY_SECTION_SIZE != 0, \"Nonzero memory section size is required.\");\nstatic_assert(MEMORY_TREE_ROW_SIZE != 0, \"Nonzero memory tree row size is required.\");\nstatic_assert(\n    ((32 - MEMORY_SECTION_BITS) % MEMORY_TREE_BITS) == 0,\n    \"Number of bits in tree row has to be exact division of available number \"\n    \"of bits.\");\n\n/**\n * Generate mask to get memory section index from address.\n *\n * Memory section of section_size 2^`section_size` separably addressable units\n * each of section_size 2^`unit_size` bytes.\n *\n * Example:\n *  `MemorySection` of 256x1B index is received as\n *  ```address & generate_mask(8, 0)```\n */\nconstexpr uint64_t generate_mask(size_t section_size, size_t unit_size) {\n    return ((1U << section_size) - 1) << unit_size;\n}\n\n/**\n * Get index in row for given offset and row number i\n */\nconstexpr size_t tree_row_bit_offset(size_t i) {\n    return 32 - MEMORY_TREE_BITS - i * MEMORY_TREE_BITS;\n}\n\n/*\n * Select branch index from memory tree.\n */\nconstexpr size_t get_tree_row(size_t offset, size_t i) {\n    return (offset & generate_mask(MEMORY_TREE_BITS, tree_row_bit_offset(i)))\n           >> tree_row_bit_offset(i);\n}\n\nMemory::Memory() : BackendMemory(BIG) {\n    // This is dummy constructor for qt internal uses only.\n    this->mt_root = nullptr;\n}\n\nMemory::Memory(Endian simulated_machine_endian) : BackendMemory(simulated_machine_endian) {\n    this->mt_root = allocate_section_tree();\n}\n\nMemory::Memory(const Memory &other) : BackendMemory(other.simulated_machine_endian) {\n    this->mt_root = copy_section_tree(other.get_memory_tree_root(), 0);\n}\n\nMemory::~Memory() {\n    free_section_tree(this->mt_root, 0);\n    delete[] this->mt_root;\n}\n\nvoid Memory::reset() {\n    free_section_tree(this->mt_root, 0);\n    delete[] this->mt_root;\n    this->mt_root = allocate_section_tree();\n}\n\nvoid Memory::reset(const Memory &m) {\n    free_section_tree(this->mt_root, 0);\n    this->mt_root = copy_section_tree(m.get_memory_tree_root(), 0);\n}\n\nMemorySection *Memory::get_section(size_t offset, bool create) const {\n    union MemoryTree *w = this->mt_root;\n    size_t row_num;\n    // Walk memory tree branch from root to leaf and create new nodes when\n    // needed and requested (`create` flag).\n    for (size_t i = 0; i < (MEMORY_TREE_DEPTH - 1); i++) {\n        row_num = get_tree_row(offset, i);\n        if (w[row_num].subtree == nullptr) {\n            // We don't have this tree so allocate it.\n            if (!create) {\n                // If we shouldn't be creating it than just return// null.\n                return nullptr;\n            }\n            w[row_num].subtree = allocate_section_tree();\n        }\n        w = w[row_num].subtree;\n    }\n    row_num = get_tree_row(offset, MEMORY_TREE_DEPTH - 1);\n    if (w[row_num].sec == nullptr) {\n        if (!create) { return nullptr; }\n        w[row_num].sec = new MemorySection(MEMORY_SECTION_SIZE, simulated_machine_endian);\n    }\n    return w[row_num].sec;\n}\n\nsize_t get_section_offset_mask(size_t addr) {\n    return addr & generate_mask(MEMORY_SECTION_BITS, 0);\n}\n\nWriteResult\nMemory::write(Offset destination, const void *source, size_t size, WriteOptions options) {\n    return repeat_access_until_completed<WriteResult>(\n        destination, source, size, options,\n        [this](Offset _destination, const void *_source, size_t _size, WriteOptions) {\n            MemorySection *section = this->get_section(_destination, true);\n            return section->write(get_section_offset_mask(_destination), _source, _size, {});\n        });\n}\n\nReadResult Memory::read(void *destination, Offset source, size_t size, ReadOptions options) const {\n    return repeat_access_until_completed<ReadResult>(\n        destination, source, size, options,\n        [this](\n            void *_destination, Offset _source, size_t _size, ReadOptions _options) -> ReadResult {\n            MemorySection *section = this->get_section(_source, false);\n            if (section == nullptr) {\n                memset(_destination, 0, _size);\n                // TODO Warning read of uninitialized memory\n                return { .n_bytes = _size };\n            } else {\n                return section->read(\n                    _destination, get_section_offset_mask(_source), _size, _options);\n            }\n        });\n}\n\nuint32_t Memory::get_change_counter() const {\n    return change_counter;\n}\n\nbool Memory::operator==(const Memory &m) const {\n    return compare_section_tree(this->mt_root, m.get_memory_tree_root(), 0);\n}\n\nbool Memory::operator!=(const Memory &m) const {\n    return !this->operator==(m);\n}\n\nconst union machine::MemoryTree *Memory::get_memory_tree_root() const {\n    return this->mt_root;\n}\n\nunion machine::MemoryTree *Memory::allocate_section_tree() {\n    auto *mt = new union MemoryTree[MEMORY_TREE_ROW_SIZE];\n    memset(mt, 0, sizeof *mt * MEMORY_TREE_ROW_SIZE);\n    return mt;\n}\n\nvoid Memory::free_section_tree(union MemoryTree *mt, size_t depth) {\n    if (depth < (MEMORY_TREE_DEPTH - 1)) { // Following level is memory tree\n        for (size_t i = 0; i < MEMORY_TREE_ROW_SIZE; i++) {\n            if (mt[i].subtree != nullptr) {\n                free_section_tree(mt[i].subtree, depth + 1);\n                delete[] mt[i].subtree;\n            }\n        }\n    } else { // Following level is memory section\n        for (size_t i = 0; i < MEMORY_TREE_ROW_SIZE; i++) {\n            delete mt[i].sec;\n        }\n    }\n}\n\nbool Memory::compare_section_tree(\n    const union MemoryTree *mt1,\n    const union MemoryTree *mt2,\n    size_t depth) {\n    if (depth < (MEMORY_TREE_DEPTH - 1)) { // Following level is memory tree\n        for (size_t i = 0; i < MEMORY_TREE_ROW_SIZE; i++) {\n            if (((mt1[i].subtree == nullptr || mt2[i].subtree == nullptr)\n                 && mt1[i].subtree != mt2[i].subtree)\n                || (mt1[i].subtree != nullptr && mt2[i].subtree != nullptr\n                    && !compare_section_tree(mt1[i].subtree, mt2[i].subtree, depth + 1))) {\n                return false;\n            }\n        }\n    } else { // Following level is memory section\n        for (size_t i = 0; i < MEMORY_TREE_ROW_SIZE; i++) {\n            if (((mt1[i].sec == nullptr || mt2[i].sec == nullptr) && mt1[i].sec != mt2[i].sec)\n                || (mt1[i].sec != nullptr && mt2[i].sec != nullptr && *mt1[i].sec != *mt2[i].sec)) {\n                return false;\n            }\n        }\n    }\n    return true;\n}\n\nunion machine::MemoryTree *Memory::copy_section_tree(const union MemoryTree *mt, size_t depth) {\n    union MemoryTree *nmt = allocate_section_tree();\n    if (depth < (MEMORY_TREE_DEPTH - 1)) { // Following level is memory tree\n        for (size_t i = 0; i < MEMORY_TREE_ROW_SIZE; i++) {\n            if (mt[i].subtree != nullptr) {\n                nmt[i].subtree = copy_section_tree(mt[i].subtree, depth + 1);\n            }\n        }\n    } else { // Following level is memory section\n        for (size_t i = 0; i < MEMORY_TREE_ROW_SIZE; i++) {\n            if (mt[i].sec != nullptr) { nmt[i].sec = new MemorySection(*mt[i].sec); }\n        }\n    }\n    return nmt;\n}\nLocationStatus Memory::location_status(Offset offset) const {\n    UNUSED(offset)\n    // Lazy allocation of memory is only internal implementation detail.\n    return LOCSTAT_NONE;\n}\n\n} // namespace machine\n"
  },
  {
    "path": "src/machine/memory/backend/memory.h",
    "content": "#ifndef MACHINE_MEMORY_H\n#define MACHINE_MEMORY_H\n\n#include \"common/endian.h\"\n#include \"machinedefs.h\"\n#include \"memory/address.h\"\n#include \"memory/backend/backend_memory.h\"\n#include \"memory/memory_utils.h\"\n#include \"simulator_exception.h\"\n#include \"utils.h\"\n\n#include <QObject>\n#include <cstdint>\n\nnamespace machine {\n\n/**\n * NOTE: Internal endian of memory must be the same as endian of the whole\n * simulated machine. Therefore it does not have internal_endian field.\n */\nclass MemorySection final : public BackendMemory {\npublic:\n    explicit MemorySection(size_t length_bytes, Endian simulated_machine_endian);\n    MemorySection(const MemorySection &other);\n    ~MemorySection() override = default;\n\n    WriteResult\n    write(Offset destination, const void *source, size_t total_size, WriteOptions options) override;\n\n    ReadResult\n    read(void *destination, Offset source, size_t size, ReadOptions options) const override;\n\n    [[nodiscard]] LocationStatus location_status(Offset offset) const override;\n\n    [[nodiscard]] size_t length() const;\n    [[nodiscard]] const byte *data() const;\n\n    bool operator==(const MemorySection &) const;\n    bool operator!=(const MemorySection &) const;\n\nprivate:\n    std::vector<byte> dt;\n};\n\n//////////////////////////////////////////////////////////////////////////////\n/// Some optimisation options\n// How big memory sections will be in bits (2^8=256 bytes)\nconstexpr size_t MEMORY_SECTION_BITS = 8;\n// How big one row of lookup tree will be in bits (2^4=16)\nconstexpr size_t MEMORY_TREE_BITS = 4;\n//////////////////////////////////////////////////////////////////////////////\n// Size of one section\nconstexpr size_t MEMORY_SECTION_SIZE = (1u << MEMORY_SECTION_BITS);\n// Size of one memory row\nconstexpr size_t MEMORY_TREE_ROW_SIZE = (1u << MEMORY_TREE_BITS);\n// Depth of tree\nconstexpr size_t MEMORY_TREE_DEPTH = ((32 - MEMORY_SECTION_BITS) / MEMORY_TREE_BITS);\n\nunion MemoryTree {\n    union MemoryTree *subtree;\n    MemorySection *sec;\n};\n\n/**\n * NOTE: Internal endian of memory must be the same as endian of the whole\n * simulated machine. Therefore it does not have internal_endian field.\n */\nclass Memory final : public BackendMemory {\n    Q_OBJECT\npublic:\n    // This is dummy constructor for qt internal uses only.\n    Memory();\n    explicit Memory(Endian simulated_machine_endian);\n    Memory(const Memory &);\n    ~Memory() override;\n    void reset(); // Reset whole content of memory (removes old tree and creates\n                  // new one)\n    void reset(const Memory &);\n\n    // returns section containing given address\n    [[nodiscard]] MemorySection *get_section(size_t offset, bool create) const;\n\n    WriteResult\n    write(Offset destination, const void *source, size_t size, WriteOptions options) override;\n\n    ReadResult\n    read(void *destination, Offset source, size_t size, ReadOptions options) const override;\n\n    [[nodiscard]] LocationStatus location_status(Offset offset) const override;\n\n    bool operator==(const Memory &) const;\n    bool operator!=(const Memory &) const;\n\n    [[nodiscard]] const union MemoryTree *get_memory_tree_root() const;\n\nprivate:\n    union MemoryTree *mt_root;\n    uint32_t change_counter = 0;\n    static union MemoryTree *allocate_section_tree();\n    static void free_section_tree(union MemoryTree *, size_t depth);\n    static bool\n    compare_section_tree(const union MemoryTree *, const union MemoryTree *, size_t depth);\n    static union MemoryTree *copy_section_tree(const union MemoryTree *, size_t depth);\n    [[nodiscard]] uint32_t get_change_counter() const;\n};\n} // namespace machine\n\nQ_DECLARE_METATYPE(machine::Memory);\n\n#endif // MEMORY_H\n"
  },
  {
    "path": "src/machine/memory/backend/memory.test.cpp",
    "content": "#include \"memory.test.h\"\n\n#include \"common/endian.h\"\n#include \"machine/machinedefs.h\"\n#include \"machine/memory/backend/memory.h\"\n#include \"machine/memory/memory_bus.h\"\n#include \"machine/memory/memory_utils.h\"\n#include \"tests/utils/integer_decomposition.h\"\n\n#include <cinttypes>\n\nusing namespace machine;\n\n// Default memory testing data. Some tests may use other values, where it\n// makes better sense.\nconstexpr array<Endian, 2> default_endians { BIG, LITTLE };\nconstexpr array<Offset, 5> default_addresses { 0x0, 0xFFFFFFFC, 0xFFFF0, 0xFFFF1, 0xFFFFFF };\nconstexpr array<Offset, 4> default_strides { 0, 1, 2, 3 };\nconstexpr array<uint64_t, 1> default_values { 0x4142434445464748 };\n\n/**\n * Test the IntegerDecomposition util, that is used for testing later.\n */\nvoid TestMemory::integer_decomposition() {\n    const uint64_t value = 0x0102030405060708;\n\n    IntegerDecomposition big_endian(value, BIG);\n\n    // Expected sub-values done by hand.\n    array<uint32_t, 2> be_u32 { 0x01020304, 0x05060708 };\n    array<uint16_t, 4> be_u16 { 0x0102, 0x0304, 0x0506, 0x0708 };\n    array<uint8_t, 8> be_u8 { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };\n\n    QCOMPARE(big_endian.u64, (uint64_t)0x0102030405060708);\n    QCOMPARE(big_endian.u32, be_u32);\n    QCOMPARE(big_endian.u16, be_u16);\n    QCOMPARE(big_endian.u8, be_u8);\n\n    IntegerDecomposition little_endian(value, LITTLE);\n\n    // Little endian arrays are reversed to big endian ones.\n    array<uint32_t, 2> le_u32 = be_u32;\n    std::reverse(le_u32.begin(), le_u32.end());\n    array<uint16_t, 4> le_u16 = be_u16;\n    std::reverse(le_u16.begin(), le_u16.end());\n    array<uint8_t, 8> le_u8 = be_u8;\n    std::reverse(le_u8.begin(), le_u8.end());\n\n    // Const cast is just for types to match, this way reverse is simpler.\n    QCOMPARE(little_endian.u64, (uint64_t)0x0102030405060708);\n    QCOMPARE(little_endian.u32, (const array<uint32_t, 2>)le_u32);\n    QCOMPARE(little_endian.u16, (const array<uint16_t, 4>)le_u16);\n    QCOMPARE(little_endian.u8, (const array<uint8_t, 8>)le_u8);\n}\n\n/**\n * Common testing dataset generator.\n *\n * @tparam N1   size of first argument array\n * @tparam N2   size of second argument array\n * @tparam N3   size of third argument array\n * @tparam N4   size of fourth argument array\n * @param simulated_endians     array of endians to test\n * @param accessed_addresses    array of addresses to test at\n * @param strides   array of offsets of reads to the previous write (because\n *                  of endian differences), only tested on 64b write\n * @param values    values to use for memory access, for smaller accesses\n *                  part of the value is use\n */\ntemplate<size_t N1, size_t N2, size_t N3, size_t N4>\nconstexpr void prepare_data(\n    const array<Endian, N1> &simulated_endians,\n    const array<Offset, N2> &accessed_addresses,\n    const array<Offset, N3> &strides,\n    const array<uint64_t, N4> &values) {\n    QTest::addColumn<Endian>(\"endian\");\n    QTest::addColumn<Offset>(\"address\");\n    QTest::addColumn<Offset>(\"stride\");\n    QTest::addColumn<IntegerDecomposition>(\"value\");\n    QTest::addColumn<IntegerDecomposition>(\"result\");\n\n    for (auto endian : simulated_endians) {\n        for (auto address : accessed_addresses) {\n            for (auto stride : strides) {\n                for (auto value : values) {\n                    // Result when read has `stride` bytes offset to the write.\n                    auto result_value\n                        = (endian == BIG) ? (value << (stride * 8)) : (value >> (stride * 8));\n                    QTest::addRow(\n                        \"endian=%s, address=0x%lx, stride=%ld, value=0x%\" PRIx64, to_string(endian),\n                        address, stride, value)\n                        << endian << address << stride << IntegerDecomposition(value, endian)\n                        << IntegerDecomposition(result_value, endian);\n                }\n            }\n        }\n    }\n}\n\nvoid TestMemory::memory_data() {\n    prepare_data(default_endians, default_addresses, default_strides, default_values);\n}\n\nvoid TestMemory::memory() {\n    QFETCH(Endian, endian);\n    QFETCH(Offset, address);\n    QFETCH(Offset, stride);\n    QFETCH(IntegerDecomposition, value);\n    QFETCH(IntegerDecomposition, result);\n\n    Memory m(endian);\n\n    // Uninitialized memory should read as zero\n    QCOMPARE(memory_read_u8(&m, address + stride), (uint8_t)0);\n    QCOMPARE(memory_read_u16(&m, address + stride), (uint16_t)0);\n    QCOMPARE(memory_read_u32(&m, address + stride), (uint32_t)0);\n    QCOMPARE(memory_read_u64(&m, address + stride), (uint64_t)0);\n\n    // Writes value and reads it in all possible ways (by parts).\n\n    // It is sufficient to test reads with stride to write of largest write. For\n    // smaller writes, correct result is much harder to prepare.\n    if (stride == 0) {\n        memory_write_u8(&m, address, value.u8.at(0));\n        QCOMPARE(memory_read_u8(&m, address), result.u8.at(0));\n\n        memory_write_u16(&m, address, value.u16.at(0));\n        QCOMPARE(memory_read_u16(&m, address), result.u16.at(0));\n        for (size_t i = 0; i < 2; ++i) {\n            QCOMPARE(memory_read_u8(&m, address + i), result.u8.at(i));\n        }\n\n        memory_write_u32(&m, address, value.u32.at(0));\n        QCOMPARE(memory_read_u32(&m, address), result.u32.at(0));\n        for (size_t i = 0; i < 2; ++i) {\n            QCOMPARE(memory_read_u16(&m, address + 2 * i), result.u16.at(i));\n        }\n        for (size_t i = 0; i < 4; ++i) {\n            QCOMPARE(memory_read_u8(&m, address + i), result.u8.at(i));\n        }\n    }\n\n    memory_write_u64(&m, address, value.u64);\n    QCOMPARE(memory_read_u64(&m, address + stride), result.u64);\n    for (size_t i = 0; i < 2; ++i) {\n        QCOMPARE(memory_read_u32(&m, address + stride + 4 * i), result.u32.at(i));\n    }\n    for (size_t i = 0; i < 4; ++i) {\n        QCOMPARE(memory_read_u16(&m, address + stride + 2 * i), result.u16.at(i));\n    }\n    for (size_t i = 0; i < 8; ++i) {\n        QCOMPARE(memory_read_u8(&m, address + stride + i), result.u8.at(i));\n    }\n}\n\nvoid TestMemory::memory_section_data() {\n    constexpr array<Offset, 4> addresses { 0x0, 0xFFFFFFF4, 0xFFFF00, 0xFFFFF2 };\n    prepare_data(default_endians, addresses, default_strides, default_values);\n}\n\nvoid TestMemory::memory_section() {\n    QFETCH(Endian, endian);\n    QFETCH(Offset, address);\n    QFETCH(Offset, stride);\n    QFETCH(IntegerDecomposition, value);\n    QFETCH(IntegerDecomposition, result);\n\n    Memory m(endian);\n\n    // First section shouldn't exists\n    QCOMPARE(m.get_section(address, false), (MemorySection *)nullptr);\n\n    // Create section\n    MemorySection *s = m.get_section(address, true);\n    QVERIFY(s != nullptr);\n\n    // Writes value and reads it in all possible ways (by parts).\n\n    // It is sufficient to test reads with stride to write of largest write. For\n    // smaller writes, correct result is much harder to prepare.\n    if (stride == 0) {\n        Offset section_offset = address % MEMORY_SECTION_SIZE;\n\n        memory_write_u8(&m, address, value.u8.at(0));\n        QCOMPARE(memory_read_u8(s, section_offset), result.u8.at(0));\n\n        memory_write_u16(&m, address, value.u16.at(0));\n        QCOMPARE(memory_read_u16(s, section_offset), result.u16.at(0));\n        for (size_t i = 0; i < 2; ++i) {\n            QCOMPARE(memory_read_u8(s, section_offset + i), result.u8.at(i));\n        }\n\n        memory_write_u32(&m, address, value.u32.at(0));\n        QCOMPARE(memory_read_u32(s, section_offset), result.u32.at(0));\n        for (size_t i = 0; i < 2; ++i) {\n            QCOMPARE(memory_read_u16(s, section_offset + 2 * i), result.u16.at(i));\n        }\n        for (size_t i = 0; i < 4; ++i) {\n            QCOMPARE(memory_read_u8(s, section_offset + i), result.u8.at(i));\n        }\n    }\n\n    Offset section_offset = (address + stride) % MEMORY_SECTION_SIZE;\n\n    memory_write_u64(&m, address, value.u64);\n    QCOMPARE(memory_read_u64(s, section_offset), result.u64);\n    for (size_t i = 0; i < 2; ++i) {\n        QCOMPARE(memory_read_u32(s, section_offset + 4 * i), result.u32.at(i));\n    }\n    for (size_t i = 0; i < 4; ++i) {\n        QCOMPARE(memory_read_u16(s, section_offset + 2 * i), result.u16.at(i));\n    }\n    for (size_t i = 0; i < 8; ++i) {\n        QCOMPARE(memory_read_u8(s, section_offset + i), result.u8.at(i));\n    }\n}\n\nvoid prepare_endian_test() {\n    QTest::addColumn<Endian>(\"endian\");\n    for (auto endian : default_endians) {\n        QTest::addRow(\"endian=%s\", to_string(endian)) << endian;\n    }\n}\n\nvoid TestMemory::memory_compare_data() {\n    prepare_endian_test();\n}\n\nvoid TestMemory::memory_compare() {\n    QFETCH(Endian, endian);\n\n    Memory m1(endian), m2(endian);\n    QCOMPARE(m1, m2);\n    memory_write_u8(&m1, 0x20, 0x0);\n    QVERIFY(m1 != m2); // This should not be equal as this identifies also\n    // memory write (difference between no write and zero\n    // write)\n    memory_write_u8(&m1, 0x20, 0x24);\n    QVERIFY(m1 != m2);\n    memory_write_u8(&m2, 0x20, 0x23);\n    QVERIFY(m1 != m2);\n    memory_write_u8(&m2, 0x20, 0x24);\n    QCOMPARE(m1, m2);\n    // Do the same with some other section\n    memory_write_u8(&m1, 0xFFFF20, 0x24);\n    QVERIFY(m1 != m2);\n    memory_write_u8(&m2, 0xFFFF20, 0x24);\n    QCOMPARE(m1, m2);\n    // And also check memory copy\n    Memory m3(m1);\n    QCOMPARE(m1, m3);\n    memory_write_u8(&m3, 0x18, 0x22);\n    QVERIFY(m1 != m3);\n}\n\nvoid TestMemory::memory_write_ctl_data() {\n    QTest::addColumn<AccessControl>(\"ctl\");\n    QTest::addColumn<Memory>(\"result\");\n\n    for (auto endian : default_endians) {\n        Memory mem(endian);\n\n        QTest::addRow(\"AC_NONE, endian=%s\", to_string(endian)) << AC_NONE << mem;\n        memory_write_u8(&mem, 0x20, 0x30);\n        QTest::addRow(\"AC_I8, endian=%s\", to_string(endian)) << AC_I8 << mem;\n        QTest::addRow(\"AC_U8, endian=%s\", to_string(endian)) << AC_U8 << mem;\n        memory_write_u16(&mem, 0x20, 0x2930);\n        QTest::addRow(\"AC_I16, endian=%s\", to_string(endian)) << AC_I16 << mem;\n        QTest::addRow(\"AC_U16, endian=%s\", to_string(endian)) << AC_U16 << mem;\n        memory_write_u32(&mem, 0x20, 0x27282930);\n        QTest::addRow(\"AC_I32, endian=%s\", to_string(endian)) << AC_I32 << mem;\n        QTest::addRow(\"AC_U32, endian=%s\", to_string(endian)) << AC_U32 << mem;\n        memory_write_u64(&mem, 0x20, 0x2324252627282930ULL);\n        QTest::addRow(\"AC_I64, endian=%s\", to_string(endian)) << AC_I64 << mem;\n        QTest::addRow(\"AC_U64, endian=%s\", to_string(endian)) << AC_U64 << mem;\n    }\n}\n\nvoid TestMemory::memory_write_ctl() {\n    QFETCH(AccessControl, ctl);\n    QFETCH(Memory, result);\n\n    // Memory is not supposed to be read directly as it does not implement\n    // frontend memory. TrivialBus was introduced to wrap Memory into the most\n    // simple FrontendMemory with no additional functionality.\n    Memory mem(result.simulated_machine_endian);\n    TrivialBus bus(&mem);\n    bus.write_ctl(ctl, 0x20_addr, (uint64_t)0x2324252627282930ULL);\n    QCOMPARE(mem, result);\n}\n\nvoid TestMemory::memory_read_ctl_data() {\n    prepare_data(default_endians, default_addresses, default_strides, default_values);\n}\n\nvoid TestMemory::memory_read_ctl() {\n    QFETCH(Endian, endian);\n    QFETCH(Offset, address);\n    QFETCH(Offset, stride);\n    QFETCH(IntegerDecomposition, value);\n    QFETCH(IntegerDecomposition, result);\n\n    Address frontend_address(address);\n    Memory mem(endian);\n    TrivialBus bus(&mem);\n\n    bus.write_u64(frontend_address, value.u64);\n\n    QCOMPARE(bus.read_ctl(AC_U64, frontend_address + stride).as_u64(), result.u64);\n\n    for (size_t i = 0; i < 2; ++i) {\n        QCOMPARE(\n            bus.read_ctl(AC_U32, frontend_address + stride + 4 * i).as_u32(), result.u32.at(i));\n    }\n    for (size_t i = 0; i < 4; ++i) {\n        QCOMPARE(\n            bus.read_ctl(AC_U16, frontend_address + stride + 2 * i).as_u16(), result.u16.at(i));\n    }\n    for (size_t i = 0; i < 4; ++i) {\n        QCOMPARE(\n            bus.read_ctl(AC_I16, frontend_address + stride + 2 * i).as_i16(),\n            (int16_t)result.u16.at(i));\n    }\n    for (size_t i = 0; i < 8; ++i) {\n        QCOMPARE(bus.read_ctl(AC_U8, frontend_address + stride + i).as_u8(), result.u8.at(i));\n    }\n    for (size_t i = 0; i < 8; ++i) {\n        QCOMPARE(\n            bus.read_ctl(AC_I8, frontend_address + stride + i).as_i8(), (int8_t)result.u8.at(i));\n    }\n}\n\nvoid TestMemory::memory_memtest_data() {\n    QTest::addColumn<Endian>(\"endian\");\n    QTest::addColumn<Offset>(\"address\");\n\n    for (auto endian : default_endians) {\n        for (auto address : default_addresses) {\n            QTest::addRow(\"endian=%s, address=0x%lx\", to_string(endian), address)\n                << endian << address;\n        }\n    }\n}\n\nvoid TestMemory::memory_memtest() {\n    QFETCH(Endian, endian);\n    QFETCH(Offset, address);\n    Address frontend_address(address);\n    Memory mem(endian);\n    TrivialBus bus(&mem);\n\n    uint64_t range_to_test = 128 * 1024;\n\n    if (frontend_address.get_raw() < 0x100000000\n        && 0x100000000 - range_to_test < frontend_address.get_raw())\n        range_to_test = uint64_t(Address(0x100000000) - frontend_address);\n\n    for (uint64_t o = 0; o < range_to_test; o += 4) {\n        bus.write_u32(frontend_address + o, uint32_t(o));\n    }\n\n    for (uint64_t o = 0; o < range_to_test; o += 4) {\n        QCOMPARE(bus.read_u32(frontend_address + o), uint32_t(o));\n        bus.write_u32(frontend_address + o, uint32_t(o) ^ 0xaabbccdd);\n    }\n\n    for (uint64_t o = 0; o < range_to_test; o += 4) {\n        QCOMPARE(bus.read_u32(frontend_address + o), uint32_t(o) ^ 0xaabbccdd);\n    }\n}\n\nQTEST_APPLESS_MAIN(TestMemory)\n"
  },
  {
    "path": "src/machine/memory/backend/memory.test.h",
    "content": "#ifndef MEMORY_TEST_H\n#define MEMORY_TEST_H\n\n#include <QtTest>\n\nclass TestMemory : public QObject {\n    Q_OBJECT\n\nprivate:\n    static void integer_decomposition();\n\nprivate slots:\n    static void memory();\n    static void memory_data();\n    static void memory_section();\n    static void memory_section_data();\n    void memory_compare();\n    void memory_compare_data();\n    static void memory_write_ctl_data();\n    static void memory_write_ctl();\n    static void memory_read_ctl_data();\n    static void memory_read_ctl();\n    static void memory_memtest_data();\n    static void memory_memtest();\n};\n\n#endif // MEMORY_TEST_H\n"
  },
  {
    "path": "src/machine/memory/backend/peripheral.cpp",
    "content": "#include \"memory/backend/peripheral.h\"\n\n#include \"common/endian.h\"\n\nusing namespace machine;\n\nSimplePeripheral::SimplePeripheral(Endian simulated_machine_endian)\n    : BackendMemory(simulated_machine_endian) {};\n\nSimplePeripheral::~SimplePeripheral() = default;\n\nWriteResult\nSimplePeripheral::write(Offset destination, const void *source, size_t size, WriteOptions options) {\n    UNUSED(source)\n    UNUSED(options)\n\n    // Write to dummy periphery is nop\n\n    emit write_notification(destination, size);\n\n    return { size, false };\n}\n\nReadResult\nSimplePeripheral::read(void *destination, Offset source, size_t size, ReadOptions options) const {\n    UNUSED(options)\n\n    memset(destination, 0x12, size); // Random value\n\n    emit read_notification(source, size);\n\n    return { size };\n}\nLocationStatus SimplePeripheral::location_status(Offset offset) const {\n    UNUSED(offset)\n    return LOCSTAT_NONE;\n}\n"
  },
  {
    "path": "src/machine/memory/backend/peripheral.h",
    "content": "#ifndef PERIPHERAL_H\n#define PERIPHERAL_H\n\n#include \"common/endian.h\"\n#include \"machinedefs.h\"\n#include \"memory/backend/backend_memory.h\"\n#include \"memory/backend/memory.h\"\n#include \"memory/memory_utils.h\"\n#include \"simulator_exception.h\"\n\n#include <QMap>\n#include <QObject>\n#include <cstdint>\n\nnamespace machine {\n\n/**\n * NOTE: This peripheral is constant, it does not case about endian. Therefore\n * it does not have internal_endian field.\n */\nclass SimplePeripheral final : public BackendMemory {\n    Q_OBJECT\npublic:\n    explicit SimplePeripheral(Endian simulated_machine_endian);\n    ~SimplePeripheral() override;\n\nsignals:\n    void write_notification(Offset address, size_t size) const;\n    void read_notification(Offset address, size_t size) const;\n\npublic:\n    WriteResult\n    write(Offset destination, const void *source, size_t size, WriteOptions options) override;\n\n    ReadResult\n    read(void *destination, Offset source, size_t size, ReadOptions options) const override;\n\n    [[nodiscard]] LocationStatus location_status(Offset offset) const override;\n};\n\n} // namespace machine\n\n#endif // PERIPHERAL_H\n"
  },
  {
    "path": "src/machine/memory/backend/peripspiled.cpp",
    "content": "#include \"memory/backend/peripspiled.h\"\n\n#include \"common/endian.h\"\n\nusing namespace machine;\n\nconstexpr size_t SPILED_REG_LED_LINE_o = 0x004;\nconstexpr size_t SPILED_REG_LED_RGB1_o = 0x010;\nconstexpr size_t SPILED_REG_LED_RGB2_o = 0x014;\nconstexpr size_t SPILED_REG_LED_KBDWR_DIRECT_o = 0x018;\n\nconstexpr size_t SPILED_REG_KBDRD_KNOBS_DIRECT_o = 0x020;\nconstexpr size_t SPILED_REG_KNOBS_8BIT_o = 0x024;\n\nPeripSpiLed::PeripSpiLed(Endian simulated_machine_endian)\n    : BackendMemory(simulated_machine_endian) {}\n\nPeripSpiLed::~PeripSpiLed() = default;\n\nWriteResult\nPeripSpiLed::write(Offset destination, const void *source, size_t size, WriteOptions options) {\n    UNUSED(options)\n    return write_by_u32(\n        destination, source, size,\n        [&](Offset src) {\n            return byteswap_if(read_reg(src), internal_endian != simulated_machine_endian);\n        },\n        [&](Offset src, uint32_t value) {\n            return write_reg(src, byteswap_if(value, internal_endian != simulated_machine_endian));\n        });\n}\n\nReadResult\nPeripSpiLed::read(void *destination, Offset source, size_t size, ReadOptions options) const {\n    UNUSED(options)\n    return read_by_u32(destination, source, size, [&](Offset src) {\n        return byteswap_if(read_reg(src), internal_endian != simulated_machine_endian);\n    });\n}\n\nuint32_t PeripSpiLed::read_reg(Offset source) const {\n    Q_ASSERT((source & 3U) == 0); // uint32_t aligned\n    uint32_t value = [&]() {\n        switch (source) {\n        case SPILED_REG_LED_LINE_o: return spiled_reg_led_line;\n        case SPILED_REG_LED_RGB1_o: return spiled_reg_led_rgb1;\n        case SPILED_REG_LED_RGB2_o: return spiled_reg_led_rgb2;\n        case SPILED_REG_LED_KBDWR_DIRECT_o: return spiled_reg_led_kbdwr_direct;\n        case SPILED_REG_KBDRD_KNOBS_DIRECT_o: return spiled_reg_kbdrd_knobs_direct;\n        case SPILED_REG_KNOBS_8BIT_o: return spiled_reg_knobs_8bit;\n        default:\n            // Todo show this to user as this is failure of supplied program\n            printf(\"[WARNING] PeripSpiLed: read to non-readable location.\\n\");\n            return 0u;\n        }\n    }();\n\n    emit read_notification(source, value);\n\n    return value;\n}\n\nbool PeripSpiLed::write_reg(Offset destination, uint32_t value) {\n    Q_ASSERT((destination & 3U) == 0); // uint32_t aligned\n\n    bool changed = [&]() {\n        switch (destination) {\n        case SPILED_REG_LED_LINE_o: {\n            if (spiled_reg_led_line != value) {\n                spiled_reg_led_line = value;\n                emit led_line_changed(spiled_reg_led_line);\n                return true;\n            }\n            return false;\n        }\n        case SPILED_REG_LED_RGB1_o:\n            if (spiled_reg_led_rgb1 != value) {\n                spiled_reg_led_rgb1 = value;\n                emit led_rgb1_changed(spiled_reg_led_rgb1);\n                return true;\n            }\n            return false;\n        case SPILED_REG_LED_RGB2_o:\n            if (spiled_reg_led_rgb2 != value) {\n                spiled_reg_led_rgb2 = value;\n                emit led_rgb2_changed(spiled_reg_led_rgb2);\n                return true;\n            }\n            return false;\n        default:\n            // Todo show this to user as this is failure of supplied program\n            printf(\"[WARNING] PeripSpiLed: write to non-writable location.\\n\");\n            return false;\n        }\n    }();\n\n    emit write_notification(destination, value);\n\n    return changed;\n}\n\nvoid PeripSpiLed::knob_update_notify(uint32_t val, uint32_t mask, size_t shift) {\n    mask <<= shift;\n    val <<= shift;\n\n    if (!((spiled_reg_knobs_8bit ^ val) & mask)) { return; }\n\n    spiled_reg_knobs_8bit &= ~mask;\n    spiled_reg_knobs_8bit |= val;\n\n    emit external_backend_change_notify(\n        this, SPILED_REG_KNOBS_8BIT_o, SPILED_REG_KNOBS_8BIT_o + 3, ae::EXTERNAL_ASYNC);\n}\n\nvoid PeripSpiLed::red_knob_update(int val) {\n    knob_update_notify(val, 0xff, 16);\n}\n\nvoid PeripSpiLed::green_knob_update(int val) {\n    knob_update_notify(val, 0xff, 8);\n}\n\nvoid PeripSpiLed::blue_knob_update(int val) {\n    knob_update_notify(val, 0xff, 0);\n}\n\nvoid PeripSpiLed::red_knob_push(bool state) {\n    knob_update_notify(state ? 1 : 0, 1, 26);\n}\n\nvoid PeripSpiLed::green_knob_push(bool state) {\n    knob_update_notify(state ? 1 : 0, 1, 25);\n}\n\nvoid PeripSpiLed::blue_knob_push(bool state) {\n    knob_update_notify(state ? 1 : 0, 1, 24);\n}\nLocationStatus PeripSpiLed::location_status(Offset offset) const {\n    switch (offset & ~3U) {\n    case SPILED_REG_LED_LINE_o: FALLTROUGH\n    case SPILED_REG_LED_RGB1_o: FALLTROUGH\n    case SPILED_REG_LED_RGB2_o: {\n        return LOCSTAT_NONE;\n    }\n    case SPILED_REG_LED_KBDWR_DIRECT_o: FALLTROUGH\n    case SPILED_REG_KBDRD_KNOBS_DIRECT_o: FALLTROUGH\n    case SPILED_REG_KNOBS_8BIT_o: {\n        return LOCSTAT_READ_ONLY;\n    }\n    default: {\n        return LOCSTAT_ILLEGAL;\n    }\n    }\n}\n"
  },
  {
    "path": "src/machine/memory/backend/peripspiled.h",
    "content": "#ifndef PERIPSPILED_H\n#define PERIPSPILED_H\n\n#include \"common/endian.h\"\n#include \"machinedefs.h\"\n#include \"memory/backend/backend_memory.h\"\n#include \"memory/memory_utils.h\"\n#include \"simulator_exception.h\"\n\n#include <cstdint>\n\nnamespace machine {\n\nclass PeripSpiLed final : public BackendMemory {\n    Q_OBJECT\npublic:\n    explicit PeripSpiLed(Endian simulated_machine_endian);\n    ~PeripSpiLed() override;\n\nsignals:\n    void write_notification(Offset address, uint32_t value) const;\n    void read_notification(Offset address, uint32_t value) const;\n\n    void led_line_changed(uint val) const;\n    void led_rgb1_changed(uint val) const;\n    void led_rgb2_changed(uint val) const;\n\npublic slots:\n    void red_knob_update(int val);\n    void green_knob_update(int val);\n    void blue_knob_update(int val);\n    void red_knob_push(bool state);\n    void green_knob_push(bool state);\n    void blue_knob_push(bool state);\n\npublic:\n    WriteResult\n    write(Offset destination, const void *source, size_t size, WriteOptions options) override;\n\n    ReadResult\n    read(void *destination, Offset source, size_t size, ReadOptions options) const override;\n\n    [[nodiscard]] LocationStatus location_status(Offset offset) const override;\n\nprivate:\n    [[nodiscard]] uint32_t read_reg(Offset source) const;\n    bool write_reg(Offset destination, uint32_t value);\n    void knob_update_notify(uint32_t val, uint32_t mask, size_t shift);\n\n    /** endian of internal registers of the periphery use. */\n    static constexpr Endian internal_endian = NATIVE_ENDIAN;\n    uint32_t spiled_reg_led_line = 0;\n    uint32_t spiled_reg_led_rgb1 = 0;\n    uint32_t spiled_reg_led_rgb2 = 0;\n    uint32_t spiled_reg_led_kbdwr_direct = 0;\n    uint32_t spiled_reg_kbdrd_knobs_direct = 0;\n    uint32_t spiled_reg_knobs_8bit = 0;\n};\n\n} // namespace machine\n\n#endif // PERIPSPILED_H\n"
  },
  {
    "path": "src/machine/memory/backend/serialport.cpp",
    "content": "#include \"memory/backend/serialport.h\"\n\n#include \"common/endian.h\"\n\n#include <common/logging.h>\n\nLOG_CATEGORY(\"machine.memory.serialport\");\n\nusing ae = machine::AccessEffects; // For enum values, type is obvious from\n                                   // context.\n\nnamespace machine {\n\nconstexpr Offset SERP_RX_ST_REG_o = 0x00u;\nconstexpr uint32_t SERP_RX_ST_REG_READY_m = 0x1u;\nconstexpr uint32_t SERP_RX_ST_REG_IE_m = 0x2u;\n\nconstexpr Offset SERP_RX_DATA_REG_o = 0x04u;\n\nconstexpr Offset SERP_TX_ST_REG_o = 0x08u;\nconstexpr uint32_t SERP_TX_ST_REG_READY_m = 0x1u;\nconstexpr uint32_t SERP_TX_ST_REG_IE_m = 0x2u;\n\nconstexpr Offset SERP_TX_DATA_REG_o = 0xcu;\n\nSerialPort::SerialPort(Endian simulated_machine_endian)\n    : BackendMemory(simulated_machine_endian)\n    , tx_irq_level(17) // The second platform HW interrupt\n    , rx_irq_level(16) // The first platform HW interrupt\n    , tx_st_reg(SERP_TX_ST_REG_READY_m) {}\n\nSerialPort::~SerialPort() = default;\n\nvoid SerialPort::pool_rx_byte() const {\n    unsigned int byte = 0;\n    bool available = false;\n    if (!(rx_st_reg & SERP_RX_ST_REG_READY_m)) {\n        rx_st_reg |= SERP_RX_ST_REG_READY_m;\n        emit rx_byte_pool(0, byte, available);\n        if (available) {\n            change_counter++;\n            rx_data_reg = byte;\n        } else {\n            rx_st_reg &= ~SERP_RX_ST_REG_READY_m;\n        }\n    }\n}\n\nWriteResult\nSerialPort::write(Offset destination, const void *source, size_t size, WriteOptions options) {\n    UNUSED(options)\n    return write_by_u32(\n        destination, source, size,\n        [&](Offset src) {\n            UNUSED(src)\n            return 0;\n        },\n        [&](Offset src, uint32_t value) {\n            return write_reg(src, byteswap_if(value, internal_endian != simulated_machine_endian));\n        });\n}\n\nReadResult\nSerialPort::read(void *destination, Offset source, size_t size, ReadOptions options) const {\n    return read_by_u32(destination, source, size, [&](Offset src) {\n        return byteswap_if(\n            read_reg(src, options.type), internal_endian != simulated_machine_endian);\n    });\n}\n\nvoid SerialPort::update_rx_irq() const {\n    bool active = (rx_st_reg & SERP_RX_ST_REG_IE_m) != 0;\n    active &= (rx_st_reg & SERP_RX_ST_REG_READY_m) != 0;\n    if (active != rx_irq_active) {\n        rx_irq_active = active;\n        emit signal_interrupt(rx_irq_level, active);\n    }\n}\n\nvoid SerialPort::rx_queue_check_internal() const {\n    if (rx_st_reg & SERP_RX_ST_REG_IE_m) { pool_rx_byte(); }\n    update_rx_irq();\n}\n\nvoid SerialPort::rx_queue_check() const {\n    rx_queue_check_internal();\n    emit external_backend_change_notify(\n        this, SERP_RX_ST_REG_o, SERP_RX_DATA_REG_o + 3, ae::EXTERNAL_ASYNC);\n}\n\nvoid SerialPort::update_tx_irq() const {\n    bool active = (tx_st_reg & SERP_TX_ST_REG_IE_m) != 0;\n    active &= (tx_st_reg & SERP_TX_ST_REG_READY_m) != 0;\n    if (active != tx_irq_active) {\n        tx_irq_active = active;\n        emit signal_interrupt(tx_irq_level, active);\n    }\n}\n\nuint32_t SerialPort::read_reg(Offset source, AccessEffects type) const {\n    Q_ASSERT((source & 3U) == 0); // uint32_t aligned\n\n    uint32_t value = 0;\n\n    switch (source) {\n    case SERP_RX_ST_REG_o:\n        pool_rx_byte();\n        value = rx_st_reg;\n        break;\n    case SERP_RX_DATA_REG_o:\n        pool_rx_byte();\n        if (rx_st_reg & SERP_RX_ST_REG_READY_m) {\n            value = rx_data_reg;\n            if (type == ae::REGULAR) {\n                rx_st_reg &= ~SERP_RX_ST_REG_READY_m;\n                update_rx_irq();\n                emit external_backend_change_notify(\n                    this, SERP_RX_ST_REG_o, SERP_RX_DATA_REG_o + 3, ae::EXTERNAL_ASYNC);\n            }\n        } else {\n            value = 0;\n        }\n        rx_queue_check_internal();\n        break;\n    case SERP_TX_ST_REG_o: value = tx_st_reg; break;\n    default: WARN(\"Serial port - read out of range (at 0x%zu).\\n\", source); break;\n    }\n\n    emit read_notification(source, value);\n\n    return value;\n}\n\nbool SerialPort::write_reg(Offset destination, uint32_t value) {\n    Q_ASSERT((destination & 3U) == 0); // uint32_t aligned\n\n    bool changed = [&]() {\n        switch (destination & ~3U) {\n        case SERP_RX_ST_REG_o:\n            rx_st_reg &= ~SERP_RX_ST_REG_IE_m;\n            rx_st_reg |= value & SERP_RX_ST_REG_IE_m;\n            rx_queue_check_internal();\n            update_rx_irq();\n            return true;\n        case SERP_TX_ST_REG_o:\n            tx_st_reg &= ~SERP_TX_ST_REG_IE_m;\n            tx_st_reg |= value & SERP_TX_ST_REG_IE_m;\n            update_tx_irq();\n            return true;\n        case SERP_TX_DATA_REG_o:\n            emit tx_byte(value & 0xffu);\n            update_tx_irq();\n            return true;\n        default: WARN(\"Serial port - write out of range (at 0x%zu).\\n\", destination); return false;\n        }\n    }();\n\n    emit write_notification(destination, value);\n\n    return changed;\n}\nLocationStatus SerialPort::location_status(Offset offset) const {\n    switch (offset & ~3U) {\n    case SERP_RX_ST_REG_o: FALLTROUGH\n    case SERP_TX_ST_REG_o: FALLTROUGH\n    case SERP_TX_DATA_REG_o: // This is actually write only, but there is no\n                             // enum for that.\n    {\n        return LOCSTAT_NONE;\n    }\n    case SERP_RX_DATA_REG_o: {\n        return LOCSTAT_READ_ONLY;\n    }\n    default: {\n        return LOCSTAT_ILLEGAL;\n    }\n    }\n}\n\nuint32_t SerialPort::get_change_counter() const {\n    return change_counter;\n}\n} // namespace machine\n"
  },
  {
    "path": "src/machine/memory/backend/serialport.h",
    "content": "#ifndef SERIALPORT_H\n#define SERIALPORT_H\n\n#include \"common/endian.h\"\n#include \"memory/backend/backend_memory.h\"\n#include \"memory/backend/peripheral.h\"\n#include \"simulator_exception.h\"\n\n#include <cstdint>\n\nnamespace machine {\n\nclass SerialPort : public BackendMemory {\n    Q_OBJECT\npublic:\n    explicit SerialPort(Endian simulated_machine_endian);\n    ~SerialPort() override;\n\nsignals:\n    void tx_byte(unsigned int data);\n    void rx_byte_pool(int fd, unsigned int &data, bool &available) const;\n    void write_notification(Offset address, uint32_t value);\n    void read_notification(Offset address, uint32_t value) const;\n    void signal_interrupt(uint irq_level, bool active) const;\n\npublic slots:\n    void rx_queue_check() const;\n\npublic:\n    WriteResult\n    write(Offset destination, const void *source, size_t size, WriteOptions options) override;\n\n    ReadResult\n    read(void *destination, Offset source, size_t size, ReadOptions options) const override;\n\n    LocationStatus location_status(Offset offset) const override;\n\nprivate:\n    uint32_t read_reg(Offset source, AccessEffects type) const;\n    bool write_reg(Offset destination, uint32_t value);\n    void rx_queue_check_internal() const;\n    void pool_rx_byte() const;\n    void update_rx_irq() const;\n    void update_tx_irq() const;\n    uint32_t get_change_counter() const;\n\n    /** endian of internal registers of the periphery use. */\n    static constexpr Endian internal_endian = NATIVE_ENDIAN;\n    const uint8_t tx_irq_level;\n    const uint8_t rx_irq_level;\n    mutable uint32_t change_counter = { 0 };\n    mutable uint32_t tx_st_reg = { 0 };\n    mutable uint32_t rx_st_reg = { 0 };\n    mutable uint32_t rx_data_reg = { 0 };\n    mutable bool tx_irq_active = false;\n    mutable bool rx_irq_active = false;\n};\n\n} // namespace machine\n\n#endif // SERIALPORT_H\n"
  },
  {
    "path": "src/machine/memory/cache/cache.cpp",
    "content": "#include \"memory/cache/cache.h\"\n\n#include \"memory/cache/cache_types.h\"\n\n#include <cstddef>\n\nusing ae = machine::AccessEffects; // For enum values, type is obvious from\n                                   // context.\n\nnamespace machine {\n\nCache::Cache(\n    FrontendMemory *memory,\n    const CacheConfig *config,\n    uint32_t memory_access_penalty_r,\n    uint32_t memory_access_penalty_w,\n    uint32_t memory_access_penalty_b,\n    bool memory_access_enable_b)\n    : FrontendMemory(memory->simulated_machine_endian)\n    , cache_config(config)\n    , mem(memory)\n    , uncached_start(0xf0000000_addr)\n    , uncached_last(0xfffffffe_addr)\n    , access_pen_r(memory_access_penalty_r)\n    , access_pen_w(memory_access_penalty_w)\n    , access_pen_b(memory_access_penalty_b)\n    , access_ena_b(memory_access_enable_b)\n    , replacement_policy(CachePolicy::get_policy_instance(config)) {\n    // Skip memory allocation if cache is disabled\n    if (!config->enabled()) { return; }\n\n    dt.resize(\n        config->associativity(),\n        std::vector<CacheLine>(\n            config->set_count(), { .valid = false,\n                                   .dirty = false,\n                                   .tag = 0,\n                                   .data = std::vector<uint32_t>(config->block_size()) }));\n}\n\nCache::~Cache() = default;\n\nWriteResult\nCache::write(AddressWithMode destination, const void *source, size_t size, WriteOptions options) {\n    if (!cache_config.enabled() || is_in_uncached_area(destination)\n        || is_in_uncached_area(destination + size)) {\n        mem_writes++;\n        emit memory_writes_update(mem_writes);\n        update_all_statistics();\n        return mem->write(destination, source, size, options);\n    }\n\n    // FIXME: Get rid of the cast\n    // access is mostly the same for read and write but one needs to write\n    // to the address\n    const bool changed = access(destination, const_cast<void *>(source), size, WRITE);\n\n    if (cache_config.write_policy() != CacheConfig::WP_BACK) {\n        mem_writes++;\n        emit memory_writes_update(mem_writes);\n        update_all_statistics();\n        return mem->write(destination, source, size, options);\n    }\n\n    return { .n_bytes = size, .changed = changed };\n}\n\nReadResult Cache::read(void *destination, AddressWithMode source, size_t size, ReadOptions options) const {\n    if (!cache_config.enabled() || is_in_uncached_area(source)\n        || is_in_uncached_area(source + size)) {\n        mem_reads++;\n        emit memory_reads_update(mem_reads);\n        update_all_statistics();\n        return mem->read(destination, source, size, options);\n    }\n\n    if (options.type == ae::INTERNAL) {\n        if (!(location_status(source) & LOCSTAT_CACHED)) {\n            mem->read(destination, source, size, options);\n        } else {\n            internal_read(source, destination, size);\n        }\n        return {};\n    }\n\n    access(source, destination, size, READ);\n\n    return {};\n}\nbool Cache::is_in_uncached_area(Address source) const {\n    return (source >= uncached_start && source <= uncached_last);\n}\n\nvoid Cache::flush() {\n    if (!cache_config.enabled()) { return; }\n\n    for (size_t assoc_index = 0; assoc_index < cache_config.associativity(); assoc_index += 1) {\n        for (size_t set_index = 0; set_index < cache_config.set_count(); set_index += 1) {\n            if (dt[assoc_index][set_index].valid) {\n                kick(assoc_index, set_index);\n                emit cache_update(assoc_index, set_index, 0, false, false, 0, nullptr, false);\n            }\n        }\n    }\n    change_counter++;\n    update_all_statistics();\n}\n\nvoid Cache::sync() {\n    flush();\n}\n\nvoid Cache::reset() {\n    // Set all cells to invalid\n    if (cache_config.enabled()) {\n        for (auto &set : dt) {\n            for (auto &block : set) {\n                block.valid = false;\n            }\n        }\n        // Note: We don't have to zero replacement policy data as those are\n        // zeroed when first used on invalid cell.\n    }\n\n    hit_read = 0;\n    hit_write = 0;\n    miss_read = 0;\n    miss_write = 0;\n    mem_reads = 0;\n    mem_writes = 0;\n    burst_reads = 0;\n    burst_writes = 0;\n\n    emit hit_update(get_hit_count());\n    emit miss_update(get_miss_count());\n    emit memory_reads_update(get_read_count());\n    emit memory_writes_update(get_write_count());\n    update_all_statistics();\n\n    if (cache_config.enabled()) {\n        for (size_t assoc_index = 0; assoc_index < cache_config.associativity(); assoc_index++) {\n            for (size_t set_index = 0; set_index < cache_config.set_count(); set_index++) {\n                emit cache_update(assoc_index, set_index, 0, false, false, 0, nullptr, false);\n            }\n        }\n    }\n}\n\nvoid Cache::internal_read(Address source, void *destination, size_t size) const {\n    CacheLocation loc = compute_location(source);\n    for (size_t assoc_index = 0; assoc_index < cache_config.associativity(); assoc_index++) {\n        if (dt[assoc_index][loc.row].valid && dt[assoc_index][loc.row].tag == loc.tag) {\n            memcpy(destination, (byte *)&dt[assoc_index][loc.row].data[loc.col] + loc.byte, size);\n            return;\n        }\n    }\n    memset(destination, 0, size); // TODO is this correct\n}\n\nbool Cache::access(Address address, void *buffer, size_t size, AccessType access_type) const {\n    const CacheLocation loc = compute_location(address);\n    size_t way = find_block_index(loc);\n\n    // check for zero because else last_affected_col can became\n    // ULONG_MAX / BLOCK_ITEM_SIZE and update can take forever\n    if (size == 0) return false;\n\n    // search failed - cache miss\n    if (way >= cache_config.associativity()) {\n        // if write through we do not need to allocate cache line does not\n        // allocate\n        if (access_type == WRITE\n            && cache_config.write_policy() == CacheConfig::WP_THROUGH_NOALLOC) {\n            miss_write++;\n            emit miss_update(get_miss_count());\n            update_all_statistics();\n\n            const size_t size_overflow = calculate_overflow_to_next_blocks(size, loc);\n            if (size_overflow > 0) {\n                const size_t size_within_block = size - size_overflow;\n                return access(\n                    address + size_within_block, (byte *)buffer + size_within_block, size_overflow,\n                    access_type);\n            } else {\n                return false;\n            }\n        }\n\n        way = replacement_policy->select_way_to_evict(loc.row);\n        kick(way, loc.row);\n\n        SANITY_ASSERT(\n            way < cache_config.associativity(), \"Probably unimplemented replacement policy\");\n    }\n\n    struct CacheLine &cd = dt[way][loc.row];\n\n    // Update statistics and otherwise read from memory\n    if (cd.valid) {\n        if (access_type == WRITE) {\n            hit_write++;\n        } else {\n            hit_read++;\n        }\n        emit hit_update(get_hit_count());\n        update_all_statistics();\n    } else {\n        if (access_type == WRITE) {\n            miss_write++;\n        } else {\n            miss_read++;\n        }\n        emit miss_update(get_miss_count());\n\n        mem->read(\n            cd.data.data(), calc_base_address(loc.tag, loc.row),\n            cache_config.block_size() * BLOCK_ITEM_SIZE, { .type = ae::REGULAR });\n\n        cd.valid = true;\n        cd.dirty = false;\n        cd.tag = loc.tag;\n\n        change_counter += cache_config.block_size();\n        mem_reads += cache_config.block_size();\n        burst_reads += cache_config.block_size() - 1;\n        emit memory_reads_update(mem_reads);\n        update_all_statistics();\n    }\n\n    replacement_policy->update_stats(way, loc.row, cd.valid);\n\n    const size_t size_overflow = calculate_overflow_to_next_blocks(size, loc);\n    const size_t size_within_block = size - size_overflow;\n\n    bool changed = false;\n\n    if (access_type == READ) {\n        memcpy(buffer, (byte *)&cd.data[loc.col] + loc.byte, size_within_block);\n    } else if (access_type == WRITE) {\n        cd.dirty = true;\n        changed = memcmp((byte *)&cd.data[loc.col] + loc.byte, buffer, size_within_block) != 0;\n        if (changed) {\n            memcpy(((byte *)&cd.data[loc.col]) + loc.byte, buffer, size_within_block);\n            change_counter++;\n        }\n    }\n    const auto last_affected_col\n        = (loc.col * BLOCK_ITEM_SIZE + loc.byte + size_within_block - 1) / BLOCK_ITEM_SIZE;\n    for (auto col = loc.col; col <= last_affected_col; col++) {\n        emit cache_update(\n            way, loc.row, col, cd.valid, cd.dirty, cd.tag, cd.data.data(), access_type);\n    }\n\n    if (size_overflow > 0) {\n        // If access overlaps single cache row, perform access to next row.\n        changed |= access(\n            address + size_within_block, (byte *)buffer + size_within_block, size_overflow,\n            access_type);\n    }\n\n    return changed;\n}\nsize_t Cache::calculate_overflow_to_next_blocks(size_t access_size, const CacheLocation &loc) const {\n    return std::max(\n        (ptrdiff_t)(loc.col * BLOCK_ITEM_SIZE + loc.byte + access_size)\n            - (ptrdiff_t)(cache_config.block_size() * BLOCK_ITEM_SIZE),\n        { 0 });\n}\n\nsize_t Cache::find_block_index(const CacheLocation &loc) const {\n    uint32_t index = 0;\n    while (index < cache_config.associativity()\n           and (!dt[index][loc.row].valid or dt[index][loc.row].tag != loc.tag)) {\n        index++;\n    }\n    return index;\n}\n\nvoid Cache::kick(size_t way, size_t row) const {\n    struct CacheLine &cd = dt[way][row];\n    if (cd.dirty && cache_config.write_policy() == CacheConfig::WP_BACK) {\n        mem->write(\n            calc_base_address(cd.tag, row), cd.data.data(),\n            cache_config.block_size() * BLOCK_ITEM_SIZE, {});\n        mem_writes += cache_config.block_size();\n        burst_writes += cache_config.block_size() - 1;\n        emit memory_writes_update(mem_writes);\n    }\n    cd.valid = false;\n    cd.dirty = false;\n\n    change_counter++;\n\n    replacement_policy->update_stats(way, row, false);\n}\n\nvoid Cache::update_all_statistics() const {\n    emit statistics_update(get_stall_count(), get_speed_improvement(), get_hit_rate());\n}\n\nAddress Cache::calc_base_address(size_t tag, size_t row) const {\n    return Address(\n        (tag * cache_config.set_count() + row) * cache_config.block_size() * BLOCK_ITEM_SIZE);\n}\n\nCacheLocation Cache::compute_location(Address address) const {\n    // Get address in multiples of 4 bytes (basic storage unit size) and get the\n    // reminder to address individual byte within basic storage unit.\n    auto word_index = address.get_raw() / BLOCK_ITEM_SIZE;\n    auto byte = address.get_raw() % BLOCK_ITEM_SIZE;\n    // Associativity does not influence location (hit will be on the\n    // same place in each way). Lets therefore assume associativity degree 1.\n    // Same address modulo `way_size_words` will alias (up to associativity).\n    auto way_size_words = cache_config.set_count() * cache_config.block_size();\n    // Index in a way, when rows and cols would be rearranged into 1D array.\n    auto index_in_way = word_index % way_size_words;\n    auto tag = word_index / way_size_words;\n\n    return { .row = index_in_way / cache_config.block_size(),\n             .col = index_in_way % cache_config.block_size(),\n             .tag = tag,\n             .byte = byte };\n}\n\nenum LocationStatus Cache::location_status(Address address) const {\n    const CacheLocation loc = compute_location(address);\n\n    if (cache_config.enabled()) {\n        for (auto const &set : dt) {\n            auto const &block = set[loc.row];\n\n            if (block.valid && block.tag == loc.tag) {\n                if (block.dirty && cache_config.write_policy() == CacheConfig::WP_BACK) {\n                    return (enum LocationStatus)(LOCSTAT_CACHED | LOCSTAT_DIRTY);\n                } else {\n                    return LOCSTAT_CACHED;\n                }\n            }\n        }\n    }\n    return mem->location_status(address);\n}\n\nconst CacheConfig &Cache::get_config() const {\n    return cache_config;\n}\n\nuint32_t Cache::get_change_counter() const {\n    return change_counter;\n}\n\nuint32_t Cache::get_hit_count() const {\n    return hit_read + hit_write;\n}\n\nuint32_t Cache::get_miss_count() const {\n    return miss_read + miss_write;\n}\n\nuint32_t Cache::get_read_count() const {\n    return mem_reads;\n}\n\nuint32_t Cache::get_write_count() const {\n    return mem_writes;\n}\n\nuint32_t Cache::get_stall_count() const {\n    uint32_t st_cycles = mem_reads * (access_pen_r - 1) + mem_writes * (access_pen_w - 1);\n    st_cycles += (miss_read + miss_write) * cache_config.block_size();\n    if (access_ena_b) {\n        st_cycles -= burst_reads * (access_pen_r - access_pen_b)\n                     + burst_writes * (access_pen_w - access_pen_b);\n    }\n    return st_cycles;\n}\n\ndouble Cache::get_speed_improvement() const {\n    uint32_t lookup_time;\n    uint32_t mem_access_time;\n    uint32_t comp = hit_read + hit_write + miss_read + miss_write;\n    if (comp == 0) { return 100.0; }\n    lookup_time = hit_read + miss_read;\n    if (cache_config.write_policy() == CacheConfig::WP_BACK) {\n        lookup_time += hit_write + miss_write;\n    }\n    mem_access_time = mem_reads * access_pen_r + mem_writes * access_pen_w;\n    if (access_ena_b) {\n        mem_access_time -= burst_reads * (access_pen_r - access_pen_b)\n                           + burst_writes * (access_pen_w - access_pen_b);\n    }\n    return (\n        (double)((miss_read + hit_read) * access_pen_r + (miss_write + hit_write) * access_pen_w)\n        / (double)(lookup_time + mem_access_time) * 100);\n}\n\ndouble Cache::get_hit_rate() const {\n    uint32_t comp = hit_read + hit_write + miss_read + miss_write;\n    if (comp == 0) { return 0.0; }\n    return (double)(hit_read + hit_write) / (double)comp * 100.0;\n}\n\n} // namespace machine\n"
  },
  {
    "path": "src/machine/memory/cache/cache.h",
    "content": "#ifndef CACHE_H\n#define CACHE_H\n\n#include \"machineconfig.h\"\n#include \"memory/cache/cache_policy.h\"\n#include \"memory/cache/cache_types.h\"\n#include \"memory/frontend_memory.h\"\n\n#include <cstdint>\n#include <memory>\n\nnamespace machine {\n\nconstexpr size_t BLOCK_ITEM_SIZE = sizeof(uint32_t);\n\n/**\n * NOTE ON TERMINOLOGY:\n * N-way set associative cache consist of N ways (where N is degree\n * of associativity). Arguments requesting N are called `associativity` of the\n * cache. Each way consist of blocks. (When we want to highlight, that we talk\n * about data + management tags, we speak of cache line. When we speak about\n * location of block within a way, we use the term `row`. Each block consists of\n * some number of basic storage units (here `uint32_t`). To locate a single unit\n * withing block, we use therm `col` (as column).\n *\n * Set is consists of all block on the same row across the ways.\n *\n * We can imagine a cache as 3D array indexed via triple (`way`, `row`, `col`).\n * Row and col are derived from part of a address deterministically. The rest\n * of the address is called `tag`. Set is obtained via linear search and placing\n * into cache, it is determined by cache replacement policy\n * (see `memory/cache/cache_policy.h`).\n */\nclass Cache : public FrontendMemory {\n    Q_OBJECT\npublic:\n    /**\n     * @param memory                    backing memory used to handle misses\n     * @param simulated_endian          endian of the simulated CPU/memory\n     * system\n     * @param config                    cache configuration struct\n     * @param memory_access_penalty_r   cycles to perform read (stats only)\n     * @param memory_access_penalty_w   cycles to perform write (stats only)\n     * @param memory_access_penalty_b   cycles to perform burst access (stats\n     *                                  only)\n     *\n     * NOTE: Memory access penalties apply only to statistics and are not taken\n     * into account during simulation itself. There is no point in doing so\n     * without superscalar execution.\n     */\n    Cache(\n        FrontendMemory *memory,\n        const CacheConfig *config,\n        uint32_t memory_access_penalty_r = 1,\n        uint32_t memory_access_penalty_w = 1,\n        uint32_t memory_access_penalty_b = 0,\n        bool memory_access_enable_b = false);\n\n    ~Cache() override;\n\n    WriteResult\n    write(AddressWithMode destination, const void *source, size_t size, WriteOptions options) override;\n\n    ReadResult\n    read(void *destination, AddressWithMode source, size_t size, ReadOptions options) const override;\n\n    uint32_t get_change_counter() const override;\n\n    void flush();         // flush cache\n    void sync() override; // Same as flush\n\n    uint32_t get_hit_count() const;       // Number of recorded hits\n    uint32_t get_miss_count() const;      // Number of recorded misses\n    uint32_t get_read_count() const;      // Number backing/main memory reads\n    uint32_t get_write_count() const;     // Number backing/main memory writes\n    uint32_t get_stall_count() const;     // Number of wasted cycles in\n                                          // memory waiting statistic\n    double get_speed_improvement() const; // Speed improvement in percents in\n                                          // comare with no used cache\n    double get_hit_rate() const;          // Usage efficiency in percents\n\n    void reset(); // Reset whole state of cache\n\n    const CacheConfig &get_config() const;\n\n    enum LocationStatus location_status(Address address) const override;\n\nsignals:\n    void hit_update(uint32_t) const;\n    void miss_update(uint32_t) const;\n    void statistics_update(uint32_t stalled_cycles, double speed_improv, double hit_rate) const;\n    void cache_update(\n        size_t way,\n        size_t row,\n        size_t col,\n        bool valid,\n        bool dirty,\n        size_t tag,\n        const uint32_t *data,\n        bool write) const;\n    void memory_writes_update(uint32_t) const;\n    void memory_reads_update(uint32_t) const;\n\nprivate:\n    const CacheConfig cache_config;\n    FrontendMemory *const mem;\n    const Address uncached_start;\n    const Address uncached_last;\n    const uint32_t access_pen_r, access_pen_w, access_pen_b;\n    const bool access_ena_b;\n    const std::unique_ptr<CachePolicy> replacement_policy;\n\n    mutable std::vector<std::vector<CacheLine>> dt;\n\n    mutable uint32_t hit_read = 0, miss_read = 0, hit_write = 0, miss_write = 0, mem_reads = 0,\n                     mem_writes = 0, burst_reads = 0, burst_writes = 0, change_counter = 0;\n\n    void internal_read(Address source, void *destination, size_t size) const;\n\n    bool access(Address address, void *buffer, size_t size, AccessType access_type) const;\n\n    void kick(size_t way, size_t row) const;\n\n    Address calc_base_address(size_t tag, size_t row) const;\n\n    void update_all_statistics() const;\n\n    CacheLocation compute_location(Address address) const;\n\n    /**\n     * Searches for given tag in a set\n     *\n     * @param loc       requested location in cache\n     * @return          associativity index of found block, max index + 1 if not\n     *                  found\n     */\n    size_t find_block_index(const CacheLocation &loc) const;\n\n    bool is_in_uncached_area(Address source) const;\n\n    /**\n     * RW access to cache may span multiple blocks but it needs to be\n     * performed per block.\n     * This functions calculated the size, that will have to be performed by\n     * repeated access (recursive call of `access` method).\n     */\n    size_t calculate_overflow_to_next_blocks(size_t access_size, const CacheLocation &loc) const;\n};\n\n} // namespace machine\n\n#endif // CACHE_H\n"
  },
  {
    "path": "src/machine/memory/cache/cache.test.cpp",
    "content": "#include \"cache.test.h\"\n\n#include \"common/endian.h\"\n#include \"machine/memory/backend/memory.h\"\n#include \"machine/memory/cache/cache.h\"\n#include \"machine/memory/cache/cache_policy.h\"\n#include \"machine/memory/memory_bus.h\"\n#include \"tests/data/cache_test_performance_data.h\"\n\n#include <cinttypes>\n#include <tests/utils/integer_decomposition.h>\n\nusing namespace machine;\nusing std::array;\nusing std::pair;\nusing std::size_t;\nusing std::tuple;\nusing Testcase\n    = tuple<Endian, Address, size_t, IntegerDecomposition, IntegerDecomposition, CacheConfig>;\n\n/*\n * Testing data for memory accesses\n * (all combinations are tested)\n */\nconstexpr array<Endian, 2> simulated_endians { BIG, LITTLE };\nconstexpr array<uint64_t, 5> accessed_addresses {\n    0x0, 0xFFFF0, 0xFFFF1, 0xFFFFFF, 0xFFFFFFCC,\n};\nconstexpr array<size_t, 3> strides { 0, 1, 2 };\nconstexpr array<uint64_t, 1> values { 0x4142434445464748 };\n\n/*\n * Cache configuration parameters for testing\n * (all combinations are tested)\n */\nconstexpr array<CacheConfig::ReplacementPolicy, 5> replacement_policies {\n    CacheConfig::RP_RAND, CacheConfig::RP_LFU, CacheConfig::RP_LRU, CacheConfig::RP_PLRU,\n    CacheConfig::RP_NMRU\n};\nconstexpr array<CacheConfig::WritePolicy, 3> write_policies {\n    CacheConfig::WP_THROUGH_NOALLOC, // THIS\n                                     // IS\n                                     // BROKEN\n                                     // IN\n                                     // CACHE\n                                     // FOR\n                                     // STRIDE\n                                     // 1\n    CacheConfig::WP_THROUGH_ALLOC, CacheConfig::WP_BACK\n};\nconstexpr array<pair<unsigned, unsigned>, 3> organizations { {\n    { 8, 1 },\n    { 1, 8 },\n    { 4, 4 },\n} };\nconstexpr array<unsigned, 3> associativity_degrees { 1, 2, 4 };\n\n// + 1 is  for disabled cache\nconstexpr size_t CONFIG_COUNT = (replacement_policies.size() * write_policies.size()\n                                 * organizations.size() * associativity_degrees.size())\n                                + 1;\n\n/**\n * Creates cache_config objects from all combinations of the above given\n * parameters.\n */\narray<CacheConfig, CONFIG_COUNT> get_testing_cache_configs() {\n    array<CacheConfig, CONFIG_COUNT> configs;\n    configs.at(0).set_enabled(false);\n    size_t i = 1;\n    for (auto replacement_policy : replacement_policies) {\n        for (auto write_policy : write_policies) {\n            for (auto organization : organizations) {\n                for (auto associativity : associativity_degrees) {\n                    configs.at(i).set_enabled(true);\n                    configs.at(i).set_replacement_policy(replacement_policy);\n                    configs.at(i).set_write_policy(write_policy);\n                    configs.at(i).set_set_count(organization.first);\n                    configs.at(i).set_block_size(organization.second);\n                    configs.at(i).set_associativity(associativity);\n                    i += 1;\n                }\n            }\n        }\n    }\n    Q_ASSERT(i == CONFIG_COUNT);\n    return configs;\n}\n\nvoid TestCache::cache_data() {\n    QTest::addColumn<CacheConfig>(\"cache_c\");\n    QTest::addColumn<unsigned>(\"hit\");\n    QTest::addColumn<unsigned>(\"miss\");\n\n    CacheConfig cache_c;\n    cache_c.set_write_policy(CacheConfig::WP_THROUGH_ALLOC);\n    cache_c.set_enabled(true);\n    cache_c.set_set_count(8);\n    cache_c.set_block_size(1);\n    cache_c.set_associativity(1);\n    QTest::newRow(\"Directly mapped\") << cache_c << (unsigned)3 << (unsigned)7;\n    cache_c.set_set_count(1);\n    cache_c.set_block_size(8);\n    QTest::newRow(\"Wide\") << cache_c << (unsigned)5 << (unsigned)5;\n    cache_c.set_set_count(4);\n    cache_c.set_block_size(4);\n    QTest::newRow(\"Square\") << cache_c << (unsigned)4 << (unsigned)6;\n}\n\nvoid TestCache::cache() {\n    QFETCH(CacheConfig, cache_c);\n    QFETCH(unsigned, hit);\n    QFETCH(unsigned, miss);\n\n    Memory m(BIG);\n    TrivialBus m_frontend(&m);\n    Cache cache(&m_frontend, &cache_c);\n\n    // Test reads //\n    memory_write_u32(&m, 0x200, 0x24);\n    memory_write_u32(&m, 0x204, 0x66);\n    memory_write_u32(&m, 0x21c, 0x12);\n    memory_write_u32(&m, 0x300, 0x32);\n    for (int i = 0; i < 2; i++) {\n        QCOMPARE(cache.read_u32(0x200_addr), (uint32_t)0x24);\n        QCOMPARE(cache.read_u32(0x204_addr), (uint32_t)0x66);\n        QCOMPARE(cache.read_u32(0x21c_addr), (uint32_t)0x12);\n        QCOMPARE(cache.read_u32(0x300_addr), (uint32_t)0x32);\n    }\n\n    // Test writes //\n    cache.write_u32(0x700_addr, 0x24);\n    QCOMPARE(memory_read_u32(&m, 0x700), (uint32_t)0x24);\n    cache.write_u32(0x700_addr, 0x23);\n    QCOMPARE(memory_read_u32(&m, 0x700), (uint32_t)0x23);\n\n    // Verify counts\n    QCOMPARE(cache.get_hit_count(), hit);\n    QCOMPARE(cache.get_miss_count(), miss);\n}\n\nvoid TestCache::cache_correctness_data() {\n    QTest::addColumn<Endian>(\"endian\");\n    QTest::addColumn<Address>(\"address\");\n    QTest::addColumn<size_t>(\"stride\");\n    QTest::addColumn<IntegerDecomposition>(\"value\");\n    QTest::addColumn<IntegerDecomposition>(\"result\");\n    QTest::addColumn<CacheConfig>(\"cache_config\");\n    // Used for cache hit/miss performance checking.\n    QTest::addColumn<std::size_t>(\"case_number\");\n\n    size_t i = 0;\n    for (auto cache_config : get_testing_cache_configs()) {\n        for (auto endian : simulated_endians) {\n            for (auto address : accessed_addresses) {\n                for (auto stride : strides) {\n                    for (auto value : values) {\n                        // Result when read has `stride` bytes offset to the\n                        // write.\n                        auto result_value\n                            = (endian == BIG) ? (value << (stride * 8)) : (value >> (stride * 8));\n                        QTest::addRow(\n                            \"endian=%s, address=0x%\" PRIx64 \", stride=%ld, \"\n                            \"value=0x%\" PRIx64 \", cache_config={ %d, \"\n                            \"r=%d, wr=%d, s=%d, b=%d, a=%d }\",\n                            to_string(endian), address, stride, value, cache_config.enabled(),\n                            cache_config.replacement_policy(), cache_config.write_policy(),\n                            cache_config.set_count(), cache_config.block_size(),\n                            cache_config.associativity())\n                            << endian << Address(address) << stride\n                            << IntegerDecomposition(value, endian)\n                            << IntegerDecomposition(result_value, endian) << cache_config << i;\n                        i += 1;\n                    }\n                }\n            }\n        }\n    }\n}\n\nvoid TestCache::cache_correctness() {\n    QFETCH(Endian, endian);\n    QFETCH(Address, address);\n    QFETCH(size_t, stride);\n    QFETCH(IntegerDecomposition, value);\n    QFETCH(IntegerDecomposition, result);\n    QFETCH(CacheConfig, cache_config);\n    QFETCH(size_t, case_number);\n\n    // Prepare memory hierarchy\n    Memory mem(endian);\n    MemoryDataBus bus(endian);\n    bus.insert_device_to_range(&mem, 0_addr, 0xffffffff_addr, false);\n    Cache cache(&bus, &cache_config);\n\n    // Uninitialized memory should read as zero\n    QCOMPARE(cache.read_u8(address + stride), (uint8_t)0);\n    QCOMPARE(cache.read_u16(address + stride), (uint16_t)0);\n    QCOMPARE(cache.read_u32(address + stride), (uint32_t)0);\n    QCOMPARE(cache.read_u64(address + stride), (uint64_t)0);\n\n    // Writes value and reads it in all possible ways (by parts).\n\n    // It is sufficient to test reads with stride to write of largest write. For\n    // smaller writes, correct result is much harder to prepare.\n    if (stride == 0) {\n        cache.write_u8(address, value.u8.at(0));\n        QCOMPARE(cache.read_u8(address), result.u8.at(0));\n\n        cache.write_u16(address, value.u16.at(0));\n        QCOMPARE(cache.read_u16(address), result.u16.at(0));\n        for (size_t i = 0; i < 2; ++i) {\n            QCOMPARE(cache.read_u8(address + i), result.u8.at(i));\n        }\n\n        cache.write_u32(address, value.u32.at(0));\n        QCOMPARE(cache.read_u32(address), result.u32.at(0));\n        for (size_t i = 0; i < 2; ++i) {\n            QCOMPARE(cache.read_u16(address + 2 * i), result.u16.at(i));\n        }\n        for (size_t i = 0; i < 4; ++i) {\n            QCOMPARE(cache.read_u8(address + i), result.u8.at(i));\n        }\n    }\n\n    cache.write_u64(address, value.u64);\n    QCOMPARE(cache.read_u64(address + stride), result.u64);\n    for (size_t i = 0; i < 2; ++i) {\n        QCOMPARE(cache.read_u32(address + stride + 4 * i), result.u32.at(i));\n    }\n    for (size_t i = 0; i < 4; ++i) {\n        QCOMPARE(cache.read_u16(address + stride + 2 * i), result.u16.at(i));\n    }\n    for (size_t i = 0; i < 8; ++i) {\n        QCOMPARE(cache.read_u8(address + stride + i), result.u8.at(i));\n    }\n\n    tuple<unsigned, unsigned> performance { cache.get_hit_count(), cache.get_miss_count() };\n\n    // CODE USED FOR PERFORMANCE DATA GENERATION\n    //        fprintf(\n    //            stderr, \"{ %d, %d }, \", cache.get_hit_count(),\n    //            cache.get_miss_count());\n\n    if ((cache_config.replacement_policy() != CacheConfig::RP_RAND)\n        && (cache_config.replacement_policy() != CacheConfig::RP_NMRU)) {\n        // Performance of random policy is implementation dependant and\n        // meaningless.\n        QCOMPARE(performance, cache_test_performance_data.at(case_number));\n    }\n}\n\nQTEST_APPLESS_MAIN(TestCache)\n"
  },
  {
    "path": "src/machine/memory/cache/cache.test.h",
    "content": "#ifndef CACHE_TEST_H\n#define CACHE_TEST_H\n\n#include <QtTest>\n\nclass TestCache : public QObject {\n    Q_OBJECT\nprivate slots:\n    static void cache_data();\n    static void cache();\n    static void cache_correctness_data();\n    static void cache_correctness();\n};\n\n#endif // CACHE_TEST_H\n"
  },
  {
    "path": "src/machine/memory/cache/cache_policy.cpp",
    "content": "#include \"cache_policy.h\"\n\n#include \"simulator_exception.h\"\n#include \"utils.h\"\n\n#include <cmath>\n#include <cstddef>\n\nnamespace machine {\n\nstd::unique_ptr<CachePolicy> CachePolicy::get_policy_instance(const CacheConfig *config) {\n    if (config->enabled()) {\n        switch (config->replacement_policy()) {\n        case CacheConfig::RP_RAND:\n            return std::make_unique<CachePolicyRAND>(config->associativity());\n        case CacheConfig::RP_LRU:\n            return std::make_unique<CachePolicyLRU>(config->associativity(), config->set_count());\n        case CacheConfig::RP_LFU:\n            return std::make_unique<CachePolicyLFU>(config->associativity(), config->set_count());\n        case CacheConfig::RP_PLRU:\n            return std::make_unique<CachePolicyPLRU>(config->associativity(), config->set_count());\n        case CacheConfig::RP_NMRU:\n            return std::make_unique<CachePolicyNMRU>(config->associativity(), config->set_count());\n        }\n    } else {\n        // Disabled cache will never use it.\n        return { nullptr };\n    }\n\n    Q_UNREACHABLE();\n}\n\nCachePolicyLRU::CachePolicyLRU(size_t associativity, size_t set_count)\n    : associativity(associativity) {\n    stats.resize(set_count);\n    for (auto &row : stats) {\n        row.reserve(associativity);\n        for (size_t i = 0; i < associativity; i++) {\n            row.push_back(i);\n        }\n    }\n}\n\nvoid CachePolicyLRU::update_stats(size_t way, size_t row, bool is_valid) {\n    // The following code is essentially a single pass of bubble sort (with\n    // temporary variable instead of inplace swapping) adding one element to\n    // back or front (respectively) of a sorted array. The sort stops, when the\n    // original location of the inserted element is reached and the original\n    // instance is moved to the temporary variable (`next_way`).\n\n    try {\n        // Statistics corresponding to single cache row\n        std::vector<uint32_t> &row_stats = stats.at(row);\n        uint32_t next_way = way;\n\n        if (is_valid) {\n            ptrdiff_t i = (ptrdiff_t)associativity - 1;\n            do {\n                std::swap(row_stats.at(i), next_way);\n                i--;\n            } while (next_way != way);\n        } else {\n            size_t i = 0;\n            do {\n                std::swap(row_stats.at(i), next_way);\n                i++;\n            } while (next_way != way);\n        }\n    } catch (std::out_of_range &e) {\n        throw SANITY_EXCEPTION(\"Out of range: LRU lost the way from priority queue.\");\n    }\n}\n\nsize_t CachePolicyLRU::select_way_to_evict(size_t row) const {\n    return stats.at(row).at(0);\n}\n\nCachePolicyLFU::CachePolicyLFU(size_t associativity, size_t set_count) {\n    stats.resize(set_count, std::vector<uint32_t>(associativity, 0));\n}\n\nvoid CachePolicyLFU::update_stats(size_t way, size_t row, bool is_valid) {\n    auto &stat_item = stats.at(row).at(way);\n\n    if (is_valid) {\n        stat_item += 1;\n    } else {\n        stat_item = 0;\n    }\n}\n\nsize_t CachePolicyLFU::select_way_to_evict(size_t row) const {\n    size_t index = 0;\n    try {\n        // Statistics corresponding to single cache row\n        auto &row_stats = stats.at(row);\n        size_t lowest = row_stats.at(0);\n        for (size_t i = 0; i < row_stats.size(); i++) {\n            if (row_stats.at(i) == 0) {\n                // Only invalid blocks have zero stat\n                index = i;\n                break;\n            }\n            if (lowest > row_stats.at(i)) {\n                lowest = row_stats.at(i);\n                index = i;\n            }\n        }\n    } catch (std::out_of_range &e) {\n        throw SANITY_EXCEPTION(\"Out of range: LRU lost the way from priority queue.\");\n    }\n    return index;\n}\n\nCachePolicyRAND::CachePolicyRAND(size_t associativity) : associativity(associativity) {\n    // Reset random generator to make result reproducible.\n    // Random is by default seeded by 1 (by cpp standard), so this makes it\n    // consistent across multiple runs.\n    // NOTE: Reproducibility applies only on the same execution environment.\n    std::srand(1); // NOLINT(cert-msc51-cpp)\n}\n\nvoid CachePolicyRAND::update_stats(size_t way, size_t row, bool is_valid) {\n    UNUSED(way) UNUSED(row) UNUSED(is_valid)\n    // NOP\n}\n\nsize_t CachePolicyRAND::select_way_to_evict(size_t row) const {\n    UNUSED(row)\n    return std::rand() % associativity; // NOLINT(cert-msc50-cpp)\n}\n\nCachePolicyPLRU::CachePolicyPLRU(size_t associativity, size_t set_count)\n    : associativity(associativity)\n    , associativityCLog2(std::ceil(log2((float)associativity))) {\n    plru_ptr.resize(set_count);\n    for (auto &row : plru_ptr) {\n        row.resize((1 << associativityCLog2) - 1, 0); // Initially point to block 0\n    }\n}\n\nvoid CachePolicyPLRU::update_stats(size_t way, size_t row, bool is_valid) {\n    UNUSED(is_valid)\n    // PLRU use a set of binary tree structured pointers to keep track of\n    // the least recently used block, the number of pointers for each\n    // row is 2^associativityCLog2 - 1, where associativityCLog2 is the ceil\n    // log2 of associativity, e.x. 1 + 2 + 4 for cache with associativity from\n    // 5 to 8. When doing state update, we have to access one pointer in each\n    // level, therefore we have to ues an index (plru_idx) to track the currently accessing pointer\n    uint32_t plru_idx = 0;            // Index of pointer\n    auto &row_ptr = plru_ptr.at(row); // Pointer of accessed row\n    for (uint32_t i = 0; i < associativityCLog2; i++) {\n        // Toggle the pointer to another direction\n        row_ptr[plru_idx] = ((way >> (associativityCLog2 - 1 - i)) & 1) ? 0 : 1;\n        plru_idx = (1 << (i + 1)) - 1;\n        plru_idx += way >> (associativityCLog2 - 1 - i);\n    }\n}\n\nsize_t CachePolicyPLRU::select_way_to_evict(size_t row) const {\n    uint32_t idx = 0;\n    uint32_t plru_idx = 0;\n    auto &row_ptr = plru_ptr.at(row);\n    for (uint32_t i = 0; i < associativityCLog2; i++) {\n        idx <<= 1;\n        uint32_t ptr = row_ptr[plru_idx];\n        idx += ptr;\n        plru_idx = (1 << (i + 1)) - 1;\n        plru_idx += idx;\n    }\n    return (idx >= associativity) ? (associativity - 1) : idx;\n}\n\nCachePolicyNMRU::CachePolicyNMRU(size_t associativity, size_t set_count)\n    : associativity(associativity) {\n    mru_ptr.resize(set_count);\n    for (auto &row : mru_ptr) {\n        row = 0; // Initially point to block 0\n    }\n    std::srand(1); // NOLINT(cert-msc51-cpp)\n}\n\nvoid CachePolicyNMRU::update_stats(size_t way, size_t row, bool is_valid) {\n    UNUSED(is_valid)\n    auto &row_ptr = mru_ptr.at(row); // Set currently accessed block to most recently used\n    row_ptr = way;\n}\n\nsize_t CachePolicyNMRU::select_way_to_evict(size_t row) const {\n    if (associativity == 1) { return 0; }\n    uint32_t idx = std::rand() % (associativity - 1);\n    auto &row_ptr = mru_ptr.at(row);\n    idx = (idx < row_ptr) ? idx : idx + 1;\n    return idx;\n}\n} // namespace machine\n"
  },
  {
    "path": "src/machine/memory/cache/cache_policy.h",
    "content": "#ifndef CACHE_POLICY_H\n#define CACHE_POLICY_H\n\n#include \"machineconfig.h\"\n#include \"memory/cache/cache_types.h\"\n\n#include <cstdint>\n#include <cstdlib>\n#include <memory>\n\nusing std::size_t;\n\nnamespace machine {\n\n/**\n * Cache replacement policy interface.\n *\n * For clarification of cache terminiology, see docstring of `Cache` in\n * `memory/cache/cache.h`.\n */\nclass CachePolicy {\npublic:\n    [[nodiscard]] virtual size_t select_way_to_evict(size_t row) const = 0;\n\n    /**\n     * To be called by cache on any change of validity.\n     * @param way           associativity way\n     * @param row           cache row (index of block/set)\n     * @param is_valid      is cache data valid (as in `cd.valid`)\n     */\n    virtual void update_stats(size_t way, size_t row, bool is_valid) = 0;\n\n    virtual ~CachePolicy() = default;\n\n    static std::unique_ptr<CachePolicy> get_policy_instance(const CacheConfig *config);\n};\n\n/**\n * Last recently used policy\n *\n *  Keeps queue of associativity indexes from last accessed\n *   to most recently accesed.\n *  Empty indexes are shifted to the begining.\n */\nclass CachePolicyLRU final : public CachePolicy {\npublic:\n    /**\n     * @param associativity     degree of assiciaivity\n     * @param set_count         number of blocks / rows in a way (or sets in\n     * cache)\n     */\n    CachePolicyLRU(size_t associativity, size_t set_count);\n\n    [[nodiscard]] size_t select_way_to_evict(size_t row) const final;\n\n    void update_stats(size_t way, size_t row, bool is_valid) final;\n\nprivate:\n    /**\n     * Last access order queues for each cache set (row)\n     */\n    std::vector<std::vector<uint32_t>> stats;\n    const size_t associativity;\n};\n\n/**\n * Least frequently used policy\n *\n *  Keeps statistics of access count for each associativity index.\n *  Resets statistics when set(row) is evicted.\n */\nclass CachePolicyLFU final : public CachePolicy {\npublic:\n    /**\n     * @param associativity     degree of assiciaivity\n     * @param set_count         number of blocks / rows is way (or sets in\n     * cache)\n     */\n    CachePolicyLFU(size_t associativity, size_t set_count);\n\n    [[nodiscard]] size_t select_way_to_evict(size_t row) const final;\n\n    void update_stats(size_t way, size_t row, bool is_valid) final;\n\nprivate:\n    std::vector<std::vector<uint32_t>> stats;\n};\n\nclass CachePolicyRAND final : public CachePolicy {\npublic:\n    /**\n     * @param associativity     degree of associativity\n     */\n    explicit CachePolicyRAND(size_t associativity);\n\n    [[nodiscard]] size_t select_way_to_evict(size_t row) const final;\n\n    void update_stats(size_t way, size_t row, bool is_valid) final;\n\nprivate:\n    size_t associativity;\n};\n\n/**\n * Pseudo Last recently used policy\n *\n * Hardware-Friendly LRU Implementation\n */\nclass CachePolicyPLRU final : public CachePolicy {\npublic:\n    /**\n     * @param associativity     degree of assiciaivity\n     * @param set_count         number of blocks / rows in a way (or sets in\n     * cache)\n     */\n    CachePolicyPLRU(size_t associativity, size_t set_count);\n\n    [[nodiscard]] size_t select_way_to_evict(size_t row) const final;\n\n    void update_stats(size_t way, size_t row, bool is_valid) final;\n\nprivate:\n    /**\n     * Pointer to Least Recently Used Block\n     */\n    std::vector<std::vector<uint32_t>> plru_ptr;\n    const size_t associativity;\n    const size_t associativityCLog2;\n};\n\n/**\n * Not Most Recently Used\n *\n * Select Randomly from Not Most\n * Recently Used Blocks\n */\nclass CachePolicyNMRU final : public CachePolicy {\npublic:\n    /**\n     * @param associativity     degree of assiciaivity\n     * @param set_count         number of blocks / rows in a way (or sets in\n     * cache)\n     */\n    CachePolicyNMRU(size_t associativity, size_t set_count);\n\n    [[nodiscard]] size_t select_way_to_evict(size_t row) const final;\n\n    void update_stats(size_t way, size_t row, bool is_valid) final;\n\nprivate:\n    /**\n     * Pointer to Most Recently Used Block\n     */\n    std::vector<uint32_t> mru_ptr;\n    const size_t associativity;\n};\n} // namespace machine\n\n#endif // CACHE_POLICY_H\n"
  },
  {
    "path": "src/machine/memory/cache/cache_types.h",
    "content": "#ifndef CACHE_TYPES_H\n#define CACHE_TYPES_H\n\n#include <cstdint>\n\nnamespace machine {\n\n/**\n * Determiners location of address in single way of cache. This mean, where\n * given addresses should be stored, if present.\n */\nstruct CacheLocation {\n    uint64_t row;\n    uint64_t col;\n    uint64_t tag;\n    uint64_t byte;\n};\n\n/**\n * Single cache line. Appropriate cache block is stored in `data`.\n */\nstruct CacheLine {\n    bool valid, dirty;\n    uint64_t tag;\n    std::vector<uint32_t> data;\n};\n\n/**\n * This is preferred over bool (write = true|false) for better readability.\n */\nenum AccessType { READ, WRITE };\n\ninline const char *to_string(AccessType a) {\n    switch (a) {\n    case READ: return \"READ\";\n    case WRITE: return \"WRITE\";\n    }\n}\n\n} // namespace machine\n\n#endif // CACHE_TYPES_H\n"
  },
  {
    "path": "src/machine/memory/frontend_memory.cpp",
    "content": "#include \"memory/frontend_memory.h\"\n\n#include \"common/endian.h\"\n#include \"tlb/tlb.h\"\n\nnamespace machine {\n\nbool FrontendMemory::write_u8(AddressWithMode address, uint8_t value, AccessEffects type) {\n    return write_generic<typeof(value)>(address, value, type);\n}\n\nbool FrontendMemory::write_u16(AddressWithMode address, uint16_t value, AccessEffects type) {\n    return write_generic<typeof(value)>(address, value, type);\n}\n\nbool FrontendMemory::write_u32(AddressWithMode address, uint32_t value, AccessEffects type) {\n    return write_generic<typeof(value)>(address, value, type);\n}\n\nbool FrontendMemory::write_u64(AddressWithMode address, uint64_t value, AccessEffects type) {\n    return write_generic<typeof(value)>(address, value, type);\n}\n\nuint8_t FrontendMemory::read_u8(AddressWithMode address, AccessEffects type) const {\n    return read_generic<uint8_t>(address, type);\n}\n\nuint16_t FrontendMemory::read_u16(AddressWithMode address, AccessEffects type) const {\n    return read_generic<uint16_t>(address, type);\n}\n\nuint32_t FrontendMemory::read_u32(AddressWithMode address, AccessEffects type) const {\n    return read_generic<uint32_t>(address, type);\n}\n\nuint64_t FrontendMemory::read_u64(AddressWithMode address, AccessEffects type) const {\n    return read_generic<uint64_t>(address, type);\n}\n\nvoid FrontendMemory::write_ctl(enum AccessControl ctl, AddressWithMode offset, RegisterValue value) {\n    switch (ctl) {\n    case AC_NONE: {\n        break;\n    }\n    case AC_I8:\n    case AC_U8: {\n        write_u8(offset, value.as_u8());\n        break;\n    }\n    case AC_I16:\n    case AC_U16: {\n        write_u16(offset, value.as_u16());\n        break;\n    }\n    case AC_I32:\n    case AC_U32: {\n        write_u32(offset, value.as_u32());\n        break;\n    }\n    case AC_I64:\n    case AC_U64: {\n        write_u64(offset, value.as_u64());\n        break;\n    }\n    default: {\n        throw SIMULATOR_EXCEPTION(\n            UnknownMemoryControl, \"Trying to write to memory with unknown ctl\",\n            QString::number(ctl));\n    }\n    }\n}\n\nRegisterValue FrontendMemory::read_ctl(enum AccessControl ctl, AddressWithMode address) const {\n    switch (ctl) {\n    case AC_NONE: return 0;\n    case AC_I8: return (int8_t)read_u8(address);\n    case AC_U8: return read_u8(address);\n    case AC_I16: return (int16_t)read_u16(address);\n    case AC_U16: return read_u16(address);\n    case AC_I32: return (int32_t)read_u32(address);\n    case AC_U32: return read_u32(address);\n    case AC_I64: return (int64_t)read_u64(address);\n    case AC_U64: return read_u64(address);\n    default: {\n        throw SIMULATOR_EXCEPTION(\n            UnknownMemoryControl, \"Trying to read from memory with unknown ctl\",\n            QString::number(ctl));\n    }\n    }\n}\n\nvoid FrontendMemory::sync() {}\n\nLocationStatus FrontendMemory::location_status(Address address) const {\n    (void)address;\n    return LOCSTAT_NONE;\n}\n\ntemplate<typename T>\nT FrontendMemory::read_generic(AddressWithMode address, AccessEffects type) const {\n    T value;\n    read(&value, address, sizeof(T), { .type = type });\n    // When cross-simulating (BIG simulator on LITTLE host machine and vice\n    // versa) data needs to be swapped before writing to memory and after\n    // reading from memory to achieve correct results of misaligned reads. See\n    // bellow.\n    //\n    // Example (4 byte write and 4 byte read offseted by 2 bytes):\n    //\n    //  BIG on LITTLE\n    //      REGISTER:           12 34 56 78\n    //      PRE-SWAP:           78 56 34 12 (still in register)\n    //      NATIVE ENDIAN MEM:  12 34 56 78 00 00 (native is LITTLE)\n    //      READ IN MEM:              56 78 00 00\n    //      REGISTER:                 00 00 78 56\n    //      POST-SWAP:                56 78 00 00 (correct)\n    //\n    //  LITTLE on BIG\n    //      REGISTER:          12 34 56 78\n    //      PRE-SWAP:          78 56 34 12  (still in register)\n    //      NATIVE ENDIAN MEM: 78 56 34 12 00 00 (native is BIG)\n    //      READ IN MEM:             34 12 00 00\n    //      REGISTER:                34 12 00 00\n    //      POST-SWAP:               00 00 12 34 (correct)\n    //\n    return byteswap_if(value, this->simulated_machine_endian != NATIVE_ENDIAN);\n}\n\ntemplate<typename T>\nbool FrontendMemory::write_generic(AddressWithMode address, const T value, AccessEffects type) {\n    // See example in read_generic for byteswap explanation.\n    const T swapped_value = byteswap_if(value, this->simulated_machine_endian != NATIVE_ENDIAN);\n    return write(address, &swapped_value, sizeof(T), { .type = type }).changed;\n}\nFrontendMemory::FrontendMemory(Endian simulated_endian)\n    : simulated_machine_endian(simulated_endian) {}\n} // namespace machine\n"
  },
  {
    "path": "src/machine/memory/frontend_memory.h",
    "content": "#ifndef FRONTEND_MEMORY_H\n#define FRONTEND_MEMORY_H\n\n#include \"common/endian.h\"\n#include \"csr/address.h\"\n#include \"machinedefs.h\"\n#include \"memory/address.h\"\n#include \"memory/memory_utils.h\"\n#include \"register_value.h\"\n#include \"simulator_exception.h\"\n\n#include <QObject>\n#include <cstdint>\n\n#include \"address_with_mode.h\"\n\n// Shortcut for enum class values, type is obvious from context.\nusing ae = machine::AccessEffects;\n\nnamespace machine {\n\n/**\n * # What is frontend memory\n *\n * The core is the entry point to the whole simulated machine. Therefore the\n * reworked concepts can be best understood from its point of view on the memory\n * subsystem. Every core configuration has three mandatory memory components.\n * Two of them  are the entry points for memory operations for program and data,\n * respectively. The entry point starts a chain of memory components of\n * arbitrary length, each backed by the next one. A component that receives a\n * request either resolves it or invokes its lower component. For the core, the\n * chain is invisible, and it only interfaces with the top-level component. The\n * chain is terminated by a memory data bus. Data bus for data memory is the\n * third component I have mentioned.  All of the above components are instances\n * of the frontend memory. Frontend memory has three characteristics. Except for\n * the memory bus, each frontend component must have a frontend component\n * backing it. The memory bus is backed by zero or more backed devices\n * (explained later). All addresses used in the frontend are full memory\n * address. By the word \"full\", I mean the numerical value observable by a C\n * program (up to address translation), in contrast to a local relative offset.\n * The Address datatype is used to store it and pass as an argument.\n * Finally, frontend memory instances are likely those found on the CPU chip\n * itself (caches, TLB...).\n */\nclass FrontendMemory : public QObject {\n    Q_OBJECT\npublic:\n    /**\n     * It is necessary to know the endian of the whole simulation in the\n     * entrypoint on the memory subsystem, which might be any instance of\n     * frontend memory.\n     *\n     * @param simulated_endian  endian of the simulated CPU/memory subsystem\n     */\n    explicit FrontendMemory(Endian simulated_endian);\n\n    bool write_u8(AddressWithMode address, uint8_t value, AccessEffects type = ae::REGULAR);\n    bool write_u16(AddressWithMode address, uint16_t value, AccessEffects type = ae::REGULAR);\n    bool write_u32(AddressWithMode address, uint32_t value, AccessEffects type = ae::REGULAR);\n    bool write_u64(AddressWithMode address, uint64_t value, AccessEffects type = ae::REGULAR);\n\n    [[nodiscard]] uint8_t read_u8(AddressWithMode address, AccessEffects type = ae::REGULAR) const;\n    [[nodiscard]] uint16_t read_u16(AddressWithMode address, AccessEffects type = ae::REGULAR) const;\n    [[nodiscard]] uint32_t read_u32(AddressWithMode address, AccessEffects type = ae::REGULAR) const;\n    [[nodiscard]] uint64_t read_u64(AddressWithMode address, AccessEffects type = ae::REGULAR) const;\n\n    /**\n     * Store with size specified by the CPU control unit.\n     *\n     * This is for CPU core only and the AccessEffects type is implicitly\n     * REGULAR.\n     * @param control_signal    CPU control unit signal\n     */\n    void write_ctl(AccessControl control_signal, AddressWithMode destination, RegisterValue value);\n\n    /**\n     * Read with size specified by the CPU control unit.\n     *\n     * This is for CPU core only and the AccessEffects type is implicitly\n     * ae::REGULAR.\n     * @param control_signal    CPU control unit signal\n     */\n    [[nodiscard]] RegisterValue read_ctl(enum AccessControl control_signal, AddressWithMode source) const;\n\n    virtual void sync();\n    [[nodiscard]] virtual LocationStatus location_status(Address address) const;\n    [[nodiscard]] virtual uint32_t get_change_counter() const = 0;\n    /**\n     * Write byte sequence to memory\n     *\n     * @param source        pointer to array of bytes to be copied\n     * @param destination   emulated address of destination to write to\n     * @param size         number of bytes to be written\n     * @return              true when memory before and after write differs\n     */\n    virtual WriteResult\n    write(AddressWithMode destination, const void *source, size_t size, WriteOptions options)\n        = 0;\n\n    /**\n     * Read sequence of bytes from memory\n     *\n     * @param source        emulated address of data to be read\n     * @param destination   pointer to destination buffer\n     * @param size          number of bytes to be read\n     * @param options       additional option like debug mode, see type\n     *                      definition\n     */\n    virtual ReadResult\n    read(void *destination, AddressWithMode source, size_t size, ReadOptions options) const\n        = 0;\n\n    /**\n     * Endian of the simulated CPU/memory system.\n     *\n     * Used when frontend_memory subclass instance is used as entrypoint to the\n     * memory system (e.g. `read_XX` and `write_XX` functions.).\n     * @see `convert_endian` in `memory/endian.h`.\n     */\n    const Endian simulated_machine_endian;\n\nsignals:\n    /**\n     * Signal used to propagate a change up through the hierarchy.\n     */\n    void external_change_notify(\n        const FrontendMemory *issuing_memory,\n        Address start_addr,\n        Address last_addr,\n        AccessEffects type) const;\n\nprivate:\n    /**\n     * Read any type from memory\n     *\n     * This function was introduced to make stupid errors\n     *  like mismatched type and and its size impossible.\n     * It provides nicer interface than `read`.\n     *  by eliminating the need for buffer.\n     *\n     * @tparam T            type value should be read as\n     * @param address       emulated address to read from\n     * @param type          read by visualization etc (type ae::INTERNAL) should\n     *                      not cause certain effects (counter increments...)\n     * @return              requested data with type T\n     */\n    template<typename T>\n    T read_generic(AddressWithMode address, AccessEffects type) const;\n\n    /**\n     * Write to any type from memory\n     *\n     * This function was introduced to make stupid errors like mismatched\n     * type and and its size impossible. It provides nicer interface than\n     * `write` by eliminating the need for buffer.\n     *\n     * @tparam T            type of value to be written\n     * @param address       emulated address to write to\n     * @param value         value of type T to be written\n     * @param type          read by visualization etc (type ae::INTERNAL).\n     * should not cause certain effects (counter increments...)\n     * @return              true when memory before and after write differs\n     */\n    template<typename T>\n    bool write_generic(AddressWithMode address, T value, AccessEffects type);\n};\n\n} // namespace machine\n\n#endif // FRONTEND_MEMORY_H\n"
  },
  {
    "path": "src/machine/memory/memory_bus.cpp",
    "content": "#include \"memory/memory_bus.h\"\n\n#include \"common/endian.h\"\n#include \"memory/memory_utils.h\"\n\nusing namespace machine;\n\nMemoryDataBus::MemoryDataBus(Endian simulated_endian) : FrontendMemory(simulated_endian) {};\n\nMemoryDataBus::~MemoryDataBus() {\n    ranges_by_addr.clear(); // No stored values are owned.\n    auto iter = ranges_by_device.begin();\n    while (iter != ranges_by_device.end()) {\n        const RangeDesc *range = iter.value();\n        iter = ranges_by_device.erase(iter); // Advances the iterator.\n        if (range->owns_device) { delete range->device; }\n        delete range;\n    }\n}\n\nWriteResult\nMemoryDataBus::write(AddressWithMode destination, const void *source, size_t size, WriteOptions options) {\n    return repeat_access_until_completed<WriteResult>(\n        destination, source, size, options,\n        [this](Address dst, const void *src, size_t s, WriteOptions opt) -> WriteResult {\n            return write_single(dst, src, s, opt);\n        });\n}\n\nWriteResult MemoryDataBus::write_single(\n    Address destination,\n    const void *source,\n    size_t size,\n    WriteOptions options) {\n    const RangeDesc *range = find_range(Address(destination));\n    if (range == nullptr) {\n        // Write to unused address range - no devices it present.\n        // This could produce a fault but for simplicity, we will\n        // just ignore the write.\n        return (WriteResult) { .n_bytes = 0, .changed = false };\n    }\n    WriteResult result\n        = range->device->write(destination - range->start_addr, source, size, options);\n\n    if (result.changed) { change_counter++; }\n\n    return result;\n}\n\nReadResult\nMemoryDataBus::read(void *destination, AddressWithMode source, size_t size, ReadOptions options) const {\n    return repeat_access_until_completed<ReadResult>(\n        destination, source, size, options,\n        [this](void *dst, Address src, size_t s, ReadOptions opt) -> ReadResult {\n            return read_single(dst, src, s, opt);\n        });\n}\n\nReadResult MemoryDataBus::read_single(\n    void *destination,\n    Address source,\n    size_t size,\n    ReadOptions options) const {\n    const RangeDesc *p_range = find_range(Address(source));\n    if (p_range == nullptr) {\n        // Write to unused address range, no devices it present.\n        // This could produce a fault but for simplicity, we will\n        // consider unused ranges to be constantly zero.\n        memset(destination, 0, size);\n        return (ReadResult) { .n_bytes = size };\n    }\n\n    return p_range->device->read(destination, source - p_range->start_addr, size, options);\n}\n\nuint32_t MemoryDataBus::get_change_counter() const {\n    return change_counter;\n}\n\nenum LocationStatus MemoryDataBus::location_status(Address address) const {\n    const RangeDesc *range = find_range(address);\n    if (range == nullptr) { return LOCSTAT_ILLEGAL; }\n    return range->device->location_status(address - range->start_addr);\n}\n\nconst MemoryDataBus::RangeDesc *MemoryDataBus::find_range(Address address) const {\n    // lowerBound finds range what has highest key (which is range->last_addr)\n    // less then or equal to address.\n    // See comment in insert_device_to_range for description, why this works.\n    auto iter = ranges_by_addr.lowerBound(address);\n    if (iter == ranges_by_addr.end()) { return nullptr; }\n\n    const RangeDesc *range = iter.value();\n    if (address >= range->start_addr && address <= range->last_addr) { return range; }\n\n    return nullptr;\n}\n\nbool MemoryDataBus::insert_device_to_range(\n    BackendMemory *device,\n    Address start_addr,\n    Address last_addr,\n    bool move_ownership) {\n    auto iter = ranges_by_addr.lowerBound(start_addr);\n    if (iter != ranges_by_addr.end() && iter.value()->overlaps(start_addr, last_addr)) {\n        // Some part of requested range in already taken.\n        return false;\n    }\n    auto *range = new RangeDesc(device, start_addr, last_addr, move_ownership);\n\n    // Why are we using last address as key?\n    //\n    // QMap can return greatest lower key (lowerBound), so by indexing by last\n    // address we can simply search any address within range. If searched\n    // address is in given range, it is larger the previous range last address\n    // and smaller or equal than the last address of its. This way we find the\n    // last address of desired range in QMap red black tree and retrieve the\n    // rang. Finally we just make sure, that the found range contains the\n    // searched address for case that range is not present.\n    ranges_by_addr.insert(last_addr, range);\n    ranges_by_device.insert(device, range);\n    connect(\n        device, &BackendMemory::external_backend_change_notify, this,\n        &MemoryDataBus::range_backend_external_change);\n    return true;\n}\n\nbool MemoryDataBus::remove_device(BackendMemory *device) {\n    const RangeDesc *range = ranges_by_device.take(device);\n    if (range == nullptr) {\n        return false; // Device not present.\n    }\n\n    ranges_by_addr.remove(range->last_addr);\n    if (range->owns_device) { delete range->device; }\n    delete range;\n\n    return true;\n}\n\nvoid MemoryDataBus::clean_range(Address start_addr, Address last_addr) {\n    for (auto iter = ranges_by_addr.lowerBound(start_addr); iter != ranges_by_addr.end(); iter++) {\n        const RangeDesc *range = iter.value();\n        if (range->start_addr <= last_addr) {\n            remove_device(range->device);\n        } else {\n            break;\n        }\n    }\n}\n\nvoid MemoryDataBus::range_backend_external_change(\n    const BackendMemory *device,\n    Offset start_offset,\n    Offset last_offset,\n    AccessEffects type) {\n    change_counter++;\n\n    // We only use device here for lookup, so const_cast is safe as find takes\n    // it by const reference .\n    for (auto i = ranges_by_device.find(const_cast<BackendMemory *>(device));\n         i != ranges_by_device.end(); i++) {\n        const RangeDesc *range = i.value();\n        emit external_change_notify(\n            this, range->start_addr + start_offset,\n            std::max(range->start_addr + last_offset, range->last_addr), type);\n    }\n}\n\nMemoryDataBus::RangeDesc::RangeDesc(\n    BackendMemory *device,\n    Address start_addr,\n    Address last_addr,\n    bool owns_device)\n    : device(device)\n    , start_addr(start_addr)\n    , last_addr(last_addr)\n    , owns_device(owns_device) {}\n\nbool MemoryDataBus::RangeDesc::contains(Address address) const {\n    return start_addr <= address && address <= last_addr;\n}\n\nbool MemoryDataBus::RangeDesc::overlaps(Address start, Address last) const {\n    return contains(start) || contains(last);\n}\n\nTrivialBus::TrivialBus(BackendMemory *backend_memory)\n    : FrontendMemory(backend_memory->simulated_machine_endian)\n    , device(backend_memory) {}\n\nWriteResult\nTrivialBus::write(AddressWithMode destination, const void *source, size_t size, WriteOptions options) {\n    change_counter += 1; // Counter is mandatory by the frontend interface.\n    return device->write(destination.get_raw(), source, size, options);\n}\n\nReadResult\nTrivialBus::read(void *destination, AddressWithMode source, size_t size, ReadOptions options) const {\n    return device->read(destination, source.get_raw(), size, options);\n}\n\nuint32_t TrivialBus::get_change_counter() const {\n    return change_counter;\n}\n"
  },
  {
    "path": "src/machine/memory/memory_bus.h",
    "content": "#ifndef MEMORY_BUS_H\n#define MEMORY_BUS_H\n\n#include \"common/endian.h\"\n#include \"machinedefs.h\"\n#include \"memory/backend/backend_memory.h\"\n#include \"memory/frontend_memory.h\"\n#include \"simulator_exception.h\"\n#include \"utils.h\"\n\n#include <QMultiMap>\n#include <QObject>\n#include <cstdint>\n\nnamespace machine {\n\n/**\n * Memory bus serves as last level of frontend memory and interconnects it with\n * backend memory devices, that are subscribed to given address range.\n *\n * Simulated core always has exactly one bus. This is necessary to access it\n * (e.g. from syscall) to map new devices. Backend memory device simulation\n * classes are implemented in `memory/backend`. For testing purposes,\n * `TrivialBus` is provided to wrap backend memory device into a minimal\n * frontend interface. Used mapping is always one to one with identity address\n * resolution. TrivialBus does not support external changes.\n * Backend memory devices subscribe to the bus via range descriptions (see\n * `RangeDecs`). Frontend address (`Address` type) is here converted using the\n * range descriptions to relative offset within the given backend memory device.\n * Downstream (frontend -> backend) communication is performed directly and\n * upstream communication is done via \"external_change\" signals.\n */\nclass MemoryDataBus : public FrontendMemory {\n    Q_OBJECT\n\npublic:\n    /**\n     * @param simulated_endian  endian of the simulated CPU/memory system\n     */\n    explicit MemoryDataBus(Endian simulated_endian);\n\n    ~MemoryDataBus() override;\n\n    /**\n     * Write method that repeats write single (for each affected range) until\n     * whole size is written.\n     *\n     * @see MemoryDataBus::write_single\n     */\n    WriteResult\n    write(AddressWithMode destination, const void *source, size_t size, WriteOptions options) override;\n\n    /**\n     * Read method that repeats write single (for each affected range) until\n     * whole size is read.\n     *\n     * @see MemoryDataBus:read_single\n     */\n    ReadResult\n    read(void *destination, AddressWithMode source, size_t size, ReadOptions options) const override;\n\n    /**\n     * Number of writes and external changes recorded.\n     */\n    uint32_t get_change_counter() const override;\n\n    /**\n     * Connect a backend device to the bus for given address range.\n     *\n     * @param device            simulated backend memory device object\n     * @param start_addr        start of address range, where the device will be\n     *                          accessible\n     * @param last_addr         end of range, devices occupies\n     * @param move_ownership    if true, bus will be responsible for for\n     *                          device destruction\n     *                          TODO: consider replace with a smartpointer\n     * @return                  result of connection, it will fail if range is\n     *                          already occupied\n     */\n    bool insert_device_to_range(\n        BackendMemory *device,\n        Address start_addr,\n        Address last_addr,\n        bool move_ownership);\n\n    /**\n     * Disconnect a device by a pointer to it.\n     * Owned device will be deallocated.\n     *\n     * @param device    simulated backend memory device object\n     * @return          result of disconnection, false if device not present\n     */\n    bool remove_device(BackendMemory *device);\n\n    /**\n     * Disconnect all devices in given range.\n     *\n     *\n     * Owned devices will be deallocated.\n     *\n     * @param start_addr\n     * @param last_addr\n     */\n    void clean_range(Address start_addr, Address last_addr);\n\n    enum LocationStatus location_status(Address address) const override;\n\nprivate slots:\n    /**\n     * Receive external changes in underlying memory devices.\n     */\n    void range_backend_external_change(\n        const BackendMemory *device,\n        Offset start_offset,\n        Offset last_offset,\n        AccessEffects type);\n\nprivate:\n    class RangeDesc; // See declaration bellow;\n    QMultiMap<BackendMemory *, OWNED const RangeDesc *> ranges_by_device;\n    /*\n     * Ranges by address noes not own any value it hold. It can be erased at\n     * once.\n     */\n    QMap<Address, const RangeDesc *> ranges_by_addr;\n    mutable uint32_t change_counter = 0;\n\n    /**\n     * Helper to write into single range. Used by `write`.\n     *\n     * Write spanning multiple ranges will succeed partially and return size,\n     * that was written.\n     * API corresponds to `BackendMemory` interface method `write`.\n     */\n    WriteResult\n    write_single(Address destination, const void *source, size_t size, WriteOptions options);\n\n    /**\n     * Helper to read from single range. Used by `read` from Backend memory\n     * interface.\n     *\n     * Read spanning multiple ranges will succeed partially and return\n     * size, that was written.\n     * API corresponds to `BackendMemory` interface method `read`.\n     */\n    ReadResult\n    read_single(void *destination, Address source, size_t size, ReadOptions options) const;\n\n    /**\n     * Get range (or nullptr) for arbitrary address (not just start or last).\n     */\n    const MemoryDataBus::RangeDesc *find_range(Address address) const;\n};\n\n/**\n * Describes a address range occupied by a single backend memory device.\n */\nclass MemoryDataBus::RangeDesc {\npublic:\n    RangeDesc(BackendMemory *device, Address start_addr, Address last_addr, bool owns_device);\n\n    /**\n     * Tells, whether given address belongs to this range.\n     */\n    [[nodiscard]] bool contains(Address address) const;\n\n    /*\n     * Tells, whether this range (of the RangeDesc) overlaps with supplied\n     * range.\n     */\n    [[nodiscard]] bool overlaps(Address start, Address last) const;\n\n    BackendMemory *const device; // TODO consider a shared pointer\n    const Address start_addr;\n    const Address last_addr;\n    const bool owns_device;\n};\n\n/**\n * Minimal frontend-backend wrapper.\n *\n * Redirects memory requests from frontend to backend one-to-one.\n * Used in test without cache (core only accepts `FrontendMemory`).\n *\n * Do not use in non-test code!\n */\nclass TrivialBus final : public FrontendMemory {\npublic:\n    explicit TrivialBus(BackendMemory *backend_memory);\n\n    WriteResult\n    write(AddressWithMode destination, const void *source, size_t size, WriteOptions options) override;\n\n    ReadResult\n    read(void *destination, AddressWithMode source, size_t size, ReadOptions options) const override;\n\n    uint32_t get_change_counter() const override;\n\nprivate:\n    BackendMemory *const device;\n    mutable uint32_t change_counter = 0;\n};\n\n} // namespace machine\n#endif // MEMORY_BUS_H\n"
  },
  {
    "path": "src/machine/memory/memory_utils.h",
    "content": "#ifndef MEMORY_UTILS_H\n#define MEMORY_UTILS_H\n\n#include \"common/endian.h\"\n#include \"utils.h\"\n\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <functional>\n\nnamespace machine {\n\n/**\n * Determines what effects should memory access cause.\n */\nenum class AccessEffects {\n    REGULAR,        //> All (memory, simulation counters, simulation flags, allocation\n                    // on read miss (write allocation is necessary)). For accessed\n                    // requested by simulated program.\n    INTERNAL,       //> Only memory. Internal access performed for visualization,\n                    // control and debugging.\n    EXTERNAL_ASYNC, //> Used for DMA.\n};\n\n/**\n * Additional options for read operation between memory layers\n *\n *  The purpose for this struct is to make the API easily\n *   extensible.\n */\nstruct ReadOptions {\n    AccessEffects type;\n};\n\n/**\n * Additional options for write operation between memory layers\n *\n *  The purpose for this struct is to make the API easily\n *   extensible.\n */\nstruct WriteOptions {\n    AccessEffects type;\n};\n\nstruct ReadResult {\n    /**\n     * Number of bytes successfully read.\n     *\n     * May be lower than requested size in case partial success\n     *  like page fault.\n     */\n    size_t n_bytes = 0;\n\n    inline ReadResult operator+(const ReadResult &other) const {\n        return {\n            this->n_bytes + other.n_bytes,\n        };\n    }\n\n    inline void operator+=(const ReadResult &other) { this->n_bytes += other.n_bytes; }\n};\n\nstruct WriteResult {\n    /**\n     * Number of bytes successfully read.\n     *\n     * May be lower than requested size in case partial success\n     *  like page fault.\n     */\n    size_t n_bytes = 0;\n\n    /**\n     * Indicates whether write caused change in memory.\n     * Used to reduce UI redraws.\n     */\n    bool changed = false;\n\n    inline WriteResult operator+(const WriteResult &other) const {\n        return {\n            this->n_bytes + other.n_bytes,\n            this->changed || other.changed,\n        };\n    }\n\n    inline void operator+=(const WriteResult &other) {\n        this->n_bytes += other.n_bytes;\n        this->changed |= other.changed;\n    }\n};\n\n/**\n * Perform n-byte read into periphery that only supports u32 access.\n *\n * When converting n-byte memory access into aligned series of discrete\n *  accesses each by u32.\n *\n * Example:\n * Periphery supports write by uint32_t. Access of size 4 targets in the middle\n *  of a uint32_t register. Then this function will return 2, which means that\n *  last 2 bytes of the retrieved data will be used (written to register).\n *\n * @tparam STORAGE_TYPE     a type periphery supports for access\n * @param ptr               pointer-like value used for access\n * @return                  size to be used from aligned access\n */\ntemplate<typename STORAGE_TYPE>\ninline void\npartial_access_parameters(size_t &data_offset, size_t &partial_size, uintptr_t ptr, size_t size) {\n    data_offset = ptr % sizeof(STORAGE_TYPE);\n    partial_size = sizeof(STORAGE_TYPE);\n    partial_size -= data_offset;\n    if (partial_size > size) partial_size = size;\n}\n\n/**\n * Perform n-byte read into periphery that only supports u32 access.\n *\n * @tparam FUNC             function :: size_t -> uint32_t\n * @param src               data offset in periphery\n * @param dst               pointer to write to\n * @param size              bytes to read\n * @param data_getter       function object which return u32 data for given\n */\ntemplate<typename FUNC>\ninline ReadResult read_by_u32(void *dst, size_t src, size_t size, FUNC data_getter) {\n    size_t current_src = src;\n    byte *current_dst = static_cast<byte *>(dst);\n    size_t remaining_size = size;\n\n    do {\n        // For simplicity, this is duplicated in write_by_u32.\n        size_t data_offset = current_src % sizeof(uint32_t);\n        size_t partial_size = std::min(sizeof(uint32_t) - data_offset, remaining_size);\n\n        uint32_t data = data_getter(current_src & ~3u);\n\n        memcpy(current_dst, (byte *)&data + data_offset, partial_size);\n\n        remaining_size -= partial_size;\n        current_src += partial_size;\n        current_dst += partial_size;\n    } while (remaining_size > 0);\n\n    return { .n_bytes = size };\n}\n\ntemplate<typename FUNC>\ninline ReadResult read_by_u16(void *dst, size_t src, size_t size, FUNC data_getter) {\n    size_t current_src = src;\n    byte *current_dst = static_cast<byte *>(dst);\n    size_t remaining_size = size;\n\n    do {\n        // For simplicity, this is duplicated in write_by_u16.\n        size_t data_offset = current_src % sizeof(uint16_t);\n        size_t partial_size = std::min(sizeof(uint16_t) - data_offset, remaining_size);\n\n        uint16_t data = data_getter(current_src & ~1u);\n\n        memcpy(current_dst, (byte *)&data + data_offset, partial_size);\n\n        remaining_size -= partial_size;\n        current_src += partial_size;\n        current_dst += partial_size;\n    } while (remaining_size > 0);\n\n    return { .n_bytes = size };\n}\n\ntemplate<typename FUNC>\ninline ReadResult read_by_u64(void *dst, size_t src, size_t size, FUNC data_getter) {\n    size_t current_src = src;\n    byte *current_dst = static_cast<byte *>(dst);\n    size_t remaining_size = size;\n\n    do {\n        // For simplicity, this is duplicated in write_by_u64.\n        size_t data_offset = current_src % sizeof(uint64_t);\n        size_t partial_size = std::min(sizeof(uint64_t) - data_offset, remaining_size);\n\n        uint64_t data = data_getter(current_src & ~7u);\n\n        memcpy(current_dst, (byte *)&data + data_offset, partial_size);\n\n        remaining_size -= partial_size;\n        current_src += partial_size;\n        current_dst += partial_size;\n    } while (remaining_size > 0);\n\n    return { .n_bytes = size };\n}\n\n/**\n * Perform n-byte write into periphery that only supports u32 access.\n *\n * @see read_by_u32\n *\n * @tparam FUNC1            function :: size_t -> uint32_t\n * @tparam FUNC2            function :: size_t, uint32_t -> bool\n * @param src               data source\n * @param dst               offset in periphery\n * @param size              n bytes\n * @param data_getter       function object which return u32 data for given\n *                           offset\n * @param data_setter       function object which writes an u32 to givem offset\n * @return                  true if write caused a change\n */\n\ntemplate<typename FUNC1, typename FUNC2>\ninline WriteResult\nwrite_by_u32(size_t dst, const void *src, size_t size, FUNC1 data_getter, FUNC2 data_setter) {\n    const byte *current_src = static_cast<const byte *>(src);\n    size_t current_dst = dst;\n    size_t remaining_size = size;\n    bool changed = false;\n\n    do {\n        // For simplicity, this is duplicated in read_by_u32.\n        size_t data_offset = current_dst % sizeof(uint32_t);\n        size_t partial_size = std::min(sizeof(uint32_t) - data_offset, remaining_size);\n        uint32_t data = 0;\n\n        if (partial_size < sizeof(data)) data = data_getter(current_dst & ~3u);\n\n        memcpy((byte *)&data + data_offset, current_src, partial_size);\n\n        changed |= data_setter(current_dst & ~3u, data);\n\n        remaining_size -= partial_size;\n        current_src += partial_size;\n        current_dst += partial_size;\n    } while (remaining_size > 0);\n\n    return { .n_bytes = size, .changed = changed };\n}\n\ntemplate<typename FUNC1, typename FUNC2>\ninline WriteResult\nwrite_by_u16(size_t dst, const void *src, size_t size, FUNC1 data_getter, FUNC2 data_setter) {\n    const byte *current_src = static_cast<const byte *>(src);\n    size_t current_dst = dst;\n    size_t remaining_size = size;\n    bool changed = false;\n\n    do {\n        // For simplicity, this is duplicated in read_by_u16.\n        size_t data_offset = current_dst % sizeof(uint16_t);\n        size_t partial_size = std::min(sizeof(uint16_t) - data_offset, remaining_size);\n        uint16_t data = 0;\n\n        if (partial_size < sizeof(data)) data = data_getter(current_dst & ~1u);\n\n        memcpy((byte *)&data + data_offset, current_src, partial_size);\n\n        changed |= data_setter(current_dst & ~1u, data);\n\n        remaining_size -= partial_size;\n        current_src += partial_size;\n        current_dst += partial_size;\n    } while (remaining_size > 0);\n\n    return { .n_bytes = size, .changed = changed };\n}\n\ntemplate<typename FUNC1, typename FUNC2>\ninline WriteResult\nwrite_by_u64(size_t dst, const void *src, size_t size, FUNC1 data_getter, FUNC2 data_setter) {\n    const byte *current_src = static_cast<const byte *>(src);\n    size_t current_dst = dst;\n    size_t remaining_size = size;\n    bool changed = false;\n\n    do {\n        // For simplicity, this is duplicated in read_by_u64.\n        size_t data_offset = current_dst % sizeof(uint64_t);\n        size_t partial_size = std::min(sizeof(uint64_t) - data_offset, remaining_size);\n        uint64_t data = 0;\n\n        if (partial_size < sizeof(data)) data = data_getter(current_dst & ~7u);\n\n        memcpy((byte *)&data + data_offset, current_src, partial_size);\n\n        changed |= data_setter(current_dst & ~7u, data);\n\n        remaining_size -= partial_size;\n        current_src += partial_size;\n        current_dst += partial_size;\n    } while (remaining_size > 0);\n\n    return { .n_bytes = size, .changed = changed };\n}\n\n/**\n * In case that underlying memory representation is fragmented, multiple\n * invocations of the same code might be needed. This is a common case with the\n * n-byte memory access API and therefore this function has been introduce to\n * minimize code duplication.\n *\n * @tparam FUNC         function with same signature as read or write\n * @tparam SRC_TYPE     corresponding type in read or write\n * @tparam DST_TYPE     corresponding type in read or write\n * @tparam OPTIONS_TYPE ReadOptions or WriteOptions\n * @tparam RESULT_TYPE  ReadResult or WriteResult\n * @param src           same arg as in read/write\n * @param dst           same arg as in read/write\n * @param size          same arg as in read/write\n * @param options       same arg as in read/write\n * @param function      lambda to perform individual accesses\n * @return number of bytes obtained, == size if fully successful\n */\ntemplate<typename RESULT_TYPE, typename FUNC, typename SRC_TYPE, typename DST_TYPE, typename OPTIONS_TYPE>\ninline RESULT_TYPE repeat_access_until_completed(\n    DST_TYPE dst,\n    SRC_TYPE src,\n    size_t size,\n    OPTIONS_TYPE options,\n    FUNC function) {\n    size_t remaining_size = size;\n    auto current_src = (uint64_t)(src);\n    auto current_dst = (uint64_t)(dst);\n    RESULT_TYPE total_result {};\n\n    // do-while is preferred, because this loop is most likely to be executed only once. Empty\n    // access is not common and does not need to be optimized.\n    do {\n        RESULT_TYPE result\n            = function((DST_TYPE)(current_dst), (SRC_TYPE)(current_src), remaining_size, options);\n        if (result.n_bytes == 0) break;\n        total_result += result;\n        current_src += result.n_bytes;\n        current_dst += result.n_bytes;\n        remaining_size -= result.n_bytes;\n    } while (remaining_size > 0);\n\n    return total_result;\n}\n\n/**\n * Helper function for memories, that do not support function like read_u32.\n * It is used in tests.\n * This is a generic version followed by named instantiations.\n */\ntemplate<typename T, typename MEM_T, typename ADDR_T>\nT memory_read(MEM_T *mem, ADDR_T address) {\n    T buffer;\n    mem->read(&buffer, address, sizeof(T), {});\n    return byteswap_if(buffer, mem->simulated_machine_endian != NATIVE_ENDIAN);\n}\n\ntemplate<typename MEM_T, typename ADDR_T>\nuint8_t memory_read_u8(MEM_T *mem, ADDR_T address) {\n    return memory_read<uint8_t>(mem, address);\n}\n\ntemplate<typename MEM_T, typename ADDR_T>\nuint16_t memory_read_u16(MEM_T *mem, ADDR_T address) {\n    return memory_read<uint16_t>(mem, address);\n}\n\ntemplate<typename MEM_T, typename ADDR_T>\nuint32_t memory_read_u32(MEM_T *mem, ADDR_T address) {\n    return memory_read<uint32_t>(mem, address);\n}\n\ntemplate<typename MEM_T, typename ADDR_T>\nuint64_t memory_read_u64(MEM_T *mem, ADDR_T address) {\n    return memory_read<uint64_t>(mem, address);\n}\n\n/**\n * Helper function for memories, that do not support function like read_u32.\n * It is used in tests.\n * This is a generic version followed by named instantiations.\n */\ntemplate<typename T, typename MEM_T, typename ADDR_T>\nvoid memory_write(MEM_T *mem, ADDR_T address, T value) {\n    const T swapped_value = byteswap_if(value, mem->simulated_machine_endian != NATIVE_ENDIAN);\n    mem->write(address, &swapped_value, sizeof(T), {});\n}\n\ntemplate<typename MEM_T, typename ADDR_T>\nvoid memory_write_u8(MEM_T *mem, ADDR_T address, uint8_t value) {\n    memory_write(mem, address, value);\n}\n\ntemplate<typename MEM_T, typename ADDR_T>\nvoid memory_write_u16(MEM_T *mem, ADDR_T address, uint16_t value) {\n    memory_write(mem, address, value);\n}\n\ntemplate<typename MEM_T, typename ADDR_T>\nvoid memory_write_u32(MEM_T *mem, ADDR_T address, uint32_t value) {\n    memory_write(mem, address, value);\n}\n\ntemplate<typename MEM_T, typename ADDR_T>\nvoid memory_write_u64(MEM_T *mem, ADDR_T address, uint64_t value) {\n    memory_write(mem, address, value);\n}\n\n} // namespace machine\n\n#endif // MEMORY_UTILS_H\n"
  },
  {
    "path": "src/machine/memory/tlb/tlb.cpp",
    "content": "#include \"tlb.h\"\n\n#include \"csr/controlstate.h\"\n#include \"machine.h\"\n#include \"memory/virtual/page_table_walker.h\"\n#include \"memory/virtual/sv32.h\"\n\nLOG_CATEGORY(\"machine.TLB\");\n\nnamespace machine {\n\nTLB::TLB(\n    FrontendMemory *memory,\n    TLBType type_,\n    const TLBConfig *config,\n    bool vm_enabled,\n    uint32_t memory_access_penalty_r,\n    uint32_t memory_access_penalty_w,\n    uint32_t memory_access_penalty_b,\n    bool memory_access_enable_b)\n    : FrontendMemory(memory->simulated_machine_endian)\n    , mem(memory)\n    , type(type_)\n    , tlb_config(config)\n    , vm_enabled(vm_enabled)\n    , access_pen_r(memory_access_penalty_r)\n    , access_pen_w(memory_access_penalty_w)\n    , access_pen_b(memory_access_penalty_b)\n    , access_ena_b(memory_access_enable_b) {\n    num_sets_ = tlb_config.get_tlb_num_sets();\n    associativity_ = tlb_config.get_tlb_associativity();\n    auto pol = tlb_config.get_tlb_replacement_policy();\n    repl_policy = make_tlb_policy(static_cast<TLBPolicyKind>(pol), associativity_, num_sets_);\n    table.assign(num_sets_, std::vector<Entry>(associativity_));\n    const char *tag = (type == PROGRAM ? \"I\" : \"D\");\n    LOG(\"TLB[%s] constructed; sets=%zu way=%zu\", tag, num_sets_, associativity_);\n\n    emit hit_update(hit_count_);\n    emit miss_update(miss_count_);\n    emit memory_reads_update(mem_reads);\n    emit memory_writes_update(mem_writes);\n    update_all_statistics();\n}\n\nvoid TLB::on_csr_write(size_t internal_id, RegisterValue val) {\n    if (internal_id != CSR::Id::SATP) return;\n    current_satp_raw = static_cast<uint32_t>(val.as_u64());\n    for (size_t s = 0; s < num_sets_; s++) {\n        for (size_t w = 0; w < associativity_; w++) {\n            auto &e = table[s][w];\n            if (e.valid) {\n                uint16_t old_asid = e.asid;\n                uint64_t old_vpn = e.vpn;\n                e.valid = false;\n                emit tlb_update(\n                    static_cast<unsigned>(w), static_cast<unsigned>(s), false, old_asid, old_vpn,\n                    0ull, false);\n            }\n        }\n    }\n    LOG(\"TLB: SATP changed → flushed all; new SATP=0x%08x\", current_satp_raw);\n    update_all_statistics();\n}\n\nvoid TLB::flush_single(VirtualAddress va, uint16_t asid) {\n    uint64_t vpn = va.get_raw() >> 12;\n    size_t s = set_index(vpn);\n    for (size_t w = 0; w < associativity_; w++) {\n        auto &e = table[s][w];\n        if (e.valid && e.vpn == vpn && e.asid == asid) {\n            uint16_t old_asid = e.asid;\n            uint64_t old_vpn = e.vpn;\n            e.valid = false;\n            const char *tag = (type == PROGRAM ? \"I\" : \"D\");\n            LOG(\"TLB[%s]: flushed VA=0x%llx ASID=%u\", tag, (unsigned long long)va.get_raw(), asid);\n            emit tlb_update(\n                static_cast<unsigned>(w), static_cast<unsigned>(s), false, old_asid, old_vpn, 0ull,\n                false);\n            update_all_statistics();\n        }\n    }\n}\n\nvoid TLB::flush() {\n    if (num_sets_ == 0 || associativity_ == 0) return;\n    const char *tag = (type == PROGRAM ? \"I\" : \"D\");\n    for (size_t s = 0; s < num_sets_; s++) {\n        for (size_t w = 0; w < associativity_; w++) {\n            auto &e = table[s][w];\n            if (e.valid) {\n                uint16_t old_asid = e.asid;\n                uint64_t old_vpn = e.vpn;\n                e.valid = false;\n                emit tlb_update(\n                    static_cast<unsigned>(w), static_cast<unsigned>(s), false, old_asid, old_vpn,\n                    0ull, false);\n            }\n        }\n    }\n    change_counter++;\n    LOG(\"TLB[%s]: flushed all entries\", tag);\n    update_all_statistics();\n}\n\nvoid TLB::sync() {\n    flush();\n}\n\nAddress TLB::translate_virtual_to_physical(AddressWithMode vaddr) {\n    uint64_t virt = vaddr.get_raw();\n\n    AccessMode mode = vaddr.access_mode();\n    CSR::PrivilegeLevel priv = mode.priv();\n    uint16_t asid = mode.asid();\n\n    bool satp_mode_on = is_mode_enabled_in_satp(current_satp_raw);\n    bool should_translate = vm_enabled && satp_mode_on && (priv != CSR::PrivilegeLevel::MACHINE);\n\n    if (!should_translate) {\n        return vaddr;\n    }\n\n    constexpr unsigned PAGE_SHIFT = 12;\n    constexpr uint64_t PAGE_MASK = (1ULL << PAGE_SHIFT) - 1;\n\n    uint64_t off = virt & ((1ULL << PAGE_SHIFT) - 1);\n    uint64_t vpn = virt >> PAGE_SHIFT;\n    size_t s = set_index(vpn);\n    const char *tag = (type == PROGRAM ? \"I\" : \"D\");\n\n    // Check TLB hit\n    for (size_t w = 0; w < associativity_; w++) {\n        auto &e = table[s][w];\n        if (e.valid && e.vpn == vpn && e.asid == asid) {\n            repl_policy->notify_access(s, w, /*valid=*/true);\n            uint64_t pbase = e.phys.get_raw() & ~((1ULL << PAGE_SHIFT) - 1);\n            hit_count_++;\n            emit hit_update(hit_count_);\n            emit tlb_update(\n                static_cast<unsigned>(w), static_cast<unsigned>(s), true, e.asid, e.vpn, pbase,\n                false);\n            update_all_statistics();\n            return Address { pbase + off };\n        }\n    }\n\n    // TLB miss -> resolve with page table walker\n    VirtualAddress va { static_cast<uint32_t>(virt) };\n    PageTableWalker walker(mem);\n\n    Address resolved_pa = walker.walk(va, current_satp_raw);\n    mem_reads += 1;\n    emit memory_reads_update(mem_reads);\n\n    // Cache the resolved mapping in the TLB\n    uint64_t phys_base = resolved_pa.get_raw() & ~PAGE_MASK;\n    size_t victim = repl_policy->select_way(s);\n    auto &ent = table[s][victim];\n    ent.valid = true;\n    ent.asid = asid;\n    ent.vpn = vpn;\n    ent.phys = Address { phys_base };\n    repl_policy->notify_access(s, victim, /*valid=*/true);\n    miss_count_++;\n    emit miss_update(miss_count_);\n    emit tlb_update(\n        static_cast<unsigned>(victim), static_cast<unsigned>(s), true, ent.asid, ent.vpn, phys_base,\n        false);\n\n    LOG(\"TLB[%s]: cached VA=0x%llx -> PA=0x%llx (ASID=%u) on miss\", tag, (unsigned long long)virt,\n        (unsigned long long)phys_base, asid);\n    update_all_statistics();\n    return Address { phys_base + off };\n}\n\nWriteResult TLB::write(AddressWithMode dst, const void *src, size_t sz, WriteOptions opts) {\n    return translate_and_write(dst, src, sz, opts);\n}\n\nReadResult TLB::read(void *dst, AddressWithMode src, size_t sz, ReadOptions opts) const {\n    return const_cast<TLB *>(this)->translate_and_read(dst, src, sz, opts);\n}\n\nWriteResult TLB::translate_and_write(AddressWithMode dst, const void *src, size_t sz, WriteOptions opts) {\n    Address pa = translate_virtual_to_physical(dst);\n    return mem->write(pa, src, sz, opts);\n}\n\nReadResult TLB::translate_and_read(void *dst, AddressWithMode src, size_t sz, ReadOptions opts) {\n    Address pa = translate_virtual_to_physical(src);\n    return mem->read(dst, pa, sz, opts);\n}\n\nbool TLB::reverse_lookup(Address paddr, VirtualAddress &out_va) const {\n    uint64_t ppn = paddr.get_raw() >> 12;\n    uint64_t offset = paddr.get_raw() & 0xFFF;\n    for (size_t s = 0; s < num_sets_; s++) {\n        for (size_t w = 0; w < associativity_; w++) {\n            auto &e = table[s][w];\n            if (e.valid && (e.phys.get_raw() >> 12) == ppn) {\n                out_va = VirtualAddress { (e.vpn << 12) | offset };\n                return true;\n            }\n        }\n    }\n    return false;\n}\n\ndouble TLB::get_hit_rate() const {\n    unsigned comp = hit_count_ + miss_count_;\n    if (comp == 0) return 0.0;\n    return (double)hit_count_ / (double)comp * 100.0;\n}\n\nuint32_t TLB::get_stall_count() const {\n    uint32_t st_cycles = mem_reads * (access_pen_r - 1) + mem_writes * (access_pen_w - 1);\n    st_cycles += miss_count_;\n    if (access_ena_b) {\n        st_cycles -= burst_reads * (access_pen_r - access_pen_b)\n                     + burst_writes * (access_pen_w - access_pen_b);\n    }\n    return st_cycles;\n}\n\nconst TLBConfig &TLB::get_config() const {\n    return tlb_config;\n}\n\ndouble TLB::get_speed_improvement() const {\n    unsigned comp = hit_count_ + miss_count_;\n    if (comp == 0) return 100.0;\n    uint32_t lookup_time = comp;\n    uint32_t mem_access_time = mem_reads * access_pen_r + mem_writes * access_pen_w;\n    if (access_ena_b) {\n        mem_access_time -= burst_reads * (access_pen_r - access_pen_b)\n                           + burst_writes * (access_pen_w - access_pen_b);\n    }\n    double baseline_time = (double)comp * (double)access_pen_r;\n    double with_tlb_time = (double)lookup_time + (double)mem_access_time;\n    if (with_tlb_time == 0.0) return 100.0;\n    return (baseline_time / with_tlb_time) * 100.0;\n}\n\nvoid TLB::update_all_statistics() {\n    emit statistics_update(get_stall_count(), get_speed_improvement(), get_hit_rate());\n}\n\nvoid TLB::reset() {\n    for (size_t s = 0; s < num_sets_; s++) {\n        for (size_t w = 0; w < associativity_; w++) {\n            auto &e = table[s][w];\n            if (e.valid) {\n                uint16_t old_asid = e.asid;\n                uint64_t old_vpn = e.vpn;\n                e.valid = false;\n                emit tlb_update(\n                    static_cast<unsigned>(w), static_cast<unsigned>(s), false, old_asid, old_vpn,\n                    0ull, false);\n            }\n        }\n    }\n\n    hit_count_ = 0;\n    miss_count_ = 0;\n    mem_reads = 0;\n    mem_writes = 0;\n    burst_reads = 0;\n    burst_writes = 0;\n    change_counter = 0;\n\n    emit hit_update(get_hit_count());\n    emit miss_update(get_miss_count());\n    emit memory_reads_update(get_read_count());\n    emit memory_writes_update(get_write_count());\n    update_all_statistics();\n}\n\n} // namespace machine\n"
  },
  {
    "path": "src/machine/memory/tlb/tlb.h",
    "content": "#ifndef TLB_H\n#define TLB_H\n\n#include \"common/logging.h\"\n#include \"csr/address.h\"\n#include \"memory/frontend_memory.h\"\n#include \"memory/virtual/sv32.h\"\n#include \"memory/virtual/virtual_address.h\"\n#include \"tlb_policy.h\"\n\n#include <cstdint>\n\nnamespace machine {\n\nenum TLBType { PROGRAM, DATA };\n\nclass Machine;\n\n// Implements a set-associative Translation Lookaside Buffer (TLB) frontend over physical memory,\n// handling virtual to physical translation, flush, and replacement policy.\nclass TLB : public FrontendMemory {\n    Q_OBJECT\npublic:\n    TLB(FrontendMemory *memory,\n        TLBType type,\n        const TLBConfig *config,\n        bool vm_enabled = true,\n        uint32_t memory_access_penalty_r = 1,\n        uint32_t memory_access_penalty_w = 1,\n        uint32_t memory_access_penalty_b = 0,\n        bool memory_access_enable_b = false);\n\n    void on_csr_write(size_t internal_id, RegisterValue val);\n    void flush_single(VirtualAddress va, uint16_t asid);\n\n    void flush();\n\n    void sync() override;\n\n    Address translate_virtual_to_physical(AddressWithMode vaddr);\n\n    WriteResult write(AddressWithMode dst, const void *src, size_t sz, WriteOptions opts) override;\n\n    ReadResult read(void *dst, AddressWithMode src, size_t sz, ReadOptions opts) const override;\n\n    uint32_t get_change_counter() const override { return mem->get_change_counter(); }\n\n    void set_replacement_policy(std::unique_ptr<TLBPolicy> p) { repl_policy = std::move(p); }\n\n    uint32_t root_page_table_ppn() const { return current_satp_raw & ((1u << PPN_BITS) - 1); }\n\n    bool reverse_lookup(Address paddr, VirtualAddress &out_va) const;\n\n    unsigned get_hit_count() const { return hit_count_; }\n    unsigned get_miss_count() const { return miss_count_; }\n    double get_hit_rate() const;\n    uint32_t get_read_count() const { return mem_reads; }\n    uint32_t get_write_count() const { return mem_writes; }\n    uint32_t get_burst_read_count() const { return burst_reads; }\n    uint32_t get_burst_write_count() const { return burst_writes; }\n\n    uint32_t get_stall_count() const;\n    double get_speed_improvement() const;\n    const TLBConfig &get_config() const;\n\n    void reset();\n    void update_all_statistics();\n\nsignals:\n    void tlb_update(\n        unsigned way,\n        unsigned set,\n        bool valid,\n        uint16_t asid,\n        uint64_t vpn,\n        uint64_t phys_base,\n        bool is_write);\n    void hit_update(unsigned val);\n    void miss_update(unsigned val);\n    void statistics_update(unsigned stalled_cycles, double speed_improv, double hit_rate);\n    void memory_reads_update(uint32_t val);\n    void memory_writes_update(uint32_t val);\n\nprivate:\n    struct Entry {\n        bool valid = false;\n        uint16_t asid = 0;\n        uint64_t vpn = 0;\n        Address phys = Address { 0 };\n        uint32_t lru = 0;\n    };\n\n    FrontendMemory *mem;\n    TLBType type;\n    const TLBConfig tlb_config;\n    uint32_t current_satp_raw = 0;\n    const bool vm_enabled;\n\n    size_t num_sets_;\n    size_t associativity_;\n    std::vector<std::vector<Entry>> table;\n    std::unique_ptr<TLBPolicy> repl_policy;\n\n    const uint32_t access_pen_r;\n    const uint32_t access_pen_w;\n    const uint32_t access_pen_b;\n    const bool access_ena_b;\n\n    mutable unsigned hit_count_ = 0;\n    mutable unsigned miss_count_ = 0;\n    mutable uint32_t mem_reads = 0;\n    mutable uint32_t mem_writes = 0;\n    mutable uint32_t burst_reads = 0;\n    mutable uint32_t burst_writes = 0;\n    mutable uint32_t change_counter = 0;\n\n    WriteResult translate_and_write(AddressWithMode dst, const void *src, size_t sz, WriteOptions opts);\n    ReadResult translate_and_read(void *dst, AddressWithMode src, size_t sz, ReadOptions opts);\n    inline size_t set_index(uint64_t vpn) const { return vpn & (num_sets_ - 1); }\n    inline bool is_mode_enabled_in_satp(uint32_t satp_raw) { return (satp_raw & (1u << 31)) != 0; }\n};\n\n} // namespace machine\n\n#endif // TLB_H\n"
  },
  {
    "path": "src/machine/memory/tlb/tlb_policy.cpp",
    "content": "#include \"tlb_policy.h\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstdlib>\n#include <numeric>\n\nnamespace machine {\n\nTLBPolicyRAND::TLBPolicyRAND(size_t assoc) : associativity(assoc) {\n    std::srand(1);\n}\nsize_t TLBPolicyRAND::select_way(size_t) const {\n    return std::rand() % associativity;\n}\nvoid TLBPolicyRAND::notify_access(size_t, size_t, bool) {\n    /* no state */\n}\n\nTLBPolicyLRU::TLBPolicyLRU(size_t assoc, size_t sets) : associativity(assoc), set_count(sets) {\n    stats.resize(sets, std::vector<uint32_t>(assoc));\n    for (auto &row : stats) {\n        std::iota(row.begin(), row.end(), 0);\n    }\n}\nsize_t TLBPolicyLRU::select_way(size_t set) const {\n    return stats[set][0];\n}\nvoid TLBPolicyLRU::notify_access(size_t set, size_t way, bool valid) {\n    auto &row = stats[set];\n    uint32_t next = way;\n    if (valid) {\n        for (int i = int(row.size()) - 1; i >= 0; --i) {\n            std::swap(row[i], next);\n            if (next == way) break;\n        }\n    } else {\n        for (unsigned int &i : row) {\n            std::swap(i, next);\n            if (next == way) break;\n        }\n    }\n}\n\nTLBPolicyLFU::TLBPolicyLFU(size_t assoc, size_t sets) : associativity(assoc), set_count(sets) {\n    stats.assign(sets, std::vector<uint32_t>(assoc, 0));\n}\nsize_t TLBPolicyLFU::select_way(size_t set) const {\n    const auto &row = stats[set];\n    size_t idx = 0;\n    uint32_t minv = row[0];\n    for (size_t i = 1; i < row.size(); ++i) {\n        if (row[i] == 0 || row[i] < minv) {\n            minv = row[i];\n            idx = i;\n            if (minv == 0) break;\n        }\n    }\n    return idx;\n}\nvoid TLBPolicyLFU::notify_access(size_t set, size_t way, bool valid) {\n    if (valid) {\n        stats[set][way]++;\n    } else {\n        stats[set][way] = 0;\n    }\n}\n\nTLBPolicyPLRU::TLBPolicyPLRU(size_t assoc, size_t sets)\n    : associativity(assoc)\n    , set_count(sets)\n    , c_log2(std::ceil(std::log2(float(assoc)))) {\n    size_t tree_size = (1u << c_log2) - 1;\n    tree.assign(sets, std::vector<uint8_t>(tree_size, 0));\n}\nsize_t TLBPolicyPLRU::select_way(size_t set) const {\n    const auto &bits = tree[set];\n    size_t idx = 0;\n    size_t node = 0;\n    for (size_t lvl = 0; lvl < c_log2; ++lvl) {\n        uint8_t b = bits[node];\n        idx = (idx << 1) | b;\n        node = ((1u << (lvl + 1)) - 1) + idx;\n    }\n    return std::min(idx, associativity - 1);\n}\nvoid TLBPolicyPLRU::notify_access(size_t set, size_t way, bool) {\n    auto &bits = tree[set];\n    size_t node = 0;\n    for (size_t lvl = 0; lvl < c_log2; ++lvl) {\n        uint8_t dir = (way >> (c_log2 - lvl - 1)) & 1;\n        bits[node] = dir ? 0 : 1;\n        node = ((1u << (lvl + 1)) - 1) + ((dir ? 1 : 0));\n    }\n}\n\nstd::unique_ptr<TLBPolicy>\nmake_tlb_policy(TLBPolicyKind kind, size_t associativity, size_t set_count) {\n    switch (kind) {\n    case TLBPolicyKind::RAND: return std::make_unique<TLBPolicyRAND>(associativity);\n    case TLBPolicyKind::LRU: return std::make_unique<TLBPolicyLRU>(associativity, set_count);\n    case TLBPolicyKind::LFU: return std::make_unique<TLBPolicyLFU>(associativity, set_count);\n    case TLBPolicyKind::PLRU: return std::make_unique<TLBPolicyPLRU>(associativity, set_count);\n    }\n    return std::make_unique<TLBPolicyLRU>(associativity, set_count);\n}\n\n} // namespace machine\n"
  },
  {
    "path": "src/machine/memory/tlb/tlb_policy.h",
    "content": "#ifndef TLB_POLICY_H\n#define TLB_POLICY_H\n\n#include <cstddef>\n#include <cstdint>\n#include <memory>\n#include <vector>\n\nnamespace machine {\n\n// Abstract TLB replacement policy interface & implementations (RAND, LRU, LFU, PLRU) for\n// set-associative tables.\nclass TLBPolicy {\npublic:\n    virtual size_t select_way(size_t set) const = 0;\n\n    virtual void notify_access(size_t set, size_t way, bool valid) = 0;\n\n    virtual ~TLBPolicy() = default;\n};\n\nclass TLBPolicyRAND final : public TLBPolicy {\n    size_t associativity;\n\npublic:\n    explicit TLBPolicyRAND(size_t assoc);\n    size_t select_way(size_t set) const override;\n    void notify_access(size_t set, size_t way, bool valid) override;\n};\n\nclass TLBPolicyLRU final : public TLBPolicy {\n    size_t associativity;\n    size_t set_count;\n    std::vector<std::vector<uint32_t>> stats;\n\npublic:\n    TLBPolicyLRU(size_t assoc, size_t sets);\n    size_t select_way(size_t set) const override;\n    void notify_access(size_t set, size_t way, bool valid) override;\n};\n\nclass TLBPolicyLFU final : public TLBPolicy {\n    size_t associativity;\n    size_t set_count;\n    std::vector<std::vector<uint32_t>> stats;\n\npublic:\n    TLBPolicyLFU(size_t assoc, size_t sets);\n    size_t select_way(size_t set) const override;\n    void notify_access(size_t set, size_t way, bool valid) override;\n};\n\nclass TLBPolicyPLRU final : public TLBPolicy {\n    size_t associativity;\n    size_t set_count;\n    size_t c_log2;\n    std::vector<std::vector<uint8_t>> tree;\n\npublic:\n    TLBPolicyPLRU(size_t assoc, size_t sets);\n    size_t select_way(size_t set) const override;\n    void notify_access(size_t set, size_t way, bool valid) override;\n};\n\nenum class TLBPolicyKind { RAND, LRU, LFU, PLRU };\n\nstd::unique_ptr<TLBPolicy>\nmake_tlb_policy(TLBPolicyKind kind, size_t associativity, size_t set_count);\n\n} // namespace machine\n\n#endif // TLB_POLICY_H\n"
  },
  {
    "path": "src/machine/memory/virtual/page_table_walker.cpp",
    "content": "#include \"page_table_walker.h\"\n\n#include \"common/logging.h\"\n#include \"machine.h\"\n#include \"memory/backend/backend_memory.h\"\n\n#include <inttypes.h>\n\nLOG_CATEGORY(\"machine.PTW\");\n\nnamespace machine {\n\nAddress PageTableWalker::walk(const VirtualAddress &va, uint32_t raw_satp) const {\n    if (((raw_satp >> 31) & 1) == 0) return Address { va.get_raw() };\n\n    auto root_ppn = raw_satp & ((1u << 22) - 1);\n    auto va_raw = static_cast<uint32_t>(va.get_raw());\n    auto vpn1 = (va_raw >> VPN1_SHIFT) & VPN_MASK;\n    auto vpn0 = (va_raw >> VPN0_SHIFT) & VPN_MASK;\n    auto ppn = root_ppn;\n\n    for (int lvl = 1; lvl >= 0; --lvl) {\n        uint32_t idx = (lvl == 1 ? vpn1 : vpn0);\n        Address pte_addr { (uint64_t(ppn) << PAGE_SHIFT) + idx * 4 };\n\n        uint32_t raw_pte;\n        memory->read(&raw_pte, pte_addr, sizeof(raw_pte), { .type = ae::INTERNAL });\n        LOG(\"PTW: L%u PTE@0x%08\" PRIx64 \" = 0x%08x\", lvl, pte_addr.get_raw(), raw_pte);\n\n        Sv32Pte pte = Sv32Pte::from_uint(raw_pte);\n\n        if (!pte.is_valid()) {\n            throw SIMULATOR_EXCEPTION(\n                PageFault, \"PTW: page fault, leaf PTE invalid\",\n                QString::number(pte_addr.get_raw(), 16));\n        }\n\n        if (pte.is_leaf()) {\n            Address pa = make_phys(va_raw, pte, lvl);\n            LOG(\"PTW: L%u leaf → PA=0x%08\" PRIx64, lvl, pa.get_raw());\n            return pa;\n        }\n\n        if (pte.r() || pte.w() || pte.x()) {\n            throw SIMULATOR_EXCEPTION(\n                PageFault, \"PTW: invalid non-leaf\", QString::number(raw_pte, 16));\n        }\n        ppn = unsigned(pte.ppn());\n    }\n\n    throw SIMULATOR_EXCEPTION(PageFault, \"PTW: no leaf found\", \"\");\n}\n\n} // namespace machine\n"
  },
  {
    "path": "src/machine/memory/virtual/page_table_walker.h",
    "content": "#ifndef PAGE_TABLE_WALKER_H\n#define PAGE_TABLE_WALKER_H\n\n#include \"memory/frontend_memory.h\"\n#include \"sv32.h\"\n#include \"virtual_address.h\"\n\n#include <inttypes.h>\n\nnamespace machine {\n\n// Performs multi-level page-table walks (SV32) in memory to resolve a virtual address to a physical\n// one.\nclass PageTableWalker {\npublic:\n    explicit PageTableWalker(FrontendMemory *mem) : memory(mem) {}\n\n    Address walk(const VirtualAddress &va, uint32_t raw_satp) const;\n\nprivate:\n    FrontendMemory *memory;\n};\n\n} // namespace machine\n\n#endif // PAGE_TABLE_WALKER_H\n"
  },
  {
    "path": "src/machine/memory/virtual/sv32.h",
    "content": "#ifndef SV32_H\n#define SV32_H\n\n// SV32-specific definitions: page-table entry (PTE) bitfields, shifts/masks, and PTE to physical\n// address helpers. This header documents the SV32 layout (RISC-V 32-bit virtual memory):\n//  - Page size: 4 KiB  (PAGE_SHIFT = 12).\n//  - Virtual page number (VPN) is split into two 10-bit indices VPN[1] and VPN[0].\n//  - PTE low bits encode flags and low-order info; high bits encode the physical\n//    page number (PPN).\nnamespace machine {\nstatic constexpr unsigned PAGE_SHIFT = 12; // Page size = 2^PAGE_SHIFT bytes. For SV32 this is 4\n                                           // KiB.\nstatic constexpr unsigned VPN_BITS = 10;   // Number of bits for each VPN level in SV32: 10 bits for\n                                           // VPN[1] and 10 bits for VPN[0].\n\n// Shift values for extracting VPN parts from a virtual address:\nstatic constexpr unsigned VPN0_SHIFT = PAGE_SHIFT; // VPN0 is the low VPN field, it starts at the\n                                                   // page offset (PAGE_SHIFT).\nstatic constexpr unsigned VPN1_SHIFT = PAGE_SHIFT + VPN_BITS; // VPN1 is the next field above VPN0.\n\nstatic constexpr unsigned VPN_MASK = (1u << VPN_BITS) - 1; // Mask to extract a single VPN level (10\n                                                           // bits, value 0..1023).\n\n// Number of bits available for the physical page number (PPN) in SV32 PTE.\n// SV32 uses 22 PPN bits (bits 10..31 of a 32-bit PTE) which permits addressing up to 4GiB of\n// physical memory.\nstatic constexpr unsigned PPN_BITS = 22;\nstatic constexpr unsigned PPN_MASK = (1u << PPN_BITS) - 1;\n\nstatic constexpr uint64_t PHYS_PPN_START = 0x200; // I have noticed that programs are loaded into\n                                                  // memory starting at 0x200.\n\n// Sv32Pte wraps the raw 32-bit PTE value and provides helpers to read flags and fields.\n// Layout (bit indices):\n//  0 V (valid)\n//  1 R (read)\n//  2 W (write)\n//  3 X (execute)\n//  4 U (user)\n//  5 G (global)\n//  6 A (accessed)\n//  7 D (dirty)\n//  8..9 RSW (reserved for supervisor use)\n//  10..31 PPN (physical page number)\n//\n// A PTE is considered a \"leaf\" when it grants read or execute permission (R or X).\n// Validation rules: PTE is valid if V==1 and (if W==1 then R must also be 1).\nstruct Sv32Pte {\n    uint64_t raw = 0;\n\n    // Bit positions (shifts) for the fields described above.\n    static constexpr unsigned V_SHIFT = 0;\n    static constexpr unsigned R_SHIFT = 1;\n    static constexpr unsigned W_SHIFT = 2;\n    static constexpr unsigned X_SHIFT = 3;\n    static constexpr unsigned U_SHIFT = 4;\n    static constexpr unsigned G_SHIFT = 5;\n    static constexpr unsigned A_SHIFT = 6;\n    static constexpr unsigned D_SHIFT = 7;\n    static constexpr unsigned RSW_SHIFT = 8;  // two bits: 8..9\n    static constexpr unsigned PPN_SHIFT = 10; // PPN starts at bit 10\n\n    // Masks for each single-bit flag\n    static constexpr uint64_t V_MASK = (1u << V_SHIFT);\n    static constexpr uint64_t R_MASK = (1u << R_SHIFT);\n    static constexpr uint64_t W_MASK = (1u << W_SHIFT);\n    static constexpr uint64_t X_MASK = (1u << X_SHIFT);\n    static constexpr uint64_t U_MASK = (1u << U_SHIFT);\n    static constexpr uint64_t G_MASK = (1u << G_SHIFT);\n    static constexpr uint64_t A_MASK = (1u << A_SHIFT);\n    static constexpr uint64_t D_MASK = (1u << D_SHIFT);\n\n    // Mask for the 2-bit RSW field (bits 8..9).\n    static constexpr uint64_t RSW_MASK = (0x3u << RSW_SHIFT);\n\n    // Mask that selects the PPN field within the raw PTE (bits 10..(10 + PPN_BITS - 1))\n    static constexpr uint64_t PPN_MASK32 = ((PPN_MASK) << PPN_SHIFT);\n\n    constexpr Sv32Pte() = default;\n    constexpr explicit Sv32Pte(uint64_t raw_) : raw(raw_) {}\n\n    constexpr uint64_t to_uint() const { return raw; }\n    static constexpr Sv32Pte from_uint(uint64_t r) { return Sv32Pte(r); }\n\n    // Flag accessors\n    constexpr bool v() const noexcept { return (raw >> V_SHIFT) & 0x1u; }\n    constexpr bool r() const noexcept { return (raw >> R_SHIFT) & 0x1u; }\n    constexpr bool w() const noexcept { return (raw >> W_SHIFT) & 0x1u; }\n    constexpr bool x() const noexcept { return (raw >> X_SHIFT) & 0x1u; }\n    constexpr bool u() const noexcept { return (raw >> U_SHIFT) & 0x1u; }\n    constexpr bool g() const noexcept { return (raw >> G_SHIFT) & 0x1u; }\n    constexpr bool a() const noexcept { return (raw >> A_SHIFT) & 0x1u; }\n    constexpr bool d() const noexcept { return (raw >> D_SHIFT) & 0x1u; }\n    constexpr uint64_t rsw() const noexcept { return (raw >> RSW_SHIFT) & 0x3u; }\n    constexpr uint64_t ppn() const noexcept { return (raw >> PPN_SHIFT) & PPN_MASK; }\n\n    // Convenience methods used by the page-table walker\n    bool is_leaf() const noexcept { return r() || x(); }\n    bool is_valid() const noexcept { return v() && (!w() || r()); }\n\n    // Helper to construct a PTE from fields.\n    static constexpr Sv32Pte make(\n        bool v_,\n        bool r_,\n        bool w_,\n        bool x_,\n        bool u_,\n        bool g_,\n        bool a_,\n        bool d_,\n        uint64_t rsw_,\n        uint64_t ppn_) {\n        uint64_t r = 0;\n        r |= (uint64_t(v_) << V_SHIFT);\n        r |= (uint64_t(r_) << R_SHIFT);\n        r |= (uint64_t(w_) << W_SHIFT);\n        r |= (uint64_t(x_) << X_SHIFT);\n        r |= (uint64_t(u_) << U_SHIFT);\n        r |= (uint64_t(g_) << G_SHIFT);\n        r |= (uint64_t(a_) << A_SHIFT);\n        r |= (uint64_t(d_) << D_SHIFT);\n        r |= ((rsw_ & 0x3u) << RSW_SHIFT);\n        r |= ((ppn_ & PPN_MASK) << PPN_SHIFT);\n        return Sv32Pte(r);\n    }\n};\n\ninline Address make_phys(uint64_t va_raw, const Sv32Pte &pte, int level) {\n    uint64_t offset = va_raw & ((1u << PAGE_SHIFT) - 1);\n    uint64_t phys_ppn = pte.ppn();\n    if (level == 1) {\n        uint64_t vpn0 = (va_raw >> PAGE_SHIFT) & VPN_MASK;\n        phys_ppn = (phys_ppn & ~VPN_MASK) | vpn0;\n    }\n    return Address { (uint64_t(phys_ppn) << PAGE_SHIFT) | offset };\n}\n} // namespace machine\n\n#endif // SV32_H\n"
  },
  {
    "path": "src/machine/memory/virtual/virtual_address.h",
    "content": "#ifndef VIRTUAL_ADDRESS_H\n#define VIRTUAL_ADDRESS_H\n\n#include \"utils.h\"\n\n#include <QMetaType>\n#include <cstdint>\n\nusing std::uint64_t;\n\nnamespace machine {\n\n// Lightweight VirtualAddress wrapper offering raw access, alignment checks, arithmetic, and\n// comparisons.\nclass VirtualAddress {\nprivate:\n    uint64_t data; // Raw virtual address\n\npublic:\n    constexpr explicit VirtualAddress(uint64_t);\n\n    constexpr VirtualAddress();\n\n    constexpr VirtualAddress(const VirtualAddress &address) = default;\n    constexpr VirtualAddress &operator=(const VirtualAddress &address) = default;\n\n    [[nodiscard]] constexpr uint64_t get_raw() const;\n\n    constexpr explicit operator uint64_t() const;\n\n    constexpr static VirtualAddress null();\n\n    [[nodiscard]] constexpr bool is_null() const;\n\n    template<typename T>\n    [[nodiscard]] constexpr bool is_aligned() const;\n\n    /* Equality */\n    constexpr inline bool operator==(const VirtualAddress &other) const;\n    constexpr inline bool operator!=(const VirtualAddress &other) const;\n\n    /* Ordering */\n    constexpr inline bool operator<(const VirtualAddress &other) const;\n    constexpr inline bool operator>(const VirtualAddress &other) const;\n    constexpr inline bool operator<=(const VirtualAddress &other) const;\n    constexpr inline bool operator>=(const VirtualAddress &other) const;\n\n    /* Offset arithmetic */\n    constexpr inline VirtualAddress operator+(const uint64_t &offset) const;\n    constexpr inline VirtualAddress operator-(const uint64_t &offset) const;\n    inline void operator+=(const uint64_t &offset);\n    inline void operator-=(const uint64_t &offset);\n\n    /* Bitwise */\n    constexpr inline VirtualAddress operator&(const uint64_t &mask) const;\n    constexpr inline VirtualAddress operator|(const uint64_t &mask) const;\n    constexpr inline VirtualAddress operator^(const uint64_t &mask) const;\n    constexpr inline VirtualAddress operator>>(const uint64_t &size) const;\n    constexpr inline VirtualAddress operator<<(const uint64_t &size) const;\n\n    /* Distance arithmetic */\n    constexpr inline int64_t operator-(const VirtualAddress &other) const;\n};\n\nconstexpr VirtualAddress operator\"\"_vaddr(unsigned long long literal) {\n    return VirtualAddress(literal);\n}\n\n// Implementations\n\nconstexpr VirtualAddress::VirtualAddress(uint64_t address) : data(address) {}\n\nconstexpr VirtualAddress::VirtualAddress() : data(0) {}\n\nconstexpr uint64_t VirtualAddress::get_raw() const {\n    return data;\n}\n\nconstexpr VirtualAddress::operator uint64_t() const {\n    return this->get_raw();\n}\n\nconstexpr VirtualAddress VirtualAddress::null() {\n    return VirtualAddress(0x0);\n}\n\nconstexpr bool VirtualAddress::is_null() const {\n    return this->get_raw() == 0;\n}\n\ntemplate<typename T>\nconstexpr bool VirtualAddress::is_aligned() const {\n    return is_aligned_generic<decltype(this->data), T>(this->data);\n}\n\n/* Equality */\n\nconstexpr bool VirtualAddress::operator==(const VirtualAddress &other) const {\n    return this->get_raw() == other.get_raw();\n}\n\nconstexpr bool VirtualAddress::operator!=(const VirtualAddress &other) const {\n    return this->get_raw() != other.get_raw();\n}\n\n/* Ordering */\n\nconstexpr bool VirtualAddress::operator<(const VirtualAddress &other) const {\n    return this->get_raw() < other.get_raw();\n}\n\nconstexpr bool VirtualAddress::operator>(const VirtualAddress &other) const {\n    return this->get_raw() > other.get_raw();\n}\n\nconstexpr bool VirtualAddress::operator<=(const VirtualAddress &other) const {\n    return this->get_raw() <= other.get_raw();\n}\n\nconstexpr bool VirtualAddress::operator>=(const VirtualAddress &other) const {\n    return this->get_raw() >= other.get_raw();\n}\n\n/* Offset arithmetic */\n\nconstexpr VirtualAddress VirtualAddress::operator+(const uint64_t &offset) const {\n    return VirtualAddress(this->get_raw() + offset);\n}\n\nconstexpr VirtualAddress VirtualAddress::operator-(const uint64_t &offset) const {\n    return VirtualAddress(this->get_raw() - offset);\n}\n\nvoid VirtualAddress::operator+=(const uint64_t &offset) {\n    data += offset;\n}\n\nvoid VirtualAddress::operator-=(const uint64_t &offset) {\n    data -= offset;\n}\n\n/* Bitwise */\n\nconstexpr VirtualAddress VirtualAddress::operator&(const uint64_t &mask) const {\n    return VirtualAddress(this->get_raw() & mask);\n}\n\nconstexpr VirtualAddress VirtualAddress::operator|(const uint64_t &mask) const {\n    return VirtualAddress(this->get_raw() | mask);\n}\n\nconstexpr VirtualAddress VirtualAddress::operator^(const uint64_t &mask) const {\n    return VirtualAddress(this->get_raw() ^ mask);\n}\n\nconstexpr VirtualAddress VirtualAddress::operator>>(const uint64_t &size) const {\n    return VirtualAddress(this->get_raw() >> size);\n}\n\nconstexpr VirtualAddress VirtualAddress::operator<<(const uint64_t &size) const {\n    return VirtualAddress(this->get_raw() << size);\n}\n\n/* Distance arithmetic */\n\nconstexpr int64_t VirtualAddress::operator-(const VirtualAddress &other) const {\n    return this->get_raw() - other.get_raw();\n}\n\n} // namespace machine\n\nQ_DECLARE_METATYPE(machine::VirtualAddress)\n\n#endif // VIRTUAL_ADDRESS_H\n"
  },
  {
    "path": "src/machine/pipeline.h",
    "content": "/**\n * State of the core pipeline.\n *\n * Each internal has a state struct. The state struct is composed of the\n * internal state for visualization and two sets of outgoing interstage\n * registers - result and final. Both are filled with the same data as the\n * stage finishes, but result is never later modified. Final will be modified\n * by flushed, exceptions etc. and it will be used for further execution.\n *\n * TODO:\n * - Move init functions here as methods (constructor/discard).\n *\n * @file\n */\n#ifndef STAGES_H\n#define STAGES_H\n\n#include \"instruction.h\"\n#include \"machinedefs.h\"\n#include \"memory/address.h\"\n#include \"registers.h\"\n\n#include <cstdint>\n#include <utility>\n\nnamespace machine {\n\nenum ForwardFrom {\n    FORWARD_NONE = 0b00,\n    FORWARD_FROM_W = 0b01,\n    FORWARD_FROM_M = 0b10,\n};\n\nstruct PCInterstage {\n    bool stop_if = false;\n};\n\nstruct PCState {\n    PCInterstage final {};\n};\n\nstruct FetchInterstage {\n    Instruction inst = Instruction::NOP; // Loaded instruction\n    Address inst_addr = STAGEADDR_NONE;  // Address of instruction\n    Address next_inst_addr = 0_addr;     // `inst_addr` + `inst.size()`\n    /** Inspecting other stages to get this value is problematic due to stalls and flushed.\n     * Therefore we pass it through the whole pipeline. */\n    Address predicted_next_inst_addr = 0_addr;\n    enum ExceptionCause excause = EXCAUSE_NONE;\n    bool is_valid = false;\n\npublic:\n    /** Reset to value corresponding to NOP. */\n    void flush() { *this = {}; }\n};\n\nstruct FetchInternalState {\n    RegisterValue fetched_value = 0;\n    unsigned excause_num = 0;\n};\n\nstruct FetchState {\n    FetchInternalState internal {};\n    FetchInterstage result {};\n    FetchInterstage final {};\n\n    FetchState(const FetchInternalState &stage, const FetchInterstage &interstage)\n        : internal(stage)\n        , result(interstage)\n        , final(interstage) {\n        this->internal.excause_num = static_cast<unsigned>(interstage.excause);\n    }\n\n    FetchState() = default;\n    FetchState(const FetchState &) = default;\n    FetchState &operator=(const FetchState &) = default;\n};\n\nstruct DecodeInterstage {\n    Instruction inst = Instruction::NOP;\n    Address inst_addr = STAGEADDR_NONE;\n    Address next_inst_addr = 0_addr;\n    Address predicted_next_inst_addr = 0_addr;\n    RegisterValue val_rs = 0;        // Value from register rs\n    RegisterValue val_rs_orig = 0;   // Value from register rs1 without forwarding\n    RegisterValue val_rt = 0;        // Value from register rt\n    RegisterValue val_rt_orig = 0;   // Value from register rs1 without forwarding\n    RegisterValue immediate_val = 0; // Sign-extended immediate value\n                                     // rd according to regd)\n    RegisterValue csr_read_val = 0;  // Value read from csr\n    CSR::Address csr_address = CSR::Address(0);\n    ExceptionCause excause = EXCAUSE_NONE;\n    ForwardFrom ff_rs = FORWARD_NONE;\n    ForwardFrom ff_rt = FORWARD_NONE;\n    AluComponent alu_component; // Selects computational component in alu - basic ALU / MUL.\n    AluCombinedOp aluop = { .alu_op = AluOp::ADD }; // Decoded ALU operation\n    AccessControl memctl = AC_NONE;                 // Decoded memory access type\n    RegisterId num_rs = 0;                          // Number of the register s1\n    RegisterId num_rt = 0;                          // Number of the register s2\n    RegisterId num_rd = 0;                          // Number of the register d\n    bool memread = false;                           // If memory should be read\n    bool memwrite = false;                          // If memory should write input\n    bool alusrc = false;      // If second value to alu is immediate value (rt used otherwise)\n    bool regwrite = false;    // If output should be written back to register\n    bool alu_req_rs = false;  // requires rs value for ALU\n    bool alu_req_rt = false;  // requires rt value for ALU or SW\n    bool branch_bxx = false;  // branch instruction\n    bool branch_jal = false;  // jump\n    bool branch_val = false;  // negate branch condition\n    bool branch_jalr = false; // JALR: write PC+4 to register and jump to ALU result\n    bool stall = false;\n    bool is_valid = false;\n    bool w_operation = false; // ALU or other operation is limited to word size (32-bits)\n    bool alu_mod = false;     // alternative versions of ADD and right-shift\n    bool alu_pc = false;      // PC is input to ALU\n    bool csr = false;         // Zicsr, implies csr read and csr write\n    bool csr_to_alu = false;\n    bool csr_write = false;\n    bool xret = false; // Return from exception, MRET and SRET\n    CSR::PrivilegeLevel xret_privlev = CSR::PrivilegeLevel::UNPRIVILEGED;\n    bool insert_stall_before = false;\n\npublic:\n    /** Reset to value corresponding to NOP. */\n    void flush() { *this = {}; }\n};\n\nstruct DecodeInternalState {\n    /**\n     * ALU OP as a number\n     * GUI needs to show a number, not enumerated value (for simple interface).\n     * Core is responsive for the conversion.\n     */\n    unsigned alu_op_num = 0;\n    unsigned excause_num = 0;\n    RegisterValue inst_bus = 0;\n    bool alu_mul = false;\n};\n\nstruct DecodeState {\n    DecodeInternalState internal {};\n    DecodeInterstage result {};\n    DecodeInterstage final {};\n\n    DecodeState(const DecodeInternalState &stage, const DecodeInterstage &interstage)\n        : internal(stage)\n        , result(interstage)\n        , final(interstage) {\n        this->internal.excause_num = static_cast<unsigned>(interstage.excause);\n        this->internal.alu_op_num = static_cast<unsigned>(interstage.aluop.alu_op);\n    }\n    DecodeState() = default;\n    DecodeState(const DecodeState &) = default;\n    DecodeState &operator=(const DecodeState &) = default;\n};\n\nstruct ExecuteInterstage {\n    Instruction inst = Instruction::NOP;\n    Address inst_addr = STAGEADDR_NONE;\n    Address next_inst_addr = 0_addr;\n    Address predicted_next_inst_addr = 0_addr;\n    Address branch_jal_target = 0_addr; //> Potential branch target (inst_addr + 4 + imm).\n    RegisterValue val_rt = 0;\n    RegisterValue alu_val = 0; // Result of ALU execution\n    RegisterValue immediate_val = 0;\n    RegisterValue csr_read_val = 0;\n    CSR::Address csr_address = CSR::Address(0);\n    ExceptionCause excause = EXCAUSE_NONE;\n    AccessControl memctl = AC_NONE;\n    RegisterId num_rd = 0;\n    bool memread = false;\n    bool memwrite = false;\n    bool regwrite = false;\n    bool is_valid = false;\n    bool branch_bxx = false;\n    bool branch_jal = false;\n    bool branch_val = false;\n    bool branch_jalr = false; //> @copydoc DecodeInterstage::branch_jalr\n    bool alu_zero = false;\n    bool csr = false;\n    bool csr_write = false;\n    bool xret = false;\n    CSR::PrivilegeLevel xret_privlev = CSR::PrivilegeLevel::UNPRIVILEGED;\n\npublic:\n    /** Reset to value corresponding to NOP. */\n    void flush() { *this = {}; }\n};\n\nstruct ExecuteInternalState {\n    RegisterValue alu_src1 = 0;\n    RegisterValue alu_src2 = 0;\n    RegisterValue immediate = 0;\n    RegisterValue rs = 0;\n    RegisterValue rt = 0;\n    unsigned stall_status = 0;\n    /**\n     * ALU OP as a number.\n     * GUI needs to show a number, not enumerated value (for simple interface).\n     * Core is responsive for the conversion.\n     */\n    unsigned alu_op_num = 0;\n    /**\n     * Forwarding setting as a number.\n     * Same note as alu_op_num.\n     */\n    unsigned forward_from_rs1_num = 0;\n    /**\n     * Forwarding setting as a number.\n     * Same note as alu_op_num.\n     */\n    unsigned forward_from_rs2_num = 0;\n    unsigned excause_num = 0;\n    bool alu_src = false;\n    bool alu_mul = false;\n    bool branch_bxx = false;\n    bool alu_pc = false; // PC is input to ALU\n};\n\nstruct ExecuteState {\n    ExecuteInternalState internal {};\n    ExecuteInterstage result {};\n    ExecuteInterstage final {};\n\n    ExecuteState(const ExecuteInternalState &stage, const ExecuteInterstage &interstage)\n        : internal(stage)\n        , result(interstage)\n        , final(interstage) {\n        this->internal.excause_num = static_cast<unsigned>(interstage.excause);\n    }\n    ExecuteState() = default;\n    ExecuteState(const ExecuteState &) = default;\n    ExecuteState &operator=(const ExecuteState &) = default;\n};\n\nstruct MemoryInterstage {\n    Instruction inst = Instruction::NOP;\n    Address inst_addr = STAGEADDR_NONE;\n    Address next_inst_addr = 0_addr;\n    Address predicted_next_inst_addr = 0_addr;\n    Address computed_next_inst_addr = 0_addr;\n    Address mem_addr = 0_addr; // Address used to access memory\n    RegisterValue towrite_val = 0;\n    ExceptionCause excause = EXCAUSE_NONE;\n    RegisterId num_rd = 0;\n    bool memtoreg = false;\n    bool regwrite = false;\n    bool is_valid = false;\n    bool csr_written = false;\n    CSR::PrivilegeLevel xret_privlev = CSR::PrivilegeLevel::UNPRIVILEGED;\n\npublic:\n    /** Reset to value corresponding to NOP. */\n    void flush() { *this = {}; }\n};\n\nstruct MemoryInternalState {\n    RegisterValue mem_read_val = 0;\n    RegisterValue mem_write_val = 0;\n    RegisterValue mem_addr = 0;\n    unsigned excause_num = 0;\n    bool memwrite = false;\n    bool memread = false;\n    bool branch_bxx = false;\n    bool branch_jal = false;\n    bool branch_outcome = false;\n    bool branch_jalx = false;\n    bool branch_jalr = false;\n    bool xret = false;\n};\n\nstruct MemoryState {\n    MemoryInternalState internal {};\n    MemoryInterstage result {};\n    MemoryInterstage final {};\n\n    MemoryState(const MemoryInternalState &stage, const MemoryInterstage &interstage)\n        : internal(stage)\n        , result(interstage)\n        , final(interstage) {\n        this->internal.excause_num = static_cast<unsigned>(interstage.excause);\n    }\n\n    MemoryState() = default;\n    MemoryState(const MemoryState &) = default;\n    MemoryState &operator=(const MemoryState &) = default;\n};\n\nstruct WritebackInternalState {\n    Instruction inst = Instruction::NOP;\n    Address inst_addr = STAGEADDR_NONE;\n    RegisterValue value = 0;\n    RegisterId num_rd = 0;\n    bool regwrite = false;\n    bool memtoreg = false;\n};\n\nstruct WritebackState {\n    WritebackInternalState internal {};\n\n    explicit WritebackState(WritebackInternalState stage) : internal(std::move(stage)) {}\n    WritebackState() = default;\n    WritebackState(const WritebackState &) = default;\n    WritebackState &operator=(const WritebackState &) = default;\n};\n\nstruct Pipeline {\n    PCState pc {};\n    FetchState fetch {};\n    DecodeState decode {};\n    ExecuteState execute {};\n    MemoryState memory {};\n    WritebackState writeback {};\n};\n} // namespace machine\n\n#endif // STAGES_H\n"
  },
  {
    "path": "src/machine/predictor.cpp",
    "content": "#include \"predictor.h\"\n\nLOG_CATEGORY(\"machine.BranchPredictor\");\n\nusing namespace machine;\n\nQStringView machine::branch_result_to_string(const BranchResult result, const bool abbrv) {\n    switch (result) {\n    case BranchResult::NOT_TAKEN: return abbrv ? u\"NT\" : u\"Not taken\";\n    case BranchResult::TAKEN: return abbrv ? u\"T\" : u\"Taken\";\n    default: return u\"\";\n    }\n}\n\nQStringView machine::predictor_state_to_string(const PredictorState state, const bool abbrv) {\n    switch (state) {\n    case PredictorState::NOT_TAKEN: return abbrv ? u\"NT\" : u\"Not taken\";\n    case PredictorState::TAKEN: return abbrv ? u\"T\" : u\"Taken\";\n    case PredictorState::STRONGLY_NOT_TAKEN: return abbrv ? u\"SNT\" : u\"Strongly not taken\";\n    case PredictorState::WEAKLY_NOT_TAKEN: return abbrv ? u\"WNT\" : u\"Weakly not taken\";\n    case PredictorState::WEAKLY_TAKEN: return abbrv ? u\"WT\" : u\"Weakly taken\";\n    case PredictorState::STRONGLY_TAKEN: return abbrv ? u\"ST\" : u\"Strongly taken\";\n    default: return u\"\";\n    }\n}\n\nQStringView machine::predictor_type_to_string(const PredictorType type) {\n    switch (type) {\n    case PredictorType::ALWAYS_NOT_TAKEN: return u\"Always not taken\";\n    case PredictorType::ALWAYS_TAKEN: return u\"Always taken\";\n    case PredictorType::BTFNT: return u\"Backward Taken Forward Not Taken\";\n    case PredictorType::SMITH_1_BIT: return u\"Smith 1 bit\";\n    case PredictorType::SMITH_2_BIT: return u\"Smith 2 bit\";\n    case PredictorType::SMITH_2_BIT_HYSTERESIS: return u\"Smith 2 bit with hysteresis\";\n    default: return u\"\";\n    }\n}\n\nQStringView machine::branch_type_to_string(const BranchType type) {\n    switch (type) {\n    case BranchType::JUMP: return u\"JUMP\";\n    case BranchType::BRANCH: return u\"BRANCH\";\n    default: return u\"\";\n    }\n}\n\nQString machine::addr_to_hex_str(const machine::Address address) {\n    QString hex_addr, zero_padding;\n    hex_addr = QString::number(address.get_raw(), 16);\n    zero_padding.fill('0', 8 - hex_addr.count());\n    return \"0x\" + zero_padding + hex_addr;\n}\n\nbool machine::is_predictor_type_dynamic(const PredictorType type) {\n    switch (type) {\n    case PredictorType::ALWAYS_NOT_TAKEN:\n    case PredictorType::ALWAYS_TAKEN:\n    case PredictorType::BTFNT: return false;\n\n    case PredictorType::SMITH_1_BIT:\n    case PredictorType::SMITH_2_BIT:\n    case PredictorType::SMITH_2_BIT_HYSTERESIS: return true;\n\n    default: return false;\n    }\n}\n\n/////////////////////////////////\n// BranchHistoryRegister class //\n/////////////////////////////////\n\n// Constructor\nBranchHistoryRegister::BranchHistoryRegister(const uint8_t number_of_bits)\n    : number_of_bits(init_number_of_bits(number_of_bits))\n    , register_mask(init_register_mask(number_of_bits)) {}\n\n// Init helper function to check the number of bits\nuint8_t BranchHistoryRegister::init_number_of_bits(const uint8_t b) const {\n    if (b > BP_MAX_BHR_BITS) {\n        WARN(\"Number of BHR bits (%u) was larger than %u during init\", b, BP_MAX_BHR_BITS);\n        return BP_MAX_BHR_BITS;\n    }\n    return b;\n}\n\n// Init helper function to create the register mask used for masking unused bits\nuint16_t BranchHistoryRegister::init_register_mask(const uint8_t b) const {\n    if (b >= BP_MAX_BHR_BITS) { return ~0x0; }\n    if (b == 0) { return 0x0; }\n    return (1 << b) - 1;\n}\n\nuint8_t BranchHistoryRegister::get_number_of_bits() const {\n    return number_of_bits;\n}\n\nuint16_t BranchHistoryRegister::get_register_mask() const {\n    return register_mask;\n}\n\nuint16_t BranchHistoryRegister::get_value() const {\n    return value;\n}\n\n// Pushes new value into the register\nvoid BranchHistoryRegister::update(const BranchResult result) {\n    // Shift all bits to the left\n    value = value << 1;\n\n    // Add the result as the new least significant bit\n    // By default the new LSB is 0, only set to 1 if branch was taken\n    if (result == BranchResult::TAKEN) { value |= 0x1; }\n\n    // Set all bits outside of the scope of the register to zero\n    value = value & register_mask;\n\n    emit bhr_updated(number_of_bits, value);\n}\n\nvoid BranchHistoryRegister::clear() {\n    value = 0x0;\n    emit bhr_updated(number_of_bits, value);\n}\n\n//////////////////////////////\n// BranchTargetBuffer class //\n//////////////////////////////\n\n// Constructor\nBranchTargetBuffer::BranchTargetBuffer(uint8_t number_of_bits)\n    : number_of_bits(init_number_of_bits(number_of_bits)) {\n    btb.resize(qPow(2, number_of_bits));\n}\n\nuint8_t BranchTargetBuffer::init_number_of_bits(const uint8_t b) const {\n    if (b > BP_MAX_BTB_BITS) {\n        WARN(\"Number of BTB bits (%u) was larger than %u during init\", b, BP_MAX_BTB_BITS);\n        return BP_MAX_BTB_BITS;\n    }\n    return b;\n}\n\nuint8_t BranchTargetBuffer::get_number_of_bits() const {\n    return number_of_bits;\n}\n\n// Calculate index for addressing Branch Target Buffer from instruction address\nuint16_t BranchTargetBuffer::calculate_index(const Address instruction_address) const {\n    return ((uint16_t)(instruction_address.get_raw() >> 2)) & ((1 << number_of_bits) - 1);\n}\n\nBranchTargetBufferEntry BranchTargetBuffer::get_entry(const Address instruction_address) const {\n    // Get index from instruction address\n    const uint16_t index { calculate_index(instruction_address) };\n\n    // Check index validity\n    if (index >= btb.capacity()) {\n        WARN(\"Tried to read from BTB at invalid index: %u\", index);\n        return BranchTargetBufferEntry();\n    }\n\n    return btb.at(index);\n}\n\n// Update BTB entry with given values, at index computed from the instruction address\nvoid BranchTargetBuffer::update(\n    const Address instruction_address,\n    const Address target_address,\n    const BranchType branch_type) {\n    // Get index from instruction address\n    const uint16_t btb_index { calculate_index(instruction_address) };\n\n    // Check index validity\n    if (btb_index >= btb.capacity()) {\n        WARN(\"Tried to update BTB at invalid index: %u\", btb_index);\n        return;\n    }\n\n    // Write new entry to the table\n    const BranchTargetBufferEntry btb_entry = { .entry_valid = true,\n                                                .instruction_address = instruction_address,\n                                                .target_address = target_address,\n                                                .branch_type = branch_type };\n    btb.at(btb_index) = btb_entry;\n\n    // Send signal with the data\n    emit btb_row_updated(btb_index, btb_entry);\n}\n\nvoid BranchTargetBuffer::clear() {\n    for (uint16_t i = 0; i < btb.capacity(); i++) {\n        btb.at(i) = BranchTargetBufferEntry();\n        emit btb_row_updated(i, btb.at(i));\n    }\n}\n\n/////////////////////\n// Predictor class //\n/////////////////////\n\n// Predictor Generic\n// #################\n\nPredictor::Predictor(\n    uint8_t number_of_bht_addr_bits,\n    uint8_t number_of_bht_bits,\n    PredictorState initial_state)\n    : number_of_bht_addr_bits(init_number_of_bht_addr_bits(number_of_bht_addr_bits))\n    , number_of_bht_bits(init_number_of_bht_bits(number_of_bht_bits))\n    , initial_state(initial_state) {\n    bht.resize(qPow(2, number_of_bht_bits));\n    clear_bht_state();\n    clear_bht_stats();\n}\n\nuint8_t Predictor::init_number_of_bht_addr_bits(const uint8_t b) const {\n    if (b > BP_MAX_BHT_ADDR_BITS) {\n        WARN(\n            \"Number of BHT bits from incstruction address (%u) was larger than %d during init\", b,\n            BP_MAX_BHT_ADDR_BITS);\n        return BP_MAX_BHT_ADDR_BITS;\n    }\n    return b;\n}\n\nuint8_t Predictor::init_number_of_bht_bits(const uint8_t b) const {\n    if (b > BP_MAX_BHT_BITS) {\n        WARN(\"Number of BHT bits (%u) was larger than %d during init\", b, BP_MAX_BHT_BITS);\n        return BP_MAX_BHT_BITS;\n    }\n    return b;\n}\n\nBranchResult Predictor::convert_state_to_prediction(PredictorState state) const {\n    if (state == PredictorState::NOT_TAKEN) {\n        return BranchResult::NOT_TAKEN;\n    } else if (state == PredictorState::TAKEN) {\n        return BranchResult::TAKEN;\n    } else if (state == PredictorState::WEAKLY_NOT_TAKEN) {\n        return BranchResult::NOT_TAKEN;\n    } else if (state == PredictorState::STRONGLY_NOT_TAKEN) {\n        return BranchResult::NOT_TAKEN;\n    } else if (state == PredictorState::WEAKLY_TAKEN) {\n        return BranchResult::TAKEN;\n    } else if (state == PredictorState::STRONGLY_TAKEN) {\n        return BranchResult::TAKEN;\n    } else {\n        WARN(\"Smith predictor was provided invalid state\");\n        return BranchResult::NOT_TAKEN;\n    }\n}\n\nvoid Predictor::update_stats(bool prediction_was_correct) {\n    stats.total += 1;\n    if (prediction_was_correct) {\n        stats.correct += 1;\n    } else {\n        stats.wrong += 1;\n    }\n    stats.accuracy = ((stats.correct * 100) / stats.total);\n    emit stats_updated(stats);\n}\n\nvoid Predictor::update_bht_stats(uint16_t bht_index, bool prediction_was_correct) {\n    if (bht_index >= bht.capacity()) {\n        WARN(\"Tried to access BHT at invalid index: %u\", bht_index);\n        return;\n    }\n\n    bht.at(bht_index).stats.total += 1;\n    if (prediction_was_correct) {\n        bht.at(bht_index).stats.correct += 1;\n    } else {\n        bht.at(bht_index).stats.wrong += 1;\n    }\n    bht.at(bht_index).stats.accuracy\n        = ((bht.at(bht_index).stats.correct * 100) / bht.at(bht_index).stats.total);\n    emit bht_row_updated(bht_index, bht.at(bht_index));\n}\n\n// Calculate index for addressing Branch History Table from BHR and instruction address\nuint16_t\nPredictor::calculate_bht_index(const uint16_t bhr_value, const Address instruction_address) const {\n    const uint16_t bhr_part = bhr_value << number_of_bht_addr_bits;\n    const uint16_t address_mask = (1 << number_of_bht_addr_bits) - 1;\n    const uint16_t address_part = ((uint16_t)(instruction_address.get_raw() >> 2)) & address_mask;\n    const uint16_t index = bhr_part | address_part;\n    return index;\n}\n\nvoid Predictor::clear_stats() {\n    stats = PredictionStatistics();\n    emit stats_updated(stats);\n}\n\nvoid Predictor::clear_bht_stats() {\n    for (uint16_t i = 0; i < bht.capacity(); i++) {\n        bht.at(i).stats = PredictionStatistics();\n        emit bht_row_updated(i, bht.at(i));\n    }\n}\n\nvoid Predictor::clear_bht_state() {\n    for (uint16_t i = 0; i < bht.capacity(); i++) {\n        bht.at(i).state = initial_state;\n        emit bht_row_updated(i, bht.at(i));\n    }\n}\n\nvoid Predictor::clear() {\n    clear_stats();\n    clear_bht_stats();\n    clear_bht_state();\n}\n\nvoid Predictor::flush() {\n    clear_bht_state();\n}\n\n// Always Not Taken\n// ################\n\nPredictorAlwaysNotTaken::PredictorAlwaysNotTaken() : Predictor(0, 0, PredictorState::UNDEFINED) {}\n\nBranchResult PredictorAlwaysNotTaken::predict(PredictionInput input) {\n    UNUSED(input);\n    return BranchResult::NOT_TAKEN;\n}\n\nvoid PredictorAlwaysNotTaken::update(PredictionFeedback feedback) {\n    update_stats(feedback.result == BranchResult::NOT_TAKEN);\n}\n\n// Always Taken\n// ############\n\nPredictorAlwaysTaken::PredictorAlwaysTaken() : Predictor(0, 0, PredictorState::UNDEFINED) {}\n\nBranchResult PredictorAlwaysTaken::predict(PredictionInput input) {\n    UNUSED(input);\n    return BranchResult::TAKEN;\n}\n\nvoid PredictorAlwaysTaken::update(PredictionFeedback feedback) {\n    update_stats(feedback.result == BranchResult::TAKEN);\n}\n\n// Backward Taken Forward Not Taken\n// ################################\n\nPredictorBTFNT::PredictorBTFNT() : Predictor(0, 0, PredictorState::UNDEFINED) {}\n\nBranchResult PredictorBTFNT::predict(PredictionInput input) {\n    if (input.target_address > input.instruction_address) {\n        // If target address is larger than jump instruction address (forward jump),\n        // predict not taken\n        return BranchResult::NOT_TAKEN;\n    } else {\n        // Otherwise (backward jump) predict taken\n        return BranchResult::TAKEN;\n    }\n}\n\nvoid PredictorBTFNT::update(PredictionFeedback feedback) {\n    if (feedback.target_address > feedback.instruction_address) {\n        update_stats(feedback.result == BranchResult::NOT_TAKEN);\n    } else {\n        update_stats(feedback.result == BranchResult::TAKEN);\n    }\n}\n\n// Smith 1 Bit\n// ###########\n\nPredictorSmith1Bit::PredictorSmith1Bit(\n    uint8_t number_of_bht_addr_bits,\n    uint8_t number_of_bht_bits,\n    PredictorState initial_state)\n    : Predictor(number_of_bht_addr_bits, number_of_bht_bits, initial_state) {};\n\nBranchResult PredictorSmith1Bit::predict(PredictionInput input) {\n    const uint16_t index { calculate_bht_index(input.bhr_value, input.instruction_address) };\n    if (index >= bht.capacity()) {\n        WARN(\"Tried to access BHT at invalid index: %u\", index);\n        return BranchResult::NOT_TAKEN;\n    }\n\n    // Decide prediction\n    return convert_state_to_prediction(bht.at(index).state);\n}\n\nvoid PredictorSmith1Bit::update(PredictionFeedback feedback) {\n    const uint16_t index { calculate_bht_index(feedback.bhr_value, feedback.instruction_address) };\n    if (index >= bht.capacity()) {\n        WARN(\"Tried to access BHT at invalid index: %u\", index);\n        return;\n    }\n\n    update_bht_stats(index, feedback.result == convert_state_to_prediction(bht.at(index).state));\n    update_stats(feedback.result == convert_state_to_prediction(bht.at(index).state));\n\n    // Update internal state\n    if (feedback.result == BranchResult::NOT_TAKEN) {\n        bht.at(index).state = PredictorState::NOT_TAKEN;\n    } else if (feedback.result == BranchResult::TAKEN) {\n        bht.at(index).state = PredictorState::TAKEN;\n    } else {\n        WARN(\"Smith 1 bit predictor has received invalid prediction result\");\n    }\n    emit bht_row_updated(index, bht.at(index));\n}\n\n// Smith 2 Bit\n// ###########\n\nPredictorSmith2Bit::PredictorSmith2Bit(\n    uint8_t number_of_bht_addr_bits,\n    uint8_t number_of_bht_bits,\n    PredictorState initial_state)\n    : Predictor(number_of_bht_addr_bits, number_of_bht_bits, initial_state) {};\n\nBranchResult PredictorSmith2Bit::predict(PredictionInput input) {\n    const uint16_t index { calculate_bht_index(input.bhr_value, input.instruction_address) };\n    if (index >= bht.capacity()) {\n        WARN(\"Tried to access BHT at invalid index: %u\", index);\n        return BranchResult::NOT_TAKEN;\n    }\n\n    // Decide prediction\n    return convert_state_to_prediction(bht.at(index).state);\n}\n\nvoid PredictorSmith2Bit::update(PredictionFeedback feedback) {\n    const uint16_t index { calculate_bht_index(feedback.bhr_value, feedback.instruction_address) };\n    if (index >= bht.capacity()) {\n        WARN(\"Tried to access BHT at invalid index: %u\", index);\n        return;\n    }\n\n    update_bht_stats(index, feedback.result == convert_state_to_prediction(bht.at(index).state));\n    update_stats(feedback.result == convert_state_to_prediction(bht.at(index).state));\n\n    // Read value from BHT at correct index\n    const PredictorState state = bht.at(index).state;\n\n    // Update internal state\n    if (feedback.result == BranchResult::NOT_TAKEN) {\n        if (state == PredictorState::STRONGLY_TAKEN) {\n            bht.at(index).state = PredictorState::WEAKLY_TAKEN;\n        } else if (state == PredictorState::WEAKLY_TAKEN) {\n            bht.at(index).state = PredictorState::WEAKLY_NOT_TAKEN;\n        } else if (state == PredictorState::WEAKLY_NOT_TAKEN) {\n            bht.at(index).state = PredictorState::STRONGLY_NOT_TAKEN;\n        } else if (state == PredictorState::STRONGLY_NOT_TAKEN) {\n            bht.at(index).state = PredictorState::STRONGLY_NOT_TAKEN;\n        } else {\n            WARN(\"Smith 2 bit predictor BHT has returned invalid state\");\n        }\n    } else if (feedback.result == BranchResult::TAKEN) {\n        if (state == PredictorState::STRONGLY_TAKEN) {\n            bht.at(index).state = PredictorState::STRONGLY_TAKEN;\n        } else if (state == PredictorState::WEAKLY_TAKEN) {\n            bht.at(index).state = PredictorState::STRONGLY_TAKEN;\n        } else if (state == PredictorState::WEAKLY_NOT_TAKEN) {\n            bht.at(index).state = PredictorState::WEAKLY_TAKEN;\n        } else if (state == PredictorState::STRONGLY_NOT_TAKEN) {\n            bht.at(index).state = PredictorState::WEAKLY_NOT_TAKEN;\n        } else {\n            WARN(\"Smith 2 bit predictor BHT has returned invalid state\");\n        }\n    } else {\n        WARN(\"Smith 2 bit predictor has received invalid prediction result\");\n    }\n    emit bht_row_updated(index, bht.at(index));\n}\n\n// Smith 2 Bit with hysteresis\n// ###########################\n\nPredictorSmith2BitHysteresis::PredictorSmith2BitHysteresis(\n    uint8_t number_of_bht_addr_bits,\n    uint8_t number_of_bht_bits,\n    PredictorState initial_state)\n    : Predictor(number_of_bht_addr_bits, number_of_bht_bits, initial_state) {};\n\nBranchResult PredictorSmith2BitHysteresis::predict(PredictionInput input) {\n    const uint16_t index { calculate_bht_index(input.bhr_value, input.instruction_address) };\n    if (index >= bht.capacity()) {\n        WARN(\"Tried to access BHT at invalid index: %u\", index);\n        return BranchResult::NOT_TAKEN;\n    }\n\n    // Decide prediction\n    return convert_state_to_prediction(bht.at(index).state);\n}\n\nvoid PredictorSmith2BitHysteresis::update(PredictionFeedback feedback) {\n    const uint16_t index { calculate_bht_index(feedback.bhr_value, feedback.instruction_address) };\n    if (index >= bht.capacity()) {\n        WARN(\"Tried to access BHT at invalid index: %u\", index);\n        return;\n    }\n\n    update_bht_stats(index, feedback.result == convert_state_to_prediction(bht.at(index).state));\n    update_stats(feedback.result == convert_state_to_prediction(bht.at(index).state));\n\n    // Read value from BHT at correct index\n    const PredictorState state = bht.at(index).state;\n\n    // Update internal state\n    if (feedback.result == BranchResult::NOT_TAKEN) {\n        if (state == PredictorState::STRONGLY_TAKEN) {\n            bht.at(index).state = PredictorState::WEAKLY_TAKEN;\n        } else if (state == PredictorState::WEAKLY_TAKEN) {\n            bht.at(index).state = PredictorState::STRONGLY_NOT_TAKEN;\n        } else if (state == PredictorState::WEAKLY_NOT_TAKEN) {\n            bht.at(index).state = PredictorState::STRONGLY_NOT_TAKEN;\n        } else if (state == PredictorState::STRONGLY_NOT_TAKEN) {\n            bht.at(index).state = PredictorState::STRONGLY_NOT_TAKEN;\n        } else {\n            WARN(\"Smith 2 bit hysteresis predictor BHT has returned invalid state\");\n        }\n    } else if (feedback.result == BranchResult::TAKEN) {\n        if (state == PredictorState::STRONGLY_TAKEN) {\n            bht.at(index).state = PredictorState::STRONGLY_TAKEN;\n        } else if (state == PredictorState::WEAKLY_TAKEN) {\n            bht.at(index).state = PredictorState::STRONGLY_TAKEN;\n        } else if (state == PredictorState::WEAKLY_NOT_TAKEN) {\n            bht.at(index).state = PredictorState::STRONGLY_TAKEN;\n        } else if (state == PredictorState::STRONGLY_NOT_TAKEN) {\n            bht.at(index).state = PredictorState::WEAKLY_NOT_TAKEN;\n        } else {\n            WARN(\"Smith 2 bit hysteresis predictor BHT has returned invalid state\");\n        }\n    } else {\n        WARN(\"Smith 2 bit hysteresis predictor has received invalid prediction result\");\n    }\n    emit bht_row_updated(index, bht.at(index));\n}\n\n///////////////////////////\n// BranchPredictor class //\n///////////////////////////\n\nBranchPredictor::BranchPredictor(\n    bool enabled,\n    PredictorType predictor_type,\n    PredictorState initial_state,\n    uint8_t number_of_btb_bits,\n    uint8_t number_of_bhr_bits,\n    uint8_t number_of_bht_addr_bits)\n    : enabled(enabled)\n    , initial_state(initial_state)\n    , number_of_btb_bits(init_number_of_btb_bits(number_of_btb_bits))\n    , number_of_bhr_bits(init_number_of_bhr_bits(number_of_bhr_bits))\n    , number_of_bht_addr_bits(init_number_of_bht_addr_bits(number_of_bht_addr_bits))\n    , number_of_bht_bits(init_number_of_bht_bits(number_of_bhr_bits, number_of_bht_addr_bits)) {\n    // Create predicotr\n    switch (predictor_type) {\n    case PredictorType::ALWAYS_NOT_TAKEN: predictor = new PredictorAlwaysNotTaken(); break;\n\n    case PredictorType::ALWAYS_TAKEN: predictor = new PredictorAlwaysTaken(); break;\n\n    case PredictorType::BTFNT: predictor = new PredictorBTFNT(); break;\n\n    case PredictorType::SMITH_1_BIT:\n        predictor\n            = new PredictorSmith1Bit(number_of_bht_addr_bits, number_of_bht_bits, initial_state);\n        break;\n\n    case PredictorType::SMITH_2_BIT:\n        predictor\n            = new PredictorSmith2Bit(number_of_bht_addr_bits, number_of_bht_bits, initial_state);\n        break;\n\n    case PredictorType::SMITH_2_BIT_HYSTERESIS:\n        predictor = new PredictorSmith2BitHysteresis(\n            number_of_bht_addr_bits, number_of_bht_bits, initial_state);\n        break;\n\n    default: throw std::invalid_argument(\"Invalid predictor type selected\");\n    }\n\n    LOG(\"Initialized branch predictor: %s\", qPrintable(get_predictor_name().toString()));\n\n    bhr = new BranchHistoryRegister(number_of_bhr_bits);\n    btb = new BranchTargetBuffer(number_of_btb_bits);\n\n    if (enabled) {\n        // Pass through BTB signals\n        connect(btb, &BranchTargetBuffer::btb_row_updated, this, &BranchPredictor::btb_row_updated);\n\n        // Pass through BHR signals\n        connect(bhr, &BranchHistoryRegister::bhr_updated, this, &BranchPredictor::bhr_updated);\n\n        // Pass through predictor signals\n        connect(\n            predictor, &Predictor::stats_updated, this, &BranchPredictor::predictor_stats_updated);\n        connect(\n            predictor, &Predictor::bht_row_updated, this,\n            &BranchPredictor::predictor_bht_row_updated);\n    }\n}\n\nBranchPredictor::~BranchPredictor() {\n    delete predictor;\n    predictor = nullptr;\n    delete bhr;\n    bhr = nullptr;\n    delete btb;\n    btb = nullptr;\n}\n\nuint8_t BranchPredictor::init_number_of_btb_bits(const uint8_t b) const {\n    if (b > BP_MAX_BTB_BITS) {\n        WARN(\"Number of BTB bits (%u) was larger than %d during init\", b, BP_MAX_BTB_BITS);\n        return BP_MAX_BTB_BITS;\n    }\n    return b;\n}\n\nuint8_t BranchPredictor::init_number_of_bhr_bits(const uint8_t b) const {\n    if (b > BP_MAX_BHR_BITS) {\n        WARN(\"Number of BHR bits (%u) was larger than %d during init\", b, BP_MAX_BHR_BITS);\n        return BP_MAX_BHR_BITS;\n    }\n    return b;\n}\n\nuint8_t BranchPredictor::init_number_of_bht_addr_bits(const uint8_t b) const {\n    if (b > BP_MAX_BHT_ADDR_BITS) {\n        WARN(\n            \"Number of BHT instruction address bits (%u) was larger than %d during init\", b,\n            BP_MAX_BHT_ADDR_BITS);\n        return BP_MAX_BHT_ADDR_BITS;\n    }\n    return b;\n}\n\nuint8_t BranchPredictor::init_number_of_bht_bits(const uint8_t b_bhr, const uint8_t b_addr) const {\n    // Clamp number of BHR bits\n    uint8_t checked_number_of_bits_bhr { b_bhr };\n    if (checked_number_of_bits_bhr > BP_MAX_BHR_BITS) {\n        checked_number_of_bits_bhr = BP_MAX_BHR_BITS;\n    }\n\n    // Clamp number of address index bits\n    uint8_t checked_number_of_bits_addr { b_addr };\n    if (checked_number_of_bits_addr > BP_MAX_BHT_ADDR_BITS) {\n        checked_number_of_bits_addr = BP_MAX_BHT_ADDR_BITS;\n    }\n\n    // Check sum\n    uint8_t b_sum { (uint8_t)(checked_number_of_bits_bhr + checked_number_of_bits_addr) };\n    if (b_sum > BP_MAX_BHT_BITS) { b_sum = BP_MAX_BHT_BITS; }\n\n    return b_sum;\n}\n\nbool BranchPredictor::get_enabled() const {\n    return enabled;\n}\n\nPredictorType BranchPredictor::get_predictor_type() const {\n    if (!enabled) { return PredictorType::UNDEFINED; }\n    return predictor->get_type();\n}\n\nQStringView BranchPredictor::get_predictor_name() const {\n    if (!enabled) { return u\"None\"; }\n    return predictor_type_to_string(predictor->get_type());\n}\n\nPredictorState BranchPredictor::get_initial_state() const {\n    if (!enabled) { return PredictorState::UNDEFINED; }\n    return initial_state;\n}\n\nuint8_t BranchPredictor::get_number_of_btb_bits() const {\n    if (!enabled) { return 0; }\n    return number_of_btb_bits;\n}\n\nuint8_t BranchPredictor::get_number_of_bhr_bits() const {\n    if (!enabled) { return 0; }\n    return number_of_bhr_bits;\n}\n\nuint8_t BranchPredictor::get_number_of_bht_addr_bits() const {\n    if (!enabled) { return 0; }\n    return number_of_bht_addr_bits;\n}\n\nuint8_t BranchPredictor::get_number_of_bht_bits() const {\n    if (!enabled) { return 0; }\n    return number_of_bht_bits;\n}\n\nconst PredictionStatistics *BranchPredictor::get_stats() const {\n    if (!enabled) { return nullptr; }\n    return &total_stats;\n}\n\nvoid BranchPredictor::increment_jumps() {\n    total_stats.total += 1;\n    total_stats.correct = total_stats.total - total_stats.wrong;\n    if (total_stats.total > 0) {\n        total_stats.accuracy = ((total_stats.correct * 100) / total_stats.total);\n    }\n    emit total_stats_updated(total_stats);\n}\n\nvoid BranchPredictor::increment_mispredictions() {\n    total_stats.wrong += 1;\n    total_stats.correct = total_stats.total - total_stats.wrong;\n    if (total_stats.total > 0) {\n        total_stats.accuracy = ((total_stats.correct * 100) / total_stats.total);\n    }\n    emit total_stats_updated(total_stats);\n}\n\nAddress BranchPredictor::predict_next_pc_address(\n    const Instruction instruction,\n    const Address instruction_address) const {\n    // Check if predictor is enabled\n    if (!enabled) { return instruction_address + 4; }\n\n    // Read entry from BTB\n    const BranchTargetBufferEntry btb_entry = btb->get_entry(instruction_address);\n    if (!btb_entry.entry_valid) { return instruction_address + 4; }\n    if (btb_entry.instruction_address != instruction_address) { return instruction_address + 4; }\n\n    // Make prediction\n    const PredictionInput prediction_input {\n        .instruction = instruction,\n        .bhr_value = bhr->get_value(),\n        .instruction_address = instruction_address,\n        .target_address = btb_entry.target_address,\n    };\n    BranchResult predicted_result { BranchResult::UNDEFINED };\n    if (btb_entry.branch_type == BranchType::BRANCH) {\n        predicted_result = predictor->predict(prediction_input);\n    } else {\n        predicted_result = BranchResult::TAKEN;\n    }\n\n    emit prediction_done(\n        btb->calculate_index(instruction_address),\n        predictor->calculate_bht_index(bhr->get_value(), instruction_address), prediction_input,\n        predicted_result, btb_entry.branch_type);\n\n    // If the branch was predicted Taken\n    if (predicted_result == BranchResult::TAKEN) { return btb_entry.target_address; }\n\n    // Default prediction - Not Taken\n    return instruction_address + 4;\n}\n\n// Function for updating the predictor and the Branch History Register (BHR)\nvoid BranchPredictor::update(\n    const Instruction instruction,\n    const Address instruction_address,\n    const Address target_address,\n    const BranchType branch_type,\n    const BranchResult result) {\n    // Check if predictor is enabled\n    if (!enabled) { return; }\n\n    // Update Branch Target Buffer\n    btb->update(instruction_address, target_address, branch_type);\n\n    // Update predictor only for conditional branches\n    const PredictionFeedback prediction_feedback { .instruction = instruction,\n                                                   .bhr_value = bhr->get_value(),\n                                                   .instruction_address = instruction_address,\n                                                   .target_address = target_address,\n                                                   .result = result,\n                                                   .branch_type = branch_type };\n    if (branch_type == BranchType::BRANCH) { predictor->update(prediction_feedback); }\n\n    increment_jumps();\n\n    emit update_done(\n        btb->calculate_index(instruction_address),\n        predictor->calculate_bht_index(bhr->get_value(), instruction_address), prediction_feedback);\n\n    // Update global branch history\n    bhr->update(result);\n}\n\nvoid BranchPredictor::clear() {\n    bhr->clear();\n    btb->clear();\n    predictor->clear();\n    emit cleared();\n}\n\nvoid BranchPredictor::flush() {\n    bhr->clear();\n    btb->clear();\n    predictor->flush();\n    emit flushed();\n}\n"
  },
  {
    "path": "src/machine/predictor.h",
    "content": "#ifndef PREDICTOR_H\n#define PREDICTOR_H\n\n#include \"common/logging.h\"\n#include \"instruction.h\"\n#include \"memory/address.h\"\n#include \"predictor_types.h\"\n\n#include <QObject>\n#include <QtMath>\n\nnamespace machine {\n\nQStringView branch_result_to_string(const BranchResult result, const bool abbrv = false);\n\nQStringView predictor_state_to_string(const PredictorState state, const bool abbrv = false);\n\nQStringView predictor_type_to_string(const PredictorType type);\n\nQStringView branch_type_to_string(const BranchType type);\n\nQString addr_to_hex_str(const machine::Address address);\n\nbool is_predictor_type_dynamic(const PredictorType type);\n\n/////////////////////////////////\n// BranchHistoryRegister class //\n/////////////////////////////////\n\nclass BranchHistoryRegister final : public QObject {\n    Q_OBJECT\n\npublic: // Constructors & Destructor\n    explicit BranchHistoryRegister(const uint8_t number_of_bits);\n\nprivate: // Internal functions\n    uint8_t init_number_of_bits(const uint8_t b) const;\n    uint16_t init_register_mask(const uint8_t b) const;\n\npublic: // General functions\n    uint8_t get_number_of_bits() const;\n    uint16_t get_register_mask() const;\n    uint16_t get_value() const;\n    void update(const BranchResult result);\n    void clear();\n\nsignals:\n    void bhr_updated(uint8_t number_of_bhr_bits, uint16_t register_value);\n\nprivate: // Internal variables\n    const uint8_t number_of_bits;\n    const uint16_t register_mask;\n    uint16_t value { 0 };\n};\n\n/////////////////////////////\n// BranchTargetBuffer class //\n/////////////////////////////\n\nstruct BranchTargetBufferEntry {\n    bool entry_valid { false };\n    Address instruction_address { Address::null() };\n    Address target_address { Address::null() };\n    BranchType branch_type { BranchType::UNDEFINED };\n};\n\nclass BranchTargetBuffer final : public QObject {\n    Q_OBJECT\n\npublic: // Constructors & Destructor\n    explicit BranchTargetBuffer(const uint8_t number_of_bits);\n\nprivate: // Internal functions\n    uint8_t init_number_of_bits(const uint8_t b) const;\n\npublic: // General functions\n    uint8_t get_number_of_bits() const;\n    uint16_t calculate_index(const Address instruction_address) const;\n    BranchTargetBufferEntry get_entry(const Address instruction_address) const;\n    void update(\n        const Address instruction_address,\n        const Address target_address,\n        const BranchType branch_type);\n    void clear();\n\nsignals:\n    void btb_row_updated(uint16_t index, BranchTargetBufferEntry btb_entry) const;\n\nprivate: // Internal variables\n    const uint8_t number_of_bits;\n    std::vector<BranchTargetBufferEntry> btb;\n};\n\n/////////////////////\n// Predictor class //\n/////////////////////\n\nstruct PredictionInput {\n    Instruction instruction {};\n    uint16_t bhr_value { 0 };\n    Address instruction_address { Address::null() };\n    Address target_address { Address::null() };\n};\n\nstruct PredictionFeedback {\n    Instruction instruction {};\n    uint16_t bhr_value { 0 };\n    Address instruction_address { Address::null() };\n    Address target_address { Address::null() };\n    BranchResult result { BranchResult::UNDEFINED };\n    BranchType branch_type { BranchType::UNDEFINED };\n};\n\nstruct PredictionStatistics {\n    float accuracy { 0 }; // 0 - 100 %\n    uint32_t total { 0 };\n    uint32_t correct { 0 };\n    uint32_t wrong { 0 };\n};\n\nstruct BranchHistoryTableEntry {\n    PredictorState state { PredictorState::UNDEFINED };\n    PredictionStatistics stats {}; // Per-entry statistics\n};\n\nclass Predictor : public QObject {\n    Q_OBJECT\n\npublic: // Constructors & Destructor\n    Predictor(\n        uint8_t number_of_bht_addr_bits,\n        uint8_t number_of_bht_bits,\n        PredictorState initial_state);\n    virtual ~Predictor() = default;\n\nprotected: // Internal functions\n    uint8_t init_number_of_bht_addr_bits(uint8_t b) const;\n    uint8_t init_number_of_bht_bits(uint8_t b) const;\n    BranchResult convert_state_to_prediction(PredictorState state) const;\n    void update_stats(bool prediction_was_correct);\n    void update_bht_stats(uint16_t bht_index, bool prediction_was_correct);\n\npublic: // General functions\n    uint16_t calculate_bht_index(const uint16_t bhr_value, const Address instruction_address) const;\n    virtual PredictorType get_type() const = 0;\n    virtual BranchResult predict(PredictionInput input) = 0; // Function which handles all actions\n                                                             // ties to making a branch prediction\n    virtual void update(PredictionFeedback feedback) = 0; // Update predictor based on jump / branch\n                                                          // result\n    void clear_stats();\n    void clear_bht_stats();\n    void clear_bht_state();\n    void clear();\n    void flush();\n\nsignals:\n    void stats_updated(PredictionStatistics stats) const;\n    void bht_row_updated(uint16_t index, BranchHistoryTableEntry entry) const;\n\nprotected:                                 // Internal variables\n    const uint8_t number_of_bht_addr_bits; // Number of Branch History Table (BHT) bits taken from\n                                           // instruction address\n    const uint8_t number_of_bht_bits;      // Number of Branch History Table (BHT) bits\n    const PredictorState initial_state;\n    PredictionStatistics stats;               // Total predictor statistics\n    std::vector<BranchHistoryTableEntry> bht; // Branch History Table (BHT)\n};\n\n//  Static Predictor - Always predicts not taking the branch\nclass PredictorAlwaysNotTaken final : public Predictor {\npublic: // Constructors & Destructor\n    PredictorAlwaysNotTaken();\n\npublic: // General functions\n    PredictorType get_type() const override { return PredictorType::ALWAYS_NOT_TAKEN; };\n    BranchResult predict(PredictionInput input) override;\n    void update(PredictionFeedback feedback) override;\n};\n\n//  Static Predictor - Always predicts taking the branch\nclass PredictorAlwaysTaken final : public Predictor {\npublic: // Constructors & Destructor\n    PredictorAlwaysTaken();\n\npublic: // General functions\n    PredictorType get_type() const override { return PredictorType::ALWAYS_TAKEN; };\n    BranchResult predict(PredictionInput input) override;\n    void update(PredictionFeedback feedback) override;\n};\n\n// Static Predictor - Backward Taken Forward Not Taken\nclass PredictorBTFNT final : public Predictor {\npublic: // Constructors & Destructor\n    PredictorBTFNT();\n\npublic: // General functions\n    PredictorType get_type() const override { return PredictorType::BTFNT; };\n    BranchResult predict(PredictionInput input) override;\n    void update(PredictionFeedback feedback) override;\n};\n\n// Dynamic Predictor - Smith 1 Bit\nclass PredictorSmith1Bit final : public Predictor {\npublic: // Constructors & Destructor\n    PredictorSmith1Bit(\n        uint8_t number_of_bht_addr_bits,\n        uint8_t number_of_bht_bits,\n        PredictorState initial_state);\n\npublic: // General functions\n    PredictorType get_type() const override { return PredictorType::SMITH_1_BIT; };\n    BranchResult predict(PredictionInput input) override;\n    void update(PredictionFeedback feedback) override;\n};\n\n// Dynamic Predictor - Smith 2 Bit\nclass PredictorSmith2Bit final : public Predictor {\npublic: // Constructors & Destructor\n    PredictorSmith2Bit(\n        uint8_t number_of_bht_addr_bits,\n        uint8_t number_of_bht_bits,\n        PredictorState initial_state);\n\npublic: // General functions\n    PredictorType get_type() const override { return PredictorType::SMITH_2_BIT; };\n    BranchResult predict(PredictionInput input) override;\n    void update(PredictionFeedback feedback) override;\n};\n\n// Dynamic Predictor - Smith 2 Bit with hysteresis\nclass PredictorSmith2BitHysteresis final : public Predictor {\npublic: // Constructors & Destructor\n    PredictorSmith2BitHysteresis(\n        uint8_t number_of_bht_addr_bits,\n        uint8_t number_of_bht_bits,\n        PredictorState initial_state);\n\npublic: // General functions\n    PredictorType get_type() const override { return PredictorType::SMITH_2_BIT_HYSTERESIS; };\n    BranchResult predict(PredictionInput input) override;\n    void update(PredictionFeedback feedback) override;\n};\n\n///////////////////////////\n// BranchPredictor class //\n///////////////////////////\n\nclass BranchPredictor : public QObject {\n    Q_OBJECT\n\npublic: // Constructors & Destructor\n    explicit BranchPredictor(\n        bool enabled = false,\n        PredictorType predictor_type = PredictorType::SMITH_1_BIT,\n        PredictorState initial_state = PredictorState::NOT_TAKEN,\n        uint8_t number_of_btb_bits = 2,\n        uint8_t number_of_bhr_bits = 0,\n        uint8_t number_of_bht_addr_bits = 2);\n    ~BranchPredictor();\n\nprivate: // Internal functions\n    uint8_t init_number_of_btb_bits(const uint8_t b) const;\n    uint8_t init_number_of_bhr_bits(const uint8_t b) const;\n    uint8_t init_number_of_bht_addr_bits(const uint8_t b) const;\n    uint8_t init_number_of_bht_bits(const uint8_t b_bhr, const uint8_t b_addr) const;\n\npublic: // General functions\n    bool get_enabled() const;\n    PredictorType get_predictor_type() const;\n    QStringView get_predictor_name() const;\n    PredictorState get_initial_state() const;\n    uint8_t get_number_of_btb_bits() const;\n    uint8_t get_number_of_bhr_bits() const;\n    uint8_t get_number_of_bht_addr_bits() const;\n    uint8_t get_number_of_bht_bits() const;\n    const PredictionStatistics *get_stats() const;\n    void increment_jumps();\n    void increment_mispredictions();\n    Address\n    predict_next_pc_address(const Instruction instruction, const Address instruction_address) const;\n    void update(\n        const Instruction instruction,\n        const Address instruction_address,\n        const Address target_address,\n        const BranchType branch_type,\n        const BranchResult result);\n    void clear();\n    void flush();\n\nsignals:\n    void total_stats_updated(PredictionStatistics total_stats);\n    void prediction_done(\n        uint16_t btb_index,\n        uint16_t bht_index,\n        PredictionInput input,\n        BranchResult result,\n        BranchType branch_type) const;\n    void update_done(uint16_t btb_index, uint16_t bht_index, PredictionFeedback feedback) const;\n    void predictor_stats_updated(PredictionStatistics stats) const;\n    void predictor_bht_row_updated(uint16_t index, BranchHistoryTableEntry entry) const;\n    void bhr_updated(uint8_t number_of_bhr_bits, uint16_t register_value) const;\n    void btb_row_updated(uint16_t index, BranchTargetBufferEntry btb_entry) const;\n    void cleared() const; // All infomration was reset\n    void flushed() const; // Only BHT state and BTB rows were reset\n\nprivate: // Internal variables\n    bool enabled { false };\n    PredictionStatistics total_stats;\n    Predictor *predictor;\n    BranchHistoryRegister *bhr;\n    BranchTargetBuffer *btb;\n    const PredictorState initial_state;\n    const uint8_t number_of_btb_bits; // Number of bits for addressing Branch Target Buffer (all\n                                      // taken from instruction address)\n    const uint8_t number_of_bhr_bits; // Number of bits in Branch History Register\n    const uint8_t number_of_bht_addr_bits; // Number of bits in Branch History Table which are taken\n                                           // from instruction address\n    const uint8_t number_of_bht_bits;      // = number_of_bhr_bits + number_of_bht_addr_bits\n};\n\n} // namespace machine\n\n#endif // PREDICTOR_H\n"
  },
  {
    "path": "src/machine/predictor_types.h",
    "content": "#ifndef PREDICTOR_TYPES_H\n#define PREDICTOR_TYPES_H\n\nnamespace machine {\nQ_NAMESPACE\n\n// Should not exceed 16, because uint16_t is used for addressing\n#define BP_MAX_BTB_BITS      8\n#define BP_MAX_BHR_BITS      8\n#define BP_MAX_BHT_ADDR_BITS 8\n#define BP_MAX_BHT_BITS      (BP_MAX_BHT_ADDR_BITS + BP_MAX_BHT_ADDR_BITS)\n\nenum class BranchType {\n    JUMP,   // JAL, JALR - Unconditional\n    BRANCH, // BXX - Conditional\n    UNDEFINED\n};\nQ_ENUM_NS(machine::BranchType)\n\nenum class BranchResult { NOT_TAKEN, TAKEN, UNDEFINED };\nQ_ENUM_NS(machine::BranchResult)\n\nenum class PredictorType {\n    ALWAYS_NOT_TAKEN,\n    ALWAYS_TAKEN,\n    BTFNT, // Backward Taken, Forward Not Taken\n    SMITH_1_BIT,\n    SMITH_2_BIT,\n    SMITH_2_BIT_HYSTERESIS,\n    UNDEFINED\n};\nQ_ENUM_NS(machine::PredictorType)\n\nenum class PredictorState {\n    NOT_TAKEN,          // Smith 1 bit\n    TAKEN,              // Smith 1 bit\n    STRONGLY_NOT_TAKEN, // Smith 2 bit\n    WEAKLY_NOT_TAKEN,   // Smith 2 bit\n    WEAKLY_TAKEN,       // Smith 2 bit\n    STRONGLY_TAKEN,     // Smith 2 bit\n    UNDEFINED\n};\nQ_ENUM_NS(machine::PredictorState)\n\n} // namespace machine\n\n#endif // PREDICTOR_TYPES_H\n"
  },
  {
    "path": "src/machine/programloader.cpp",
    "content": "#include \"programloader.h\"\n\n#include \"common/endian.h\"\n#include \"common/logging.h\"\n#include \"simulator_exception.h\"\n\n#include <exception>\n#include <stdexcept>\n#include <sys/types.h>\n\n// This is a workaround to ignore libelfin ref-counting cycle.\n#ifdef __SANITIZE_ADDRESS__\n    #include <sanitizer/lsan_interface.h>\n#endif\n\nLOG_CATEGORY(\"machine.ProgramLoader\");\n\nusing namespace machine;\n\nconstexpr int EM_RISCV = 243;\n\nclass MemLoader : public elf::loader {\npublic:\n    MemLoader(const QString &fname) : file(fname), mapped(nullptr), size(0) {\n        if (!file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) {\n            throw SIMULATOR_EXCEPTION(\n                Input, QString(\"Can't open input elf file for reading (\") + fname + QString(\")\"),\n                file.errorString());\n        }\n        size = file.size();\n        mapped = file.map(0, size);\n        if (mapped == nullptr) {\n            throw SIMULATOR_EXCEPTION(\n                Input, QString(\"Can't mmap input elf file (\") + fname + QString(\")\"),\n                file.errorString());\n        }\n    }\n\n    ~MemLoader() override { close(); }\n\n    void close() {\n        if (mapped != nullptr) {\n            file.unmap(mapped);\n            mapped = nullptr;\n        }\n        if (file.isOpen()) { file.close(); }\n    }\n\n    const void *load(off_t offset, size_t len) override {\n        if ((size_t)offset + len > (size_t)size) {\n            throw SANITY_EXCEPTION(\"ELF loader requested offset exceeds file size\");\n        }\n        return mapped + offset;\n    }\n\nprivate:\n    QFile file;\n    unsigned char *mapped;\n    std::int64_t size;\n};\n\nProgramLoader::ProgramLoader(const QString &file) {\n    try {\n#ifdef __SANITIZE_ADDRESS__\n        __lsan_disable();\n#endif\n        elf_file = elf::elf(std::make_shared<MemLoader>(file));\n#ifdef __SANITIZE_ADDRESS__\n        __lsan_enable();\n#endif\n    } catch (const std::exception &e) {\n#ifdef __SANITIZE_ADDRESS__\n        __lsan_enable();\n#endif\n        throw SIMULATOR_EXCEPTION(Input, \"Elf library initialization failed\", e.what());\n    }\n\n    const auto &hdr = elf_file.get_hdr();\n    executable_entry = Address(hdr.entry);\n\n    if (hdr.type != elf::et::exec) {\n        throw SIMULATOR_EXCEPTION(Input, \"Invalid input file type\", \"\");\n    }\n\n    if (hdr.machine != EM_RISCV) {\n        throw SIMULATOR_EXCEPTION(Input, \"Invalid input file architecture\", \"\");\n    }\n\n    if (hdr.ei_class == elf::elfclass::_32) {\n        LOG(\"Loaded executable: 32bit\");\n        architecture_type = ARCH32;\n    } else if (hdr.ei_class == elf::elfclass::_64) {\n        LOG(\"Loaded executable: 64bit\");\n        architecture_type = ARCH64;\n        WARN(\"64bit simulation is not fully supported.\");\n    } else {\n        throw SIMULATOR_EXCEPTION(\n            Input,\n            \"Unsupported architecture type.\"\n            \"This simulator only supports 32bit and 64bit CPUs.\",\n            \"\");\n    }\n\n    for (const auto &seg : elf_file.segments()) {\n        if (seg.get_hdr().type == elf::pt::load) { load_segments.push_back(seg); }\n    }\n}\n\nProgramLoader::ProgramLoader(const char *file) : ProgramLoader(QString::fromLocal8Bit(file)) {}\n\nProgramLoader::~ProgramLoader() {\n    // This is a fix for upstream issue where libelf creates a ref-counting cycle.\n    auto loader = elf_file.get_loader();\n    if (loader) {\n        auto mem_loader = std::dynamic_pointer_cast<MemLoader>(loader);\n        if (mem_loader) { mem_loader->close(); }\n    }\n}\n\nvoid ProgramLoader::to_memory(Memory *mem) {\n    for (const auto &seg : load_segments) {\n        uint64_t base_address = seg.get_hdr().vaddr;\n        const char *data = (const char *)seg.data();\n        size_t filesz = seg.get_hdr().filesz;\n        for (size_t i = 0; i < filesz; i++) {\n            memory_write_u8(mem, base_address + i, (uint8_t)data[i]);\n        }\n    }\n}\n\nAddress ProgramLoader::end() {\n    uint64_t last = 0;\n    for (const auto &seg : load_segments) {\n        uint64_t end_addr = seg.get_hdr().vaddr + seg.get_hdr().filesz;\n        if (end_addr > last) { last = end_addr; }\n    }\n    return Address(last + 0x10); // We add offset so we are sure that also\n                                 // pipeline is empty TODO propagate address\n                                 // deeper\n}\n\nAddress ProgramLoader::get_executable_entry() const {\n    return executable_entry;\n}\n\nSymbolTable *ProgramLoader::get_symbol_table() {\n    auto *p_st = new SymbolTable();\n    for (const auto &sec : elf_file.sections()) {\n        if (sec.get_hdr().type == elf::sht::symtab) {\n            for (const auto &sym : sec.as_symtab()) {\n                const auto &d = sym.get_data();\n                p_st->add_symbol(sym.get_name().c_str(), d.value, d.size, d.info, d.other);\n            }\n        }\n    }\n    return p_st;\n}\n\nEndian ProgramLoader::get_endian() const {\n    if (elf_file.get_hdr().ei_data == elf::elfdata::lsb) {\n        return LITTLE;\n    } else if (elf_file.get_hdr().ei_data == elf::elfdata::msb) {\n        return BIG;\n    } else {\n        throw SIMULATOR_EXCEPTION(\n            Input,\n            \"ELF header e_ident malformed.\"\n            \"Unknown value of the byte EI_DATA.\"\n            \"Expected value little (=1) or big (=2).\",\n            \"\");\n    }\n}\n\nArchitectureType ProgramLoader::get_architecture_type() const {\n    return architecture_type;\n}\n"
  },
  {
    "path": "src/machine/programloader.h",
    "content": "#ifndef PROGRAM_H\n#define PROGRAM_H\n\n#include \"common/endian.h\"\n#include \"memory/backend/memory.h\"\n#include \"symboltable.h\"\n\n#include <QFile>\n#include <cstdint>\n#include <elf/elf++.hh>\n#include <qstring.h>\n#include <qvector.h>\n\nnamespace machine {\n\nenum ArchitectureType {\n    ARCH32,\n    ARCH64,\n};\n\nclass ProgramLoader {\npublic:\n    explicit ProgramLoader(const char *file);\n    explicit ProgramLoader(const QString &file);\n    ~ProgramLoader();\n\n    void to_memory(Memory *mem); // Writes all loaded sections to memory TODO:\n                                 // really to memory ???\n    Address end();               // Return address after which there is no more code for\n                                 // sure\n    Address get_executable_entry() const;\n    SymbolTable *get_symbol_table();\n\n    Endian get_endian() const;\n\n    /** Tells whether the executable is 32bit or 64bit. */\n    ArchitectureType get_architecture_type() const;\n\nprivate:\n    elf::elf elf_file;\n    ArchitectureType architecture_type;\n    Address executable_entry;\n    std::vector<elf::segment> load_segments;\n};\n\n} // namespace machine\n\n#endif // PROGRAM_H\n"
  },
  {
    "path": "src/machine/programloader.test.cpp",
    "content": "#include \"programloader.test.h\"\n\n#include \"machine/instruction.h\"\n#include \"machine/memory/memory_utils.h\"\n#include \"machine/programloader.h\"\n#include \"memory/backend/memory.h\"\n\nusing namespace machine;\n\n// This is common program start (initial value of program counter)\n#define PC_INIT 0x00000200\n\nconst char *EXECUTABLE_NAME = \"data\";\n\nvoid TestProgramLoader::program_loader() {\n    if (not QFile::exists(EXECUTABLE_NAME)) {\n        QSKIP(\"Executable is not present, cannot test program loader.\");\n    }\n\n    ProgramLoader pl(EXECUTABLE_NAME);\n    Memory m(BIG);\n    pl.to_memory(&m);\n\n    // \taddi $1, $0, 6\n    //    QCOMPARE(Instruction(memory_read_u32(&m, PC_INIT)), Instruction(8, 0, 1, 6));\n    // j (8)0020000 (only 28 bits are used and they are logically shifted left\n    // by 2)\n    //    QCOMPARE(Instruction(memory_read_u32(&m, PC_INIT + 4)), Instruction(2, Address(0x20000 >>\n    //    2)));\n    // TODO add some more code to data and do more compares (for example more\n    // sections)\n}\n\nQTEST_APPLESS_MAIN(TestProgramLoader)"
  },
  {
    "path": "src/machine/programloader.test.h",
    "content": "#ifndef PROGRAMLOADER_TEST_H\n#define PROGRAMLOADER_TEST_H\n\n#include <QtTest>\n\nclass TestProgramLoader : public QObject {\n    Q_OBJECT\n\npublic slots:\n    void program_loader();\n};\n\n#endif // PROGRAMLOADER_TEST_H\n"
  },
  {
    "path": "src/machine/register_value.h",
    "content": "#ifndef REGISTER_VALUE_H\n#define REGISTER_VALUE_H\n\n#include \"machine/machineconfig.h\"\n\n#include <QMetaType>\n\nnamespace machine {\n\n/*\n * Register size configuration\n *\n * TODO: make compile time option\n */\nusing register_storage_t = uint64_t;\n\n/**\n * Represents a value stored in register\n *\n * Register value is semantically considered to be only an array of bits\n *  and with no meaning assumed, therefore no operations are implemented\n *  and value has to be interpreted as numerical value.\n *\n * By default, registers are initialized to zero.\n */\nclass RegisterValue {\npublic:\n    /**\n     *\n     * NOTE ON IMPLICIT CONVERSION:\n     *  Implicit conversion from unsigned integer is allowed as RegisterValue\n     *  as it essentially mean to forget the meaning of the value. Reverse\n     *  direction is always explicit.\n     *\n     * Constructor needs to be defined even for uint32_t as cpp cannot decide\n     *  whether to use uint64_t or int32_t.\n     */\n    constexpr inline RegisterValue(uint64_t value) // NOLINT(google-explicit-constructor)\n        : data(value) {};\n\n    // Must be present to avoid ambiguity.\n    constexpr inline RegisterValue(uint32_t value) // NOLINT(google-explicit-constructor)\n        : data(value) {};\n\n    constexpr inline RegisterValue() : data(0) {};\n\n    constexpr inline RegisterValue(const RegisterValue &other) = default;\n    constexpr inline RegisterValue &operator=(const RegisterValue &other) = default;\n\n    /* Sign-extending constructors */\n\n    constexpr inline RegisterValue(int64_t value) // NOLINT(google-explicit-constructor)\n        : data(value) {};\n\n    constexpr inline RegisterValue(int32_t value) // NOLINT(google-explicit-constructor)\n        : data(value) {};\n\n    constexpr inline RegisterValue(int16_t value) // NOLINT(google-explicit-constructor)\n        : data(value) {};\n\n    constexpr inline RegisterValue(int8_t value) // NOLINT(google-explicit-constructor)\n        : data(value) {};\n\n    [[nodiscard]] constexpr inline uint64_t as_xlen(Xlen xlen) const {\n        switch (xlen) {\n        case Xlen::_32: return as_u32();\n        case Xlen::_64: return as_u64();\n        default: UNREACHABLE\n        }\n    }\n\n    [[nodiscard]] constexpr inline int8_t as_i8() const { return (int8_t)data; };\n\n    [[nodiscard]] constexpr inline uint8_t as_u8() const { return (uint8_t)data; };\n\n    [[nodiscard]] constexpr inline int16_t as_i16() const { return (int16_t)data; };\n\n    [[nodiscard]] constexpr inline uint16_t as_u16() const { return (uint16_t)data; };\n\n    [[nodiscard]] constexpr inline int32_t as_i32() const { return (int32_t)data; };\n\n    [[nodiscard]] constexpr inline uint32_t as_u32() const { return (uint32_t)data; };\n\n    [[nodiscard]] constexpr inline int64_t as_i64() const { return (int64_t)data; };\n\n    [[nodiscard]] constexpr inline uint64_t as_u64() const { return (uint64_t)data; };\n\n    constexpr explicit operator int8_t() const { return as_i8(); };\n\n    constexpr explicit operator uint8_t() const { return as_u8(); };\n\n    constexpr explicit operator int16_t() const { return as_i16(); };\n\n    constexpr explicit operator uint16_t() const { return as_u16(); };\n\n    constexpr explicit operator int32_t() const { return as_i32(); };\n\n    constexpr explicit operator uint32_t() const { return as_u32(); };\n\n    constexpr explicit operator int64_t() const { return as_i64(); };\n\n    constexpr explicit operator uint64_t() const { return as_u64(); };\n\n    /**\n     * Equality operator is implemented as bit by bit comparison is reasonable\n     *  for bit array.\n     * It is necessary to make gp-register array comparable.\n     */\n    constexpr inline bool operator==(const RegisterValue &other) const {\n        return data == other.data;\n    }\n\n    constexpr inline bool operator!=(const RegisterValue &other) const { return !(other == *this); }\n\nprivate:\n    register_storage_t data;\n};\n\n} // namespace machine\n\nQ_DECLARE_METATYPE(machine::RegisterValue)\n\n#endif // REGISTER_VALUE_H\n"
  },
  {
    "path": "src/machine/registers.cpp",
    "content": "#include \"registers.h\"\n\n#include \"memory/address.h\"\n#include \"simulator_exception.h\"\n\nusing namespace machine;\n\n// TODO should this be configurable?\n//////////////////////////////////////////////////////////////////////////////\n/// Program counter initial value\n#define PC_INIT 0x00000200_addr\n#define SP_INIT 0xbfffff00_addr\n//////////////////////////////////////////////////////////////////////////////\n\nRegisters::Registers() : QObject() {\n    reset();\n}\n\nRegisters::Registers(const Registers &orig) : QObject() {\n    this->pc = orig.read_pc();\n    this->gp = orig.gp;\n}\n\nAddress Registers::read_pc() const {\n    return this->pc;\n}\n\nvoid Registers::write_pc(machine::Address address) {\n    if (address.get_raw() % 4) {\n        throw SIMULATOR_EXCEPTION(\n            UnalignedJump, \"Trying to jump to unaligned address\",\n            QString::number(address.get_raw(), 16));\n    }\n    this->pc = address;\n    emit pc_update(this->pc);\n}\n\nRegisterValue Registers::read_gp(RegisterId reg) const {\n    if (reg == 0) {\n        return { 0 }; // $0 always reads as 0\n    }\n\n    RegisterValue value = read_gp_internal(reg);\n    emit gp_read(reg, value);\n    return value;\n}\n\nRegisterValue Registers::read_gp_internal(RegisterId reg) const {\n    if (reg == 0) {\n        return { 0 }; // $0 always reads as 0\n    }\n    return gp.at(reg);\n}\n\nvoid Registers::write_gp(RegisterId reg, RegisterValue value) {\n    if (reg == 0) {\n        return; // Skip write to $0\n    }\n\n    this->gp.at(reg) = value;\n    emit gp_update(reg, value);\n}\n\nbool Registers::operator==(const Registers &c) const {\n    if (read_pc() != c.read_pc()) { return false; }\n    if (this->gp != c.gp) { return false; }\n    return true;\n}\n\nbool Registers::operator!=(const Registers &c) const {\n    return !this->operator==(c);\n}\n\nvoid Registers::reset() {\n    write_pc(PC_INIT); // Initialize to beginning program section\n    for (int i = 1; i < 32; i++) {\n        write_gp(i, 0);\n    }\n    write_gp(2_reg, SP_INIT.get_raw()); // initialize to safe RAM area -\n                                        // corresponds to Linux\n}\n"
  },
  {
    "path": "src/machine/registers.h",
    "content": "#ifndef REGISTERS_H\n#define REGISTERS_H\n\n#include \"memory/address.h\"\n#include \"register_value.h\"\n#include \"simulator_exception.h\"\n\n#include <QObject>\n#include <array>\n#include <cstdint>\n\nnamespace machine {\n\n/**\n * General-purpose register count\n */\nconstexpr size_t REGISTER_COUNT = 32;\n\n/**\n * General-purpose register identifier\n */\nclass RegisterId {\npublic:\n    inline constexpr RegisterId(uint8_t value); // NOLINT(google-explicit-constructor)\n    inline RegisterId();\n\n    constexpr operator size_t() const { return data; }; // NOLINT(google-explicit-constructor)\n\nprivate:\n    uint8_t data;\n};\n\ninline constexpr RegisterId::RegisterId(uint8_t value) : data(value) {\n    // Bounds on the id are checked at creation time and its value is immutable.\n    // Therefore, all check at when used are redundant.\n    // Main advantage is, that possible errors will appear when creating the\n    // value, which is probably close to the bug source.\n\n    SANITY_ASSERT(\n        value < REGISTER_COUNT, QString(\"Trying to create register id for out-of-bounds register \")\n                                    + QString::number(data));\n}\ninline RegisterId::RegisterId() : RegisterId(0) {}\n\ninline RegisterId operator\"\"_reg(unsigned long long value) {\n    return { static_cast<uint8_t>(value) };\n}\n\n/**\n * Register file\n */\nclass Registers : public QObject {\n    Q_OBJECT\npublic:\n    Registers();\n    Registers(const Registers &);\n\n    Address read_pc() const;        // Return current value of program counter\n    void write_pc(Address address); // Absolute jump in program counter\n\n    RegisterValue read_gp(RegisterId reg) const;          // Read general-purpose\n                                                          // register\n    RegisterValue read_gp_internal(RegisterId reg) const; // For use from GUI.\n    void write_gp(RegisterId reg, RegisterValue value);   // Write general-purpose\n                                                          // register\n\n    bool operator==(const Registers &c) const;\n    bool operator!=(const Registers &c) const;\n\n    void reset(); // Reset all values to zero (except pc)\n\nsignals:\n    void pc_update(Address val);\n    void gp_update(RegisterId reg, RegisterValue val);\n    void gp_read(RegisterId reg, RegisterValue val) const;\n\nprivate:\n    /**\n     * General purpose registers\n     *\n     * Zero register is always zero, but is allocated to avoid off-by-one.\n     * Getters and setters will never try to read or write zero register.\n     */\n    std::array<RegisterValue, REGISTER_COUNT> gp {};\n    Address pc {}; // program counter\n};\n\n} // namespace machine\n\nQ_DECLARE_METATYPE(machine::Registers)\n\n#endif // REGISTERS_H\n"
  },
  {
    "path": "src/machine/registers.test.cpp",
    "content": "#include \"registers.test.h\"\n\n#include \"machine/registers.h\"\n\nusing namespace machine;\n\nvoid TestRegisters::registers_gp0() {\n    Registers r;\n    QCOMPARE(r.read_gp(0), RegisterValue(0));\n    r.write_gp(0, 0xff);\n    QCOMPARE(r.read_gp(0), RegisterValue(0));\n}\n\nvoid TestRegisters::registers_rw_gp() {\n    Registers r;\n    for (int i = 1; i < 32; i++) {\n        r.write_gp(i, 0xf00 + i);\n        QCOMPARE(r.read_gp(i), RegisterValue(0xf00 + i));\n    }\n}\n\nvoid TestRegisters::registers_compare() {\n    Registers r1, r2;\n    QCOMPARE(r1, r2);\n    // General purpose register\n    r1.write_gp(1, 24);\n    QVERIFY(r1 != r2);\n    r2.write_gp(1, 24);\n    QCOMPARE(r1, r2);\n    // Program counter\n    r1.write_pc(r1.read_pc() + 4);\n    QVERIFY(r1 != r2);\n    r2.write_pc(r2.read_pc() + 4);\n    QCOMPARE(r1, r2);\n    // Now let's try copy (and verify only with gp this time)\n    Registers r3(r1);\n    QCOMPARE(r3, r1);\n    r3.write_gp(12, 19);\n    QVERIFY(r1 != r3);\n    r1.write_gp(12, 19);\n    QCOMPARE(r3, r1);\n}\n\nQTEST_APPLESS_MAIN(TestRegisters)"
  },
  {
    "path": "src/machine/registers.test.h",
    "content": "#ifndef REGISTERS_TEST_H\n#define REGISTERS_TEST_H\n\n#include <QtTest>\n\nclass TestRegisters : public QObject {\n    Q_OBJECT\n\npublic slots:\n    static void registers_gp0();\n    static void registers_rw_gp();\n    static void registers_compare();\n};\n\n#endif // REGISTERS_TEST_H\n"
  },
  {
    "path": "src/machine/simulator_exception.cpp",
    "content": "#include \"simulator_exception.h\"\n\n#include <cstring>\n#include <iostream>\n#include <utility>\n\nusing namespace machine;\n\nSimulatorException::SimulatorException(QString reason, QString ext, QString file, int line) {\n    this->name = \"Exception\";\n    this->reason = std::move(reason);\n    this->ext = std::move(ext);\n    this->file = std::move(file);\n    this->line = line;\n    this->cached_what = nullptr;\n}\n\nSimulatorException::~SimulatorException() {\n    if (this->cached_what) { delete[] this->cached_what; }\n}\n\nconst char *SimulatorException::what() const noexcept {\n    if (this->cached_what) { return this->cached_what; }\n\n    std::string message = this->msg(true).toStdString();\n    this->cached_what = new char[message.length() + 1];\n    std::strcpy(this->cached_what, message.c_str());\n\n    return this->cached_what;\n}\n\nQString SimulatorException::msg(bool pos) const {\n    QString message;\n    message += name;\n    if (pos) {\n        message += QString(\" (\") + QString(this->file) + QString(\":\") + QString::number(this->line)\n                   + QString(\")\");\n    }\n    message += \": \" + this->reason;\n    if (!this->ext.isEmpty()) {\n        message += QString(\": \");\n        message += this->ext;\n    }\n    return message;\n}\n\n#define EXCEPTION(NAME, PARENT)                                                                    \\\n    SimulatorException##NAME::SimulatorException##NAME(                                            \\\n        QString reason, QString ext, QString file, int line)                                       \\\n        : SimulatorException##PARENT(reason, ext, file, line) {                                    \\\n        name = #NAME;                                                                              \\\n    }\nSIMULATOR_EXCEPTIONS\n#undef EXCEPTION\n"
  },
  {
    "path": "src/machine/simulator_exception.h",
    "content": "#ifndef SIMULATOR_EXCEPTION_H\n#define SIMULATOR_EXCEPTION_H\n\n#include <exception>\n#include <qstring.h>\n\nnamespace machine {\n\n// Base exception for all machine ones\nclass SimulatorException : public std::exception {\npublic:\n    SimulatorException(QString reason, QString ext, QString file, int line);\n    ~SimulatorException();\n    const char *what() const noexcept override;\n    QString msg(bool pos) const;\n\nprotected:\n    QString name, reason, ext, file;\n    int line;\n\nprivate:\n    mutable char *cached_what;\n};\n\n/* This is list of all QtRvSim specific exceptions\n *\n * Input:\n *  Exception durring input loading\n * Runtime:\n *  Exceptions caused by machine invalid input or unsupported action\n * UnsupportedInstruction:\n *  Decoded instruction is not supported.\n *  This can be cause by really using some unimplemented instruction or because\n * of problems in instruction decode. UnsupportedAluOperation: Decoded ALU\n * operation is not supported This is basically same exception as\n * SimulatorExceptionUnsupportedInstruction but it is emmited from ALU when\n * executed and not before that. Overflow: Integer operation resulted to\n * overflow (or underflow as we are working with unsigned values) This is for\n * sure caused by program it self. UnalignedJump: Instruction is jumping to\n * unaligned address (ADDR % 4 != 0) This can be caused by bug or by user\n * program as it can be jumping relative to register This shouldn't be happening\n * with non-register jumps as those should be verified by compiler\n * UnknownMemoryControl:\n *  Used unknown MemoryAccess control value (write_ctl or read_ctl)\n *  This can be raised by invalid instruction but in such case we shoul raise\n * UnknownInstruction instead So this should signal just some QtRvSim bug.\n * OutOfMemoryAccess:\n *  Trying to access address outside of the memory\n *  As we are simulating whole 32bit memory address space then this is most\n * probably QtRvSim bug if raised not program. Sanity: This is sanity check\n * exception\n * PageFault:\n *  A page-fault occurs during virtual memory translation when the requested\n *  access cannot be satisfied. Typical causes:\n *   - No PTE present for the accessed virtual page (page not mapped).\n *   - PTE is present but not valid (V == 0).\n *   - Permissions violation (access type not allowed by PTE: read/write/execute).\n *   - Malformed or unexpected PTE contents (e.g. non-leaf with R/W/X set).\n *   - Wrong privilege level or ASID mismatch.\n *  PageFault is a runtime exception raised by the page-table walker and\n *  is recoverable after the page-fault handler allocates pages or installs\n *  mappings (demand paging).\n */\n#define SIMULATOR_EXCEPTIONS                                                                       \\\n    EXCEPTION(Input, )                                                                             \\\n    EXCEPTION(Runtime, )                                                                           \\\n    EXCEPTION(UnsupportedInstruction, Runtime)                                                     \\\n    EXCEPTION(UnsupportedAluOperation, Runtime)                                                    \\\n    EXCEPTION(Overflow, Runtime)                                                                   \\\n    EXCEPTION(UnalignedJump, Runtime)                                                              \\\n    EXCEPTION(UnknownMemoryControl, Runtime)                                                       \\\n    EXCEPTION(OutOfMemoryAccess, Runtime)                                                          \\\n    EXCEPTION(PageFault, Runtime)                                                                  \\\n    EXCEPTION(Sanity, )                                                                            \\\n    EXCEPTION(SyscallUnknown, Runtime)\n\n#define EXCEPTION(NAME, PARENT)                                                                    \\\n    class SimulatorException##NAME : public SimulatorException##PARENT {                           \\\n    public:                                                                                        \\\n        SimulatorException##NAME(QString reason, QString ext, QString file, int line);             \\\n    };\nSIMULATOR_EXCEPTIONS\n#undef EXCEPTION\n\n// This is helper macro for throwing QtRvSim exceptions\n#define SIMULATOR_EXCEPTION(TYPE, REASON, EXT)                                                     \\\n    (machine::SimulatorException##TYPE(QString(REASON), QString(EXT), QString(__FILE__), __LINE__))\n\n#define SANITY_EXCEPTION(MSG)                                                                      \\\n    SIMULATOR_EXCEPTION(                                                                           \\\n        Sanity, \"Internal error\",                                                                  \\\n        \"An internal error occurred in the simulator. We are sorry for the inconvenience.\"         \\\n        \"To help get the simulator fixed ASAP, please report this incident to your \"               \\\n        \"teacher and/or file an issue at\\n\\n\"                                                      \\\n        \"https://github.com/cvut/qtrvsim/issues.\\n\\n\"                                              \\\n        \"Please attach the program you were executing, used configuration of the \"                 \\\n        \"simulator, description of steps you have taken and a copy of the following \"              \\\n        \"message:\\n\\n\" #MSG)\n\n// Sanity comparison potentially throwing SimulatorExceptionSanity\n#define SANITY_ASSERT(COND, MSG)                                                                   \\\n    do {                                                                                           \\\n        if (!(COND)) throw SANITY_EXCEPTION(\"Sanity check failed (\" #COND \"):\" #MSG);              \\\n    } while (false)\n\n} // namespace machine\n\n#endif // SIMULATOR_EXCEPTION_H\n"
  },
  {
    "path": "src/machine/symboltable.cpp",
    "content": "#include \"symboltable.h\"\n\n#include <utility>\n\nusing namespace machine;\n\nSymbolTableEntry::SymbolTableEntry(\n    QString name,\n    SymbolValue value,\n    SymbolSize size,\n    SymbolInfo info,\n    SymbolOther other)\n    : name(std::move(name))\n    , value(value)\n    , size(size)\n    , info(info)\n    , other(other) {}\n\nSymbolTable::SymbolTable(QObject *parent)\n    : QObject(parent)\n    , map_name_to_symbol()\n    , map_value_to_symbol() {}\n\nSymbolTable::~SymbolTable() {\n    map_name_to_symbol.clear(); // Does not own data.\n    auto iter = map_value_to_symbol.begin();\n    while (iter != map_value_to_symbol.end()) {\n        const SymbolTableEntry *p_entry = iter.value();\n        iter = map_value_to_symbol.erase(iter); // Advances iterator.\n        delete p_entry;\n    }\n}\n\nvoid SymbolTable::add_symbol(\n    const QString &name,\n    SymbolValue value,\n    uint32_t size,\n    unsigned char info,\n    unsigned char other) {\n    auto *p_entry = new SymbolTableEntry(name, value, size, info, other);\n    map_value_to_symbol.insert(value, p_entry);\n    map_name_to_symbol.insert(name, p_entry);\n}\n\nvoid SymbolTable::remove_symbol(const QString &name) {\n    auto *p_entry = map_name_to_symbol.take(name);\n    if (p_entry == nullptr) { return; }\n    map_value_to_symbol.remove(p_entry->value, p_entry);\n    delete p_entry;\n}\n\nvoid SymbolTable::set_symbol(\n    const QString &name,\n    SymbolValue value,\n    uint32_t size,\n    unsigned char info,\n    unsigned char other) {\n    remove_symbol(name);\n    add_symbol(name, value, size, info, other);\n}\n\n// TODO cpp17 - return optional\nbool SymbolTable::name_to_value(SymbolValue &value, const QString &name) const {\n    auto *p_entry = map_name_to_symbol.value(name);\n    if (p_entry == nullptr) {\n        value = 0;\n        return false;\n    }\n    value = p_entry->value;\n    return true;\n}\n\n// TODO cpp17 - return optional\nbool SymbolTable::location_to_name(QString &name, SymbolValue value) const {\n    auto *p_entry = map_value_to_symbol.value(value);\n    if (p_entry == nullptr) {\n        name = \"\";\n        return false;\n    }\n    name = p_entry->name;\n    return true;\n}\n\nQStringList SymbolTable::names() const {\n    return map_name_to_symbol.keys();\n}\n"
  },
  {
    "path": "src/machine/symboltable.h",
    "content": "#ifndef SYMBOLTABLE_H\n#define SYMBOLTABLE_H\n\n#include \"utils.h\"\n\n#include <QMap>\n#include <QMultiMap>\n#include <QObject>\n#include <QString>\n#include <QStringList>\n\nnamespace machine {\n\nusing SymbolValue = uint64_t;\nusing SymbolSize = uint32_t;\nusing SymbolInfo = unsigned char;\nusing SymbolOther = unsigned char;\n\nstruct SymbolTableEntry {\n    SymbolTableEntry(\n        QString name,\n        SymbolValue value,\n        SymbolSize size,\n        SymbolInfo info = 0,\n        SymbolOther other = 0);\n\n    const QString name;\n    const SymbolValue value;\n    const SymbolSize size;\n    const SymbolInfo info;\n    const SymbolOther other;\n};\n\n/**\n * ELF symbol table.\n *\n * Used tu jump to symbol and for integrated assembler.\n * To learn more, about ELF symbol table internal, I recommend this\n * [link](https://blogs.oracle.com/solaris/inside-elf-symbol-tables-v2).\n */\nclass SymbolTable : public QObject {\n    Q_OBJECT\npublic:\n    explicit SymbolTable(QObject *parent = nullptr);\n    ~SymbolTable() override;\n\n    void add_symbol(\n        const QString &name,\n        SymbolValue value,\n        SymbolSize size,\n        SymbolInfo info = 0,\n        SymbolOther other = 0);\n\n    void set_symbol(\n        const QString &name,\n        SymbolValue value,\n        SymbolSize size,\n        SymbolInfo info = 0,\n        SymbolOther other = 0);\n\n    void remove_symbol(const QString &name);\n\n    QStringList names() const;\npublic slots:\n    bool name_to_value(SymbolValue &value, const QString &name) const;\n    /**\n     * FIXME: This is design is flawed. The table can contain multiple names for\n     * single location as it is multimap.\n     */\n    bool location_to_name(QString &name, SymbolValue value) const;\n\nprivate:\n    // QString cannot be made const, because it would not fit into QT gui API.\n    QMap<QString, OWNED SymbolTableEntry *> map_name_to_symbol;\n    QMultiMap<SymbolValue, SymbolTableEntry *> map_value_to_symbol;\n};\n\n} // namespace machine\n\n#endif // SYMBOLTABLE_H\n"
  },
  {
    "path": "src/machine/tests/data/cache_test_performance_data.h",
    "content": "#ifndef CACHE_TEST_PERFORMANCE_DATA_H\n#define CACHE_TEST_PERFORMANCE_DATA_H\n\n#include <array>\n#include <tuple>\nusing std::array;\nusing std::tuple;\n\n/**\n * Cache hits amd misses recorded after creation of these test.\n * Values are invalidated by any change of testcases.\n * Values were not checked manually, failure of test indicates only a change\n * not a bug.\n */\nconstexpr std::array<tuple<unsigned, unsigned>, 3270> cache_test_performance_data { {\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },\n    { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },\n    { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },\n    { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 18, 28 }, { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 18, 28 }, { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },\n    { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },\n    { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },\n    { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 16, 30 },\n    { 18, 3 },  { 18, 3 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 16, 30 }, { 18, 3 },\n    { 18, 3 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },\n    { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },\n    { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },\n    { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },\n    { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },\n    { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 16, 30 }, { 18, 3 },  { 18, 3 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 16, 30 }, { 18, 3 },  { 18, 3 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },\n    { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },\n    { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },\n    { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },\n    { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 18, 28 }, { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 18, 28 }, { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },\n    { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },\n    { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },\n    { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 16, 30 }, { 18, 3 },  { 18, 3 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 16, 30 }, { 18, 3 },  { 18, 3 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },\n    { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },\n    { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },\n    { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 16, 30 },\n    { 18, 3 },  { 18, 3 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 16, 30 }, { 18, 3 },\n    { 18, 3 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },\n    { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },\n    { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },\n    { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },\n    { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },\n    { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 18, 28 }, { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 18, 28 }, { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },\n    { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },\n    { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },\n    { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },\n    { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 16, 30 }, { 18, 3 },  { 18, 3 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 16, 30 }, { 18, 3 },  { 18, 3 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },\n    { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },\n    { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },\n    { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 16, 30 }, { 18, 3 },  { 18, 3 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 16, 30 }, { 18, 3 },  { 18, 3 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },\n    { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },\n    { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },\n    { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 18, 28 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 18, 28 }, { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },\n    { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },\n    { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },\n    { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },\n    { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },\n    { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 16, 30 }, { 18, 3 },  { 18, 3 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 16, 30 }, { 18, 3 },  { 18, 3 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },\n    { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },\n    { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },\n    { 21, 3 },  { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },\n    { 35, 2 },  { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },\n    { 27, 4 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },\n    { 27, 3 },  { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 35, 2 },  { 27, 3 },  { 25, 3 },  { 35, 2 },  { 27, 3 },\n    { 25, 3 },  { 45, 3 },  { 26, 3 },  { 29, 3 },  { 48, 3 },  { 21, 3 },  { 27, 4 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 16, 30 }, { 18, 3 },  { 18, 3 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 16, 30 }, { 18, 3 },  { 18, 3 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },\n    { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },\n    { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },\n    { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },\n    { 0, 0 },   { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },\n    { 0, 0 },   { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n    { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },\n    { 19, 1 },  { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },   { 33, 1 },\n    { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },  { 33, 1 },  { 19, 1 },  { 19, 1 },\n    { 44, 2 },  { 19, 2 },  { 19, 2 },  { 0, 0 },   { 0, 0 },   { 0, 0 },\n} };\n\n#endif // CACHE_TEST_PERFORMANCE_DATA_H"
  },
  {
    "path": "src/machine/tests/utils/integer_decomposition.h",
    "content": "#ifndef INTEGER_DECOMPOSITION_H\n#define INTEGER_DECOMPOSITION_H\n\n#include \"common/endian.h\"\n\n#include <array>\n#include <cstdint>\n\nusing namespace machine;\nusing std::array;\nusing std::uint16_t;\nusing std::uint32_t;\nusing std::uint64_t;\nusing std::uint8_t;\n\ntemplate<typename T, size_t N>\nconstexpr void decompose_int_to_array(array<T, N> &dst, uint64_t value, Endian endian) {\n    for (size_t i = 0; i < N; ++i) {\n        T val = (T)(value >> (8 * sizeof(T) * i));\n        if (endian == LITTLE) {\n            dst.at(i) = val;\n        } else {\n            dst.at(N - i - 1) = val;\n        }\n    }\n}\n\nstruct IntegerDecomposition {\n    uint64_t u64 {};\n    array<uint32_t, 2> u32 {};\n    array<uint16_t, 4> u16 {};\n    array<uint8_t, 8> u8 {};\n\n    constexpr IntegerDecomposition() = default;\n\n    constexpr IntegerDecomposition(uint64_t value, Endian endian) {\n        u64 = value;\n        decompose_int_to_array(u32, value, endian);\n        decompose_int_to_array(u16, value, endian);\n        decompose_int_to_array(u8, value, endian);\n    }\n\n    constexpr bool operator==(const IntegerDecomposition &rhs) const {\n        return u64 == rhs.u64 && u32 == rhs.u32 && u16 == rhs.u16 && u8 == rhs.u8;\n    }\n\n    constexpr bool operator!=(const IntegerDecomposition &rhs) const { return !(rhs == *this); }\n};\n\nQ_DECLARE_METATYPE(IntegerDecomposition)\n\n#endif // INTEGER_DECOMPOSITION_H\n"
  },
  {
    "path": "src/machine/utils.h",
    "content": "#ifndef UTILS_H\n#define UTILS_H\n\n#include <algorithm>\n#include <array>\n#include <cassert>\n#include <cstdint>\n#include <cstdlib>\n#include <stdexcept>\n#include <type_traits>\n\nusing std::size_t;\n\n#if __GNUC__ >= 7\n    #define FALLTROUGH __attribute__((fallthrough));\n#else\n    #define FALLTROUGH\n#endif\n\n/**\n * Raw byte in memory\n * - Intended for raw byte array\n */\ntypedef unsigned char byte;\n\ninline constexpr uint32_t sign_extend(uint16_t v) {\n    return ((v & 0x8000) ? 0xFFFF0000 : 0) | v;\n}\n\ntemplate<class T>\nvoid ignore(const T &) {}\n\n#define UNIMPLEMENTED throw std::logic_error(\"Unimplemented\");\n#define PANIC         throw std::logic_error(\"The program panicked.\");\n#define UNREACHABLE   Q_UNREACHABLE();\n#define UNUSED(arg)   ignore(arg);\n/**\n * Annotate pointer ownership.\n * Smartpointer may be used in the future.\n */\n#define OWNED\n\n/**\n * Test whether given address is aligned to the given type.\n *\n * @tparam Address      type used to store the address\n * @tparam T            type to check alignment\n * @param address       address to check\n * @return              true if is aligned\n */\ntemplate<typename Address, typename T>\ninline bool is_aligned_generic(Address address) {\n    return static_cast<uintptr_t>(address) % std::alignment_of<T>::value;\n}\n\n/**\n * Divide and round up\n */\ntemplate<typename T1, typename T2>\ninline constexpr T1 divide_and_ceil(T1 divident, T2 divisor) {\n    return ((divident + divisor - 1) / divisor);\n}\n\n/**\n * Rounds number `n` down to the multiple of `base`.\n * (To by used as better macro.)\n *\n * @tparam T1   type of n\n * @tparam T2   type of base\n */\ntemplate<typename T1, typename T2>\ninline constexpr T1 round_down_to_multiple(T1 n, T2 base) {\n    return (n / base) * base;\n}\n\n/**\n * Rounds number `n` up to the multiple of `base`.\n * (To by used as better macro.)\n *\n * @tparam T1   type of n\n * @tparam T2   type of base\n */\ntemplate<typename T1, typename T2>\ninline constexpr T1 round_up_to_multiple(T1 n, T2 base) {\n    return round_down_to_multiple(n + base, base);\n}\n\n#endif // UTILS_H\n"
  },
  {
    "path": "src/os_emulation/CMakeLists.txt",
    "content": "project(os_emulation\n        DESCRIPTION \"Simple emulation of a Linux like kernel\")\n\nset(CMAKE_AUTOMOC ON)\n\nset(os_emulation_SOURCES\n        ossyscall.cpp\n        )\nset(os_emulation_HEADERS\n        ossyscall.h\n        syscall_nr.h\n        target_errno.h\n        )\n\nadd_library(os_emulation STATIC\n        ${os_emulation_SOURCES}\n        ${os_emulation_HEADERS})\ntarget_link_libraries(os_emulation\n\t\tPRIVATE ${QtLib}::Core)"
  },
  {
    "path": "src/os_emulation/ossyscall.cpp",
    "content": "#include \"ossyscall.h\"\n\n#include \"machine/core.h\"\n#include \"machine/utils.h\"\n#include \"posix_polyfill.h\"\n#include \"syscall_nr.h\"\n#include \"target_errno.h\"\n\n#include <cerrno>\n#include <cinttypes>\n#include <cstdio>\n#include <fcntl.h>\n#include <sys/stat.h>\n\nusing namespace machine;\nusing namespace osemu;\n\n// The copied from musl-libc\n\n#define TARGET_O_CREAT     0100\n#define TARGET_O_EXCL      0200\n#define TARGET_O_NOCTTY    0400\n#define TARGET_O_TRUNC     01000\n#define TARGET_O_APPEND    02000\n#define TARGET_O_NONBLOCK  04000\n#define TARGET_O_DSYNC     010000\n#define TARGET_O_SYNC      04010000\n#define TARGET_O_RSYNC     04010000\n#define TARGET_O_DIRECTORY 0200000\n#define TARGET_O_NOFOLLOW  0400000\n#define TARGET_O_CLOEXEC   02000000\n\n#define TARGET_O_PATH 010000000\n\n#define TARGET_O_SYNC1 040000\n\n#define TARGET_O_ACCMODE (03 | TARGET_O_PATH)\n#define TARGET_O_RDONLY  00\n#define TARGET_O_WRONLY  01\n#define TARGET_O_RDWR    02\n\n#define TARGET_AT_FDCWD -100\n\nstatic const QMap<int, int> map_target_o_flags_to_o_flags = {\n#ifdef O_CREAT\n    { TARGET_O_CREAT, O_CREAT },\n#endif\n#ifdef O_EXCL\n    { TARGET_O_EXCL, O_EXCL },\n#endif\n#ifdef O_NOCTTY\n    { TARGET_O_NOCTTY, O_NOCTTY },\n#endif\n#ifdef O_TRUNC\n    { TARGET_O_TRUNC, O_TRUNC },\n#endif\n#ifdef O_APPEND\n    { TARGET_O_APPEND, O_APPEND },\n#endif\n#ifdef O_NONBLOCK\n    { TARGET_O_NONBLOCK, O_NONBLOCK },\n#endif\n#ifdef O_DSYNC\n    { TARGET_O_DSYNC, O_DSYNC },\n#endif\n#ifdef O_SYNC\n    { TARGET_O_SYNC1, O_SYNC },\n#endif\n#ifdef O_RSYNC\n    { TARGET_O_SYNC1, O_RSYNC },\n#endif\n#ifdef O_DIRECTORY\n    { TARGET_O_DIRECTORY, O_DIRECTORY },\n#endif\n#ifdef O_NOFOLLOW\n    { TARGET_O_NOFOLLOW, O_NOFOLLOW },\n#endif\n#ifdef O_CLOEXEC\n    { TARGET_O_CLOEXEC, O_CLOEXEC },\n#endif\n};\n\n#if defined(S_IRUSR) & defined(S_IWUSR) & defined(S_IRGRP) & defined(S_IWGRP) & defined(S_IROTH)   \\\n    & defined(S_IWOTH)\n    #define OPEN_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)\n#else\n    #define OPEN_MODE 0\n#endif\n\n// The list copyied from musl-libc\n\nstatic const QMap<int, int> errno_map = {\n#ifdef EPERM\n    { EPERM, TARGET_EPERM },\n#endif\n#ifdef ENOENT\n    { ENOENT, TARGET_ENOENT },\n#endif\n#ifdef ESRCH\n    { ESRCH, TARGET_ESRCH },\n#endif\n#ifdef EINTR\n    { EINTR, TARGET_EINTR },\n#endif\n#ifdef EIO\n    { EIO, TARGET_EIO },\n#endif\n#ifdef ENXIO\n    { ENXIO, TARGET_ENXIO },\n#endif\n#ifdef E2BIG\n    { E2BIG, TARGET_E2BIG },\n#endif\n#ifdef ENOEXEC\n    { ENOEXEC, TARGET_ENOEXEC },\n#endif\n#ifdef EBADF\n    { EBADF, TARGET_EBADF },\n#endif\n#ifdef ECHILD\n    { ECHILD, TARGET_ECHILD },\n#endif\n#ifdef EAGAIN\n    { EAGAIN, TARGET_EAGAIN },\n#endif\n#ifdef ENOMEM\n    { ENOMEM, TARGET_ENOMEM },\n#endif\n#ifdef EACCES\n    { EACCES, TARGET_EACCES },\n#endif\n#ifdef EFAULT\n    { EFAULT, TARGET_EFAULT },\n#endif\n#ifdef ENOTBLK\n    { ENOTBLK, TARGET_ENOTBLK },\n#endif\n#ifdef EBUSY\n    { EBUSY, TARGET_EBUSY },\n#endif\n#ifdef EEXIST\n    { EEXIST, TARGET_EEXIST },\n#endif\n#ifdef EXDEV\n    { EXDEV, TARGET_EXDEV },\n#endif\n#ifdef ENODEV\n    { ENODEV, TARGET_ENODEV },\n#endif\n#ifdef ENOTDIR\n    { ENOTDIR, TARGET_ENOTDIR },\n#endif\n#ifdef EISDIR\n    { EISDIR, TARGET_EISDIR },\n#endif\n#ifdef EINVAL\n    { EINVAL, TARGET_EINVAL },\n#endif\n#ifdef ENFILE\n    { ENFILE, TARGET_ENFILE },\n#endif\n#ifdef EMFILE\n    { EMFILE, TARGET_EMFILE },\n#endif\n#ifdef ENOTTY\n    { ENOTTY, TARGET_ENOTTY },\n#endif\n#ifdef ETXTBSY\n    { ETXTBSY, TARGET_ETXTBSY },\n#endif\n#ifdef EFBIG\n    { EFBIG, TARGET_EFBIG },\n#endif\n#ifdef ENOSPC\n    { ENOSPC, TARGET_ENOSPC },\n#endif\n#ifdef ESPIPE\n    { ESPIPE, TARGET_ESPIPE },\n#endif\n#ifdef EROFS\n    { EROFS, TARGET_EROFS },\n#endif\n#ifdef EMLINK\n    { EMLINK, TARGET_EMLINK },\n#endif\n#ifdef EPIPE\n    { EPIPE, TARGET_EPIPE },\n#endif\n#ifdef EDOM\n    { EDOM, TARGET_EDOM },\n#endif\n#ifdef ERANGE\n    { ERANGE, TARGET_ERANGE },\n#endif\n#ifdef ENOMSG\n    { ENOMSG, TARGET_ENOMSG },\n#endif\n#ifdef EIDRM\n    { EIDRM, TARGET_EIDRM },\n#endif\n#ifdef ECHRNG\n    { ECHRNG, TARGET_ECHRNG },\n#endif\n#ifdef EL2NSYNC\n    { EL2NSYNC, TARGET_EL2NSYNC },\n#endif\n#ifdef EL3HLT\n    { EL3HLT, TARGET_EL3HLT },\n#endif\n#ifdef EL3RST\n    { EL3RST, TARGET_EL3RST },\n#endif\n#ifdef ELNRNG\n    { ELNRNG, TARGET_ELNRNG },\n#endif\n#ifdef EUNATCH\n    { EUNATCH, TARGET_EUNATCH },\n#endif\n#ifdef ENOCSI\n    { ENOCSI, TARGET_ENOCSI },\n#endif\n#ifdef EL2HLT\n    { EL2HLT, TARGET_EL2HLT },\n#endif\n#ifdef EDEADLK\n    { EDEADLK, TARGET_EDEADLK },\n#endif\n#ifdef ENOLCK\n    { ENOLCK, TARGET_ENOLCK },\n#endif\n#ifdef EBADE\n    { EBADE, TARGET_EBADE },\n#endif\n#ifdef EBADR\n    { EBADR, TARGET_EBADR },\n#endif\n#ifdef EXFULL\n    { EXFULL, TARGET_EXFULL },\n#endif\n#ifdef ENOANO\n    { ENOANO, TARGET_ENOANO },\n#endif\n#ifdef EBADRQC\n    { EBADRQC, TARGET_EBADRQC },\n#endif\n#ifdef EBADSLT\n    { EBADSLT, TARGET_EBADSLT },\n#endif\n#ifdef EDEADLOCK\n    { EDEADLOCK, TARGET_EDEADLOCK },\n#endif\n#ifdef EBFONT\n    { EBFONT, TARGET_EBFONT },\n#endif\n#ifdef ENOSTR\n    { ENOSTR, TARGET_ENOSTR },\n#endif\n#ifdef ENODATA\n    { ENODATA, TARGET_ENODATA },\n#endif\n#ifdef ETIME\n    { ETIME, TARGET_ETIME },\n#endif\n#ifdef ENOSR\n    { ENOSR, TARGET_ENOSR },\n#endif\n#ifdef ENONET\n    { ENONET, TARGET_ENONET },\n#endif\n#ifdef ENOPKG\n    { ENOPKG, TARGET_ENOPKG },\n#endif\n#ifdef EREMOTE\n    { EREMOTE, TARGET_EREMOTE },\n#endif\n#ifdef ENOLINK\n    { ENOLINK, TARGET_ENOLINK },\n#endif\n#ifdef EADV\n    { EADV, TARGET_EADV },\n#endif\n#ifdef ESRMNT\n    { ESRMNT, TARGET_ESRMNT },\n#endif\n#ifdef ECOMM\n    { ECOMM, TARGET_ECOMM },\n#endif\n#ifdef EPROTO\n    { EPROTO, TARGET_EPROTO },\n#endif\n#ifdef EDOTDOT\n    { EDOTDOT, TARGET_EDOTDOT },\n#endif\n#ifdef EMULTIHOP\n    { EMULTIHOP, TARGET_EMULTIHOP },\n#endif\n#ifdef EBADMSG\n    { EBADMSG, TARGET_EBADMSG },\n#endif\n#ifdef ENAMETOOLONG\n    { ENAMETOOLONG, TARGET_ENAMETOOLONG },\n#endif\n#ifdef EOVERFLOW\n    { EOVERFLOW, TARGET_EOVERFLOW },\n#endif\n#ifdef ENOTUNIQ\n    { ENOTUNIQ, TARGET_ENOTUNIQ },\n#endif\n#ifdef EBADFD\n    { EBADFD, TARGET_EBADFD },\n#endif\n#ifdef EREMCHG\n    { EREMCHG, TARGET_EREMCHG },\n#endif\n#ifdef ELIBACC\n    { ELIBACC, TARGET_ELIBACC },\n#endif\n#ifdef ELIBBAD\n    { ELIBBAD, TARGET_ELIBBAD },\n#endif\n#ifdef ELIBSCN\n    { ELIBSCN, TARGET_ELIBSCN },\n#endif\n#ifdef ELIBMAX\n    { ELIBMAX, TARGET_ELIBMAX },\n#endif\n#ifdef ELIBEXEC\n    { ELIBEXEC, TARGET_ELIBEXEC },\n#endif\n#ifdef EILSEQ\n    { EILSEQ, TARGET_EILSEQ },\n#endif\n#ifdef ENOSYS\n    { ENOSYS, TARGET_ENOSYS },\n#endif\n#ifdef ELOOP\n    { ELOOP, TARGET_ELOOP },\n#endif\n#ifdef ERESTART\n    { ERESTART, TARGET_ERESTART },\n#endif\n#ifdef ESTRPIPE\n    { ESTRPIPE, TARGET_ESTRPIPE },\n#endif\n#ifdef ENOTEMPTY\n    { ENOTEMPTY, TARGET_ENOTEMPTY },\n#endif\n#ifdef EUSERS\n    { EUSERS, TARGET_EUSERS },\n#endif\n#ifdef ENOTSOCK\n    { ENOTSOCK, TARGET_ENOTSOCK },\n#endif\n#ifdef EDESTADDRREQ\n    { EDESTADDRREQ, TARGET_EDESTADDRREQ },\n#endif\n#ifdef EMSGSIZE\n    { EMSGSIZE, TARGET_EMSGSIZE },\n#endif\n#ifdef EPROTOTYPE\n    { EPROTOTYPE, TARGET_EPROTOTYPE },\n#endif\n#ifdef ENOPROTOOPT\n    { ENOPROTOOPT, TARGET_ENOPROTOOPT },\n#endif\n#ifdef EPROTONOSUPPORT\n    { EPROTONOSUPPORT, TARGET_EPROTONOSUPPORT },\n#endif\n#ifdef ESOCKTNOSUPPORT\n    { ESOCKTNOSUPPORT, TARGET_ESOCKTNOSUPPORT },\n#endif\n#ifdef EOPNOTSUPP\n    { EOPNOTSUPP, TARGET_EOPNOTSUPP },\n#endif\n#ifdef ENOTSUP\n    { ENOTSUP, TARGET_ENOTSUP },\n#endif\n#ifdef EPFNOSUPPORT\n    { EPFNOSUPPORT, TARGET_EPFNOSUPPORT },\n#endif\n#ifdef EAFNOSUPPORT\n    { EAFNOSUPPORT, TARGET_EAFNOSUPPORT },\n#endif\n#ifdef EADDRINUSE\n    { EADDRINUSE, TARGET_EADDRINUSE },\n#endif\n#ifdef EADDRNOTAVAIL\n    { EADDRNOTAVAIL, TARGET_EADDRNOTAVAIL },\n#endif\n#ifdef ENETDOWN\n    { ENETDOWN, TARGET_ENETDOWN },\n#endif\n#ifdef ENETUNREACH\n    { ENETUNREACH, TARGET_ENETUNREACH },\n#endif\n#ifdef ENETRESET\n    { ENETRESET, TARGET_ENETRESET },\n#endif\n#ifdef ECONNABORTED\n    { ECONNABORTED, TARGET_ECONNABORTED },\n#endif\n#ifdef ECONNRESET\n    { ECONNRESET, TARGET_ECONNRESET },\n#endif\n#ifdef ENOBUFS\n    { ENOBUFS, TARGET_ENOBUFS },\n#endif\n#ifdef EISCONN\n    { EISCONN, TARGET_EISCONN },\n#endif\n#ifdef ENOTCONN\n    { ENOTCONN, TARGET_ENOTCONN },\n#endif\n#ifdef EUCLEAN\n    { EUCLEAN, TARGET_EUCLEAN },\n#endif\n#ifdef ENOTNAM\n    { ENOTNAM, TARGET_ENOTNAM },\n#endif\n#ifdef ENAVAIL\n    { ENAVAIL, TARGET_ENAVAIL },\n#endif\n#ifdef EISNAM\n    { EISNAM, TARGET_EISNAM },\n#endif\n#ifdef EREMOTEIO\n    { EREMOTEIO, TARGET_EREMOTEIO },\n#endif\n#ifdef ESHUTDOWN\n    { ESHUTDOWN, TARGET_ESHUTDOWN },\n#endif\n#ifdef ETOOMANYREFS\n    { ETOOMANYREFS, TARGET_ETOOMANYREFS },\n#endif\n#ifdef ETIMEDOUT\n    { ETIMEDOUT, TARGET_ETIMEDOUT },\n#endif\n#ifdef ECONNREFUSED\n    { ECONNREFUSED, TARGET_ECONNREFUSED },\n#endif\n#ifdef EHOSTDOWN\n    { EHOSTDOWN, TARGET_EHOSTDOWN },\n#endif\n#ifdef EHOSTUNREACH\n    { EHOSTUNREACH, TARGET_EHOSTUNREACH },\n#endif\n#ifdef EWOULDBLOCK\n    { EWOULDBLOCK, TARGET_EWOULDBLOCK },\n#endif\n#ifdef EALREADY\n    { EALREADY, TARGET_EALREADY },\n#endif\n#ifdef EINPROGRESS\n    { EINPROGRESS, TARGET_EINPROGRESS },\n#endif\n#ifdef ESTALE\n    { ESTALE, TARGET_ESTALE },\n#endif\n#ifdef ECANCELED\n    { ECANCELED, TARGET_ECANCELED },\n#endif\n#ifdef ENOMEDIUM\n    { ENOMEDIUM, TARGET_ENOMEDIUM },\n#endif\n#ifdef EMEDIUMTYPE\n    { EMEDIUMTYPE, TARGET_EMEDIUMTYPE },\n#endif\n#ifdef ENOKEY\n    { ENOKEY, TARGET_ENOKEY },\n#endif\n#ifdef EKEYEXPIRED\n    { EKEYEXPIRED, TARGET_EKEYEXPIRED },\n#endif\n#ifdef EKEYREVOKED\n    { EKEYREVOKED, TARGET_EKEYREVOKED },\n#endif\n#ifdef EKEYREJECTED\n    { EKEYREJECTED, TARGET_EKEYREJECTED },\n#endif\n#ifdef EOWNERDEAD\n    { EOWNERDEAD, TARGET_EOWNERDEAD },\n#endif\n#ifdef ENOTRECOVERABLE\n    { ENOTRECOVERABLE, TARGET_ENOTRECOVERABLE },\n#endif\n#ifdef ERFKILL\n    { ERFKILL, TARGET_ERFKILL },\n#endif\n#ifdef EHWPOISON\n    { EHWPOISON, TARGET_EHWPOISON },\n#endif\n#ifdef EDQUOT\n    { EDQUOT, TARGET_EDQUOT },\n#endif\n};\n\nuint32_t result_errno_if_error(uint32_t result) {\n    if (result < (uint32_t)-4096) return result;\n    result = (uint32_t)-errno_map.value(errno);\n    if (!result) result = (uint32_t)-1;\n    return result;\n}\n\nint status_from_result(uint32_t result) {\n    if (result < (uint32_t)-4096)\n        return 0;\n    else\n        return -result;\n}\n\ntypedef int (OsSyscallExceptionHandler::*syscall_handler_t)(\n    uint64_t &result,\n    Core *core,\n    uint64_t syscall_num,\n    uint64_t a1,\n    uint64_t a2,\n    uint64_t a3,\n    uint64_t a4,\n    uint64_t a5,\n    uint64_t a6);\n\nstruct mips_syscall_desc_t {\n    const char *name;\n    unsigned int args;\n    syscall_handler_t handler;\n};\n\nstruct rv_syscall_desc_t {\n    unsigned int args;\n    syscall_handler_t handler;\n    const char *name;\n};\n\nstatic const rv_syscall_desc_t rv_syscall_args[] = {\n#include \"syscallent.h\"\n};\n\nconst unsigned rv_syscall_count = sizeof(rv_syscall_args) / sizeof(*rv_syscall_args);\n\nOsSyscallExceptionHandler::OsSyscallExceptionHandler(\n    bool known_syscall_stop,\n    bool unknown_syscall_stop,\n    QString fs_root)\n    : fd_mapping(3, FD_TERMINAL) {\n    brk_limit = 0;\n    anonymous_base = 0x60000000;\n    anonymous_last = anonymous_base;\n    this->known_syscall_stop = known_syscall_stop;\n    this->unknown_syscall_stop = unknown_syscall_stop;\n    this->fs_root = fs_root;\n}\n\nbool OsSyscallExceptionHandler::handle_exception(\n    Core *core,\n    Registers *regs,\n    ExceptionCause excause,\n    Address inst_addr,\n    Address next_addr,\n    Address jump_branch_pc,\n    Address mem_ref_addr) {\n    uint64_t syscall_num = regs->read_gp(17).as_u64();\n    const rv_syscall_desc_t *sdesc;\n    RegisterValue a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0;\n    uint64_t result;\n    int status;\n\n    FrontendMemory *mem_program = core->get_mem_program();\n    (void)mem_program;\n\n#if 1\n    printf(\n        \"Exception cause %d instruction PC 0x%08\" PRIx64 \" next PC 0x%08\" PRIx64 \" jump branch \"\n        \"PC 0x%08\" PRIx64 \"registers PC 0x%08\" PRIx64 \" mem ref 0x%08\" PRIx64 \"\\n\",\n        excause, inst_addr.get_raw(), next_addr.get_raw(), jump_branch_pc.get_raw(),\n        regs->read_pc().get_raw(), mem_ref_addr.get_raw());\n#else\n    (void)excause;\n    (void)inst_addr;\n    (void)next_addr;\n    (void)mem_ref_addr;\n    (void)regs;\n    (void)jump_branch_pc;\n#endif\n\n    // handle Linux syscalls\n    if (syscall_num < rv_syscall_count) {\n        sdesc = &rv_syscall_args[syscall_num];\n    } else {\n        throw SIMULATOR_EXCEPTION(\n            SyscallUnknown, \"System call number unknown \", QString::number(syscall_num));\n    }\n    // sdesc is populated by syscall description matching syscall number.\n    // if such syscall doesn't exist, this part is unreachable, because\n    // of the above exception.\n\n    // read out values from registers. Not sure I like this...\n    // Maybe the handling functions themselves should do this on their own.\n    a1 = regs->read_gp(10);\n    a2 = regs->read_gp(11);\n    a3 = regs->read_gp(12);\n    a4 = regs->read_gp(13);\n    a5 = regs->read_gp(14);\n    a6 = regs->read_gp(15);\n\n#if 1\n    printf(\n        \"Syscall %s number %\" PRId64 \"/0x%\" PRIx64 \" a1=%\" PRIu64 \" a2=%\" PRIu64 \" a3=%\" PRIu64\n        \" a4=%\" PRIu64 \"\\n\",\n        sdesc->name, syscall_num, syscall_num, a1.as_u64(), a2.as_u64(), a3.as_u64(), a4.as_u64());\n\n#endif\n    status = (this->*sdesc->handler)(\n        result, core, syscall_num, a1.as_u64(), a2.as_u64(), a3.as_u64(), a4.as_u64(), a5.as_u64(),\n        a6.as_u64());\n    if (known_syscall_stop) { emit core->stop_on_exception_reached(); }\n\n    if (status < 0) {\n        regs->write_gp(10, status);\n    } else {\n        regs->write_gp(10, result);\n    }\n\n    return true;\n}\n\nbool OsSyscallExceptionHandler::map_stdin_to_hostfile(const QString &hostpath) {\n    if (hostpath.isEmpty()) return false;\n    int hostfd = open(hostpath.toLatin1().data(), O_RDONLY);\n    if (hostfd < 0) {\n        fprintf(stderr, \"map_stdin_to_hostfile: failed to open host file '%s' errno=%d\", hostpath.toLatin1().data(), errno);\n        return false;\n    }\n    if (fd_mapping.size() <= 0) {\n        fd_mapping.push_back(FD_UNUSED);\n    }\n    // If there is an existing host fd mapped at target fd 0, close it (but don't close FD_TERMINAL/FD_UNUSED/FD_INVALID)\n    int prev = fd_mapping[0];\n    if (prev >= 0 && prev != FD_TERMINAL && prev != FD_UNUSED && prev != FD_INVALID) {\n        close(prev);\n    }\n    fd_mapping[0] = hostfd;\n    return true;\n}\n\nint32_t OsSyscallExceptionHandler::write_mem(\n    machine::FrontendMemory *mem,\n    Address addr,\n    const QVector<uint8_t> &data,\n    uint32_t count) {\n    if ((uint32_t)data.size() < count) count = data.size();\n\n    for (uint32_t i = 0; i < count; i++) {\n        mem->write_u8(addr, data[i]);\n        addr += 1;\n    }\n    return count;\n}\n\nint32_t OsSyscallExceptionHandler::read_mem(\n    machine::FrontendMemory *mem,\n    Address addr,\n    QVector<uint8_t> &data,\n    uint32_t count) {\n    data.resize(count);\n    for (uint32_t i = 0; i < count; i++) {\n        data[i] = mem->read_u8(addr);\n        addr += 1;\n    }\n    return count;\n}\n\nint32_t OsSyscallExceptionHandler::write_io(int fd, const QVector<uint8_t> &data, uint32_t count) {\n    if ((uint32_t)data.size() < count) count = data.size();\n    if (fd == FD_UNUSED) {\n        return -1;\n    } else if (fd == FD_TERMINAL) {\n        for (uint32_t i = 0; i < count; i++)\n            emit char_written(fd, data[i]);\n    } else {\n        count = write(fd, data.data(), count);\n    }\n    return result_errno_if_error(count);\n}\n\nint32_t OsSyscallExceptionHandler::read_io(\n    int fd,\n    QVector<uint8_t> &data,\n    uint32_t count,\n    bool add_nl_at_eof) {\n    data.resize(count);\n    if ((uint32_t)data.size() < count) count = data.size();\n    if (fd == FD_UNUSED) {\n        return -1;\n    } else if (fd == FD_TERMINAL) {\n        for (uint32_t i = 0; i < count; i++) {\n            unsigned int byte;\n            bool available = false;\n            emit rx_byte_pool(fd, byte, available);\n            if (!available) {\n                // add final newline if there are no more data\n                if (add_nl_at_eof) data[i] = '\\n';\n                count = i + 1;\n                break;\n            }\n            data[i] = byte;\n        }\n    } else {\n        count = read(fd, data.data(), count);\n    }\n    if ((int32_t)count >= 0) data.resize(count);\n    return result_errno_if_error(count);\n}\n\nint OsSyscallExceptionHandler::allocate_fd(int val) {\n    int i;\n    for (i = 0; i < fd_mapping.size(); i++) {\n        if (fd_mapping[i] == FD_UNUSED) {\n            fd_mapping[i] = val;\n            return i;\n        }\n    }\n    i = fd_mapping.size();\n    fd_mapping.resize(i + 1);\n    fd_mapping[i] = val;\n    return i;\n}\n\nint OsSyscallExceptionHandler::file_open(QString fname, int flags, int mode) {\n    int targetfd, fd;\n    int hostflags = 0;\n    (void)mode;\n    for (auto i = map_target_o_flags_to_o_flags.begin(); i != map_target_o_flags_to_o_flags.end();\n         i++)\n        if (flags & i.key()) hostflags |= i.value();\n\n    switch (flags & TARGET_O_ACCMODE) {\n    case TARGET_O_RDONLY: hostflags |= O_RDONLY; break;\n    case TARGET_O_WRONLY: hostflags |= O_WRONLY; break;\n    case TARGET_O_RDWR: hostflags |= O_RDWR; break;\n    }\n\n    if (fs_root.size() == 0) { return allocate_fd(FD_TERMINAL); }\n\n    fname = filepath_to_host(fname);\n\n    fd = open(fname.toLatin1().data(), hostflags, OPEN_MODE);\n    if (fd >= 0) {\n        targetfd = allocate_fd(fd);\n    } else {\n        targetfd = result_errno_if_error(fd);\n    }\n    return targetfd;\n}\n\nint OsSyscallExceptionHandler::targetfd_to_fd(int targetfd) {\n    if (targetfd < 0) return FD_INVALID;\n    if (targetfd >= fd_mapping.size()) return FD_INVALID;\n    return fd_mapping.at(targetfd);\n}\n\nvoid OsSyscallExceptionHandler::close_fd(int targetfd) {\n    if (targetfd <= fd_mapping.size()) fd_mapping[targetfd] = FD_UNUSED;\n}\n\nQString OsSyscallExceptionHandler::filepath_to_host(QString path) {\n    int pos = 0;\n    int prev;\n    while (true) {\n        if (((path.size() - pos == 2) && (path.mid(pos, -1) == \"..\"))\n            || ((path.size() - pos > 2) && (path.mid(pos, 3) == \"../\"))) {\n            if (pos == 0) {\n                prev = 0;\n            } else {\n                if (pos == 1)\n                    prev = 0;\n                else\n                    prev = path.lastIndexOf('/', pos - 2) + 1;\n            }\n            path.remove(prev, pos + 3 - prev);\n            pos = prev;\n            continue;\n        }\n        pos = path.indexOf('/', pos);\n        if (pos == -1) break;\n        pos += 1;\n    }\n    if ((path.size() >= 1) && path.at(0) == '/')\n        path = fs_root + path;\n    else\n        path = fs_root + '/' + path;\n    return path;\n}\n\nint OsSyscallExceptionHandler::syscall_default_handler(\n    uint64_t &result,\n    Core *core,\n    uint64_t syscall_num,\n    uint64_t a1,\n    uint64_t a2,\n    uint64_t a3,\n    uint64_t a4,\n    uint64_t a5,\n    uint64_t a6) {\n    const rv_syscall_desc_t *sdesc = &rv_syscall_args[syscall_num];\n#if 1\n    printf(\n        \"Unimplemented syscall %s number %\" PRId64 \"/0x%\" PRIx64 \" a1 %\" PRId64 \" a2 %\" PRId64\n        \" a3 %\" PRId64 \" a4 %\" PRId64 \"\\n\",\n        sdesc->name, syscall_num, syscall_num, a1, a2, a3, a4);\n\n#endif\n    (void)core;\n    (void)syscall_num;\n    (void)a1;\n    (void)a2;\n    (void)a3;\n    (void)a4;\n    (void)a5;\n    (void)a6;\n    result = 0;\n    if (unknown_syscall_stop) emit core->stop_on_exception_reached();\n    return TARGET_ENOSYS;\n}\n\n// void exit(int status);\nint OsSyscallExceptionHandler::do_sys_exit(\n    uint64_t &result,\n    Core *core,\n    uint64_t syscall_num,\n    uint64_t a1,\n    uint64_t a2,\n    uint64_t a3,\n    uint64_t a4,\n    uint64_t a5,\n    uint64_t a6) {\n    (void)core;\n    (void)syscall_num;\n    (void)a1;\n    (void)a2;\n    (void)a3;\n    (void)a4;\n    (void)a5;\n    (void)a6;\n\n    result = 0;\n    int status = a1;\n\n    printf(\"sys_exit status %d\\n\", status);\n    emit core->stop_on_exception_reached();\n\n    return 0;\n}\n\n// ssize_t writev(int fd, const struct iovec *iov, int iovcnt);\nint OsSyscallExceptionHandler::do_sys_writev(\n    uint64_t &result,\n    Core *core,\n    uint64_t syscall_num,\n    uint64_t a1,\n    uint64_t a2,\n    uint64_t a3,\n    uint64_t a4,\n    uint64_t a5,\n    uint64_t a6) {\n    (void)core;\n    (void)syscall_num;\n    (void)a1;\n    (void)a2;\n    (void)a3;\n    (void)a4;\n    (void)a5;\n    (void)a6;\n\n    result = 0;\n    int fd = a1;\n    Address iov = Address(core->get_xlen_from_reg(a2));\n    int iovcnt = a3;\n    FrontendMemory *mem = core->get_mem_data();\n    int32_t count;\n    QVector<uint8_t> data;\n\n    printf(\"sys_writev to fd %d\\n\", fd);\n\n    fd = targetfd_to_fd(fd);\n    if (fd == FD_INVALID) {\n        result = -TARGET_EINVAL;\n        return 0;\n    }\n\n    while (iovcnt-- > 0) {\n        Address iov_base = Address(mem->read_u32(iov));\n        uint32_t iov_len = mem->read_u32(iov + 4);\n        iov += 8;\n\n        read_mem(mem, iov_base, data, iov_len);\n        count = write_io(fd, data, iov_len);\n        if (count >= 0) {\n            result += count;\n        } else {\n            if (result == 0) result = count;\n        }\n        if (count < (int32_t)iov_len) break;\n    }\n\n    return status_from_result(result);\n}\n\n// ssize_t write(int fd, const void *buf, size_t count);\nint OsSyscallExceptionHandler::do_sys_write(\n    uint64_t &result,\n    Core *core,\n    uint64_t syscall_num,\n    uint64_t a1,\n    uint64_t a2,\n    uint64_t a3,\n    uint64_t a4,\n    uint64_t a5,\n    uint64_t a6) {\n    (void)core;\n    (void)syscall_num;\n    (void)a1;\n    (void)a2;\n    (void)a3;\n    (void)a4;\n    (void)a5;\n    (void)a6;\n\n    result = 0;\n    int fd = a1;\n    Address buf = Address(core->get_xlen_from_reg(a2));\n    int size = core->get_xlen_from_reg(a3);\n    FrontendMemory *mem = core->get_mem_data();\n    int32_t count;\n    QVector<uint8_t> data;\n\n    printf(\"sys_write to fd %d\\n\", fd);\n\n    fd = targetfd_to_fd(fd);\n    if (fd == FD_INVALID) {\n        result = -TARGET_EINVAL;\n        return 0;\n    }\n\n    read_mem(mem, buf, data, size);\n    count = write_io(fd, data, size);\n\n    result = count;\n\n    return status_from_result(result);\n}\n\n// ssize_t readv(int fd, const struct iovec *iov, int iovcnt);\nint OsSyscallExceptionHandler::do_sys_readv(\n    uint64_t &result,\n    Core *core,\n    uint64_t syscall_num,\n    uint64_t a1,\n    uint64_t a2,\n    uint64_t a3,\n    uint64_t a4,\n    uint64_t a5,\n    uint64_t a6) {\n    (void)core;\n    (void)syscall_num;\n    (void)a1;\n    (void)a2;\n    (void)a3;\n    (void)a4;\n    (void)a5;\n    (void)a6;\n\n    result = 0;\n    int fd = a1;\n    Address iov = Address(core->get_xlen_from_reg(a2));\n    int iovcnt = a3;\n    FrontendMemory *mem = core->get_mem_data();\n    int32_t count;\n    QVector<uint8_t> data;\n\n    printf(\"sys_readv to fd %d\\n\", fd);\n\n    fd = targetfd_to_fd(fd);\n    if (fd == FD_INVALID) {\n        result = -TARGET_EINVAL;\n        return 0;\n    }\n\n    while (iovcnt-- > 0) {\n        Address iov_base = Address(mem->read_u32(iov));\n        uint32_t iov_len = mem->read_u32(iov + 4);\n        iov += 8;\n\n        count = read_io(fd, data, iov_len, true);\n        if (count >= 0) {\n            write_mem(mem, iov_base, data, count);\n            result += count;\n        } else {\n            if (result == 0) result = count;\n        }\n        if (count < (int32_t)iov_len) break;\n    }\n\n    return status_from_result(result);\n}\n\n// ssize_t read(int fd, void *buf, size_t count);\nint OsSyscallExceptionHandler::do_sys_read(\n    uint64_t &result,\n    Core *core,\n    uint64_t syscall_num,\n    uint64_t a1,\n    uint64_t a2,\n    uint64_t a3,\n    uint64_t a4,\n    uint64_t a5,\n    uint64_t a6) {\n    (void)core;\n    (void)syscall_num;\n    (void)a1;\n    (void)a2;\n    (void)a3;\n    (void)a4;\n    (void)a5;\n    (void)a6;\n\n    result = 0;\n    int fd = a1;\n    Address buf = Address(core->get_xlen_from_reg(a2));\n    int size = core->get_xlen_from_reg(a3);\n    FrontendMemory *mem = core->get_mem_data();\n    int32_t count;\n    QVector<uint8_t> data;\n\n    printf(\"sys_read to fd %d\\n\", fd);\n\n    fd = targetfd_to_fd(fd);\n    if (fd == FD_INVALID) {\n        result = -TARGET_EINVAL;\n        return 0;\n    }\n\n    result = 0;\n\n    count = read_io(fd, data, size, true);\n    if (count >= 0) { write_mem(mem, buf, data, size); }\n    result = count;\n\n    return status_from_result(result);\n}\n\n// int openat(int fd, const char *pathname, int flags, mode_t mode);\nint OsSyscallExceptionHandler::do_sys_openat(\n    uint64_t &result,\n    Core *core,\n    uint64_t syscall_num,\n    uint64_t a1,\n    uint64_t a2,\n    uint64_t a3,\n    uint64_t a4,\n    uint64_t a5,\n    uint64_t a6) {\n    (void)core;\n    (void)syscall_num;\n    (void)a1;\n    (void)a2;\n    (void)a3;\n    (void)a4;\n    (void)a5;\n    (void)a6;\n\n    result = 0;\n    if (int64_t(a1) != TARGET_AT_FDCWD) {\n        printf(\"Unimplemented openat argument a1 %\" PRId64 \"\\n\", a1);\n        if (unknown_syscall_stop) { emit core->stop_on_exception_reached(); }\n        return TARGET_ENOSYS;\n    }\n    Address pathname_ptr = Address(core->get_xlen_from_reg(a2));\n    int flags = a3;\n    int mode = a4;\n    uint32_t ch;\n    FrontendMemory *mem = core->get_mem_data();\n\n    printf(\"sys_open filename\\n\");\n\n    QString fname;\n    while (true) {\n        ch = mem->read_u8(pathname_ptr);\n        pathname_ptr += 1;\n        if (ch == 0) break;\n        fname.append(QChar(ch));\n    }\n\n    result = file_open(fname, flags, mode);\n\n    return status_from_result(result);\n}\n\n// int close(int fd);\nint OsSyscallExceptionHandler::do_sys_close(\n    uint64_t &result,\n    Core *core,\n    uint64_t syscall_num,\n    uint64_t a1,\n    uint64_t a2,\n    uint64_t a3,\n    uint64_t a4,\n    uint64_t a5,\n    uint64_t a6) {\n    (void)core;\n    (void)syscall_num;\n    (void)a1;\n    (void)a2;\n    (void)a3;\n    (void)a4;\n    (void)a5;\n    (void)a6;\n\n    result = 0;\n    int fd = a1;\n\n    printf(\"sys_close fd %d\\n\", fd);\n\n    int targetfd = fd;\n    fd = targetfd_to_fd(fd);\n    if (fd == FD_INVALID) {\n        result = -TARGET_EINVAL;\n        return 0;\n    }\n\n    close(fd);\n    close_fd(targetfd);\n\n    return status_from_result(result);\n}\n\n// int ftruncate(int fd, off_t length);\nint OsSyscallExceptionHandler::do_sys_ftruncate(\n    uint64_t &result,\n    Core *core,\n    uint64_t syscall_num,\n    uint64_t a1,\n    uint64_t a2,\n    uint64_t a3,\n    uint64_t a4,\n    uint64_t a5,\n    uint64_t a6) {\n    (void)core;\n    (void)syscall_num;\n    (void)a1;\n    (void)a2;\n    (void)a3;\n    (void)a4;\n    (void)a5;\n    (void)a6;\n\n    result = 0;\n    int fd = a1;\n    uint64_t length = core->get_xlen_from_reg(a2);\n    if (core->get_xlen() == Xlen::_32) { length |= core->get_xlen_from_reg(a3) << 32; }\n\n    printf(\"sys_ftruncate fd %d\\n\", fd);\n\n    fd = targetfd_to_fd(fd);\n    if (fd == FD_INVALID) {\n        result = -TARGET_EINVAL;\n        return 0;\n    }\n\n    result = result_errno_if_error(ftruncate(fd, length));\n\n    return status_from_result(result);\n}\n\n// int or void * brk(void *addr);\nint OsSyscallExceptionHandler::do_sys_brk(\n    uint64_t &result,\n    Core *core,\n    uint64_t syscall_num,\n    uint64_t a1,\n    uint64_t a2,\n    uint64_t a3,\n    uint64_t a4,\n    uint64_t a5,\n    uint64_t a6) {\n    (void)core;\n    (void)syscall_num;\n    (void)a1;\n    (void)a2;\n    (void)a3;\n    (void)a4;\n    (void)a5;\n    (void)a6;\n\n    result = 0;\n    uint32_t new_limit = a1;\n    brk_limit = new_limit;\n    result = brk_limit;\n\n    return 0;\n}\n\n// void *mmap(void *addr, size_t length, int prot,\n//             int flags, int fd, off_t pgoffset);\nint OsSyscallExceptionHandler::do_sys_mmap(\n    uint64_t &result,\n    Core *core,\n    uint64_t syscall_num,\n    uint64_t a1,\n    uint64_t a2,\n    uint64_t a3,\n    uint64_t a4,\n    uint64_t a5,\n    uint64_t a6) {\n    (void)core;\n    (void)syscall_num;\n    (void)a1;\n    (void)a2;\n    (void)a3;\n    (void)a4;\n    (void)a5;\n    (void)a6;\n    // TODO: actually mmmap file, now that we can.\n\n    result = 0;\n    uint32_t addr = a1;\n    uint32_t lenght = a2;\n    uint32_t prot = a3;\n    uint32_t flags = a4;\n    uint32_t fd = a5;\n    uint64_t offset = a6;\n\n    printf(\n        \"sys_mmap addr = 0x%08lx lenght= 0x%08lx prot = 0x%08lx flags = \"\n        \"0x%08lx fd = %d offset = 0x%08llx\\n\",\n        (unsigned long)addr, (unsigned long)lenght, (unsigned long)prot, (unsigned long)flags,\n        (int)fd, (unsigned long long)offset);\n\n    result = anonymous_last;\n    anonymous_last += lenght;\n\n    return 0;\n}\n"
  },
  {
    "path": "src/os_emulation/ossyscall.h",
    "content": "#ifndef OSSYCALL_H\n#define OSSYCALL_H\n\n#include \"machine/core.h\"\n#include \"machine/instruction.h\"\n#include \"machine/machineconfig.h\"\n#include \"machine/memory/backend/memory.h\"\n#include \"machine/memory/frontend_memory.h\"\n#include \"machine/registers.h\"\n#include \"machine/simulator_exception.h\"\n\n#include <QObject>\n#include <QString>\n#include <QVector>\n\nnamespace osemu {\n\n#define OSSYCALL_HANDLER_DECLARE(name)                                                             \\\n    int name(                                                                                      \\\n        uint64_t &result, machine::Core *core, uint64_t syscall_num, uint64_t a1, uint64_t a2,     \\\n        uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6)\n\nclass OsSyscallExceptionHandler : public machine::ExceptionHandler {\n    Q_OBJECT\npublic:\n    explicit OsSyscallExceptionHandler(\n        bool known_syscall_stop = false,\n        bool unknown_syscall_stop = false,\n        QString fs_root = \"\");\n    bool handle_exception(\n        machine::Core *core,\n        machine::Registers *regs,\n        machine::ExceptionCause excause,\n        machine::Address inst_addr,\n        machine::Address next_addr,\n        machine::Address jump_branch_pc,\n        machine::Address mem_ref_addr) override;\n    /**\n     * Map target fd 0 (stdin) to a host file opened from @hostpath.\n     * Returns true on success (host file opened and mapped), false on error.\n     */\n    bool map_stdin_to_hostfile(const QString &hostpath);\n    OSSYCALL_HANDLER_DECLARE(syscall_default_handler);\n    OSSYCALL_HANDLER_DECLARE(do_sys_exit);\n    OSSYCALL_HANDLER_DECLARE(do_sys_set_thread_area);\n    OSSYCALL_HANDLER_DECLARE(do_sys_writev);\n    OSSYCALL_HANDLER_DECLARE(do_sys_write);\n    OSSYCALL_HANDLER_DECLARE(do_sys_readv);\n    OSSYCALL_HANDLER_DECLARE(do_sys_read);\n    OSSYCALL_HANDLER_DECLARE(do_sys_openat);\n    OSSYCALL_HANDLER_DECLARE(do_sys_close);\n    OSSYCALL_HANDLER_DECLARE(do_sys_ftruncate);\n    OSSYCALL_HANDLER_DECLARE(do_sys_brk);\n    OSSYCALL_HANDLER_DECLARE(do_sys_mmap);\n\n    OSSYCALL_HANDLER_DECLARE(do_spim_print_integer);\n    OSSYCALL_HANDLER_DECLARE(do_spim_print_string);\n    OSSYCALL_HANDLER_DECLARE(do_spim_read_string);\n    OSSYCALL_HANDLER_DECLARE(do_spim_sbrk);\n    OSSYCALL_HANDLER_DECLARE(do_spim_exit);\n    OSSYCALL_HANDLER_DECLARE(do_spim_print_character);\n    OSSYCALL_HANDLER_DECLARE(do_spim_read_character);\n\nsignals:\n    void char_written(int fd, unsigned int val);\n    void rx_byte_pool(int fd, unsigned int &data, bool &available);\n\nprivate:\n    enum FdMapping {\n        FD_UNUSED = -1,\n        FD_INVALID = -1,\n        FD_TERMINAL = -2,\n    };\n    int32_t write_mem(\n        machine::FrontendMemory *mem,\n        machine::Address addr,\n        const QVector<uint8_t> &data,\n        uint32_t count);\n    int32_t read_mem(\n        machine::FrontendMemory *mem,\n        machine::Address addr,\n        QVector<uint8_t> &data,\n        uint32_t count);\n    int32_t write_io(int fd, const QVector<uint8_t> &data, uint32_t count);\n    int32_t read_io(int fd, QVector<uint8_t> &data, uint32_t count, bool add_nl_at_eof = false);\n    int allocate_fd(int val = FD_UNUSED);\n    int file_open(QString fname, int flags, int mode);\n    int targetfd_to_fd(int targetfd);\n    void close_fd(int targetfd);\n    QString filepath_to_host(QString path);\n\n    QVector<int> fd_mapping;\n    uint32_t brk_limit;\n    uint32_t anonymous_base;\n    uint32_t anonymous_last;\n    bool known_syscall_stop;\n    bool unknown_syscall_stop;\n    QString fs_root;\n};\n\n#undef OSSYCALL_HANDLER_DECLARE\n\n} // namespace osemu\n\n#endif // OSSYCALL_H\n"
  },
  {
    "path": "src/os_emulation/posix_polyfill.h",
    "content": "#ifndef POSIX_POLYFILL_H\n#define POSIX_POLYFILL_H\n\n#ifndef _WIN32\n    // POSIX already provides these functions, just include unistd\n    #include <unistd.h>\n#else\n    // wrap the \"Low-Level I/O API\" provided by Microsoft CRT, which exposes\n    //  a POSIX-like wrapper over Win32\n    #include <io.h>\n\n    #define open      _open\n    #define close     _close\n    #define read      _read\n    #define write     _write\n    #define ftruncate _chsize_s\n\n#endif // _WIN32\n\n#endif // POSIX_POLYFILL_H\n"
  },
  {
    "path": "src/os_emulation/syscall_nr.h",
    "content": "#ifndef SYSCALL_NR_H\n#define SYSCALL_NR_H\n\n/*\n * Linux o32 style syscalls are in the range from 4000 to 4999.\n */\n#define TARGET_NR_Linux                  4000\n#define TARGET_NR_syscall                (TARGET_NR_Linux + 0)\n#define TARGET_NR_exit                   (TARGET_NR_Linux + 1)\n#define TARGET_NR_fork                   (TARGET_NR_Linux + 2)\n#define TARGET_NR_read                   (TARGET_NR_Linux + 3)\n#define TARGET_NR_write                  (TARGET_NR_Linux + 4)\n#define TARGET_NR_open                   (TARGET_NR_Linux + 5)\n#define TARGET_NR_close                  (TARGET_NR_Linux + 6)\n#define TARGET_NR_waitpid                (TARGET_NR_Linux + 7)\n#define TARGET_NR_creat                  (TARGET_NR_Linux + 8)\n#define TARGET_NR_link                   (TARGET_NR_Linux + 9)\n#define TARGET_NR_unlink                 (TARGET_NR_Linux + 10)\n#define TARGET_NR_execve                 (TARGET_NR_Linux + 11)\n#define TARGET_NR_chdir                  (TARGET_NR_Linux + 12)\n#define TARGET_NR_time                   (TARGET_NR_Linux + 13)\n#define TARGET_NR_mknod                  (TARGET_NR_Linux + 14)\n#define TARGET_NR_chmod                  (TARGET_NR_Linux + 15)\n#define TARGET_NR_lchown                 (TARGET_NR_Linux + 16)\n#define TARGET_NR_break                  (TARGET_NR_Linux + 17)\n#define TARGET_NR_unused18               (TARGET_NR_Linux + 18)\n#define TARGET_NR_lseek                  (TARGET_NR_Linux + 19)\n#define TARGET_NR_getpid                 (TARGET_NR_Linux + 20)\n#define TARGET_NR_mount                  (TARGET_NR_Linux + 21)\n#define TARGET_NR_umount                 (TARGET_NR_Linux + 22)\n#define TARGET_NR_setuid                 (TARGET_NR_Linux + 23)\n#define TARGET_NR_getuid                 (TARGET_NR_Linux + 24)\n#define TARGET_NR_stime                  (TARGET_NR_Linux + 25)\n#define TARGET_NR_ptrace                 (TARGET_NR_Linux + 26)\n#define TARGET_NR_alarm                  (TARGET_NR_Linux + 27)\n#define TARGET_NR_unused28               (TARGET_NR_Linux + 28)\n#define TARGET_NR_pause                  (TARGET_NR_Linux + 29)\n#define TARGET_NR_utime                  (TARGET_NR_Linux + 30)\n#define TARGET_NR_stty                   (TARGET_NR_Linux + 31)\n#define TARGET_NR_gtty                   (TARGET_NR_Linux + 32)\n#define TARGET_NR_access                 (TARGET_NR_Linux + 33)\n#define TARGET_NR_nice                   (TARGET_NR_Linux + 34)\n#define TARGET_NR_ftime                  (TARGET_NR_Linux + 35)\n#define TARGET_NR_sync                   (TARGET_NR_Linux + 36)\n#define TARGET_NR_kill                   (TARGET_NR_Linux + 37)\n#define TARGET_NR_rename                 (TARGET_NR_Linux + 38)\n#define TARGET_NR_mkdir                  (TARGET_NR_Linux + 39)\n#define TARGET_NR_rmdir                  (TARGET_NR_Linux + 40)\n#define TARGET_NR_dup                    (TARGET_NR_Linux + 41)\n#define TARGET_NR_pipe                   (TARGET_NR_Linux + 42)\n#define TARGET_NR_times                  (TARGET_NR_Linux + 43)\n#define TARGET_NR_prof                   (TARGET_NR_Linux + 44)\n#define TARGET_NR_brk                    (TARGET_NR_Linux + 45)\n#define TARGET_NR_setgid                 (TARGET_NR_Linux + 46)\n#define TARGET_NR_getgid                 (TARGET_NR_Linux + 47)\n#define TARGET_NR_signal                 (TARGET_NR_Linux + 48)\n#define TARGET_NR_geteuid                (TARGET_NR_Linux + 49)\n#define TARGET_NR_getegid                (TARGET_NR_Linux + 50)\n#define TARGET_NR_acct                   (TARGET_NR_Linux + 51)\n#define TARGET_NR_umount2                (TARGET_NR_Linux + 52)\n#define TARGET_NR_lock                   (TARGET_NR_Linux + 53)\n#define TARGET_NR_ioctl                  (TARGET_NR_Linux + 54)\n#define TARGET_NR_fcntl                  (TARGET_NR_Linux + 55)\n#define TARGET_NR_mpx                    (TARGET_NR_Linux + 56)\n#define TARGET_NR_setpgid                (TARGET_NR_Linux + 57)\n#define TARGET_NR_ulimit                 (TARGET_NR_Linux + 58)\n#define TARGET_NR_unused59               (TARGET_NR_Linux + 59)\n#define TARGET_NR_umask                  (TARGET_NR_Linux + 60)\n#define TARGET_NR_chroot                 (TARGET_NR_Linux + 61)\n#define TARGET_NR_ustat                  (TARGET_NR_Linux + 62)\n#define TARGET_NR_dup2                   (TARGET_NR_Linux + 63)\n#define TARGET_NR_getppid                (TARGET_NR_Linux + 64)\n#define TARGET_NR_getpgrp                (TARGET_NR_Linux + 65)\n#define TARGET_NR_setsid                 (TARGET_NR_Linux + 66)\n#define TARGET_NR_sigaction              (TARGET_NR_Linux + 67)\n#define TARGET_NR_sgetmask               (TARGET_NR_Linux + 68)\n#define TARGET_NR_ssetmask               (TARGET_NR_Linux + 69)\n#define TARGET_NR_setreuid               (TARGET_NR_Linux + 70)\n#define TARGET_NR_setregid               (TARGET_NR_Linux + 71)\n#define TARGET_NR_sigsuspend             (TARGET_NR_Linux + 72)\n#define TARGET_NR_sigpending             (TARGET_NR_Linux + 73)\n#define TARGET_NR_sethostname            (TARGET_NR_Linux + 74)\n#define TARGET_NR_setrlimit              (TARGET_NR_Linux + 75)\n#define TARGET_NR_getrlimit              (TARGET_NR_Linux + 76)\n#define TARGET_NR_getrusage              (TARGET_NR_Linux + 77)\n#define TARGET_NR_gettimeofday           (TARGET_NR_Linux + 78)\n#define TARGET_NR_settimeofday           (TARGET_NR_Linux + 79)\n#define TARGET_NR_getgroups              (TARGET_NR_Linux + 80)\n#define TARGET_NR_setgroups              (TARGET_NR_Linux + 81)\n#define TARGET_NR_reserved82             (TARGET_NR_Linux + 82)\n#define TARGET_NR_symlink                (TARGET_NR_Linux + 83)\n#define TARGET_NR_unused84               (TARGET_NR_Linux + 84)\n#define TARGET_NR_readlink               (TARGET_NR_Linux + 85)\n#define TARGET_NR_uselib                 (TARGET_NR_Linux + 86)\n#define TARGET_NR_swapon                 (TARGET_NR_Linux + 87)\n#define TARGET_NR_reboot                 (TARGET_NR_Linux + 88)\n#define TARGET_NR_readdir                (TARGET_NR_Linux + 89)\n#define TARGET_NR_mmap                   (TARGET_NR_Linux + 90)\n#define TARGET_NR_munmap                 (TARGET_NR_Linux + 91)\n#define TARGET_NR_truncate               (TARGET_NR_Linux + 92)\n#define TARGET_NR_ftruncate              (TARGET_NR_Linux + 93)\n#define TARGET_NR_fchmod                 (TARGET_NR_Linux + 94)\n#define TARGET_NR_fchown                 (TARGET_NR_Linux + 95)\n#define TARGET_NR_getpriority            (TARGET_NR_Linux + 96)\n#define TARGET_NR_setpriority            (TARGET_NR_Linux + 97)\n#define TARGET_NR_profil                 (TARGET_NR_Linux + 98)\n#define TARGET_NR_statfs                 (TARGET_NR_Linux + 99)\n#define TARGET_NR_fstatfs                (TARGET_NR_Linux + 100)\n#define TARGET_NR_ioperm                 (TARGET_NR_Linux + 101)\n#define TARGET_NR_socketcall             (TARGET_NR_Linux + 102)\n#define TARGET_NR_syslog                 (TARGET_NR_Linux + 103)\n#define TARGET_NR_setitimer              (TARGET_NR_Linux + 104)\n#define TARGET_NR_getitimer              (TARGET_NR_Linux + 105)\n#define TARGET_NR_stat                   (TARGET_NR_Linux + 106)\n#define TARGET_NR_lstat                  (TARGET_NR_Linux + 107)\n#define TARGET_NR_fstat                  (TARGET_NR_Linux + 108)\n#define TARGET_NR_unused109              (TARGET_NR_Linux + 109)\n#define TARGET_NR_iopl                   (TARGET_NR_Linux + 110)\n#define TARGET_NR_vhangup                (TARGET_NR_Linux + 111)\n#define TARGET_NR_idle                   (TARGET_NR_Linux + 112)\n#define TARGET_NR_vm86                   (TARGET_NR_Linux + 113)\n#define TARGET_NR_wait4                  (TARGET_NR_Linux + 114)\n#define TARGET_NR_swapoff                (TARGET_NR_Linux + 115)\n#define TARGET_NR_sysinfo                (TARGET_NR_Linux + 116)\n#define TARGET_NR_ipc                    (TARGET_NR_Linux + 117)\n#define TARGET_NR_fsync                  (TARGET_NR_Linux + 118)\n#define TARGET_NR_sigreturn              (TARGET_NR_Linux + 119)\n#define TARGET_NR_clone                  (TARGET_NR_Linux + 120)\n#define TARGET_NR_setdomainname          (TARGET_NR_Linux + 121)\n#define TARGET_NR_uname                  (TARGET_NR_Linux + 122)\n#define TARGET_NR_modify_ldt             (TARGET_NR_Linux + 123)\n#define TARGET_NR_adjtimex               (TARGET_NR_Linux + 124)\n#define TARGET_NR_mprotect               (TARGET_NR_Linux + 125)\n#define TARGET_NR_sigprocmask            (TARGET_NR_Linux + 126)\n#define TARGET_NR_create_module          (TARGET_NR_Linux + 127)\n#define TARGET_NR_init_module            (TARGET_NR_Linux + 128)\n#define TARGET_NR_delete_module          (TARGET_NR_Linux + 129)\n#define TARGET_NR_get_kernel_syms        (TARGET_NR_Linux + 130)\n#define TARGET_NR_quotactl               (TARGET_NR_Linux + 131)\n#define TARGET_NR_getpgid                (TARGET_NR_Linux + 132)\n#define TARGET_NR_fchdir                 (TARGET_NR_Linux + 133)\n#define TARGET_NR_bdflush                (TARGET_NR_Linux + 134)\n#define TARGET_NR_sysfs                  (TARGET_NR_Linux + 135)\n#define TARGET_NR_personality            (TARGET_NR_Linux + 136)\n#define TARGET_NR_afs_syscall            (TARGET_NR_Linux + 137) /* Syscall for Andrew File System */\n#define TARGET_NR_setfsuid               (TARGET_NR_Linux + 138)\n#define TARGET_NR_setfsgid               (TARGET_NR_Linux + 139)\n#define TARGET_NR__llseek                (TARGET_NR_Linux + 140)\n#define TARGET_NR_getdents               (TARGET_NR_Linux + 141)\n#define TARGET_NR__newselect             (TARGET_NR_Linux + 142)\n#define TARGET_NR_flock                  (TARGET_NR_Linux + 143)\n#define TARGET_NR_msync                  (TARGET_NR_Linux + 144)\n#define TARGET_NR_readv                  (TARGET_NR_Linux + 145)\n#define TARGET_NR_writev                 (TARGET_NR_Linux + 146)\n#define TARGET_NR_cacheflush             (TARGET_NR_Linux + 147)\n#define TARGET_NR_cachectl               (TARGET_NR_Linux + 148)\n#define TARGET_NR_sysmips                (TARGET_NR_Linux + 149)\n#define TARGET_NR_unused150              (TARGET_NR_Linux + 150)\n#define TARGET_NR_getsid                 (TARGET_NR_Linux + 151)\n#define TARGET_NR_fdatasync              (TARGET_NR_Linux + 152)\n#define TARGET_NR__sysctl                (TARGET_NR_Linux + 153)\n#define TARGET_NR_mlock                  (TARGET_NR_Linux + 154)\n#define TARGET_NR_munlock                (TARGET_NR_Linux + 155)\n#define TARGET_NR_mlockall               (TARGET_NR_Linux + 156)\n#define TARGET_NR_munlockall             (TARGET_NR_Linux + 157)\n#define TARGET_NR_sched_setparam         (TARGET_NR_Linux + 158)\n#define TARGET_NR_sched_getparam         (TARGET_NR_Linux + 159)\n#define TARGET_NR_sched_setscheduler     (TARGET_NR_Linux + 160)\n#define TARGET_NR_sched_getscheduler     (TARGET_NR_Linux + 161)\n#define TARGET_NR_sched_yield            (TARGET_NR_Linux + 162)\n#define TARGET_NR_sched_get_priority_max (TARGET_NR_Linux + 163)\n#define TARGET_NR_sched_get_priority_min (TARGET_NR_Linux + 164)\n#define TARGET_NR_sched_rr_get_interval  (TARGET_NR_Linux + 165)\n#define TARGET_NR_nanosleep              (TARGET_NR_Linux + 166)\n#define TARGET_NR_mremap                 (TARGET_NR_Linux + 167)\n#define TARGET_NR_accept                 (TARGET_NR_Linux + 168)\n#define TARGET_NR_bind                   (TARGET_NR_Linux + 169)\n#define TARGET_NR_connect                (TARGET_NR_Linux + 170)\n#define TARGET_NR_getpeername            (TARGET_NR_Linux + 171)\n#define TARGET_NR_getsockname            (TARGET_NR_Linux + 172)\n#define TARGET_NR_getsockopt             (TARGET_NR_Linux + 173)\n#define TARGET_NR_listen                 (TARGET_NR_Linux + 174)\n#define TARGET_NR_recv                   (TARGET_NR_Linux + 175)\n#define TARGET_NR_recvfrom               (TARGET_NR_Linux + 176)\n#define TARGET_NR_recvmsg                (TARGET_NR_Linux + 177)\n#define TARGET_NR_send                   (TARGET_NR_Linux + 178)\n#define TARGET_NR_sendmsg                (TARGET_NR_Linux + 179)\n#define TARGET_NR_sendto                 (TARGET_NR_Linux + 180)\n#define TARGET_NR_setsockopt             (TARGET_NR_Linux + 181)\n#define TARGET_NR_shutdown               (TARGET_NR_Linux + 182)\n#define TARGET_NR_socket                 (TARGET_NR_Linux + 183)\n#define TARGET_NR_socketpair             (TARGET_NR_Linux + 184)\n#define TARGET_NR_setresuid              (TARGET_NR_Linux + 185)\n#define TARGET_NR_getresuid              (TARGET_NR_Linux + 186)\n#define TARGET_NR_query_module           (TARGET_NR_Linux + 187)\n#define TARGET_NR_poll                   (TARGET_NR_Linux + 188)\n#define TARGET_NR_nfsservctl             (TARGET_NR_Linux + 189)\n#define TARGET_NR_setresgid              (TARGET_NR_Linux + 190)\n#define TARGET_NR_getresgid              (TARGET_NR_Linux + 191)\n#define TARGET_NR_prctl                  (TARGET_NR_Linux + 192)\n#define TARGET_NR_rt_sigreturn           (TARGET_NR_Linux + 193)\n#define TARGET_NR_rt_sigaction           (TARGET_NR_Linux + 194)\n#define TARGET_NR_rt_sigprocmask         (TARGET_NR_Linux + 195)\n#define TARGET_NR_rt_sigpending          (TARGET_NR_Linux + 196)\n#define TARGET_NR_rt_sigtimedwait        (TARGET_NR_Linux + 197)\n#define TARGET_NR_rt_sigqueueinfo        (TARGET_NR_Linux + 198)\n#define TARGET_NR_rt_sigsuspend          (TARGET_NR_Linux + 199)\n#define TARGET_NR_pread64                (TARGET_NR_Linux + 200)\n#define TARGET_NR_pwrite64               (TARGET_NR_Linux + 201)\n#define TARGET_NR_chown                  (TARGET_NR_Linux + 202)\n#define TARGET_NR_getcwd                 (TARGET_NR_Linux + 203)\n#define TARGET_NR_capget                 (TARGET_NR_Linux + 204)\n#define TARGET_NR_capset                 (TARGET_NR_Linux + 205)\n#define TARGET_NR_sigaltstack            (TARGET_NR_Linux + 206)\n#define TARGET_NR_sendfile               (TARGET_NR_Linux + 207)\n#define TARGET_NR_getpmsg                (TARGET_NR_Linux + 208)\n#define TARGET_NR_putpmsg                (TARGET_NR_Linux + 209)\n#define TARGET_NR_mmap2                  (TARGET_NR_Linux + 210)\n#define TARGET_NR_truncate64             (TARGET_NR_Linux + 211)\n#define TARGET_NR_ftruncate64            (TARGET_NR_Linux + 212)\n#define TARGET_NR_stat64                 (TARGET_NR_Linux + 213)\n#define TARGET_NR_lstat64                (TARGET_NR_Linux + 214)\n#define TARGET_NR_fstat64                (TARGET_NR_Linux + 215)\n#define TARGET_NR_pivot_root             (TARGET_NR_Linux + 216)\n#define TARGET_NR_mincore                (TARGET_NR_Linux + 217)\n#define TARGET_NR_madvise                (TARGET_NR_Linux + 218)\n#define TARGET_NR_getdents64             (TARGET_NR_Linux + 219)\n#define TARGET_NR_fcntl64                (TARGET_NR_Linux + 220)\n#define TARGET_NR_reserved221            (TARGET_NR_Linux + 221)\n#define TARGET_NR_gettid                 (TARGET_NR_Linux + 222)\n#define TARGET_NR_readahead              (TARGET_NR_Linux + 223)\n#define TARGET_NR_setxattr               (TARGET_NR_Linux + 224)\n#define TARGET_NR_lsetxattr              (TARGET_NR_Linux + 225)\n#define TARGET_NR_fsetxattr              (TARGET_NR_Linux + 226)\n#define TARGET_NR_getxattr               (TARGET_NR_Linux + 227)\n#define TARGET_NR_lgetxattr              (TARGET_NR_Linux + 228)\n#define TARGET_NR_fgetxattr              (TARGET_NR_Linux + 229)\n#define TARGET_NR_listxattr              (TARGET_NR_Linux + 230)\n#define TARGET_NR_llistxattr             (TARGET_NR_Linux + 231)\n#define TARGET_NR_flistxattr             (TARGET_NR_Linux + 232)\n#define TARGET_NR_removexattr            (TARGET_NR_Linux + 233)\n#define TARGET_NR_lremovexattr           (TARGET_NR_Linux + 234)\n#define TARGET_NR_fremovexattr           (TARGET_NR_Linux + 235)\n#define TARGET_NR_tkill                  (TARGET_NR_Linux + 236)\n#define TARGET_NR_sendfile64             (TARGET_NR_Linux + 237)\n#define TARGET_NR_futex                  (TARGET_NR_Linux + 238)\n#define TARGET_NR_sched_setaffinity      (TARGET_NR_Linux + 239)\n#define TARGET_NR_sched_getaffinity      (TARGET_NR_Linux + 240)\n#define TARGET_NR_io_setup               (TARGET_NR_Linux + 241)\n#define TARGET_NR_io_destroy             (TARGET_NR_Linux + 242)\n#define TARGET_NR_io_getevents           (TARGET_NR_Linux + 243)\n#define TARGET_NR_io_submit              (TARGET_NR_Linux + 244)\n#define TARGET_NR_io_cancel              (TARGET_NR_Linux + 245)\n#define TARGET_NR_exit_group             (TARGET_NR_Linux + 246)\n#define TARGET_NR_lookup_dcookie         (TARGET_NR_Linux + 247)\n#define TARGET_NR_epoll_create           (TARGET_NR_Linux + 248)\n#define TARGET_NR_epoll_ctl              (TARGET_NR_Linux + 249)\n#define TARGET_NR_epoll_wait             (TARGET_NR_Linux + 250)\n#define TARGET_NR_remap_file_pages       (TARGET_NR_Linux + 251)\n#define TARGET_NR_set_tid_address        (TARGET_NR_Linux + 252)\n#define TARGET_NR_restart_syscall        (TARGET_NR_Linux + 253)\n#define TARGET_NR_fadvise64_64           (TARGET_NR_Linux + 254)\n#define TARGET_NR_statfs64               (TARGET_NR_Linux + 255)\n#define TARGET_NR_fstatfs64              (TARGET_NR_Linux + 256)\n#define TARGET_NR_timer_create           (TARGET_NR_Linux + 257)\n#define TARGET_NR_timer_settime          (TARGET_NR_Linux + 258)\n#define TARGET_NR_timer_gettime          (TARGET_NR_Linux + 259)\n#define TARGET_NR_timer_getoverrun       (TARGET_NR_Linux + 260)\n#define TARGET_NR_timer_delete           (TARGET_NR_Linux + 261)\n#define TARGET_NR_clock_settime          (TARGET_NR_Linux + 262)\n#define TARGET_NR_clock_gettime          (TARGET_NR_Linux + 263)\n#define TARGET_NR_clock_getres           (TARGET_NR_Linux + 264)\n#define TARGET_NR_clock_nanosleep        (TARGET_NR_Linux + 265)\n#define TARGET_NR_tgkill                 (TARGET_NR_Linux + 266)\n#define TARGET_NR_utimes                 (TARGET_NR_Linux + 267)\n#define TARGET_NR_mbind                  (TARGET_NR_Linux + 268)\n#define TARGET_NR_get_mempolicy          (TARGET_NR_Linux + 269)\n#define TARGET_NR_set_mempolicy          (TARGET_NR_Linux + 270)\n#define TARGET_NR_mq_open                (TARGET_NR_Linux + 271)\n#define TARGET_NR_mq_unlink              (TARGET_NR_Linux + 272)\n#define TARGET_NR_mq_timedsend           (TARGET_NR_Linux + 273)\n#define TARGET_NR_mq_timedreceive        (TARGET_NR_Linux + 274)\n#define TARGET_NR_mq_notify              (TARGET_NR_Linux + 275)\n#define TARGET_NR_mq_getsetattr          (TARGET_NR_Linux + 276)\n#define TARGET_NR_vserver                (TARGET_NR_Linux + 277)\n#define TARGET_NR_waitid                 (TARGET_NR_Linux + 278)\n/* #define TARGET_NR_sys_setaltroot\t(TARGET_NR_Linux + 279) */\n#define TARGET_NR_add_key           (TARGET_NR_Linux + 280)\n#define TARGET_NR_request_key       (TARGET_NR_Linux + 281)\n#define TARGET_NR_keyctl            (TARGET_NR_Linux + 282)\n#define TARGET_NR_set_thread_area   (TARGET_NR_Linux + 283)\n#define TARGET_NR_inotify_init      (TARGET_NR_Linux + 284)\n#define TARGET_NR_inotify_add_watch (TARGET_NR_Linux + 285)\n#define TARGET_NR_inotify_rm_watch  (TARGET_NR_Linux + 286)\n#define TARGET_NR_migrate_pages     (TARGET_NR_Linux + 287)\n#define TARGET_NR_openat            (TARGET_NR_Linux + 288)\n#define TARGET_NR_mkdirat           (TARGET_NR_Linux + 289)\n#define TARGET_NR_mknodat           (TARGET_NR_Linux + 290)\n#define TARGET_NR_fchownat          (TARGET_NR_Linux + 291)\n#define TARGET_NR_futimesat         (TARGET_NR_Linux + 292)\n#define TARGET_NR_fstatat64         (TARGET_NR_Linux + 293)\n#define TARGET_NR_unlinkat          (TARGET_NR_Linux + 294)\n#define TARGET_NR_renameat          (TARGET_NR_Linux + 295)\n#define TARGET_NR_linkat            (TARGET_NR_Linux + 296)\n#define TARGET_NR_symlinkat         (TARGET_NR_Linux + 297)\n#define TARGET_NR_readlinkat        (TARGET_NR_Linux + 298)\n#define TARGET_NR_fchmodat          (TARGET_NR_Linux + 299)\n#define TARGET_NR_faccessat         (TARGET_NR_Linux + 300)\n#define TARGET_NR_pselect6          (TARGET_NR_Linux + 301)\n#define TARGET_NR_ppoll             (TARGET_NR_Linux + 302)\n#define TARGET_NR_unshare           (TARGET_NR_Linux + 303)\n#define TARGET_NR_splice            (TARGET_NR_Linux + 304)\n#define TARGET_NR_sync_file_range   (TARGET_NR_Linux + 305)\n#define TARGET_NR_tee               (TARGET_NR_Linux + 306)\n#define TARGET_NR_vmsplice          (TARGET_NR_Linux + 307)\n#define TARGET_NR_move_pages        (TARGET_NR_Linux + 308)\n#define TARGET_NR_set_robust_list   (TARGET_NR_Linux + 309)\n#define TARGET_NR_get_robust_list   (TARGET_NR_Linux + 310)\n#define TARGET_NR_kexec_load        (TARGET_NR_Linux + 311)\n#define TARGET_NR_getcpu            (TARGET_NR_Linux + 312)\n#define TARGET_NR_epoll_pwait       (TARGET_NR_Linux + 313)\n#define TARGET_NR_ioprio_set        (TARGET_NR_Linux + 314)\n#define TARGET_NR_ioprio_get        (TARGET_NR_Linux + 315)\n#define TARGET_NR_utimensat         (TARGET_NR_Linux + 316)\n#define TARGET_NR_signalfd          (TARGET_NR_Linux + 317)\n#define TARGET_NR_timerfd           (TARGET_NR_Linux + 318)\n#define TARGET_NR_eventfd           (TARGET_NR_Linux + 319)\n#define TARGET_NR_fallocate         (TARGET_NR_Linux + 320)\n#define TARGET_NR_timerfd_create    (TARGET_NR_Linux + 321)\n#define TARGET_NR_timerfd_gettime   (TARGET_NR_Linux + 322)\n#define TARGET_NR_timerfd_settime   (TARGET_NR_Linux + 323)\n#define TARGET_NR_signalfd4         (TARGET_NR_Linux + 324)\n#define TARGET_NR_eventfd2          (TARGET_NR_Linux + 325)\n#define TARGET_NR_epoll_create1     (TARGET_NR_Linux + 326)\n#define TARGET_NR_dup3              (TARGET_NR_Linux + 327)\n#define TARGET_NR_pipe2             (TARGET_NR_Linux + 328)\n#define TARGET_NR_inotify_init1     (TARGET_NR_Linux + 329)\n#define TARGET_NR_preadv            (TARGET_NR_Linux + 330)\n#define TARGET_NR_pwritev           (TARGET_NR_Linux + 331)\n#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 332)\n#define TARGET_NR_perf_event_open   (TARGET_NR_Linux + 333)\n#define TARGET_NR_accept4           (TARGET_NR_Linux + 334)\n#define TARGET_NR_recvmmsg          (TARGET_NR_Linux + 335)\n#define TARGET_NR_fanotify_init     (TARGET_NR_Linux + 336)\n#define TARGET_NR_fanotify_mark     (TARGET_NR_Linux + 337)\n#define TARGET_NR_prlimit64         (TARGET_NR_Linux + 338)\n#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 339)\n#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 340)\n#define TARGET_NR_clock_adjtime     (TARGET_NR_Linux + 341)\n#define TARGET_NR_syncfs            (TARGET_NR_Linux + 342)\n#define TARGET_NR_sendmmsg          (TARGET_NR_Linux + 343)\n#define TARGET_NR_setns             (TARGET_NR_Linux + 344)\n#define TARGET_NR_process_vm_readv  (TARGET_NR_Linux + 345)\n#define TARGET_NR_process_vm_writev (TARGET_NR_Linux + 346)\n#define TARGET_NR_kcmp              (TARGET_NR_Linux + 347)\n#define TARGET_NR_finit_module      (TARGET_NR_Linux + 348)\n\n#define TARGET_NR_sched_setattr   (TARGET_NR_Linux + 349)\n#define TARGET_NR_sched_getattr   (TARGET_NR_Linux + 350)\n#define TARGET_NR_renameat2       (TARGET_NR_Linux + 351)\n#define TARGET_NR_seccomp         (TARGET_NR_Linux + 352)\n#define TARGET_NR_getrandom       (TARGET_NR_Linux + 353)\n#define TARGET_NR_memfd_create    (TARGET_NR_Linux + 354)\n#define TARGET_NR_bpf             (TARGET_NR_Linux + 355)\n#define TARGET_NR_execveat        (TARGET_NR_Linux + 356)\n#define TARGET_NR_userfaultfd     (TARGET_NR_Linux + 357)\n#define TARGET_NR_membarrier      (TARGET_NR_Linux + 358)\n#define TARGET_NR_mlock2          (TARGET_NR_Linux + 359)\n#define TARGET_NR_copy_file_range (TARGET_NR_Linux + 360)\n#define TARGET_NR_preadv2         (TARGET_NR_Linux + 361)\n#define TARGET_NR_pwritev2        (TARGET_NR_Linux + 362)\n#define TARGET_NR_pkey_mprotect   (TARGET_NR_Linux + 363)\n#define TARGET_NR_pkey_alloc      (TARGET_NR_Linux + 364)\n#define TARGET_NR_pkey_free       (TARGET_NR_Linux + 365)\n#define TARGET_NR_statx           (TARGET_NR_Linux + 366)\n#define TARGET_NR_rseq            (TARGET_NR_Linux + 367)\n#define TARGET_NR_io_pgetevents   (TARGET_NR_Linux + 368)\n\n#endif /*SYSCALL_NR_H*/\n"
  },
  {
    "path": "src/os_emulation/syscallent.h",
    "content": "/*\n * Copyright (c) 2015-2021 The strace developers.\n * All rights reserved.\n *\n * SPDX-License-Identifier: LGPL-2.1-or-later\n */\n\n#define HANDLER(syscall) &OsSyscallExceptionHandler::syscall\n\n[0] = { 2, HANDLER(syscall_default_handler), \"io_setup\" },\n    [1] = { 1, HANDLER(syscall_default_handler), \"io_destroy\" },\n    [2] = { 3, HANDLER(syscall_default_handler), \"io_submit\" },\n    [3] = { 3, HANDLER(syscall_default_handler), \"io_cancel\" },\n    [4] = { 5, HANDLER(syscall_default_handler), \"io_getevents\" },\n    [5] = { 5, HANDLER(syscall_default_handler), \"setxattr\" },\n    [6] = { 5, HANDLER(syscall_default_handler), \"lsetxattr\" },\n    [7] = { 5, HANDLER(syscall_default_handler), \"fsetxattr\" },\n    [8] = { 4, HANDLER(syscall_default_handler), \"getxattr\" },\n    [9] = { 4, HANDLER(syscall_default_handler), \"lgetxattr\" },\n    [10] = { 4, HANDLER(syscall_default_handler), \"fgetxattr\" },\n    [11] = { 3, HANDLER(syscall_default_handler), \"listxattr\" },\n    [12] = { 3, HANDLER(syscall_default_handler), \"llistxattr\" },\n    [13] = { 3, HANDLER(syscall_default_handler), \"flistxattr\" },\n    [14] = { 2, HANDLER(syscall_default_handler), \"removexattr\" },\n    [15] = { 2, HANDLER(syscall_default_handler), \"lremovexattr\" },\n    [16] = { 2, HANDLER(syscall_default_handler), \"fremovexattr\" },\n    [17] = { 2, HANDLER(syscall_default_handler), \"getcwd\" },\n    [18] = { 3, HANDLER(syscall_default_handler), \"lookup_dcookie\" },\n    [19] = { 2, HANDLER(syscall_default_handler), \"eventfd2\" },\n    [20] = { 1, HANDLER(syscall_default_handler), \"epoll_create1\" },\n    [21] = { 4, HANDLER(syscall_default_handler), \"epoll_ctl\" },\n    [22] = { 6, HANDLER(syscall_default_handler), \"epoll_pwait\" },\n    [23] = { 1, HANDLER(syscall_default_handler), \"dup\" },\n    [24] = { 3, HANDLER(syscall_default_handler), \"dup3\" },\n    [25] = { 3, HANDLER(syscall_default_handler), \"fcntl\" },\n    [26] = { 1, HANDLER(syscall_default_handler), \"inotify_init1\" },\n    [27] = { 3, HANDLER(syscall_default_handler), \"inotify_add_watch\" },\n    [28] = { 2, HANDLER(syscall_default_handler), \"inotify_rm_watch\" },\n    [29] = { 3, HANDLER(syscall_default_handler), \"ioctl\" },\n    [30] = { 3, HANDLER(syscall_default_handler), \"ioprio_set\" },\n    [31] = { 2, HANDLER(syscall_default_handler), \"ioprio_get\" },\n    [32] = { 2, HANDLER(syscall_default_handler), \"flock\" },\n    [33] = { 4, HANDLER(syscall_default_handler), \"mknodat\" },\n    [34] = { 3, HANDLER(syscall_default_handler), \"mkdirat\" },\n    [35] = { 3, HANDLER(syscall_default_handler), \"unlinkat\" },\n    [36] = { 3, HANDLER(syscall_default_handler), \"symlinkat\" },\n    [37] = { 5, HANDLER(syscall_default_handler), \"linkat\" },\n    [38] = { 4, HANDLER(syscall_default_handler), \"renameat\" },\n    [39] = { 2, HANDLER(syscall_default_handler), \"umount2\" },\n    [40] = { 5, HANDLER(syscall_default_handler), \"mount\" },\n    [41] = { 2, HANDLER(syscall_default_handler), \"pivot_root\" },\n    [42] = { 3, HANDLER(syscall_default_handler), \"nfsservctl\" },\n    [43] = { 2, HANDLER(syscall_default_handler), \"statfs\" },\n    [44] = { 2, HANDLER(syscall_default_handler), \"fstatfs\" },\n    [45] = { 2, HANDLER(syscall_default_handler), \"truncate\" },\n    [46] = { 2, HANDLER(do_sys_ftruncate), \"ftruncate\" },\n    [47] = { 4, HANDLER(syscall_default_handler), \"fallocate\" },\n    [48] = { 3, HANDLER(syscall_default_handler), \"faccessat\" },\n    [49] = { 1, HANDLER(syscall_default_handler), \"chdir\" },\n    [50] = { 1, HANDLER(syscall_default_handler), \"fchdir\" },\n    [51] = { 1, HANDLER(syscall_default_handler), \"chroot\" },\n    [52] = { 2, HANDLER(syscall_default_handler), \"fchmod\" },\n    [53] = { 3, HANDLER(syscall_default_handler), \"fchmodat\" },\n    [54] = { 5, HANDLER(syscall_default_handler), \"fchownat\" },\n    [55] = { 3, HANDLER(syscall_default_handler), \"fchown\" },\n    [56] = { 4, HANDLER(do_sys_openat), \"openat\" }, [57] = { 1, HANDLER(do_sys_close), \"close\" },\n    [58] = { 0, HANDLER(syscall_default_handler), \"vhangup\" },\n    [59] = { 2, HANDLER(syscall_default_handler), \"pipe2\" },\n    [60] = { 4, HANDLER(syscall_default_handler), \"quotactl\" },\n    [61] = { 3, HANDLER(syscall_default_handler), \"getdents64\" },\n    [62] = { 3, HANDLER(syscall_default_handler), \"lseek\" },\n    [63] = { 3, HANDLER(do_sys_read), \"read\" }, [64] = { 3, HANDLER(do_sys_write), \"write\" },\n    [65] = { 3, HANDLER(do_sys_readv), \"readv\" }, [66] = { 3, HANDLER(do_sys_writev), \"writev\" },\n    [67] = { 4, HANDLER(syscall_default_handler), \"pread64\" },\n    [68] = { 4, HANDLER(syscall_default_handler), \"pwrite64\" },\n    [69] = { 4, HANDLER(syscall_default_handler), \"preadv\" },\n    [70] = { 4, HANDLER(syscall_default_handler), \"pwritev\" },\n    [71] = { 4, HANDLER(syscall_default_handler), \"sendfile\" },\n    [72] = { 6, HANDLER(syscall_default_handler), \"pselect6\" },\n    [73] = { 5, HANDLER(syscall_default_handler), \"ppoll\" },\n    [74] = { 4, HANDLER(syscall_default_handler), \"signalfd4\" },\n    [75] = { 4, HANDLER(syscall_default_handler), \"vmsplice\" },\n    [76] = { 6, HANDLER(syscall_default_handler), \"splice\" },\n    [77] = { 4, HANDLER(syscall_default_handler), \"tee\" },\n    [78] = { 4, HANDLER(syscall_default_handler), \"readlinkat\" },\n    [79] = { 4, HANDLER(syscall_default_handler), \"newfstatat\" },\n    [80] = { 2, HANDLER(syscall_default_handler), \"fstat\" },\n    [81] = { 0, HANDLER(syscall_default_handler), \"sync\" },\n    [82] = { 1, HANDLER(syscall_default_handler), \"fsync\" },\n    [83] = { 1, HANDLER(syscall_default_handler), \"fdatasync\" },\n    [84] = { 4, HANDLER(syscall_default_handler), \"sync_file_range\" },\n    [85] = { 2, HANDLER(syscall_default_handler), \"timerfd_create\" },\n    [86] = { 4, HANDLER(syscall_default_handler), \"timerfd_settime\" },\n    [87] = { 2, HANDLER(syscall_default_handler), \"timerfd_gettime\" },\n    [88] = { 4, HANDLER(syscall_default_handler), \"utimensat\" },\n    [89] = { 1, HANDLER(syscall_default_handler), \"acct\" },\n    [90] = { 2, HANDLER(syscall_default_handler), \"capget\" },\n    [91] = { 2, HANDLER(syscall_default_handler), \"capset\" },\n    [92] = { 1, HANDLER(syscall_default_handler), \"personality\" },\n    [93] = { 1, HANDLER(do_sys_exit), \"exit\" },\n    [94] = { 1, HANDLER(syscall_default_handler), \"exit_group\" },\n    [95] = { 5, HANDLER(syscall_default_handler), \"waitid\" },\n    [96] = { 1, HANDLER(syscall_default_handler), \"set_tid_address\" },\n    [97] = { 1, HANDLER(syscall_default_handler), \"unshare\" },\n    [98] = { 6, HANDLER(syscall_default_handler), \"futex\" },\n    [99] = { 2, HANDLER(syscall_default_handler), \"set_robust_list\" },\n    [100] = { 3, HANDLER(syscall_default_handler), \"get_robust_list\" },\n    [101] = { 2, HANDLER(syscall_default_handler), \"nanosleep\" },\n    [102] = { 2, HANDLER(syscall_default_handler), \"getitimer\" },\n    [103] = { 3, HANDLER(syscall_default_handler), \"setitimer\" },\n    [104] = { 4, HANDLER(syscall_default_handler), \"kexec_load\" },\n    [105] = { 3, HANDLER(syscall_default_handler), \"init_module\" },\n    [106] = { 2, HANDLER(syscall_default_handler), \"delete_module\" },\n    [107] = { 3, HANDLER(syscall_default_handler), \"timer_create\" },\n    [108] = { 2, HANDLER(syscall_default_handler), \"timer_gettime\" },\n    [109] = { 1, HANDLER(syscall_default_handler), \"timer_getoverrun\" },\n    [110] = { 4, HANDLER(syscall_default_handler), \"timer_settime\" },\n    [111] = { 1, HANDLER(syscall_default_handler), \"timer_delete\" },\n    [112] = { 2, HANDLER(syscall_default_handler), \"clock_settime\" },\n    [113] = { 2, HANDLER(syscall_default_handler), \"clock_gettime\" },\n    [114] = { 2, HANDLER(syscall_default_handler), \"clock_getres\" },\n    [115] = { 4, HANDLER(syscall_default_handler), \"clock_nanosleep\" },\n    [116] = { 3, HANDLER(syscall_default_handler), \"syslog\" },\n    [117] = { 4, HANDLER(syscall_default_handler), \"ptrace\" },\n    [118] = { 2, HANDLER(syscall_default_handler), \"sched_setparam\" },\n    [119] = { 3, HANDLER(syscall_default_handler), \"sched_setscheduler\" },\n    [120] = { 1, HANDLER(syscall_default_handler), \"sched_getscheduler\" },\n    [121] = { 2, HANDLER(syscall_default_handler), \"sched_getparam\" },\n    [122] = { 3, HANDLER(syscall_default_handler), \"sched_setaffinity\" },\n    [123] = { 3, HANDLER(syscall_default_handler), \"sched_getaffinity\" },\n    [124] = { 0, HANDLER(syscall_default_handler), \"sched_yield\" },\n    [125] = { 1, HANDLER(syscall_default_handler), \"sched_get_priority_max\" },\n    [126] = { 1, HANDLER(syscall_default_handler), \"sched_get_priority_min\" },\n    [127] = { 2, HANDLER(syscall_default_handler), \"sched_rr_get_interval\" },\n    [128] = { 0, HANDLER(syscall_default_handler), \"restart_syscall\" },\n    [129] = { 2, HANDLER(syscall_default_handler), \"kill\" },\n    [130] = { 2, HANDLER(syscall_default_handler), \"tkill\" },\n    [131] = { 3, HANDLER(syscall_default_handler), \"tgkill\" },\n    [132] = { 2, HANDLER(syscall_default_handler), \"sigaltstack\" },\n    [133] = { 2, HANDLER(syscall_default_handler), \"rt_sigsuspend\" },\n    [134] = { 4, HANDLER(syscall_default_handler), \"rt_sigaction\" },\n    [135] = { 4, HANDLER(syscall_default_handler), \"rt_sigprocmask\" },\n    [136] = { 2, HANDLER(syscall_default_handler), \"rt_sigpending\" },\n    [137] = { 4, HANDLER(syscall_default_handler), \"rt_sigtimedwait\" },\n    [138] = { 3, HANDLER(syscall_default_handler), \"rt_sigqueueinfo\" },\n    [139] = { 0, HANDLER(syscall_default_handler), \"rt_sigreturn\" },\n    [140] = { 3, HANDLER(syscall_default_handler), \"setpriority\" },\n    [141] = { 2, HANDLER(syscall_default_handler), \"getpriority\" },\n    [142] = { 4, HANDLER(syscall_default_handler), \"reboot\" },\n    [143] = { 2, HANDLER(syscall_default_handler), \"setregid\" },\n    [144] = { 1, HANDLER(syscall_default_handler), \"setgid\" },\n    [145] = { 2, HANDLER(syscall_default_handler), \"setreuid\" },\n    [146] = { 1, HANDLER(syscall_default_handler), \"setuid\" },\n    [147] = { 3, HANDLER(syscall_default_handler), \"setresuid\" },\n    [148] = { 3, HANDLER(syscall_default_handler), \"getresuid\" },\n    [149] = { 3, HANDLER(syscall_default_handler), \"setresgid\" },\n    [150] = { 3, HANDLER(syscall_default_handler), \"getresgid\" },\n    [151] = { 1, HANDLER(syscall_default_handler), \"setfsuid\" },\n    [152] = { 1, HANDLER(syscall_default_handler), \"setfsgid\" },\n    [153] = { 1, HANDLER(syscall_default_handler), \"times\" },\n    [154] = { 2, HANDLER(syscall_default_handler), \"setpgid\" },\n    [155] = { 1, HANDLER(syscall_default_handler), \"getpgid\" },\n    [156] = { 1, HANDLER(syscall_default_handler), \"getsid\" },\n    [157] = { 0, HANDLER(syscall_default_handler), \"setsid\" },\n    [158] = { 2, HANDLER(syscall_default_handler), \"getgroups\" },\n    [159] = { 2, HANDLER(syscall_default_handler), \"setgroups\" },\n    [160] = { 1, HANDLER(syscall_default_handler), \"uname\" },\n    [161] = { 2, HANDLER(syscall_default_handler), \"sethostname\" },\n    [162] = { 2, HANDLER(syscall_default_handler), \"setdomainname\" },\n    [163] = { 2, HANDLER(syscall_default_handler), \"getrlimit\" },\n    [164] = { 2, HANDLER(syscall_default_handler), \"setrlimit\" },\n    [165] = { 2, HANDLER(syscall_default_handler), \"getrusage\" },\n    [166] = { 1, HANDLER(syscall_default_handler), \"umask\" },\n    [167] = { 5, HANDLER(syscall_default_handler), \"prctl\" },\n    [168] = { 3, HANDLER(syscall_default_handler), \"getcpu\" },\n    [169] = { 2, HANDLER(syscall_default_handler), \"gettimeofday\" },\n    [170] = { 2, HANDLER(syscall_default_handler), \"settimeofday\" },\n    [171] = { 1, HANDLER(syscall_default_handler), \"adjtimex\" },\n    [172] = { 0, HANDLER(syscall_default_handler), \"getpid\" },\n    [173] = { 0, HANDLER(syscall_default_handler), \"getppid\" },\n    [174] = { 0, HANDLER(syscall_default_handler), \"getuid\" },\n    [175] = { 0, HANDLER(syscall_default_handler), \"geteuid\" },\n    [176] = { 0, HANDLER(syscall_default_handler), \"getgid\" },\n    [177] = { 0, HANDLER(syscall_default_handler), \"getegid\" },\n    [178] = { 0, HANDLER(syscall_default_handler), \"gettid\" },\n    [179] = { 1, HANDLER(syscall_default_handler), \"sysinfo\" },\n    [180] = { 4, HANDLER(syscall_default_handler), \"mq_open\" },\n    [181] = { 1, HANDLER(syscall_default_handler), \"mq_unlink\" },\n    [182] = { 5, HANDLER(syscall_default_handler), \"mq_timedsend\" },\n    [183] = { 5, HANDLER(syscall_default_handler), \"mq_timedreceive\" },\n    [184] = { 2, HANDLER(syscall_default_handler), \"mq_notify\" },\n    [185] = { 3, HANDLER(syscall_default_handler), \"mq_getsetattr\" },\n    [186] = { 2, HANDLER(syscall_default_handler), \"msgget\" },\n    [187] = { 3, HANDLER(syscall_default_handler), \"msgctl\" },\n    [188] = { 5, HANDLER(syscall_default_handler), \"msgrcv\" },\n    [189] = { 4, HANDLER(syscall_default_handler), \"msgsnd\" },\n    [190] = { 3, HANDLER(syscall_default_handler), \"semget\" },\n    [191] = { 4, HANDLER(syscall_default_handler), \"semctl\" },\n    [192] = { 4, HANDLER(syscall_default_handler), \"semtimedop\" },\n    [193] = { 3, HANDLER(syscall_default_handler), \"semop\" },\n    [194] = { 3, HANDLER(syscall_default_handler), \"shmget\" },\n    [195] = { 3, HANDLER(syscall_default_handler), \"shmctl\" },\n    [196] = { 3, HANDLER(syscall_default_handler), \"shmat\" },\n    [197] = { 1, HANDLER(syscall_default_handler), \"shmdt\" },\n    [198] = { 3, HANDLER(syscall_default_handler), \"socket\" },\n    [199] = { 4, HANDLER(syscall_default_handler), \"socketpair\" },\n    [200] = { 3, HANDLER(syscall_default_handler), \"bind\" },\n    [201] = { 2, HANDLER(syscall_default_handler), \"listen\" },\n    [202] = { 3, HANDLER(syscall_default_handler), \"accept\" },\n    [203] = { 3, HANDLER(syscall_default_handler), \"connect\" },\n    [204] = { 3, HANDLER(syscall_default_handler), \"getsockname\" },\n    [205] = { 3, HANDLER(syscall_default_handler), \"getpeername\" },\n    [206] = { 6, HANDLER(syscall_default_handler), \"sendto\" },\n    [207] = { 6, HANDLER(syscall_default_handler), \"recvfrom\" },\n    [208] = { 5, HANDLER(syscall_default_handler), \"setsockopt\" },\n    [209] = { 5, HANDLER(syscall_default_handler), \"getsockopt\" },\n    [210] = { 2, HANDLER(syscall_default_handler), \"shutdown\" },\n    [211] = { 3, HANDLER(syscall_default_handler), \"sendmsg\" },\n    [212] = { 3, HANDLER(syscall_default_handler), \"recvmsg\" },\n    [213] = { 3, HANDLER(syscall_default_handler), \"readahead\" },\n    [214] = { 1, HANDLER(do_sys_brk), \"brk\" },\n    [215] = { 2, HANDLER(syscall_default_handler), \"munmap\" },\n    [216] = { 5, HANDLER(syscall_default_handler), \"mremap\" },\n    [217] = { 5, HANDLER(syscall_default_handler), \"add_key\" },\n    [218] = { 4, HANDLER(syscall_default_handler), \"request_key\" },\n    [219] = { 5, HANDLER(syscall_default_handler), \"keyctl\" },\n    [220] = { 5, HANDLER(syscall_default_handler), \"clone\" },\n    [221] = { 3, HANDLER(syscall_default_handler), \"execve\" },\n    [222] = { 6, HANDLER(do_sys_mmap), \"mmap\" },\n    [223] = { 4, HANDLER(syscall_default_handler), \"fadvise64\" },\n    [224] = { 2, HANDLER(syscall_default_handler), \"swapon\" },\n    [225] = { 1, HANDLER(syscall_default_handler), \"swapoff\" },\n    [226] = { 3, HANDLER(syscall_default_handler), \"mprotect\" },\n    [227] = { 3, HANDLER(syscall_default_handler), \"msync\" },\n    [228] = { 2, HANDLER(syscall_default_handler), \"mlock\" },\n    [229] = { 2, HANDLER(syscall_default_handler), \"munlock\" },\n    [230] = { 1, HANDLER(syscall_default_handler), \"mlockall\" },\n    [231] = { 0, HANDLER(syscall_default_handler), \"munlockall\" },\n    [232] = { 3, HANDLER(syscall_default_handler), \"mincore\" },\n    [233] = { 3, HANDLER(syscall_default_handler), \"madvise\" },\n    [234] = { 5, HANDLER(syscall_default_handler), \"remap_file_pages\" },\n    [235] = { 6, HANDLER(syscall_default_handler), \"mbind\" },\n    [236] = { 5, HANDLER(syscall_default_handler), \"get_mempolicy\" },\n    [237] = { 3, HANDLER(syscall_default_handler), \"set_mempolicy\" },\n    [238] = { 4, HANDLER(syscall_default_handler), \"migrate_pages\" },\n    [239] = { 6, HANDLER(syscall_default_handler), \"move_pages\" },\n    [240] = { 4, HANDLER(syscall_default_handler), \"rt_tgsigqueueinfo\" },\n    [241] = { 5, HANDLER(syscall_default_handler), \"perf_event_open\" },\n    [242] = { 4, HANDLER(syscall_default_handler), \"accept4\" },\n    [243] = { 5, HANDLER(syscall_default_handler), \"recvmmsg\" },\n// this array has to be contiguous because C++.\n\n#undef HANDLER\n"
  },
  {
    "path": "src/os_emulation/target_errno.h",
    "content": "#ifndef TARGET_ERRNO_H\n#define TARGET_ERRNO_H\n\n#define TARGET_EPERM           1\n#define TARGET_ENOENT          2\n#define TARGET_ESRCH           3\n#define TARGET_EINTR           4\n#define TARGET_EIO             5\n#define TARGET_ENXIO           6\n#define TARGET_E2BIG           7\n#define TARGET_ENOEXEC         8\n#define TARGET_EBADF           9\n#define TARGET_ECHILD          10\n#define TARGET_EAGAIN          11\n#define TARGET_ENOMEM          12\n#define TARGET_EACCES          13\n#define TARGET_EFAULT          14\n#define TARGET_ENOTBLK         15\n#define TARGET_EBUSY           16\n#define TARGET_EEXIST          17\n#define TARGET_EXDEV           18\n#define TARGET_ENODEV          19\n#define TARGET_ENOTDIR         20\n#define TARGET_EISDIR          21\n#define TARGET_EINVAL          22\n#define TARGET_ENFILE          23\n#define TARGET_EMFILE          24\n#define TARGET_ENOTTY          25\n#define TARGET_ETXTBSY         26\n#define TARGET_EFBIG           27\n#define TARGET_ENOSPC          28\n#define TARGET_ESPIPE          29\n#define TARGET_EROFS           30\n#define TARGET_EMLINK          31\n#define TARGET_EPIPE           32\n#define TARGET_EDOM            33\n#define TARGET_ERANGE          34\n#define TARGET_ENOMSG          35\n#define TARGET_EIDRM           36\n#define TARGET_ECHRNG          37\n#define TARGET_EL2NSYNC        38\n#define TARGET_EL3HLT          39\n#define TARGET_EL3RST          40\n#define TARGET_ELNRNG          41\n#define TARGET_EUNATCH         42\n#define TARGET_ENOCSI          43\n#define TARGET_EL2HLT          44\n#define TARGET_EDEADLK         45\n#define TARGET_ENOLCK          46\n#define TARGET_EBADE           50\n#define TARGET_EBADR           51\n#define TARGET_EXFULL          52\n#define TARGET_ENOANO          53\n#define TARGET_EBADRQC         54\n#define TARGET_EBADSLT         55\n#define TARGET_EDEADLOCK       56\n#define TARGET_EBFONT          59\n#define TARGET_ENOSTR          60\n#define TARGET_ENODATA         61\n#define TARGET_ETIME           62\n#define TARGET_ENOSR           63\n#define TARGET_ENONET          64\n#define TARGET_ENOPKG          65\n#define TARGET_EREMOTE         66\n#define TARGET_ENOLINK         67\n#define TARGET_EADV            68\n#define TARGET_ESRMNT          69\n#define TARGET_ECOMM           70\n#define TARGET_EPROTO          71\n#define TARGET_EDOTDOT         73\n#define TARGET_EMULTIHOP       74\n#define TARGET_EBADMSG         77\n#define TARGET_ENAMETOOLONG    78\n#define TARGET_EOVERFLOW       79\n#define TARGET_ENOTUNIQ        80\n#define TARGET_EBADFD          81\n#define TARGET_EREMCHG         82\n#define TARGET_ELIBACC         83\n#define TARGET_ELIBBAD         84\n#define TARGET_ELIBSCN         85\n#define TARGET_ELIBMAX         86\n#define TARGET_ELIBEXEC        87\n#define TARGET_EILSEQ          88\n#define TARGET_ENOSYS          89\n#define TARGET_ELOOP           90\n#define TARGET_ERESTART        91\n#define TARGET_ESTRPIPE        92\n#define TARGET_ENOTEMPTY       93\n#define TARGET_EUSERS          94\n#define TARGET_ENOTSOCK        95\n#define TARGET_EDESTADDRREQ    96\n#define TARGET_EMSGSIZE        97\n#define TARGET_EPROTOTYPE      98\n#define TARGET_ENOPROTOOPT     99\n#define TARGET_EPROTONOSUPPORT 120\n#define TARGET_ESOCKTNOSUPPORT 121\n#define TARGET_EOPNOTSUPP      122\n#define TARGET_ENOTSUP         EOPNOTSUPP\n#define TARGET_EPFNOSUPPORT    123\n#define TARGET_EAFNOSUPPORT    124\n#define TARGET_EADDRINUSE      125\n#define TARGET_EADDRNOTAVAIL   126\n#define TARGET_ENETDOWN        127\n#define TARGET_ENETUNREACH     128\n#define TARGET_ENETRESET       129\n#define TARGET_ECONNABORTED    130\n#define TARGET_ECONNRESET      131\n#define TARGET_ENOBUFS         132\n#define TARGET_EISCONN         133\n#define TARGET_ENOTCONN        134\n#define TARGET_EUCLEAN         135\n#define TARGET_ENOTNAM         137\n#define TARGET_ENAVAIL         138\n#define TARGET_EISNAM          139\n#define TARGET_EREMOTEIO       140\n#define TARGET_ESHUTDOWN       143\n#define TARGET_ETOOMANYREFS    144\n#define TARGET_ETIMEDOUT       145\n#define TARGET_ECONNREFUSED    146\n#define TARGET_EHOSTDOWN       147\n#define TARGET_EHOSTUNREACH    148\n#define TARGET_EWOULDBLOCK     EAGAIN\n#define TARGET_EALREADY        149\n#define TARGET_EINPROGRESS     150\n#define TARGET_ESTALE          151\n#define TARGET_ECANCELED       158\n#define TARGET_ENOMEDIUM       159\n#define TARGET_EMEDIUMTYPE     160\n#define TARGET_ENOKEY          161\n#define TARGET_EKEYEXPIRED     162\n#define TARGET_EKEYREVOKED     163\n#define TARGET_EKEYREJECTED    164\n#define TARGET_EOWNERDEAD      165\n#define TARGET_ENOTRECOVERABLE 166\n#define TARGET_ERFKILL         167\n#define TARGET_EHWPOISON       168\n#define TARGET_EDQUOT          1133\n\n#endif /*TARGET_ERRNO_H*/\n"
  },
  {
    "path": "src/project_info.h.in",
    "content": "/**\n * Transfers project information form CMake to C++.\n * @file\n */\n#ifndef QTRVSIM_PROJECT_INFO_H_IN\n#define QTRVSIM_PROJECT_INFO_H_IN\n\nconstexpr const char *COPYRIGHT_HTML = \"@COPYRIGHT_HTML@\";\nconstexpr const char *LICENCE_HTML = \"@LICENCE_SHORT_HTML@\";\n\n\n#endif // QTRVSIM_PROJECT_INFO_H_IN\n"
  },
  {
    "path": "tests/cli/asm_error/program.S",
    "content": "THIS IS INVALID ASM CODE"
  },
  {
    "path": "tests/cli/modifiers/program.S",
    "content": "lw x0, 0(x0)\nlw x0, fail(x0)\nlw x0, %lo(label)(x0)\naddi x0, x0, %lo(0xDEA)\n\nli x1, label\nlui x2, %hi(label)\naddi x2, x2, %lo(label)\nbne x1, x2, fail\nebreak\nnop\nnop\n\nfail:\n.data\n.word 0\n\n\n.org 0xABCDE123\nlabel:"
  },
  {
    "path": "tests/cli/modifiers/stdout.txt",
    "content": "Machine stopped on BREAK exception.\n"
  },
  {
    "path": "tests/cli/modifiers-pcrel/program.S",
    "content": "label:\nnop\nnop\nla x1, label\nauipc x2, %pcrel_hi(label)\naddi x2, x2, %pcrel_lo(label)\nbne x1, x1, fail\nebreak\nnop\nnop\n\nfail:\n.data\n.word 0\n"
  },
  {
    "path": "tests/cli/modifiers-pcrel/stdout.txt",
    "content": "Machine stopped on BREAK exception.\n"
  },
  {
    "path": "tests/cli/stalls/program.S",
    "content": ".text\n\n_start:\nloop:\n\taddi x1, x0, 0x11\n\taddi x2, x0, 0x22\n\taddi x3, x0, 0x33\n\tlw   x4, 0x44(x0)\n\taddi x5, x4, 0x55\n\tbeq  x0, x0, tgt1\n\taddi x11, x0, 0x11\n\taddi x12, x0, 0x22\n\taddi x13, x0, 0x33\n\taddi x14, x0, 0x44\n\taddi x15, x0, 0x55\ntgt1:\n\taddi x21, x0, 0x11\n\taddi x22, x0, 0x22\n\taddi x23, x0, 0x33\n\taddi x24, x0, 0x44\n\taddi x25, x0, 0x55\n\n\tebreak"
  },
  {
    "path": "tests/cli/stalls/stdout.txt",
    "content": "Machine stopped on BREAK exception.\nMachine state report:\nPC:0x00000244\nR0:0x00000000 R1:0x00000011 R2:0x00000022 R3:0x00000033 R4:0x00000000 R5:0x00000055 R6:0x00000000 R7:0x00000000 R8:0x00000000 R9:0x00000000 R10:0x00000000 R11:0x00000000 R12:0x00000000 R13:0x00000000 R14:0x00000000 R15:0x00000000 R16:0x00000000 R17:0x00000000 R18:0x00000000 R19:0x00000000 R20:0x00000000 R21:0x00000011 R22:0x00000022 R23:0x00000033 R24:0x00000044 R25:0x00000055 R26:0x00000000 R27:0x00000000 R28:0x00000000 R29:0x00000000 R30:0x00000000 R31:0x00000000\ncycle: 0x0000000c mvendorid: 0x00000000 marchid: 0x00000000 mimpid: 0x00000000 mhardid: 0x00000000 mstatus: 0x00000000 misa: 0x40001111 mie: 0x00000000 mtvec: 0x00000000 mscratch: 0x00000000 mepc: 0x00000240 mcause: 0x00000003 mtval: 0x00000000 mip: 0x00000000 mtinst: 0x00000000 mtval2: 0x00000000 mcycle: 0x0000000c minstret: 0x0000000b sstatus: 0x00000000 stvec: 0x00000000 sscratch: 0x00000000 sepc: 0x00000000 scause: 0x00000000 stval: 0x00000000 satp: 0x00000000\n"
  },
  {
    "path": "tests/cli/virtual_memory/dtlb/program.S",
    "content": "// D-TLB test — map data pages at MAP_VA; write 'A'..'H' to the first byte of eight pages and read/print them.\n\n.globl _start\n.option norelax\n\n// Serial port/terminal registers\n.equ SERIAL_PORT_BASE,      0xffffc000 // base address of serial port region\n.equ SERP_RX_ST_REG_o,      0x0000 // Offset of RX_ST_REG\n.equ SERP_RX_DATA_REG_o,    0x0004 // Offset of RX_DATA_REG\n.equ SERP_TX_ST_REG_o,      0x0008 // Offset of TX_ST_REG\n.equ SERP_TX_ST_REG_READY_m,0x1 // Transmitter can accept next byte\n.equ SERP_TX_DATA_REG_o,    0x000c // Offset of TX_DATA_REG\n\n.equ ROOT_PT_PHYS, 0x1000\n.equ MAP_VA, 0xC4000000\n\n// PTE flags: 0xCF = 11001111\n// bit0 V = 1 (Valid)\n// bit1 R = 1 (Readable)\n// bit2 W = 1  Writable)\n// bit3 X = 1 (Executable)\n// bit4 U = 0 (NOT user-accessible)\n// bit5 G = 0 (NOT global)\n// bit6 A = 1 (Accessed)\n// bit7 D = 1 (Dirty)\n.equ PTE_FLAGS_FULL, 0xCF\n\n// mstatus MPP manipulation masks (for preparing mret to change privilege)\n.equ MSTATUS_MPP_CLEAR, 0x1000 // mask to clear MPP[12] (set bit 12 -> will be cleared via csrrc)\n.equ MSTATUS_MPP_SET, 0x800 // mask to set MPP[11]  (set bit 11 -> will be set via csrrs)\n\n.equ SATP_ENABLE, 0x80000001 // satp value to enable paging (implementation-specific)\n\n.org 0x00000200\n.text\n_start:\n    // t0 = physical address of root page table\n    li t0, ROOT_PT_PHYS\n\n    // t4 = virtual address we want to map (MAP_VA)\n    li t4, MAP_VA\n\n    // Build leaf PTE\n    srli t1, t4, 12\n    slli t1, t1, 10\n    li t6, PTE_FLAGS_FULL\n    or t1, t1, t6\n\n    srli t5, t4, 22\n    slli t2, t5, 2\n    add t3, t0, t2\n    sw t1, 0(t3)\n    fence\n\n    // Second-level page table physical address (must be page-aligned)\n    li s0, 0x2000\n\n    // root PTE => pointer to second level table (non-leaf PTE: V=1, R/W/X = 0)\n    // compute ppn of second-level table = 0x2000 >> 12 = 2\n    li t1, 2\n    slli t1, t1, 10 // place ppn at bits [31:10]\n    li t6, 1 // V flag only (non-leaf)\n    or t1, t1, t6 // root PTE pointing to second-level table\n    li t4, SERIAL_PORT_BASE // serial VA = 0xffffc000\n    srli t5, t4, 22 // vpn1 for serial VA (0x3ff)\n    slli t2, t5, 2\n    add t3, t0, t2 // address of root PTE slot for serial region\n    sw t1, 0(t3)\n    fence\n\n    // Now create the level-0 (leaf) PTE inside the second-level page table\n    // leaf PTE: PPN = device_phys >> 12, flags = PTE_FLAGS_FULL\n    // compute leaf PTE value (maps the single 4KB page)\n    srli t1, t4, 12 // t1 = device_phys >> 12 (we want identity mapping)\n    slli t1, t1, 10 // place ppn\n    li t6, PTE_FLAGS_FULL\n    or t1, t1, t6 // t1 = leaf PTE\n\n    // compute vpn0 index and byte offset in second-level table\n    srli t5, t4, 12\n    andi t5, t5, 0x3FF // vpn0 = (va>>12) & 0x3FF\n    slli t5, t5, 2 // byte offset = vpn0*4\n    add t3, s0, t5 // second-level-table-phys (s0) + offset\n    sw t1, 0(t3)\n    fence\n\n    // Ensure satp is cleared before setting new value (flush previous translations)\n    li t0, 0\n    csrw satp, t0\n\n    // Enable the MMU by writing SATP; this switches address translation on\n    li t0, SATP_ENABLE\n    csrw satp, t0\n    fence\n\n    // Prepare mstatus MPP so that mret will return to Supervisor mode:\n    // Clear MPP[12] bit then set MPP[11] bit (resulting MPP=01 => Supervisor).\n    li t0, MSTATUS_MPP_CLEAR\n    csrrc zero, mstatus, t0 // clear bit 12 of mstatus.MPP\n    li t0, MSTATUS_MPP_SET\n    csrrs zero, mstatus, t0 // set bit 11 of mstatus.MPP\n\n    // Set mepc to the virtual address of vm_entry and return from machine mode to\n    // the prepared privilege level (Supervisor) using mret.\n    la t0, vm_entry // load address of vm_entry (virtual address after mapping)\n    csrw mepc, t0\n    mret\n\n.org 0xC4000000\n.text\nvm_entry:\n    li t0, SERIAL_PORT_BASE\n    la t1, MAP_VA // pointer to start of mapped virtual region\n    li t2, 0 // page counter\n    li t3, 8 // number of pages to write/read (A..H)\n    li t4, 65 // ASCII 'A'\n    li t5, 0x1000 // page size (4KB)\n\n// write_pages_loop: write one byte (A..H) at the start of each mapped page.\nwrite_pages_loop:\n    sb t4, 0(t1)\n    add t1, t1, t5\n    addi t4, t4, 1\n    addi t2, t2, 1\n    blt t2, t3, write_pages_loop\n\n    // Reset pointer and counter to read back and print the first byte of each page.\n    la t1, MAP_VA\n    li t2, 0\n\nread_print_loop:\n    lb t6, 0(t1) // load the byte stored at start of current page\n\n    // wait_tx: poll transmitter status until ready, then write byte to TX data reg.\nwait_tx:\n    lw t4, SERP_TX_ST_REG_o(t0)\n    andi t4, t4, SERP_TX_ST_REG_READY_m\n    beq t4, zero, wait_tx\n    sw t6, SERP_TX_DATA_REG_o(t0)\n\n    add t1, t1, t5\n    addi t2, t2, 1\n    blt t2, t3, read_print_loop\n\n    ebreak\n\n1:  auipc t0, 0\n    jalr zero, 0(t0)\n"
  },
  {
    "path": "tests/cli/virtual_memory/dtlb/stdout.txt",
    "content": "Machine stopped on BREAK exception.\nMachine state report:\nPC:0xc4000078\nR0:0x00000000 R1:0x00000000 R2:0xbfffff00 R3:0x00000000 R4:0x00000000 R5:0xffffffffffffc000 R6:0xffffffffc4008000 R7:0x00000008 R8:0x00002000 R9:0x00000000 R10:0x00000000 R11:0x00000000 R12:0x00000000 R13:0x00000000 R14:0x00000000 R15:0x00000000 R16:0x00000000 R17:0x00000000 R18:0x00000000 R19:0x00000000 R20:0x00000000 R21:0x00000000 R22:0x00000000 R23:0x00000000 R24:0x00000000 R25:0x00000000 R26:0x00000000 R27:0x00000000 R28:0x00000008 R29:0x00000001 R30:0x00001000 R31:0x00000048\ncycle: 0x000000b2 mvendorid: 0x00000000 marchid: 0x00000000 mimpid: 0x00000000 mhardid: 0x00000000 mstatus: 0x00000080 misa: 0x40001111 mie: 0x00000000 mtvec: 0x00000000 mscratch: 0x00000000 mepc: 0xc4000074 mcause: 0x00000003 mtval: 0x00000000 mip: 0x00000000 mtinst: 0x00000000 mtval2: 0x00000000 mcycle: 0x000000b2 minstret: 0x000000b1 sstatus: 0x00000000 stvec: 0x00000000 sscratch: 0x00000000 sepc: 0x00000000 scause: 0x00000000 stval: 0x00000000 satp: 0x80000001\n"
  },
  {
    "path": "tests/cli/virtual_memory/exec/program.S",
    "content": "// Place a tiny function in the mapped virtual page and jump to it (tests X bit).\n\n.globl _start\n.option norelax\n\n// Serial port/terminal registers\n.equ SERIAL_PORT_BASE,      0xffffc000 // base address of serial port region\n.equ SERP_RX_ST_REG_o,      0x0000 // Offset of RX_ST_REG\n.equ SERP_RX_DATA_REG_o,    0x0004 // Offset of RX_DATA_REG\n.equ SERP_TX_ST_REG_o,      0x0008 // Offset of TX_ST_REG\n.equ SERP_TX_ST_REG_READY_m,0x1 // Transmitter can accept next byte\n.equ SERP_TX_DATA_REG_o,    0x000c // Offset of TX_DATA_REG\n\n.equ ROOT_PT_PHYS, 0x1000\n.equ MAP_VA, 0xC4000000\n\n// PTE flags: 0xCF = 11001111\n// bit0 V = 1 (Valid)\n// bit1 R = 1 (Readable)\n// bit2 W = 1  Writable)\n// bit3 X = 1 (Executable)\n// bit4 U = 0 (NOT user-accessible)\n// bit5 G = 0 (NOT global)\n// bit6 A = 1 (Accessed)\n// bit7 D = 1 (Dirty)\n.equ PTE_FLAGS_FULL, 0xCF\n\n// mstatus MPP manipulation masks (for preparing mret to change privilege)\n.equ MSTATUS_MPP_CLEAR, 0x1000 // mask to clear MPP[12] (set bit 12 -> will be cleared via csrrc)\n.equ MSTATUS_MPP_SET, 0x800 // mask to set MPP[11]  (set bit 11 -> will be set via csrrs)\n\n.equ SATP_ENABLE, 0x80000001 // satp value to enable paging (implementation-specific)\n\n.org 0x00000200\n.text\n_start:\n    // t0 = physical address of root page table\n    li t0, ROOT_PT_PHYS\n\n    // t4 = virtual address we want to map (MAP_VA)\n    li t4, MAP_VA\n\n    // Build leaf PTE\n    srli t1, t4, 12\n    slli t1, t1, 10\n    li t6, PTE_FLAGS_FULL\n    or t1, t1, t6\n\n    srli t5, t4, 22\n    slli t2, t5, 2\n    add t3, t0, t2\n    sw t1, 0(t3)\n    fence\n\n    // Second-level page table physical address (must be page-aligned)\n    li s0, 0x2000\n\n    // root PTE => pointer to second level table (non-leaf PTE: V=1, R/W/X = 0)\n    // compute ppn of second-level table = 0x2000 >> 12 = 2\n    li t1, 2\n    slli t1, t1, 10 // place ppn at bits [31:10]\n    li t6, 1 // V flag only (non-leaf)\n    or t1, t1, t6 // root PTE pointing to second-level table\n    li t4, SERIAL_PORT_BASE // serial VA = 0xffffc000\n    srli t5, t4, 22 // vpn1 for serial VA (0x3ff)\n    slli t2, t5, 2\n    add t3, t0, t2 // address of root PTE slot for serial region\n    sw t1, 0(t3)\n    fence\n\n    // Now create the level-0 (leaf) PTE inside the second-level page table\n    // leaf PTE: PPN = device_phys >> 12, flags = PTE_FLAGS_FULL\n    // compute leaf PTE value (maps the single 4KB page)\n    srli t1, t4, 12 // t1 = device_phys >> 12 (we want identity mapping)\n    slli t1, t1, 10 // place ppn\n    li t6, PTE_FLAGS_FULL\n    or t1, t1, t6 // t1 = leaf PTE\n\n    // compute vpn0 index and byte offset in second-level table\n    srli t5, t4, 12\n    andi t5, t5, 0x3FF // vpn0 = (va>>12) & 0x3FF\n    slli t5, t5, 2 // byte offset = vpn0*4\n    add t3, s0, t5 // second-level-table-phys (s0) + offset\n    sw t1, 0(t3)\n    fence\n\n    // Ensure satp is cleared before setting new value (flush previous translations)\n    li t0, 0\n    csrw satp, t0\n\n    // Enable the MMU by writing SATP; this switches address translation on\n    li t0, SATP_ENABLE\n    csrw satp, t0\n    fence\n\n    // Prepare mstatus MPP so that mret will return to Supervisor mode:\n    // Clear MPP[12] bit then set MPP[11] bit (resulting MPP=01 => Supervisor).\n    li t0, MSTATUS_MPP_CLEAR\n    csrrc zero, mstatus, t0 // clear bit 12 of mstatus.MPP\n    li t0, MSTATUS_MPP_SET\n    csrrs zero, mstatus, t0 // set bit 11 of mstatus.MPP\n\n    // Set mepc to the virtual address of vm_entry and return from machine mode to\n    // the prepared privilege level (Supervisor) using mret.\n    la t0, vm_entry // load address of vm_entry (virtual address after mapping)\n    csrw mepc, t0\n    mret\n\n.org 0xC4000000\n.text\nvm_entry:\n    li a0, SERIAL_PORT_BASE\n\n    // Call the function placed in the same mapped page\n    la t0, mapped_function\n    jalr zero, 0(t0)\n\n    ebreak\n\n// small function placed in the mapped page\n.org 0xC4000100\nmapped_function:\n    li a0, SERIAL_PORT_BASE\n    li t1, 88\nwait_tx:\n    lw t0, SERP_TX_ST_REG_o(a0)\n    andi t0, t0, SERP_TX_ST_REG_READY_m\n    beq t0, zero, wait_tx\n    sw t1, SERP_TX_DATA_REG_o(a0)\n    ebreak\n"
  },
  {
    "path": "tests/cli/virtual_memory/exec/stdout.txt",
    "content": "Machine stopped on BREAK exception.\nMachine state report:\nPC:0xc4000124\nR0:0x00000000 R1:0x00000000 R2:0xbfffff00 R3:0x00000000 R4:0x00000000 R5:0x00000001 R6:0x00000058 R7:0x00000ffc R8:0x00002000 R9:0x00000000 R10:0xffffffffffffc000 R11:0x00000000 R12:0x00000000 R13:0x00000000 R14:0x00000000 R15:0x00000000 R16:0x00000000 R17:0x00000000 R18:0x00000000 R19:0x00000000 R20:0x00000000 R21:0x00000000 R22:0x00000000 R23:0x00000000 R24:0x00000000 R25:0x00000000 R26:0x00000000 R27:0x00000000 R28:0x00002ff0 R29:0xffffffffffffc000 R30:0x00000ff0 R31:0x000000cf\ncycle: 0x00000047 mvendorid: 0x00000000 marchid: 0x00000000 mimpid: 0x00000000 mhardid: 0x00000000 mstatus: 0x00000080 misa: 0x40001111 mie: 0x00000000 mtvec: 0x00000000 mscratch: 0x00000000 mepc: 0xc4000120 mcause: 0x00000003 mtval: 0x00000000 mip: 0x00000000 mtinst: 0x00000000 mtval2: 0x00000000 mcycle: 0x00000047 minstret: 0x00000046 sstatus: 0x00000000 stvec: 0x00000000 sscratch: 0x00000000 sepc: 0x00000000 scause: 0x00000000 stval: 0x00000000 satp: 0x80000001\n"
  },
  {
    "path": "tests/cli/virtual_memory/itlb/program.S",
    "content": "// I-TLB test — map code pages at MAP_VA and execute tiny functions placed on eight\n// pages to print A–H, exercising instruction fetch from different pages.\n\n.globl _start\n.option norelax\n\n// Serial port/terminal registers\n.equ SERIAL_PORT_BASE,      0xffffc000 // base address of serial port region\n.equ SERP_RX_ST_REG_o,      0x0000 // Offset of RX_ST_REG\n.equ SERP_RX_DATA_REG_o,    0x0004 // Offset of RX_DATA_REG\n.equ SERP_TX_ST_REG_o,      0x0008 // Offset of TX_ST_REG\n.equ SERP_TX_ST_REG_READY_m,0x1 // Transmitter can accept next byte\n.equ SERP_TX_DATA_REG_o,    0x000c // Offset of TX_DATA_REG\n\n.equ ROOT_PT_PHYS, 0x1000\n.equ MAP_VA, 0xC4000000\n\n// PTE flags: 0xCF = 11001111\n// bit0 V = 1 (Valid)\n// bit1 R = 1 (Readable)\n// bit2 W = 1  Writable)\n// bit3 X = 1 (Executable)\n// bit4 U = 0 (NOT user-accessible)\n// bit5 G = 0 (NOT global)\n// bit6 A = 1 (Accessed)\n// bit7 D = 1 (Dirty)\n.equ PTE_FLAGS_FULL, 0xCF\n\n// mstatus MPP manipulation masks (for preparing mret to change privilege)\n.equ MSTATUS_MPP_CLEAR, 0x1000 // mask to clear MPP[12] (set bit 12 -> will be cleared via csrrc)\n.equ MSTATUS_MPP_SET, 0x800 // mask to set MPP[11]  (set bit 11 -> will be set via csrrs)\n\n.equ SATP_ENABLE, 0x80000001 // satp value to enable paging (implementation-specific)\n\n.org 0x00000200\n.text\n_start:\n    // t0 = physical address of root page table\n    li t0, ROOT_PT_PHYS\n\n    // t4 = virtual address we want to map (MAP_VA)\n    li t4, MAP_VA\n\n    // Build leaf PTE\n    srli t1, t4, 12\n    slli t1, t1, 10\n    li t6, PTE_FLAGS_FULL\n    or t1, t1, t6\n\n    srli t5, t4, 22\n    slli t2, t5, 2\n    add t3, t0, t2\n    sw t1, 0(t3)\n    fence\n\n    // Second-level page table physical address (must be page-aligned)\n    li s0, 0x2000\n\n    // root PTE => pointer to second level table (non-leaf PTE: V=1, R/W/X = 0)\n    // compute ppn of second-level table = 0x2000 >> 12 = 2\n    li t1, 2\n    slli t1, t1, 10 // place ppn at bits [31:10]\n    li t6, 1 // V flag only (non-leaf)\n    or t1, t1, t6 // root PTE pointing to second-level table\n    li t4, SERIAL_PORT_BASE // serial VA = 0xffffc000\n    srli t5, t4, 22 // vpn1 for serial VA (0x3ff)\n    slli t2, t5, 2\n    add t3, t0, t2 // address of root PTE slot for serial region\n    sw t1, 0(t3)\n    fence\n\n    // Now create the level-0 (leaf) PTE inside the second-level page table\n    // leaf PTE: PPN = device_phys >> 12, flags = PTE_FLAGS_FULL\n    // compute leaf PTE value (maps the single 4KB page)\n    srli t1, t4, 12 // t1 = device_phys >> 12 (we want identity mapping)\n    slli t1, t1, 10 // place ppn\n    li t6, PTE_FLAGS_FULL\n    or t1, t1, t6 // t1 = leaf PTE\n\n    // compute vpn0 index and byte offset in second-level table\n    srli t5, t4, 12\n    andi t5, t5, 0x3FF // vpn0 = (va>>12) & 0x3FF\n    slli t5, t5, 2 // byte offset = vpn0*4\n    add t3, s0, t5 // second-level-table-phys (s0) + offset\n    sw t1, 0(t3)\n    fence\n\n    // Ensure satp is cleared before setting new value (flush previous translations)\n    li t0, 0\n    csrw satp, t0\n\n    // Enable the MMU by writing SATP; this switches address translation on\n    li t0, SATP_ENABLE\n    csrw satp, t0\n    fence\n\n    // Prepare mstatus MPP so that mret will return to Supervisor mode:\n    // Clear MPP[12] bit then set MPP[11] bit (resulting MPP=01 => Supervisor).\n    li t0, MSTATUS_MPP_CLEAR\n    csrrc zero, mstatus, t0 // clear bit 12 of mstatus.MPP\n    li t0, MSTATUS_MPP_SET\n    csrrs zero, mstatus, t0 // set bit 11 of mstatus.MPP\n\n    // Set mepc to the virtual address of vm_entry and return from machine mode to\n    // the prepared privilege level (Supervisor) using mret.\n    la t0, vm_entry // load address of vm_entry (virtual address after mapping)\n    csrw mepc, t0\n    mret\n\n\n.org 0xC4007000\n.text\nvm_entry:\n    li t0, SERIAL_PORT_BASE\n    li t4, 65 // 'A'\n\n    // print_self: print current char, then step through executing code on other mapped pages.\nprint_self:\n    lw t6, SERP_TX_ST_REG_o(t0)\n    andi t6, t6, SERP_TX_ST_REG_READY_m\n    beq t6, zero, print_self\n    sw t4, SERP_TX_DATA_REG_o(t0)\n    addi t4, t4, 1\n\n    // Execute code placed in separate mapped pages. Each page contains a tiny\n    // function that prints the current character then returns using the address\n    // stored in t3. The sequence tests I-TLB / instruction fetch of different pages.\n    la t1, page_func_0\n    la t3, resume_0\n    jalr zero, 0(t1)\nresume_0:\n    addi t4, t4, 1\n\n    la t1, page_func_1\n    la t3, resume_1\n    jalr zero, 0(t1)\nresume_1:\n    addi t4, t4, 1\n\n    la t1, page_func_2\n    la t3, resume_2\n    jalr zero, 0(t1)\nresume_2:\n    addi t4, t4, 1\n\n    la t1, page_func_3\n    la t3, resume_3\n    jalr zero, 0(t1)\nresume_3:\n    addi t4, t4, 1\n\n    la t1, page_func_4\n    la t3, resume_4\n    jalr zero, 0(t1)\nresume_4:\n    addi t4, t4, 1\n\n    la t1, page_func_5\n    la t3, resume_5\n    jalr zero, 0(t1)\nresume_5:\n    addi t4, t4, 1\n\n    la t1, page_func_6\n    la t3, resume_6\n    jalr zero, 0(t1)\nresume_6:\n    addi t4, t4, 1\n\n    ebreak\n\n\n.org 0xC4000000\npage_func_0:\n    // Each page_func_* polls transmitter, writes current char (t4) then jumps\n    // back to the resume address stored in t3. These functions live on\n    // separate mapped pages to exercise instruction fetch from different pages.\n    lw t6, SERP_TX_ST_REG_o(t0)\n    andi t6, t6, SERP_TX_ST_REG_READY_m\n    beq  t6, zero, page_func_0\n    sw t4, SERP_TX_DATA_REG_o(t0)\n    jalr zero, 0(t3)\n\n.org 0xC4001000\npage_func_1:\n    lw t6, SERP_TX_ST_REG_o(t0)\n    andi t6, t6, SERP_TX_ST_REG_READY_m\n    beq t6, zero, page_func_1\n    sw t4, SERP_TX_DATA_REG_o(t0)\n    jalr zero, 0(t3)\n\n.org 0xC4002000\npage_func_2:\n    lw t6, SERP_TX_ST_REG_o(t0)\n    andi t6, t6, SERP_TX_ST_REG_READY_m\n    beq t6, zero, page_func_2\n    sw t4, SERP_TX_DATA_REG_o(t0)\n    jalr zero, 0(t3)\n\n.org 0xC4003000\npage_func_3:\n    lw t6, SERP_TX_ST_REG_o(t0)\n    andi t6, t6, SERP_TX_ST_REG_READY_m\n    beq t6, zero, page_func_3\n    sw t4, SERP_TX_DATA_REG_o(t0)\n    jalr zero, 0(t3)\n\n.org 0xC4004000\npage_func_4:\n    lw t6, SERP_TX_ST_REG_o(t0)\n    andi t6, t6, SERP_TX_ST_REG_READY_m\n    beq t6, zero, page_func_4\n    sw t4, SERP_TX_DATA_REG_o(t0)\n    jalr zero, 0(t3)\n\n.org 0xC4005000\npage_func_5:\n    lw t6, SERP_TX_ST_REG_o(t0)\n    andi t6, t6, SERP_TX_ST_REG_READY_m\n    beq t6, zero, page_func_5\n    sw t4, SERP_TX_DATA_REG_o(t0)\n    jalr zero, 0(t3)\n\n.org 0xC4006000\npage_func_6:\n    lw t6, SERP_TX_ST_REG_o(t0)\n    andi t6, t6, SERP_TX_ST_REG_READY_m\n    beq t6, zero, page_func_6\n    sw t4, SERP_TX_DATA_REG_o(t0)\n    jalr zero, 0(t3)"
  },
  {
    "path": "tests/cli/virtual_memory/itlb/stdout.txt",
    "content": "Machine stopped on BREAK exception.\nMachine state report:\nPC:0xc40070d0\nR0:0x00000000 R1:0x00000000 R2:0xbfffff00 R3:0x00000000 R4:0x00000000 R5:0xffffffffffffc000 R6:0xffffffffc4006000 R7:0x00000ffc R8:0x00002000 R9:0x00000000 R10:0x00000000 R11:0x00000000 R12:0x00000000 R13:0x00000000 R14:0x00000000 R15:0x00000000 R16:0x00000000 R17:0x00000000 R18:0x00000000 R19:0x00000000 R20:0x00000000 R21:0x00000000 R22:0x00000000 R23:0x00000000 R24:0x00000000 R25:0x00000000 R26:0x00000000 R27:0x00000000 R28:0xffffffffc40070c8 R29:0x00000049 R30:0x00000ff0 R31:0x00000001\ncycle: 0x00000090 mvendorid: 0x00000000 marchid: 0x00000000 mimpid: 0x00000000 mhardid: 0x00000000 mstatus: 0x00000080 misa: 0x40001111 mie: 0x00000000 mtvec: 0x00000000 mscratch: 0x00000000 mepc: 0xc40070cc mcause: 0x00000003 mtval: 0x00000000 mip: 0x00000000 mtinst: 0x00000000 mtval2: 0x00000000 mcycle: 0x00000090 minstret: 0x0000008f sstatus: 0x00000000 stvec: 0x00000000 sscratch: 0x00000000 sepc: 0x00000000 scause: 0x00000000 stval: 0x00000000 satp: 0x80000001\n"
  },
  {
    "path": "tests/cli/virtual_memory/memrw/program.S",
    "content": "// Write ASCII bytes into the mapped virtual page and read them back printing to verify.\n\n.globl _start\n.option norelax\n\n// Serial port/terminal registers\n.equ SERIAL_PORT_BASE,      0xffffc000 // base address of serial port region\n.equ SERP_RX_ST_REG_o,      0x0000 // Offset of RX_ST_REG\n.equ SERP_RX_DATA_REG_o,    0x0004 // Offset of RX_DATA_REG\n.equ SERP_TX_ST_REG_o,      0x0008 // Offset of TX_ST_REG\n.equ SERP_TX_ST_REG_READY_m,0x1 // Transmitter can accept next byte\n.equ SERP_TX_DATA_REG_o,    0x000c // Offset of TX_DATA_REG\n\n.equ ROOT_PT_PHYS, 0x1000\n.equ MAP_VA, 0xC4000000\n\n// PTE flags: 0xCF = 11001111\n// bit0 V = 1 (Valid)\n// bit1 R = 1 (Readable)\n// bit2 W = 1  Writable)\n// bit3 X = 1 (Executable)\n// bit4 U = 0 (NOT user-accessible)\n// bit5 G = 0 (NOT global)\n// bit6 A = 1 (Accessed)\n// bit7 D = 1 (Dirty)\n.equ PTE_FLAGS_FULL, 0xCF\n\n// mstatus MPP manipulation masks (for preparing mret to change privilege)\n.equ MSTATUS_MPP_CLEAR, 0x1000 // mask to clear MPP[12] (set bit 12 -> will be cleared via csrrc)\n.equ MSTATUS_MPP_SET, 0x800 // mask to set MPP[11]  (set bit 11 -> will be set via csrrs)\n\n.equ SATP_ENABLE, 0x80000001 // satp value to enable paging (implementation-specific)\n\n.org 0x00000200\n.text\n_start:\n    // t0 = physical address of root page table\n    li t0, ROOT_PT_PHYS\n\n    // t4 = virtual address we want to map (MAP_VA)\n    li t4, MAP_VA\n\n    // Build leaf PTE\n    srli t1, t4, 12\n    slli t1, t1, 10\n    li t6, PTE_FLAGS_FULL\n    or t1, t1, t6\n\n    srli t5, t4, 22\n    slli t2, t5, 2\n    add t3, t0, t2\n    sw t1, 0(t3)\n    fence\n\n    // Second-level page table physical address (must be page-aligned)\n    li s0, 0x2000\n\n    // root PTE => pointer to second level table (non-leaf PTE: V=1, R/W/X = 0)\n    // compute ppn of second-level table = 0x2000 >> 12 = 2\n    li t1, 2\n    slli t1, t1, 10 // place ppn at bits [31:10]\n    li t6, 1 // V flag only (non-leaf)\n    or t1, t1, t6 // root PTE pointing to second-level table\n    li t4, SERIAL_PORT_BASE // serial VA = 0xffffc000\n    srli t5, t4, 22 // vpn1 for serial VA (0x3ff)\n    slli t2, t5, 2\n    add t3, t0, t2 // address of root PTE slot for serial region\n    sw t1, 0(t3)\n    fence\n\n    // Now create the level-0 (leaf) PTE inside the second-level page table\n    // leaf PTE: PPN = device_phys >> 12, flags = PTE_FLAGS_FULL\n    // compute leaf PTE value (maps the single 4KB page)\n    srli t1, t4, 12 // t1 = device_phys >> 12 (we want identity mapping)\n    slli t1, t1, 10 // place ppn\n    li t6, PTE_FLAGS_FULL\n    or t1, t1, t6 // t1 = leaf PTE\n\n    // compute vpn0 index and byte offset in second-level table\n    srli t5, t4, 12\n    andi t5, t5, 0x3FF // vpn0 = (va>>12) & 0x3FF\n    slli t5, t5, 2 // byte offset = vpn0*4\n    add t3, s0, t5 // second-level-table-phys (s0) + offset\n    sw t1, 0(t3)\n    fence\n\n    // Ensure satp is cleared before setting new value (flush previous translations)\n    li t0, 0\n    csrw satp, t0\n\n    // Enable the MMU by writing SATP; this switches address translation on\n    li t0, SATP_ENABLE\n    csrw satp, t0\n    fence\n\n    // Prepare mstatus MPP so that mret will return to Supervisor mode:\n    // Clear MPP[12] bit then set MPP[11] bit (resulting MPP=01 => Supervisor).\n    li t0, MSTATUS_MPP_CLEAR\n    csrrc zero, mstatus, t0 // clear bit 12 of mstatus.MPP\n    li t0, MSTATUS_MPP_SET\n    csrrs zero, mstatus, t0 // set bit 11 of mstatus.MPP\n\n    // Set mepc to the virtual address of vm_entry and return from machine mode to\n    // the prepared privilege level (Supervisor) using mret.\n    la t0, vm_entry // load address of vm_entry (virtual address after mapping)\n    csrw mepc, t0\n    mret\n\n.org 0xC4000000\n.text\nvm_entry:\n    li a0, SERIAL_PORT_BASE\n\n    // pointer to mapped virtual page\n    la a1, MAP_VA\n\n    // write ASCII letters A..H into the first 8 bytes\n    li t0, 65\n    sb t0, 0(a1)\n    li t0, 66\n    sb t0, 1(a1)\n    li t0, 67\n    sb t0, 2(a1)\n    li t0, 68\n    sb t0, 3(a1)\n    li t0, 69\n    sb t0, 4(a1)\n    li t0, 70\n    sb t0, 5(a1)\n    li t0, 71\n    sb t0, 6(a1)\n    li t0, 72\n    sb t0, 7(a1)\n\n    // Now read back and print each byte\n    li t5, 0\nread_print_loop:\n    lb t1, 0(a1)\n    // print t1\nwait_tx2:\n    lw t0, SERP_TX_ST_REG_o(a0)\n    andi t0, t0, SERP_TX_ST_REG_READY_m\n    beq t0, zero, wait_tx2\n    sw t1, SERP_TX_DATA_REG_o(a0)\n\n    addi a1, a1, 1\n    addi t5, t5, 1\n    li t6, 8\n    blt t5, t6, read_print_loop\n\n    ebreak"
  },
  {
    "path": "tests/cli/virtual_memory/memrw/stdout.txt",
    "content": "Machine stopped on BREAK exception.\nMachine state report:\nPC:0xc40000a4\nR0:0x00000000 R1:0x00000000 R2:0xbfffff00 R3:0x00000000 R4:0x00000000 R5:0x00000001 R6:0x00000048 R7:0x00000ffc R8:0x00002000 R9:0x00000000 R10:0xffffffffffffc000 R11:0xffffffffc4000008 R12:0x00000000 R13:0x00000000 R14:0x00000000 R15:0x00000000 R16:0x00000000 R17:0x00000000 R18:0x00000000 R19:0x00000000 R20:0x00000000 R21:0x00000000 R22:0x00000000 R23:0x00000000 R24:0x00000000 R25:0x00000000 R26:0x00000000 R27:0x00000000 R28:0x00002ff0 R29:0xffffffffffffc000 R30:0x00000008 R31:0x00000008\ncycle: 0x000000a8 mvendorid: 0x00000000 marchid: 0x00000000 mimpid: 0x00000000 mhardid: 0x00000000 mstatus: 0x00000080 misa: 0x40001111 mie: 0x00000000 mtvec: 0x00000000 mscratch: 0x00000000 mepc: 0xc40000a0 mcause: 0x00000003 mtval: 0x00000000 mip: 0x00000000 mtinst: 0x00000000 mtval2: 0x00000000 mcycle: 0x000000a8 minstret: 0x000000a7 sstatus: 0x00000000 stvec: 0x00000000 sscratch: 0x00000000 sepc: 0x00000000 scause: 0x00000000 stval: 0x00000000 satp: 0x80000001\n"
  },
  {
    "path": "tests/cli/virtual_memory/template/program.S",
    "content": "// Test template: Sets up a page table, enables virtual memory, and prints \"Hello world\" via serial port.\n\n.globl _start\n.option norelax\n\n// Serial port/terminal registers\n.equ SERIAL_PORT_BASE,      0xffffc000 // base address of serial port region\n.equ SERP_RX_ST_REG_o,      0x0000 // Offset of RX_ST_REG\n.equ SERP_RX_DATA_REG_o,    0x0004 // Offset of RX_DATA_REG\n.equ SERP_TX_ST_REG_o,      0x0008 // Offset of TX_ST_REG\n.equ SERP_TX_ST_REG_READY_m,0x1 // Transmitter can accept next byte\n.equ SERP_TX_DATA_REG_o,    0x000c // Offset of TX_DATA_REG\n\n.equ ROOT_PT_PHYS, 0x1000\n.equ MAP_VA, 0xC4000000\n\n// PTE flags: 0xCF = 11001111\n// bit0 V = 1 (Valid)\n// bit1 R = 1 (Readable)\n// bit2 W = 1  Writable)\n// bit3 X = 1 (Executable)\n// bit4 U = 0 (NOT user-accessible)\n// bit5 G = 0 (NOT global)\n// bit6 A = 1 (Accessed)\n// bit7 D = 1 (Dirty)\n.equ PTE_FLAGS_FULL, 0xCF\n\n// mstatus MPP manipulation masks (for preparing mret to change privilege)\n.equ MSTATUS_MPP_CLEAR, 0x1000 // mask to clear MPP[12] (set bit 12 -> will be cleared via csrrc)\n.equ MSTATUS_MPP_SET, 0x800 // mask to set MPP[11]  (set bit 11 -> will be set via csrrs)\n\n.equ SATP_ENABLE, 0x80000001 // satp value to enable paging (implementation-specific)\n\n.org 0x00000200\n.text\n_start:\n    // t0 = physical address of root page table\n    li t0, ROOT_PT_PHYS\n\n    // t4 = virtual address we want to map (MAP_VA)\n    li t4, MAP_VA\n\n    // Build leaf PTE\n    srli t1, t4, 12\n    slli t1, t1, 10\n    li t6, PTE_FLAGS_FULL\n    or t1, t1, t6\n\n    srli t5, t4, 22\n    slli t2, t5, 2\n    add t3, t0, t2\n    sw t1, 0(t3)\n    fence\n\n    // Second-level page table physical address (must be page-aligned)\n    li s0, 0x2000\n\n    // root PTE => pointer to second level table (non-leaf PTE: V=1, R/W/X = 0)\n    // compute ppn of second-level table = 0x2000 >> 12 = 2\n    li t1, 2\n    slli t1, t1, 10 // place ppn at bits [31:10]\n    li t6, 1 // V flag only (non-leaf)\n    or t1, t1, t6 // root PTE pointing to second-level table\n    li t4, SERIAL_PORT_BASE // serial VA = 0xffffc000\n    srli t5, t4, 22 // vpn1 for serial VA (0x3ff)\n    slli t2, t5, 2\n    add t3, t0, t2 // address of root PTE slot for serial region\n    sw t1, 0(t3)\n    fence\n\n    // Now create the level-0 (leaf) PTE inside the second-level page table\n    // leaf PTE: PPN = device_phys >> 12, flags = PTE_FLAGS_FULL\n    // compute leaf PTE value (maps the single 4KB page)\n    srli t1, t4, 12 // t1 = device_phys >> 12 (we want identity mapping)\n    slli t1, t1, 10 // place ppn\n    li t6, PTE_FLAGS_FULL\n    or t1, t1, t6 // t1 = leaf PTE\n\n    // compute vpn0 index and byte offset in second-level table\n    srli t5, t4, 12\n    andi t5, t5, 0x3FF // vpn0 = (va>>12) & 0x3FF\n    slli t5, t5, 2 // byte offset = vpn0*4\n    add t3, s0, t5 // second-level-table-phys (s0) + offset\n    sw t1, 0(t3)\n    fence\n\n    // Ensure satp is cleared before setting new value (flush previous translations)\n    li t0, 0\n    csrw satp, t0\n\n    // Enable the MMU by writing SATP; this switches address translation on\n    li t0, SATP_ENABLE\n    csrw satp, t0\n    fence\n\n    // Prepare mstatus MPP so that mret will return to Supervisor mode:\n    // Clear MPP[12] bit then set MPP[11] bit (resulting MPP=01 => Supervisor).\n    li t0, MSTATUS_MPP_CLEAR\n    csrrc zero, mstatus, t0 // clear bit 12 of mstatus.MPP\n    li t0, MSTATUS_MPP_SET\n    csrrs zero, mstatus, t0 // set bit 11 of mstatus.MPP\n\n    // Set mepc to the virtual address of vm_entry and return from machine mode to\n    // the prepared privilege level (Supervisor) using mret.\n    la t0, vm_entry // load address of vm_entry (virtual address after mapping)\n    csrw mepc, t0\n    mret\n\n.org 0xC4000000\n.text\nvm_entry:\n    li a0, SERIAL_PORT_BASE\n    la a1, hello_str\n\nprint_next_char:\n    // Load next byte from string; if zero (end), branch to done\n    lb t1, 0(a1)\n    beq t1, zero, print_done\n    addi a1, a1, 1 // advance to next character\n\nwait_tx_ready:\n    // Poll transmit status register until TX ready bit is set\n    lw t0, SERP_TX_ST_REG_o(a0)\n    andi t0, t0, SERP_TX_ST_REG_READY_m\n    beq t0, zero, wait_tx_ready\n\n    // Write byte to transmit-data register and loop for next char\n    sw t1, SERP_TX_DATA_REG_o(a0)\n    jal zero, print_next_char\n\nprint_done:\n    ebreak\n\n1:  auipc t0, 0\n    jalr zero, 0(t0)\n\n.data\n.org 0xC4000100\nhello_str:\n    .asciz \"Hello world.\\n\""
  },
  {
    "path": "tests/cli/virtual_memory/template/stdout.txt",
    "content": "Machine stopped on BREAK exception.\nMachine state report:\nPC:0xc4000034\nR0:0x00000000 R1:0x00000000 R2:0xbfffff00 R3:0x00000000 R4:0x00000000 R5:0x00000001 R6:0x00000000 R7:0x00000ffc R8:0x00002000 R9:0x00000000 R10:0xffffffffffffc000 R11:0xffffffffc400010d R12:0x00000000 R13:0x00000000 R14:0x00000000 R15:0x00000000 R16:0x00000000 R17:0x00000000 R18:0x00000000 R19:0x00000000 R20:0x00000000 R21:0x00000000 R22:0x00000000 R23:0x00000000 R24:0x00000000 R25:0x00000000 R26:0x00000000 R27:0x00000000 R28:0x00002ff0 R29:0xffffffffffffc000 R30:0x00000ff0 R31:0x000000cf\ncycle: 0x000000a8 mvendorid: 0x00000000 marchid: 0x00000000 mimpid: 0x00000000 mhardid: 0x00000000 mstatus: 0x00000080 misa: 0x40001111 mie: 0x00000000 mtvec: 0x00000000 mscratch: 0x00000000 mepc: 0xc4000030 mcause: 0x00000003 mtval: 0x00000000 mip: 0x00000000 mtinst: 0x00000000 mtval2: 0x00000000 mcycle: 0x000000a8 minstret: 0x000000a7 sstatus: 0x00000000 stvec: 0x00000000 sscratch: 0x00000000 sepc: 0x00000000 scause: 0x00000000 stval: 0x00000000 satp: 0x80000001\n"
  },
  {
    "path": "tests/riscv-official/.gitignore",
    "content": "./isa/elf\n./isa/dump\n./isa/selftests/elf\n./code/__pychache__\n"
  },
  {
    "path": "tests/riscv-official/README.md",
    "content": "# qtrvsim-testing\n\nAdaptation of official RISC-V tests for QtRvSim.\n\n## Dependencies\n\n- qtrvsim-cli\n- riscv64-unknown-elf-gcc OR clang\n- riscv64-unknown-elf-binutils OR llvm\n- python (>= 3.4)\n\nNote: with gcc make sure it supports all the used march options.\n\n## Usage\n\n```shell\npython qtrvsim_tester.py /path/to/qtrvsim-cli\n```\n\n- For more information use: `python qtrvsim_tester.py -h`\n\n## Clang\n\nTo use clang instead of gcc set those environment variables:\n\n```shell\nexport RISCV_COMPILER=clang\nexport USE_CLANG_OPTS=true\nexport RISCV_OBJDUMP_CMD=llvm-objdump\n```\n"
  },
  {
    "path": "tests/riscv-official/code/constants.py",
    "content": "ISA_PATH = \"isa\"\nELF_PATH = \"isa/elf/\"\nSELF_PATH = \"isa/selftests\"\nSELF_ELF_PATH = SELF_PATH + \"/elf\"\nCACHE_SETTINGS = [\"--d-cache\", \"lru,2,2,2,wb\", \"--i-cache\", \"lru,2,2,2\"]"
  },
  {
    "path": "tests/riscv-official/code/helpers.py",
    "content": "def max_str_list(list):\n    if (len(list) > 0):\n        return len(max(list, key=lambda x: len(x)))\n    return 0\n\n\n# Decorative method, if output of the qtrvsim-cli is changed this will probably break!!!\ndef print_formated_output(reg_dump):\n    stdout = reg_dump.stdout.decode(\"utf-8\")\n    stderr = reg_dump.stderr.decode(\"utf-8\")\n    print(stderr)\n    print(stdout)\n\n\ndef res_translate(res):\n    if (res == 0):\n        return \"FAIL\"\n    elif (res == 1):\n        return \"PASS\"\n    else:\n        return \"ERROR\"\n\n\ndef res_print(test, test_res, test_reg_dump, params):\n    if (test_res == 1):\n        if (not params.nopass):\n            if (not params.fileprt):\n                print(test + \": \" + '\\033[92m' + \"PASS\" + '\\033[0m')\n            else:\n                print(test+\":PASS\")\n            if (params.dregs):\n                print_formated_output(test_reg_dump)\n        return 1\n    elif (test_res == 0):\n        if (not params.fileprt):\n            print(test + \": \" + '\\033[93m' + \"FAIL\" + '\\033[0m')\n        else:\n            print(test+\":FAIL\")\n        if (not params.nodump or params.dregs):\n            print_formated_output(test_reg_dump)\n        return 0\n    else:\n        if (not params.fileprt):\n            print(test + \": \" + '\\033[91m' + \"ERROR\" + '\\033[0m')\n        else:\n            print(test+\":ERROR\")\n        if (not params.nodump or params.dregs):\n            print_formated_output(test_reg_dump)\n        return 0\n\n\ndef get_RVxx(tests, isa):\n    isa = str(isa).lower()\n    tests32 = list(filter(lambda t: str(t).__contains__(\"32\"+isa), tests))\n    tests64 = list(filter(lambda t: str(t).__contains__(\"64\"+isa), tests))\n    tests32.sort()\n    tests64.sort()\n    return tests32, tests64"
  },
  {
    "path": "tests/riscv-official/code/myparse.py",
    "content": "import argparse\n\n\ndef init_parser():\n    parser = argparse.ArgumentParser(\n        description=\"Defeault tests are RV32UI and RV64UI. No pipeline and cache.\")\n    parser.add_argument('qtrvsim_cli',\n                        default='',\n                        help=\"Qtrvsim_cli to run tests. (RVxxUI is default)\")\n\n    # Test selection\n    bsel = parser.add_argument_group(\n        \"bit lenght selection for official tests:\")\n    bsel.add_argument(\"--no-32\",\n                      action=\"count\",\n                      default=0,\n                      dest=\"no32\",\n\n                      help=\"Disables 32-bit tests. (Only for official tests)\")\n    bsel.add_argument(\"--no-64\",\n                      action=\"count\",\n                      default=0,\n                      dest=\"no64\",\n                      help=\"Disables 64-bit tests. (Only for official tests)\")\n\n    # Additional tests\n    tsel = parser.add_argument_group(\"additional set of tests:\")\n    tsel.add_argument(\"-E\", \"--external\",\n                      nargs=1,\n                      action=\"store\",\n                      default=\"\",\n                      help=\"Path to additional tests. Required to adhere to PASS,FAIL behavior of edited test enviroment.\")\n    tsel.add_argument(\"-M\", \"--multiply\",\n                      action=\"count\",\n                      default=0,\n                      help=\"Additional set of tests for multiplication.\")\n    tsel.add_argument(\"-A\", \"--atomic\",\n                      action=\"count\",\n                      default=0,\n                      help=\"Additional set of tests for atomic instructions.\")\n    tsel.add_argument(\"--CSR\",\n                      action=\"count\",\n                      default=0,\n                      help=\"Additional set of tests for control and status registers.\")\n\n    # QtRVSim_cli settings\n    opts = parser.add_argument_group(\"QtRVSim options:\")\n    opts.add_argument(\"--pipeline\",\n                      action=\"count\",\n                      default=0,\n                      help=\"Simulator runs in pipelined configuration.\")\n    opts.add_argument(\"--cache\",\n                      action=\"count\",\n                      default=0,\n                      help=\"Simulator runs with d-cache and i-cache implemented. (d lru,2,2,2,wb ; i lru,2,2,2)\")\n\n    # Test aplication output options\n    eout = parser.add_argument_group(\"output print options:\")\n    eout.add_argument(\"--no-pass\",\n                      action=\"count\",\n                      default=0,\n                      dest=\"nopass\",\n                      help=\"Prints only failed tests for readability.\")\n    eout.add_argument(\"--no-dump\",\n                      action=\"count\",\n                      default=0,\n                      dest=\"nodump\",\n                      help=\"Omits printing of registers in failed test.\")\n    eout.add_argument(\"--d-regs\",\n                      action=\"count\",\n                      default=0,\n                      dest=\"dregs\",\n                      help=\"Prints output of qtrvsim --d-regs param. (Higher priority than other output options.)\")\n    eout.add_argument(\"--file\",\n                      action=\"count\",\n                      default=0,\n                      dest=\"fileprt\",\n                      help=\"Removes most graphical output features.\")\n\n    # Test aplication good to have features\n    gth = parser.add_argument_group(\"good to have:\")\n    gth.add_argument(\"-R\", \"--rebuild\",\n                     action=\"count\",\n                     default=0,\n                     help=\"Deletes tests and buidls them anew.\")\n    gth.add_argument(\"-S\", \"--selftest\",\n                     action=\"count\",\n                     default=0,\n                     dest=\"selftest\",\n                     help=\"Enable simple tests to check if tester behaves properly.\")\n    gth.add_argument(\"--clean\",\n                     action=\"count\",\n                     default=0,\n                     dest=\"clean\",\n                     help=\"Deletes all official and seftest elf files.\")\n    return parser\n"
  },
  {
    "path": "tests/riscv-official/code/selftesting.py",
    "content": "import testing as ts\nimport constants as cn\n\ndef self_test(sim_bin, params, src_path, tests):\n    sh_pass = []\n    sh_fail = []\n    error = []\n    for test in tests:\n        test_res, test_reg_dump = ts.check_reg_dump(\n            ts.run_test(sim_bin, params, src_path + cn.SELF_ELF_PATH+\"/\", test))\n        if (test_res == 1 and str(test).__contains__(\"fail\")):   \n            sh_fail.append(test)\n        if (test_res == 0 and str(test).__contains__(\"pass\")):   \n            sh_pass.append(test)\n        if(test_res == 2):                                        \n            error.append(test)\n    print(\"Test passed but was expected to fail:{0}\".format(sh_fail))\n    print(\"Test failed but was expected to pass:{0}\".format(sh_pass))\n    print(\"Test ended in error                 :{0}\\n\".format(error))"
  },
  {
    "path": "tests/riscv-official/code/testing.py",
    "content": "import os\nimport sys\nimport pathlib\nimport subprocess\n\nimport helpers as hp\nimport constants as cn\n\n\ndef test_sim_bin(sim_bin):\n    try:\n        sim_test = subprocess.run(\n            [sim_bin, \"-h\"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n    except:\n        parser.print_help()\n        sys.exit(1)\n    if (sim_test.returncode == 0):\n        return sim_bin, True\n    return \"\", False\n\n\ndef load_filenames(dir_path, rebuild):\n    test_files = list(filter(\n        lambda x: not str(x).startswith(\".\"), os.listdir(dir_path+\"/elf\")))\n    if (len(test_files) != 0 and not rebuild):\n        return test_files, True\n    else:\n        try:\n            if (rebuild):\n                print(\"Building {0}\".format(dir_path))\n            else:\n                print(\"No tests found. Trying to build tests.\")\n            tests_built = subprocess.run(\n                [\"make\", \"-C\", dir_path],\n                stdout=subprocess.DEVNULL,\n                stderr=subprocess.DEVNULL)\n            return load_filenames(dir_path, False)\n        except:\n            print(\"Failed to build tests. {0} ; {1}\".format(\n                dir_path, test_files))\n            sys.exit(1)\n\n\n# If output of the qtrvsim-cli is changed this will probably break !!!\ndef check_reg_dump(reg_dump):\n    res = 2\n    stdout = str(reg_dump.stdout)\n    if (stdout.__contains__(\"R11:\")):\n        index = stdout.find(\"R11:\")\n        if (stdout[index:index+18].__contains__(\"600d\")):\n            res = 1\n        elif (stdout[index:index+18].__contains__(\"bad\")):\n            res = 0\n    return res, reg_dump\n\n\ndef run_test(sim_bin, params, dir_path, filename):\n    test_path = dir_path + filename\n    try:\n        param_bin = [sim_bin, test_path, \"--d-regs\"]\n        if (params.pipeline):\n            param_bin.append(\"--pipelined\")\n        if (params.cache):\n            param_bin.extend(cn.CACHE_SETTINGS)\n        return subprocess.run(param_bin, capture_output=True)\n    except subprocess.CalledProcessError as err:\n        print(err)\n        sys.exit(1)\n\n\ndef run_tests(sim_bin, params, dir_path, tests):\n    succ = 0\n    max_width = 0\n    if (not params.fileprt):\n        max_width = hp.max_str_list(tests)\n    for test in tests:\n        reg_dump = run_test(sim_bin, params, dir_path, test)\n        test = test.ljust(max_width)\n        test_res, test_reg_dump = check_reg_dump(reg_dump)\n        succ += hp.res_print(test, test_res, test_reg_dump, params)\n    if (not params.fileprt):\n        print(str(succ) + \"/\" + str(len(tests)) + \" tests succesfull.\\n\")\n\n\ndef run_official_tests(sim_bin, params, src_path, tests):\n    if (not params.no32):\n        if (not params.fileprt):\n            print(\"--- 32 bit register tests ---\")\n        run_tests(sim_bin, params, src_path + cn.ELF_PATH, tests[0])\n    if (not params.no64):\n        if (not params.fileprt):\n            print(\"--- 64 bit register tests ---\")\n        run_tests(sim_bin, params, src_path + cn.ELF_PATH, tests[1])\n\n\ndef test_selector(sim_bin, params, src_path, tests):\n    if (params.pipeline):\n        print(\"Simulator runs in pipelined mode.\")\n    if (params.cache):\n        print(\"Simulator runs in cache mode.\")\n    if (params.fileprt):\n        line = \"\"\n    else:\n        line = \"-+-+-+-+-+-+-+-+-\"\n    print(line+\"RVxxUI\"+line)\n    run_official_tests(sim_bin, params, src_path, hp.get_RVxx(tests, \"ui\"))\n\n    if (params.multiply):\n        print(line+\"RVxxUM\"+line)\n        run_official_tests(sim_bin, params, src_path, hp.get_RVxx(tests, \"um\"))\n\n    if (params.atomic):\n        print(line+\"RVxxUA\"+line)\n        run_official_tests(sim_bin, params, src_path, hp.get_RVxx(tests, \"ua\"))\n\n    if (params.CSR):\n        print(line+\"RVxxSI\"+line)\n        run_official_tests(sim_bin, params, src_path, hp.get_RVxx(tests, \"si\"))\n        print(line+\"RVxxMI\"+line)\n        run_official_tests(sim_bin, params, src_path, hp.get_RVxx(tests, \"mi\"))\n\n    if (len(params.external) > 0):\n        print(line+\"External Tests\"+line)\n        dir_path = params.external[0]\n        test_files = os.listdir(dir_path)\n        test_files = list(\n            filter(lambda x: not str(x).startswith(\".\"), test_files))\n        run_tests(sim_bin, params, dir_path, test_files)\n\n\ndef delete_elf(src_path):\n    try:\n        subprocess.run([\"make\", \"-C\", src_path + cn.ISA_PATH, \"clean\"],\n                       stdout=subprocess.DEVNULL,\n                       stderr=subprocess.DEVNULL)\n        subprocess.run([\"make\", \"-C\", src_path + cn.SELF_PATH, \"clean\"],\n                       stdout=subprocess.DEVNULL,\n                       stderr=subprocess.DEVNULL)\n    except subprocess.CalledProcessError as err:\n        print(\"Unable to delete elf files.\")\n        print(err)\n"
  },
  {
    "path": "tests/riscv-official/env/p/BackUp/riscv_test.h",
    "content": "#define _ENV_PHYSICAL_SINGLE_CORE_H\n\n#include \"../../riscv-tests/env/encoding.h\"\n\n//-----------------------------------------------------------------------\n// Begin Macro\n//-----------------------------------------------------------------------\n\n#define RVTEST_RV64U                                                    \\\n  .macro init;                                                          \\\n  .endm\n\n#define RVTEST_RV32U                                                    \\\n  .macro init;                                                          \\\n  .endm\n\n#if __riscv_xlen == 64\n# define CHECK_XLEN li a0, 1; slli a0, a0, 31; bgez a0, 1f; RVTEST_PASS; 1:\n#else\n# define CHECK_XLEN li a0, 1; slli a0, a0, 31; bltz a0, 1f; RVTEST_PASS; 1:\n#endif\n\n#define INIT_XREG                                                       \\\n  li x1, 0;                                                             \\\n  li x2, 0;                                                             \\\n  li x3, 0;                                                             \\\n  li x4, 0;                                                             \\\n  li x5, 0;                                                             \\\n  li x6, 0;                                                             \\\n  li x7, 0;                                                             \\\n  li x8, 0;                                                             \\\n  li x9, 0;                                                             \\\n  li x10, 0;                                                            \\\n  li x11, 0;                                                            \\\n  li x12, 0;                                                            \\\n  li x13, 0;                                                            \\\n  li x14, 0;                                                            \\\n  li x15, 0;                                                            \\\n  li x16, 0;                                                            \\\n  li x17, 0;                                                            \\\n  li x18, 0;                                                            \\\n  li x19, 0;                                                            \\\n  li x20, 0;                                                            \\\n  li x21, 0;                                                            \\\n  li x22, 0;                                                            \\\n  li x23, 0;                                                            \\\n  li x24, 0;                                                            \\\n  li x25, 0;                                                            \\\n  li x26, 0;                                                            \\\n  li x27, 0;                                                            \\\n  li x28, 0;                                                            \\\n  li x29, 0;                                                            \\\n  li x30, 0;                                                            \\\n  li x31, 0;\n\n#define RVTEST_CODE_BEGIN                                               \\\n        .section .text.init;                                            \\\n        .align  6;                                                      \\\n        .globl _start;                                                  \\\n_start:                                                                 \\\n    INIT_XREG;                                                          \\\n    li TESTNUM, 0;                                                      \\\n    CHECK_XLEN;                                                         \\\n    .align 2;\n\n//-----------------------------------------------------------------------\n// End Macro\n//-----------------------------------------------------------------------\n\n#define RVTEST_CODE_END                                                 \n\n//-----------------------------------------------------------------------\n// Pass/Fail Macro\n//-----------------------------------------------------------------------\n\n#define RVTEST_PASS                                                     \\\n        fence;                                                          \\\n        li TESTNUM, 1;                                                  \\\n        li a7, 93;                                                      \\\n        li a1, 0x60;                                                    \\\n        slli a1, a1, 8;                                                 \\\n        addi a1, a1, 0xd;                                               \\\n        slli a1, a1, 12;                                                \\\n        ecall\n\n#define TESTNUM gp\n\n#define RVTEST_FAIL                                                     \\\n        fence;                                                          \\\n1:      beqz TESTNUM, 1b;                                               \\\n        sll TESTNUM, TESTNUM, 1;                                        \\\n        or TESTNUM, TESTNUM, 1;                                         \\\n        li a7, 93;                                                      \\\n        addi a0, TESTNUM, 0;                                            \\\n        addi a1,a1,0xBA;                                                \\\n        slli a1,a1,4;                                                   \\\n        addi a1, a1, 0xD;                                               \\\n        slli a1, a1, 16;                                                \\\n        ecall\n\n//-----------------------------------------------------------------------\n// Data Section Macro\n//-----------------------------------------------------------------------\n\n#define EXTRA_DATA\n\n#define RVTEST_DATA_BEGIN                                               \\\n//        EXTRA_DATA                                                    \\\n        .pushsection .tohost,\"aw\",@progbits;                            \\\n        .align 6; .global tohost; tohost: .dword 0;                     \\\n        .align 6; .global fromhost; fromhost: .dword 0;                 \\\n        .popsection;                                                    \\\n        .align 4; .global begin_signature; begin_signature:\n\n#define RVTEST_DATA_END //.align 4; .global end_signature; end_signature:\n"
  },
  {
    "path": "tests/riscv-official/env/p/link.ld",
    "content": "OUTPUT_ARCH( \"riscv\" )\nENTRY(_start)\n\nSECTIONS\n{\n  . = 0x80000000;\n  .text.init : { *(.text.init) }\n  . = ALIGN(0x1000);\n  .tohost : { *(.tohost) }\n  . = ALIGN(0x1000);\n  .text : { *(.text) }\n  . = ALIGN(0x1000);\n  .data : { *(.data) }\n  .bss : { *(.bss) }\n  _end = .;\n}\n\n"
  },
  {
    "path": "tests/riscv-official/env/p/riscv_test.h",
    "content": "#define _ENV_PHYSICAL_SINGLE_CORE_H\n\n#include \"../../riscv-tests/env/encoding.h\"\n\n//-----------------------------------------------------------------------\n// Begin Macro\n//-----------------------------------------------------------------------\n\n#define RVTEST_RV64U                                                    \\\n  .macro init;                                                          \\\n  .endm\n\n#define RVTEST_RV32U                                                    \\\n  .macro init;                                                          \\\n  .endm\n\n#define RVTEST_RV64M                                                    \\\n  .macro init;                                                          \\\n  RVTEST_ENABLE_MACHINE;                                                \\\n  .endm\n\n#define RVTEST_RV32M                                                    \\\n  .macro init;                                                          \\\n  RVTEST_ENABLE_MACHINE;                                                \\\n  .endm\n\n#define RVTEST_RV64S                                                    \\\n  .macro init;                                                          \\\n  RVTEST_ENABLE_SUPERVISOR;                                             \\\n  .endm\n\n#define RVTEST_RV32S                                                    \\\n  .macro init;                                                          \\\n  RVTEST_ENABLE_SUPERVISOR;                                             \\\n  .endm\n\n#if __riscv_xlen == 64\n# define CHECK_XLEN li a0, 1; slli a0, a0, 31; bgez a0, 1f; RVTEST_ARCH_CHECK_FAIL; 1:\n#else\n# define CHECK_XLEN li a0, 1; slli a0, a0, 31; bltz a0, 1f; RVTEST_ARCH_CHECK_FAIL; 1:\n#endif\n\n#define INIT_XREG                                                       \\\n  li x1, 0;                                                             \\\n  li x2, 0;                                                             \\\n  li x3, 0;                                                             \\\n  li x4, 0;                                                             \\\n  li x5, 0;                                                             \\\n  li x6, 0;                                                             \\\n  li x7, 0;                                                             \\\n  li x8, 0;                                                             \\\n  li x9, 0;                                                             \\\n  li x10, 0;                                                            \\\n  li x11, 0;                                                            \\\n  li x12, 0;                                                            \\\n  li x13, 0;                                                            \\\n  li x14, 0;                                                            \\\n  li x15, 0;                                                            \\\n  li x16, 0;                                                            \\\n  li x17, 0;                                                            \\\n  li x18, 0;                                                            \\\n  li x19, 0;                                                            \\\n  li x20, 0;                                                            \\\n  li x21, 0;                                                            \\\n  li x22, 0;                                                            \\\n  li x23, 0;                                                            \\\n  li x24, 0;                                                            \\\n  li x25, 0;                                                            \\\n  li x26, 0;                                                            \\\n  li x27, 0;                                                            \\\n  li x28, 0;                                                            \\\n  li x29, 0;                                                            \\\n  li x30, 0;                                                            \\\n  li x31, 0;\n\n#define RVTEST_CODE_BEGIN                                               \\\n        .section .text.init;                                            \\\n        .align  6;                                                      \\\n        .globl _start;                                                  \\\n_start:                                                                 \\\n    INIT_XREG;                                                          \\\n    CHECK_XLEN;                                                         \\\n    .align 2;\n\n//-----------------------------------------------------------------------\n// End Macro\n//-----------------------------------------------------------------------\n\n#define RVTEST_CODE_END                                                 \n\n//-----------------------------------------------------------------------\n// Pass/Fail Macro\n//-----------------------------------------------------------------------\n\n#define RVTEST_PASS                                                     \\\n        li a1, 0x60;                                                    \\\n        slli a1, a1, 8;                                                 \\\n        addi a1, a1, 0xd;                                               \\\n        slli a1, a1, 12;                                                \\\n        addi a0, zero, 0;                                               \\\n        addi a7, zero, 93;                                              \\\n        ecall\n\n#define TESTNUM gp\n\n#define RVTEST_FAIL                                                     \\\n        addi a1, a1, 0xBA;                                              \\\n        slli a1, a1, 4;                                                 \\\n        addi a1, a1, 0xD;                                               \\\n        slli a1, a1, 16;                                                \\\n        addi a0, TESTNUM, -1;                                           \\\n        sra  a0, a0, 31;                                                \\\n        or   a0, a0, TESTNUM;                                           \\\n        addi a7, zero, 93;                                              \\\n        ecall\n\n#define RVTEST_ARCH_CHECK_FAIL                                          \\\n        addi a1,a1,0xAC;                                                \\\n        slli a1,a1,4;                                                   \\\n        addi a1, a1, 0xF;                                               \\\n        slli a1, a1, 16;                                                \\\n        addi a0, zero, 2000;                                            \\\n        addi a7, zero, 93;                                              \\\n        ecall\n\n//-----------------------------------------------------------------------\n// Data Section Macro\n//-----------------------------------------------------------------------\n\n#define EXTRA_DATA\n#define RVTEST_DATA_BEGIN\n#define RVTEST_DATA_END\n"
  },
  {
    "path": "tests/riscv-official/isa/.gitignore",
    "content": "rv*-*\n"
  },
  {
    "path": "tests/riscv-official/isa/Makefile",
    "content": "#=======================================================================\n# Makefile for riscv-tests/isa\n#-----------------------------------------------------------------------\n\nXLEN ?= 64\n\nsrc_dir := .\n\nisa_dir := ../riscv-tests/isa\n\nifeq ($(XLEN),64)\ninclude $(isa_dir)/rv64ui/Makefrag\ninclude $(isa_dir)/rv64ua/Makefrag\ninclude $(isa_dir)/rv64si/Makefrag\ninclude $(isa_dir)/rv64mi/Makefrag\ninclude $(isa_dir)/rv64um/Makefrag\nendif\ninclude $(isa_dir)/rv32ui/Makefrag\ninclude $(isa_dir)/rv32ua/Makefrag\ninclude $(isa_dir)/rv32si/Makefrag\ninclude $(isa_dir)/rv32mi/Makefrag\ninclude $(isa_dir)/rv32um/Makefrag\n\ndefault: clean all\n\n#--------------------------------------------------------------------\n# Build rules\n#--------------------------------------------------------------------\n\ninclude toolchain_setup\n\nvpath %.S $(isa_dir) $(self_test)\n\n#------------------------------------------------------------\n# Build assembly tests\n\n%.dump: %\n\t$(RISCV_OBJDUMP) elf/$< > dump/$@\n\n%.out: %\n\t$(RISCV_SIM) --isa=rv64gc $< 2> $@\n\n%.out32: %\n\t$(RISCV_SIM) --isa=rv32gc $< 2> $@\n\ndefine compile_template\n\n\n\n$$($(1)_p_tests): $(1)-p-%: $(1)/%.S\n\t$$(RISCV_COMPILER) $(2) $$(RISCV_COMPILER_OPTS) -I$(src_dir)/../env/p -I$(isa_dir)/macros/scalar -T$(src_dir)/../env/p/link.ld $$< -o elf/$$@\n$(1)_tests += $$($(1)_p_tests)\n\n$(1)_tests_dump = $$(addsuffix .dump, $$($(1)_tests))\n\n$(1): $$($(1)_tests_dump)\n\n.PHONY: $(1)\n\nCOMPILER_SUPPORTS_$(1) := $$(shell $$(RISCV_COMPILER) $(2) -c -x c /dev/null -o /dev/null 2> /dev/null; echo $$$$?)\n\nifeq ($$(COMPILER_SUPPORTS_$(1)),0)\ntests += $$($(1)_tests)\nendif\n\nendef\n$(eval $(call compile_template,rv32ui, $(MARCH_OPTS_32)))\n$(eval $(call compile_template,rv32um, $(MARCH_OPTS_32)))\n$(eval $(call compile_template,rv32ua, $(MARCH_OPTS_32)))\n$(eval $(call compile_template,rv32si, $(MARCH_OPTS_32)))\n$(eval $(call compile_template,rv32mi, $(MARCH_OPTS_32)))\nifeq ($(XLEN),64)\n$(eval $(call compile_template,rv64ui, $(MARCH_OPTS_64)))\n$(eval $(call compile_template,rv64um, $(MARCH_OPTS_64)))\n$(eval $(call compile_template,rv64ua, $(MARCH_OPTS_64)))\n$(eval $(call compile_template,rv64si, $(MARCH_OPTS_64)))\n$(eval $(call compile_template,rv64mi, $(MARCH_OPTS_64)))\nendif\n\ntests_dump = $(addsuffix .dump, $(tests))\ntests_hex = $(addsuffix .hex, $(tests))\ntests_out = $(addsuffix .out, $(filter rv64%,$(tests)))\ntests32_out = $(addsuffix .out32, $(filter rv32%,$(tests)))\n\nrun: $(tests_out) $(tests32_out)\n\njunk += $(tests) $(tests_dump) $(tests_hex) $(tests_out) $(tests32_out)\n\n#------------------------------------------------------------\n\nself-tests:\n\tcd selftests && make\n\nall: $(tests_dump) self-tests\n\n#------------------------------------------------------------\n# Clean up\n\nclean:\n\trm -rf $(junk)\n\trm -fr elf/*\n\trm -fr dump/*\n\trm -fr $(self_test)/elf/*\n"
  },
  {
    "path": "tests/riscv-official/isa/selftests/Makefile",
    "content": "#=======================================================================\n# Makefile for riscv-tests/isa\n#-----------------------------------------------------------------------\n\nXLEN ?= 64\n\nsrc_dir := ../.\n\nisa_dir := ../../riscv-tests/isa\n\nself_test := .\nself_tests_t := $(wildcard tests/*.S)\nself_tests := $(self_tests_t:tests/%=%)\nelf_tests := $(self_tests:.S=.elf)\n\ndefault: clean self\n\n#--------------------------------------------------------------------\n# Build rules\n#--------------------------------------------------------------------\n\ninclude ../toolchain_setup\n\nvpath %.S $(self_test)\n\n#------------------------------------------------------------\n# Build assembly tests\n\nself: $(elf_tests)\n\n%32.elf: tests/%32.S\n\t$(RISCV_COMPILER) $(MARCH_OPTS_32) $(RISCV_COMPILER_OPTS) -I$(src_dir)/../env/p -I$(isa_dir)/macros/scalar -T$(src_dir)/../env/p/link.ld $< -o $(self_test)/elf/$*32\n\n%64.elf: tests/%64.S\n\t$(RISCV_COMPILER) $(MARCH_OPTS_64) $(RISCV_COMPILER_OPTS) -I$(src_dir)/../env/p -I$(isa_dir)/macros/scalar -T$(src_dir)/../env/p/link.ld $< -o $(self_test)/elf/$*64\n#------------------------------------------------------------\n# Clean up\n\nclean:\n\trm -fr elf/*\n"
  },
  {
    "path": "tests/riscv-official/isa/selftests/options_test.sh",
    "content": "#!/bin/bash\nTESTER=$1\nBIN=$2\nEXTERNAL=$3\nNULL=/dev/null\n\n# Test without required cli bin\npython $TESTER 1> $NULL 2> $NULL\nRES1=$?\n\n# Test default\npython $TESTER $BIN 1> $NULL 2> $NULL\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"default   passed\"\nelse\n    echo \"default   failed\"\nfi\n\n# Test help\npython $TESTER $BIN 1> $NULL 2> $NULL -h\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"help      passed\"\nelse\n    echo \"help      failed\"\nfi\n\n# Test no-32\npython $TESTER $BIN 1> $NULL 2> $NULL --no-32\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"no-32     passed\"\nelse\n    echo \"no-32     failed\"\nfi\n\n# Test no-64\npython $TESTER $BIN 1> $NULL 2> $NULL --no-64\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"no-64     passed\"\nelse\n    echo \"no-64     failed\"\nfi\n\n# Test external\npython $TESTER $BIN 1> $NULL 2> $NULL -E $EXTERNAL\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"external  passed\"\nelse\n    echo \"external  failed\"\nfi\n\n# Test multiply\npython $TESTER $BIN 1> $NULL 2> $NULL -M\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"multiply  passed\"\nelse\n    echo \"multiply  failed\"\nfi\n\n# Test atomic\npython $TESTER $BIN 1> $NULL 2> $NULL -A\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"atomic    passed\"\nelse\n    echo \"atomic    failed\"\nfi\n\n# Test CSR\npython $TESTER $BIN 1> $NULL 2> $NULL --CSR\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"CSR       passed\"\nelse\n    echo \"CSR       failed\"\nfi\n\n# Test pipeline\npython $TESTER $BIN 1> $NULL 2> $NULL --pipeline\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"pipeline  passed\"\nelse\n    echo \"pipeline  failed\"\nfi\n\n# Test cache\npython $TESTER $BIN 1> $NULL 2> $NULL --cache\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"cache     passed\"\nelse\n    echo \"cache     failed\"\nfi\n\n# Test no-pass\npython $TESTER $BIN 1> $NULL 2> $NULL --no-pass\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"no-pass   passed\"\nelse\n    echo \"no-pass   failed\"\nfi\n\n# Test no-dump\npython $TESTER $BIN 1> $NULL 2> $NULL --no-dump\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"no-dump   passed\"\nelse\n    echo \"no-dump   failed\"\nfi\n\n# Test d-regs\npython $TESTER $BIN 1> $NULL 2> $NULL --d-regs\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"d-regs    passed\"\nelse\n    echo \"d-regs    failed\"\nfi\n\n# Test file\npython $TESTER $BIN 1> $NULL 2> $NULL --file\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"file      passed\"\nelse\n    echo \"file      failed\"\nfi\n\n# Test rebuild\npython $TESTER $BIN 1> $NULL 2> $NULL -R\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"rebuild   passed\"\nelse\n    echo \"rebuild   failed\"\nfi\n\n# Test seltest\npython $TESTER $BIN 1> $NULL 2> $NULL -S\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"seltest   passed\"\nelse\n    echo \"seltest   failed\"\nfi\n\n# Test clean\npython $TESTER $BIN 1> $NULL 2> $NULL --clean\nTMP=$?\nif [ $TMP -eq 0 ];\nthen\n    echo \"clean     passed\"\nelse\n    echo \"clean     failed\"\nfi"
  },
  {
    "path": "tests/riscv-official/isa/selftests/tests/addi-fail32.S",
    "content": "# See LICENSE for license details.\n\n#include \"riscv_test.h\"\n#undef RVTEST_RV64U\n#define RVTEST_RV64U RVTEST_RV32U\n\n#include \"addi-fail64.S\"\n"
  },
  {
    "path": "tests/riscv-official/isa/selftests/tests/addi-fail64.S",
    "content": "# See LICENSE for license details.\n\n#*****************************************************************************\n# addi.S\n#-----------------------------------------------------------------------------\n#\n# Test addi instruction.\n#\n\n#include \"riscv_test.h\"\n#include \"test_macros.h\"\n\nRVTEST_RV64U\nRVTEST_CODE_BEGIN\n\n  #-------------------------------------------------------------\n  # Arithmetic tests\n  #-------------------------------------------------------------\n\n  TEST_IMM_OP( 2,  addi, 0x00000000, 0x00000000, 0xfff ); # <--- here is the mistake, it's on purpose\n  TEST_IMM_OP( 3,  addi, 0x00000002, 0x00000001, 0x001 );\n  TEST_IMM_OP( 4,  addi, 0x0000000a, 0x00000003, 0x007 );\n\n  TEST_IMM_OP( 5,  addi, 0xfffffffffffff800, 0x0000000000000000, 0x800 );\n  TEST_IMM_OP( 6,  addi, 0xffffffff80000000, 0xffffffff80000000, 0x000 );\n  TEST_IMM_OP( 7,  addi, 0xffffffff7ffff800, 0xffffffff80000000, 0x800 );\n\n  TEST_IMM_OP( 8,  addi, 0x00000000000007ff, 0x00000000, 0x7ff );\n  TEST_IMM_OP( 9,  addi, 0x000000007fffffff, 0x7fffffff, 0x000 );\n  TEST_IMM_OP( 10, addi, 0x00000000800007fe, 0x7fffffff, 0x7ff );\n\n  TEST_IMM_OP( 11, addi, 0xffffffff800007ff, 0xffffffff80000000, 0x7ff );\n  TEST_IMM_OP( 12, addi, 0x000000007ffff7ff, 0x000000007fffffff, 0x800 );\n\n  TEST_IMM_OP( 13, addi, 0xffffffffffffffff, 0x0000000000000000, 0xfff );\n  TEST_IMM_OP( 14, addi, 0x0000000000000000, 0xffffffffffffffff, 0x001 );\n  TEST_IMM_OP( 15, addi, 0xfffffffffffffffe, 0xffffffffffffffff, 0xfff );\n\n  TEST_IMM_OP( 16, addi, 0x0000000080000000, 0x7fffffff, 0x001 );\n\n  #-------------------------------------------------------------\n  # Source/Destination tests\n  #-------------------------------------------------------------\n\n  TEST_IMM_SRC1_EQ_DEST( 17, addi, 24, 13, 11 );\n\n  #-------------------------------------------------------------\n  # Bypassing tests\n  #-------------------------------------------------------------\n\n  TEST_IMM_DEST_BYPASS( 18, 0, addi, 24, 13, 11 );\n  TEST_IMM_DEST_BYPASS( 19, 1, addi, 23, 13, 10 );\n  TEST_IMM_DEST_BYPASS( 20, 2, addi, 22, 13,  9 );\n\n  TEST_IMM_SRC1_BYPASS( 21, 0, addi, 24, 13, 11 );\n  TEST_IMM_SRC1_BYPASS( 22, 1, addi, 23, 13, 10 );\n  TEST_IMM_SRC1_BYPASS( 23, 2, addi, 22, 13,  9 );\n\n  TEST_IMM_ZEROSRC1( 24, addi, 32, 32 );\n  TEST_IMM_ZERODEST( 25, addi, 33, 50 );\n\n  TEST_PASSFAIL\n\nRVTEST_CODE_END\n\n  .data\nRVTEST_DATA_BEGIN\n\n  TEST_DATA\n\nRVTEST_DATA_END\n"
  },
  {
    "path": "tests/riscv-official/isa/selftests/tests/simple-fail32.S",
    "content": "# See LICENSE for license details.\n\n#include \"riscv_test.h\"\n#undef RVTEST_RV64U\n#define RVTEST_RV64U RVTEST_RV32U\n\n#include \"simple-fail64.S\"\n"
  },
  {
    "path": "tests/riscv-official/isa/selftests/tests/simple-fail64.S",
    "content": "# See LICENSE for license details.\n\n#*****************************************************************************\n# simple.S\n#-----------------------------------------------------------------------------\n#\n# This is the most basic self checking test. If your simulator does not\n# pass thiss then there is little chance that it will pass any of the\n# more complicated self checking tests.\n#\n\n#include \"riscv_test.h\"\n#include \"test_macros.h\"\n\nRVTEST_RV64U\nRVTEST_CODE_BEGIN\n\nRVTEST_FAIL # <--- here is the mistake, it's on purpose\n\nRVTEST_CODE_END\n\n  .data\nRVTEST_DATA_BEGIN\n\n  TEST_DATA\n\nRVTEST_DATA_END\n"
  },
  {
    "path": "tests/riscv-official/isa/selftests/tests/simple-pass32.S",
    "content": "# See LICENSE for license details.\n\n#include \"riscv_test.h\"\n#undef RVTEST_RV64U\n#define RVTEST_RV64U RVTEST_RV32U\n\n#include \"simple-pass64.S\"\n"
  },
  {
    "path": "tests/riscv-official/isa/selftests/tests/simple-pass64.S",
    "content": "# See LICENSE for license details.\n\n#*****************************************************************************\n# simple.S\n#-----------------------------------------------------------------------------\n#\n# This is the most basic self checking test. If your simulator does not\n# pass thiss then there is little chance that it will pass any of the\n# more complicated self checking tests.\n#\n\n#include \"riscv_test.h\"\n#include \"test_macros.h\"\n\nRVTEST_RV64U\nRVTEST_CODE_BEGIN\n\nRVTEST_PASS # <--- here is the mistake, it's on purpose\n\nRVTEST_CODE_END\n\n  .data\nRVTEST_DATA_BEGIN\n\n  TEST_DATA\n\nRVTEST_DATA_END\n"
  },
  {
    "path": "tests/riscv-official/isa/toolchain_setup",
    "content": "RISCV_PREFIX ?= riscv$(XLEN)-unknown-elf-\nRISCV_COMPILER ?= $(RISCV_PREFIX)gcc\nRISCV_COMPILER_OPTS ?= -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles\nRISCV_OBJDUMP_CMD ?= $(RISCV_PREFIX)objdump\nRISCV_OBJDUMP ?= $(RISCV_OBJDUMP_CMD) --disassemble-all --disassemble-zeroes --section=.text --section=.text.startup\n--section=.text.init --section=.data\nRISCV_SIM ?= spike\n\n# Clang used different options regarding march\nUSE_CLANG_OPTS ?= false\n\nifeq ($(USE_CLANG_OPTS), true)\nMARCH_OPTS_32 = -march=rv32g -mabi=ilp32 --target=riscv32 -mno-relax -fuse-ld=lld\nMARCH_OPTS_64 = -march=rv64g -mabi=lp64 --target=riscv64 -mno-relax -fuse-ld=lld\nelse\nMARCH_OPTS_32 = -march=rv32g -mabi=ilp32\nMARCH_OPTS_64 = -march=rv64g -mabi=lp64\nendif"
  },
  {
    "path": "tests/riscv-official/qtrvsim_tester.py",
    "content": "import sys\nimport os\n\nFILENAME = \"qtrvsim_tester.py\"\nSRC_DIR = os.path.realpath(__file__).replace(FILENAME, \"\")\n\nsys.path.append(SRC_DIR+\"code\")\nimport constants as cn\nimport myparse as mp\nimport testing as ts\nimport selftesting as sts\n\nparser = mp.init_parser()\nparams = parser.parse_args()\nif (params.dregs):\n    params.nodump = 0\n    params.nopass = 0\n\nif(params.clean):\n    ts.delete_elf(SRC_DIR)\n    sys.exit(0)\n        \nsim_bin, bin_check = ts.test_sim_bin(params.qtrvsim_cli)\n\nif(not bin_check):\n    print(\"Problem with qtrvsim_cli binary!\")\n    sys.exit(1)\n\nself_files, s_file_check = ts.load_filenames(\n    SRC_DIR + cn.SELF_PATH, bool(params.rebuild))\ntest_files, t_file_check = ts.load_filenames(\n    SRC_DIR + cn.ISA_PATH, bool(params.rebuild))\n\nif (params.selftest):\n    sts.self_test(sim_bin, params, SRC_DIR, self_files)\n\nts.test_selector(sim_bin, params, SRC_DIR, test_files)\n\nsys.exit(0)\n"
  },
  {
    "path": "tests/stud-support/build_tests.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\nBuild stud-support ELFs for integration testing\n\nThis script compiles selected stud-support examples into RISC-V ELFs\nusing Clang/LLVM toolchain or GCC.\n\"\"\"\n\nimport argparse\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport glob\n\n# List of tests to build: (output_name, source_directory, [optional: single_file])\nTESTS_TO_BUILD = [\n    (\"selection_sort\", \"seminaries/qtrvsim/selection-sort\"),\n    (\"vect_add\", \"seminaries/qtrvsim/vect-add\"),\n    (\"vect_add2\", \"seminaries/qtrvsim/vect-add2\"),\n    (\"vect_inc\", \"seminaries/qtrvsim/vect-inc\"),\n    (\"branchpred_1\", \"seminaries/qtrvsim/branchpred-1\"),\n    (\"ffs_as_log2\", \"seminaries/qtrvsim/ffs-as-log2\"),\n    (\"uart_echo_irq\", \"seminaries/qtrvsim/uart-echo-irq\"),\n    (\"call_clobber\", \"seminaries/qtrvsim/call-syscall\", \"lec10-01-call4-clobber.S\"),\n    (\"call_save\", \"seminaries/qtrvsim/call-syscall\", \"lec10-02-call4-save.S\"),\n    (\"fact_buggy\", \"seminaries/qtrvsim/call-syscall\", \"lec10-03-fact-buggy.S\"),\n    (\"fact_ok\", \"seminaries/qtrvsim/call-syscall\", \"lec10-04-fact-ok.S\"),\n    (\"call_10args\", \"seminaries/qtrvsim/call-syscall\", \"lec10-05-call-10args.S\"),\n    (\"linus_hello\", \"seminaries/qtrvsim/call-syscall\", \"lec10-06-linus-hello.S\"),\n]\n\ndef get_toolchain_config(use_clang=False):\n    \"\"\"Get toolchain configuration\"\"\"\n    arch = os.getenv(\"ARCH\", \"riscv64-unknown-elf\")\n    \n    # Prefer GCC unless Clang is forced or GCC is missing and Clang is present\n    if use_clang or (not shutil.which(f\"{arch}-gcc\") and shutil.which(\"clang\")):\n        cc = os.getenv(\"CC\", \"clang\")\n        cflags = os.getenv(\"CFLAGS\", \"--target=riscv32 -march=rv32g -nostdlib -static -fuse-ld=lld\")\n        return {\n            \"name\": \"Clang/LLVM\",\n            \"cc\": cc,\n            \"cflags\": cflags,\n            \"ld\": cc,\n            \"ldflags\": cflags,\n            \"objcopy\": \"llvm-objcopy\",\n            \"required\": [cc, \"ld.lld\"]\n        }\n    \n    cc = os.getenv(\"CC\", f\"{arch}-gcc\")\n    return {\n        \"name\": \"GNU\",\n        \"cc\": cc,\n        \"cflags\": os.getenv(\"CFLAGS\", \"\"),\n        \"ld\": f\"{arch}-ld\",\n        \"ldflags\": \"\",\n        \"objcopy\": f\"{arch}-objcopy\",\n        \"required\": [cc]\n    }\n\ndef check_toolchain(config):\n    \"\"\"Check if required tools are available\"\"\"\n    for tool in config[\"required\"]:\n        if not shutil.which(tool):\n            return False, f\"Tool not found: {tool}\"\n    return True, \"OK\"\n\ndef build_test(source_dir, output_name, toolchain, stud_support_root, output_dir, verbose=False, single_file=None):\n    \"\"\"Build a single test\"\"\"\n    full_source_dir = os.path.join(stud_support_root, source_dir)\n    output_elf = os.path.join(output_dir, f\"{output_name}.elf\")\n    \n    print(f\"Building {output_name}...\")\n    if not os.path.isdir(full_source_dir):\n        print(f\"  ERROR: Source directory not found: {full_source_dir}\")\n        return False, \"source not found\"\n\n    try:\n        if single_file:\n            source_file = os.path.join(full_source_dir, single_file)\n            if not os.path.isfile(source_file):\n                print(f\"  ERROR: Source file not found: {source_file}\")\n                return False, \"source file not found\"\n            \n            _, ext = os.path.splitext(source_file)\n            cmd = [toolchain[\"cc\"]]\n            if ext in ['.S', '.s']:\n                cmd.append(\"-D__ASSEMBLY__\")\n            if toolchain[\"cflags\"]:\n                cmd.extend(toolchain[\"cflags\"].split())\n            elif toolchain[\"name\"] == \"GNU\":\n                # Default flags for GCC if not specified\n                cmd.extend([\"-march=rv32im\", \"-mabi=ilp32\", \"-nostdlib\", \"-static\"])\n\n            cmd.extend([\"-o\", output_elf, source_file])\n            \n            result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)\n            if result.returncode != 0:\n                print(f\"  ERROR: Build failed with return code {result.returncode}\")\n                print(\"  Output:\")\n                print(result.stdout)\n                print(\"  Error:\")\n                print(result.stderr)\n                return False, \"build error\"\n            \n            print(f\"  SUCCESS: {output_elf}\")\n            return True, None\n\n        else:\n            # Makefile build\n            subprocess.run([\"make\", \"clean\"], cwd=full_source_dir, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)\n            \n            make_vars = [\n                f\"CC={toolchain['cc']}\",\n                f\"AS={toolchain['cc']}\",\n                f\"CXX={toolchain['cc']}\",\n                f\"LD={toolchain['ld']}\",\n                f\"OBJCOPY={toolchain['objcopy']}\",\n                f\"LOADLIBES=\"\n            ]\n            \n            if toolchain['cflags']:\n                make_vars.extend([\n                    f\"CFLAGS={toolchain['cflags']}\",\n                    f\"AFLAGS={toolchain['cflags']}\",\n                    f\"CXXFLAGS={toolchain['cflags']}\"\n                ])\n            \n            if toolchain['ldflags']:\n                make_vars.append(f\"LDFLAGS={toolchain['ldflags']}\")\n            \n            result = subprocess.run([\"make\"] + make_vars, cwd=full_source_dir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)\n            if result.returncode != 0:\n                print(f\"  ERROR: Build failed with return code {result.returncode}\")\n                print(\"  Output:\")\n                print(result.stdout)\n                print(\"  Error:\")\n                print(result.stderr)\n                return False, \"build error\"\n\n            # Find built ELF\n            candidates = [os.path.join(full_source_dir, os.path.basename(source_dir))] + \\\n                         glob.glob(os.path.join(full_source_dir, \"*.elf\"))\n            \n            for candidate in candidates:\n                if os.path.isfile(candidate) and os.access(candidate, os.X_OK):\n                    # Simple check if it looks like an ELF (starts with \\x7fELF)\n                    with open(candidate, 'rb') as f:\n                        if f.read(4) == b'\\x7fELF':\n                            shutil.copy2(candidate, output_elf)\n                            print(f\"  SUCCESS: {output_elf}\")\n                            return True, None\n            \n            print(f\"  ERROR: Built ELF not found in {full_source_dir}\")\n            return False, \"ELF not found\"\n\n    except Exception as e:\n        print(f\"  ERROR: Exception: {e}\")\n        return False, f\"exception: {e}\"\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Build stud-support ELFs\")\n    parser.add_argument(\"--verbose\", \"-v\", action=\"store_true\", help=\"Show detailed output\")\n    parser.add_argument(\"--use-gcc\", action=\"store_true\", help=\"Force GNU toolchain\")\n    parser.add_argument(\"--use-clang\", action=\"store_true\", help=\"Force Clang toolchain\")\n    parser.add_argument(\"--output-dir\", help=\"Output directory\")\n    parser.add_argument(\"--stud-support-path\", required=True, help=\"Path to stud-support repo\")\n    args = parser.parse_args()\n    \n    output_dir = args.output_dir or os.path.join(os.path.dirname(os.path.realpath(__file__)), \"elfs\")\n    if not os.path.isdir(args.stud_support_path):\n        print(f\"Error: stud-support not found at {args.stud_support_path}\")\n        return 1\n    \n    toolchain = get_toolchain_config(args.use_clang)\n    if args.use_gcc and toolchain[\"name\"] != \"GNU\":\n        # User forced GCC but we defaulted to Clang (shouldn't happen with current logic but good to be safe)\n        toolchain = get_toolchain_config(False) \n        \n    available, msg = check_toolchain(toolchain)\n    if not available:\n        print(f\"Error: {msg}\\nPlease install riscv64-unknown-elf-gcc or clang/lld.\")\n        return 1\n    \n    os.makedirs(output_dir, exist_ok=True)\n    print(f\"Using toolchain: {toolchain['name']} ({toolchain['cc']})\")\n    print(f\"Output: {output_dir}\\n\")\n    \n    results = [build_test(t[1], t[0], toolchain, args.stud_support_path, output_dir, args.verbose, t[2] if len(t)>2 else None) for t in TESTS_TO_BUILD]\n    success_count = sum(1 for r in results if r[0])\n    \n    print(f\"\\nBuild Summary: {success_count}/{len(TESTS_TO_BUILD)} successful\")\n    if success_count < len(TESTS_TO_BUILD):\n        print(\"Failed tests:\")\n        for i, (success, reason) in enumerate(results):\n            if not success: print(f\"  - {TESTS_TO_BUILD[i][0]} ({reason})\")\n        return 1\n        \n    return 0\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "tests/stud-support/run_tests.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\nstud-support Integration Test Runner for QtRvSim\n\"\"\"\n\nimport argparse\nimport os\nimport re\nimport subprocess\nimport sys\n\n# Define all stud-support tests\nTESTS = [\n    {\n        \"name\": \"selection_sort\",\n        \"elf_path\": \"elfs/selection_sort.elf\",\n        \"description\": \"Selection sort algorithm test\",\n        \"expected_registers\": {\"s0\": 60, \"s1\": 60},\n        \"min_cycles\": 500, \"max_cycles\": 5000,\n    },\n    {\n        \"name\": \"vect_add\",\n        \"elf_path\": \"elfs/vect_add.elf\",\n        \"description\": \"Vector addition test\",\n        \"expected_registers\": {\"x8\": 0},\n        \"expected_memory\": [(0x400 + 64, 0x11), (0x400 + 68, 0x22), (0x400 + 72, 0x33)],\n        \"min_cycles\": 100, \"max_cycles\": 2000,\n    },\n    {\n        \"name\": \"vect_add2\",\n        \"elf_path\": \"elfs/vect_add2.elf\",\n        \"description\": \"Vector addition variant test\",\n        \"expected_registers\": {\"t3\": 0},\n        \"min_cycles\": 100, \"max_cycles\": 2000,\n    },\n    {\n        \"name\": \"vect_inc\",\n        \"elf_path\": \"elfs/vect_inc.elf\",\n        \"description\": \"Vector increment test\",\n        \"expected_registers\": {\"t3\": 0},\n        \"min_cycles\": 100, \"max_cycles\": 2000,\n    },\n    {\n        \"name\": \"branchpred_1\",\n        \"elf_path\": \"elfs/branchpred_1.elf\",\n        \"description\": \"Branch prediction test\",\n        \"expected_registers\": {\"s2\": 20, \"s0\": 4, \"s1\": 5},\n        \"min_cycles\": 50, \"max_cycles\": 500,\n    },\n    {\n        \"name\": \"ffs_as_log2\",\n        \"elf_path\": \"elfs/ffs_as_log2.elf\",\n        \"description\": \"Find first set as log2 test\",\n        \"expected_registers\": {\"a0\": 7, \"t1\": 7},\n        \"min_cycles\": 20, \"max_cycles\": 200,\n    },\n    {\n        \"name\": \"uart_echo_irq\",\n        \"elf_path\": \"elfs/uart_echo_irq.elf\",\n        \"description\": \"UART echo with interrupts test (hits BREAK early)\",\n        \"min_cycles\": 5, \"max_cycles\": 50,\n    },\n    {\n        \"name\": \"call_clobber\",\n        \"elf_path\": \"elfs/call_clobber.elf\",\n        \"description\": \"Function call with register clobbering\",\n        \"expected_registers\": {\"a0\": 0xfffffffffffffffc},\n        \"min_cycles\": 15, \"max_cycles\": 200,\n    },\n    {\n        \"name\": \"call_save\",\n        \"elf_path\": \"elfs/call_save.elf\",\n        \"description\": \"Function call with register saving\",\n        \"expected_registers\": {\"a0\": 0xfffffffffffffffc},\n        \"min_cycles\": 15, \"max_cycles\": 200,\n    },\n    {\n        \"name\": \"fact_ok\",\n        \"elf_path\": \"elfs/fact_ok.elf\",\n        \"description\": \"Correct factorial implementation\",\n        \"expected_registers\": {\"t0\": 24, \"a0\": 24},\n        \"min_cycles\": 50, \"max_cycles\": 500,\n    },\n    {\n        \"name\": \"call_10args\",\n        \"elf_path\": \"elfs/call_10args.elf\",\n        \"description\": \"Function call with 10 arguments\",\n        \"expected_registers\": {\"a0\": 55},\n        \"min_cycles\": 30, \"max_cycles\": 300,\n    },\n    {\n        \"name\": \"linus_hello\",\n        \"elf_path\": \"elfs/linus_hello.elf\",\n        \"description\": \"Linux-style hello world with syscalls (hits ECALL early)\",\n        \"min_cycles\": 5, \"max_cycles\": 50,\n    },\n]\n\n# RISC-V ABI name to register number mapping\nABI_TO_NUM = {\n    \"zero\": 0, \"ra\": 1, \"sp\": 2, \"gp\": 3, \"tp\": 4,\n    \"t0\": 5, \"t1\": 6, \"t2\": 7,\n    \"s0\": 8, \"fp\": 8, \"s1\": 9,\n    \"a0\": 10, \"a1\": 11, \"a2\": 12, \"a3\": 13, \"a4\": 14, \"a5\": 15, \"a6\": 16, \"a7\": 17,\n    \"s2\": 18, \"s3\": 19, \"s4\": 20, \"s5\": 21, \"s6\": 22, \"s7\": 23, \"s8\": 24, \"s9\": 25, \"s10\": 26, \"s11\": 27,\n    \"t3\": 28, \"t4\": 29, \"t5\": 30, \"t6\": 31\n}\n\ndef get_register_value(registers, reg_name):\n    \"\"\"Get register value by name, supporting multiple naming conventions\"\"\"\n    if reg_name in registers: return registers[reg_name]\n    \n    # Try as \"R\" + number format\n    r_name = f\"R{reg_name}\" if not reg_name.startswith('R') else reg_name\n    if r_name in registers: return registers[r_name]\n        \n    # Try converting ABI name to register number\n    if reg_name in ABI_TO_NUM:\n        r_name = f\"R{ABI_TO_NUM[reg_name]}\"\n        if r_name in registers: return registers[r_name]\n            \n    # Try as \"x\" + number format\n    if reg_name.startswith('x'):\n        try:\n            r_name = f\"R{int(reg_name[1:])}\"\n            if r_name in registers: return registers[r_name]\n        except ValueError: pass\n            \n    return None\n\ndef verify_result(test, result, verbose):\n    \"\"\"Verify test result against expectations\"\"\"\n    if result.returncode != 0:\n        return False, f\"Exit code {result.returncode} (expected 0)\" + (f\"\\nStdout: {result.stdout}\" if verbose else \"\")\n\n    # Parse output\n    registers = {m.group(1): int(m.group(2), 16) for m in re.finditer(r'([A-Za-z0-9_]+):0x([0-9a-fA-F]+)', result.stdout)}\n    cycles_match = re.search(r'cycles:\\s*(\\d+)', result.stdout)\n    cycles = int(cycles_match.group(1)) if cycles_match else None\n\n    # Verify registers\n    for reg, expected in test.get(\"expected_registers\", {}).items():\n        actual = get_register_value(registers, reg)\n        if actual is None:\n            return False, f\"Register {reg} not found\" + (f\"\\nAvailable: {list(registers.keys())}\" if verbose else \"\")\n        if actual != expected:\n            return False, f\"Register {reg}: expected 0x{expected:x}, got 0x{actual:x}\" + (f\"\\nAll: {registers}\" if verbose else \"\")\n\n    # Verify cycles\n    if cycles is not None:\n        if cycles < test.get(\"min_cycles\", 0):\n            return False, f\"Cycles {cycles} < min {test['min_cycles']}\"\n        if test.get(\"max_cycles\") and cycles > test[\"max_cycles\"]:\n            return False, f\"Cycles {cycles} > max {test['max_cycles']}\"\n\n    # Verify marker\n    if test.get(\"marker\") and test[\"marker\"] not in result.stdout:\n        return False, f\"Marker '{test['marker']}' not found\" + (f\"\\nStdout: {result.stdout}\" if verbose else \"\")\n\n    return True, f\"PASS ({cycles} cycles)\" if cycles else \"PASS\"\n\ndef run_test(qtrvsim_cli, test, elf_dir, verbose=False, pipeline=False, cache=False):\n    \"\"\"Run a single test\"\"\"\n    elf_path = os.path.join(elf_dir, os.path.basename(test[\"elf_path\"]))\n    if not os.path.exists(elf_path): return False, f\"ELF not found: {elf_path}\"\n    \n    cmd = [qtrvsim_cli, elf_path, \"--dump-registers\", \"--dump-cycles\"]\n    if pipeline: cmd.append(\"--pipelined\")\n    if cache: cmd.extend([\"--d-cache\", \"lru,2,2,2\", \"--i-cache\", \"lru,2,2,2\"])\n    \n    try:\n        result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, timeout=30)\n        return verify_result(test, result, verbose)\n    except subprocess.TimeoutExpired:\n        return False, \"Test timed out (30s)\"\n    except Exception as e:\n        return False, f\"Exception: {e}\"\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Run stud-support integration tests\")\n    parser.add_argument(\"--build-dir\", required=True, help=\"Directory containing test ELFs\")\n    parser.add_argument(\"--qtrvsim-cli\", required=True, help=\"Path to qtrvsim_cli executable\")\n    parser.add_argument(\"--stud-support-path\", required=True, help=\"stud-support repo path\")\n    parser.add_argument(\"-v\", \"--verbose\", action=\"store_true\", help=\"Verbose output\")\n    parser.add_argument(\"-f\", \"--filter\", help=\"Filter tests\")\n    parser.add_argument(\"--pipeline\", action=\"store_true\", help=\"Pipelined mode\")\n    parser.add_argument(\"--cache\", action=\"store_true\", help=\"Cache enabled\")\n    args = parser.parse_args()\n    \n    elf_dir = args.build_dir\n    \n    def is_exe(fpath):\n        return os.path.isfile(fpath) and (os.name == 'nt' or os.access(fpath, os.X_OK))\n\n    qtrvsim_cli = args.qtrvsim_cli\n    if not is_exe(qtrvsim_cli):\n        # Try appending .exe for Windows if not present\n        if os.name == 'nt' and not qtrvsim_cli.endswith('.exe') and is_exe(qtrvsim_cli + '.exe'):\n            qtrvsim_cli += '.exe'\n        else:\n            print(f\"Error: qtrvsim_cli not found or not executable at {qtrvsim_cli}\")\n            return 1\n        \n    tests = [t for t in TESTS if not args.filter or args.filter in t[\"name\"]]\n    if not tests:\n        print(f\"No tests match filter: {args.filter}\")\n        return 1\n        \n    mode = \"pipelined+cached\" if args.pipeline and args.cache else \"pipelined\" if args.pipeline else \"cached\" if args.cache else \"single-cycle\"\n    print(f\"Running {len(tests)} tests ({mode})\\nUsing: {qtrvsim_cli}\\nELFs: {elf_dir}\\n\" + \"=\"*70)\n    \n    results = [run_test(qtrvsim_cli, t, elf_dir, args.verbose, args.pipeline, args.cache) for t in tests]\n    passed = sum(1 for r in results if r[0])\n    \n    for i, (success, msg) in enumerate(results):\n        status = \"[OK]  \" if success else \"[FAIL]\"\n        print(f\"{status} {tests[i]['name']:40s} {msg}\")\n        \n    print(\"=\"*70 + f\"\\nResults: {passed}/{len(tests)} passed\")\n    if passed < len(tests):\n        print(\"\\nFailed tests:\")\n        for i, (success, _) in enumerate(results):\n            if not success: print(f\"  - {tests[i]['name']}\")\n            \n    return 1 if passed < len(tests) else 0\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  }
]