├── .editorconfig ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── package-lock.json ├── package.json ├── pioasm.js ├── prettier.config.cjs └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | 7 | [Makefile] 8 | indent_style = tab 9 | indent_size = 4 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | pioasm.d.ts 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Uri Shaked 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | ---- 24 | 25 | pioasm license: 26 | 27 | Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. 28 | 29 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 30 | following conditions are met: 31 | 32 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following 33 | disclaimer. 34 | 35 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 36 | disclaimer in the documentation and/or other materials provided with the distribution. 37 | 38 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products 39 | derived from this software without specific prior written permission. 40 | 41 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 42 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 43 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 44 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 45 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 46 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 47 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # pioasm.js 2 | # Copyright (C) 2021, Uri Shaked 3 | # 4 | # To compile: 5 | # 1. Install emscripten sdk (2.x) and set the corresponding environment variables 6 | # 2. Install the pi-pico-sdk, and set PICO_SDK_PATH to point at it, e.g. 7 | # export PICO_SDK_PATH=~/pico/pico-sdk 8 | 9 | EMCC_FLAGS = -s WASM=1 -s MODULARIZE=1 -s EXPORTED_RUNTIME_METHODS=['callMain','FS_writeFile'] -O3 10 | OBJ_FILES = CMakeFiles/pioasm.dir/main.cpp.o 11 | 12 | all: build/pioasm.js build/pioasm-browser.js 13 | 14 | build/pioasm.js: $(OBJ_FILES) 15 | emcc -o build/pioasm.js $(EMCC_FLAGS) `find build -name "*.o"` 16 | 17 | build/pioasm-browser.js: $(OBJ_FILES) | build/pioasm.js build/browser 18 | emcc -o build/browser/pioasm.js $(EMCC_FLAGS) -s ENVIRONMENT=web `find build -name "*.o"` 19 | mv build/browser/pioasm.js build/pioasm-browser.js 20 | 21 | clean: 22 | rm -rf build 23 | 24 | CMakeFiles/pioasm.dir/main.cpp.o: build/Makefile 25 | cd build && emmake make 26 | 27 | build/Makefile: build 28 | cd build && emcmake cmake -D CMAKE_CXX_FLAGS="-fexceptions" ${PICO_SDK_PATH}/tools/pioasm 29 | 30 | build: 31 | mkdir build 32 | echo '{"type": "commonjs"}' > build/package.json 33 | 34 | build/browser: 35 | mkdir build/browser 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pioasm-wasm 2 | 3 | Raspberry Pi Pico pioasm tool compiled to Web Assembly. 4 | 5 | ## Online pioasm 6 | 7 | The online version of pioasm allows you to compile PIO assembly code to C/Python/Ada right in your browser: 8 | 9 | https://wokwi.com/tools/pioasm 10 | 11 | ## Library usage example 12 | 13 | You can install the library from npm: 14 | 15 | ``` 16 | npm install --save pioasm 17 | ``` 18 | 19 | or yarn: 20 | 21 | ``` 22 | yarn add pioasm 23 | ``` 24 | 25 | Usage example: 26 | 27 | ```javascript 28 | import { PIOAssembler } from 'pioasm'; 29 | 30 | const source = ` 31 | .program blink 32 | pull block 33 | out y, 32 34 | `; 35 | 36 | const pioasm = new PIOAssembler(); 37 | pioasm.assemble(source).then(result => { 38 | console.log(result.output); 39 | }) 40 | ``` 41 | 42 | ## License 43 | 44 | This project, excluding pioasm, is released under the MIT license. Consult [the LICENSE file](LICENSE) for more details. 45 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pioasm", 3 | "version": "2.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/emscripten": { 8 | "version": "1.39.4", 9 | "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.4.tgz", 10 | "integrity": "sha512-k3LLVMFrdNA9UCvMDPWMbFrGPNb+GcPyw29ktJTo1RCN7RmxFG5XzPZcPKRlnLuLT/FRm8wp4ohvDwNY7GlROQ==" 11 | }, 12 | "typescript": { 13 | "version": "4.3.2", 14 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", 15 | "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", 16 | "dev": true 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pioasm", 3 | "version": "2.0.0", 4 | "description": "Raspberry Pi Pico pioasm compiled to Web Assembly", 5 | "repository": "https://github.com/wokwi/pioasm-wasm", 6 | "main": "pioasm.js", 7 | "type": "module", 8 | "files": [ 9 | "pioasm.js", 10 | "pioasm.d.ts", 11 | "build/pioasm.js", 12 | "build/pioasm-browser.js", 13 | "build/pioasm.wasm", 14 | "LICENSE" 15 | ], 16 | "browser": { 17 | "#pioasm-emcc": "./build/pioasm-browser.js" 18 | }, 19 | "imports": { 20 | "#pioasm-emcc": { 21 | "browser": "./build/pioasm-browser.js", 22 | "default": "./build/pioasm.js" 23 | } 24 | }, 25 | "scripts": { 26 | "prepublish": "npm run build", 27 | "build": "tsc --declaration --emitDeclarationOnly --allowJS pioasm.js", 28 | "test": "node test" 29 | }, 30 | "keywords": [ 31 | "Raspberry Pi Pico", 32 | "PIO", 33 | "Programmable IO", 34 | "Web Assembly", 35 | "WASM", 36 | "Emscripten" 37 | ], 38 | "author": "Uri Shaked", 39 | "license": "MIT", 40 | "devDependencies": { 41 | "typescript": "^4.3.2" 42 | }, 43 | "dependencies": { 44 | "@types/emscripten": "^1.39.4" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /pioasm.js: -------------------------------------------------------------------------------- 1 | /** pioasm in Web Assembly 2 | * Copyright (C) 2021, Uri Shaked 3 | */ 4 | 5 | /// 6 | 7 | import wasm from '#pioasm-emcc'; 8 | 9 | /** 10 | * PIO Output format: 11 | * - c-sdk: C header suitable for use with the Raspberry Pi Pico SDK 12 | * - python: Python file suitable for use with MicroPython 13 | * - hex: Raw hex output (only valid for single program inputs) 14 | * - ada: Ada specification 15 | * 16 | * @typedef {'c-sdk'|'python'|'hex'|'ada'} PioOutputFormat 17 | */ 18 | 19 | /** 20 | * PIO Assembler wrapper class 21 | */ 22 | export class PIOAssembler { 23 | /** @private */ 24 | exitCode = 0; 25 | /** @private */ 26 | outputBuffer = []; 27 | /** @private */ 28 | instance; 29 | 30 | constructor() {} 31 | 32 | /** 33 | * Loads the pioasm Web Assembly module. Normally, `pioasm()` will load the module for 34 | * you, but you can use the `load()` method to pre-loader the Web Assembly module, or 35 | * if you need to provide custom options to EMScripten. 36 | * 37 | * For instance, you can override the `locateFile(url: string, scriptDirectory: string)` 38 | * method to configure the URL for the compiled web assembly module. 39 | * 40 | * @param {Partial} [options] 41 | * @returns {Promise} 42 | */ 43 | async load(options) { 44 | if (!this.instance) { 45 | this.instance = wasm({ 46 | noInitialRun: true, 47 | print: (msg) => { 48 | this.outputBuffer.push(msg); 49 | }, 50 | printErr: (msg) => { 51 | this.outputBuffer.push(msg); 52 | }, 53 | quit: (code) => { 54 | this.exitCode = code; 55 | }, 56 | ...options, 57 | }); 58 | } 59 | return this.instance; 60 | } 61 | 62 | /** 63 | * Compiles the given PIO source file. 64 | * 65 | * @param {string} source PIO source to compile 66 | * @param {PioOutputFormat} [format='c-sdk'] Output format 67 | * @param {string} [outputParam] Add a parameter to be passed to the output format generator 68 | * @returns Promise<{output: string, exitCode: number}> 69 | */ 70 | async assemble(source, format = 'c-sdk', outputParam) { 71 | const runtime = await this.load(); 72 | runtime.FS_writeFile('/input.pio', source); 73 | this.outputBuffer = []; 74 | this.exitCode = 0; 75 | const argv = ['-o', format]; 76 | if (outputParam) { 77 | argv.push('-p', outputParam); 78 | } 79 | argv.push('input.pio'); 80 | runtime.callMain(argv); 81 | return { 82 | output: this.outputBuffer.join('\n'), 83 | exitCode: this.exitCode, 84 | }; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /prettier.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'always', 3 | printWidth: 100, 4 | singleQuote: true, 5 | tabWidth: 2, 6 | endOfLine: 'auto', 7 | }; 8 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * pioasm-wasm test code 3 | */ 4 | 5 | import assert from 'assert'; 6 | import { PIOAssembler } from './pioasm.js'; 7 | 8 | const testInput = ` 9 | .program blink 10 | pull block 11 | out y, 32 12 | `; 13 | 14 | const expectedOutput = `# -------------------------------------------------- # 15 | # This file is autogenerated by pioasm; do not edit! # 16 | # -------------------------------------------------- # 17 | 18 | import rp2 19 | from machine import Pin 20 | # ----- # 21 | # blink # 22 | # ----- # 23 | 24 | @rp2.asm_pio() 25 | def blink(): 26 | wrap_target() 27 | pull(block) # 0 28 | out(y, 32) # 1 29 | wrap() 30 | 31 | `; 32 | 33 | const badProgram = ` 34 | .program bad 35 | jmp unknown 36 | `; 37 | 38 | async function main() { 39 | const pioasm = new PIOAssembler(); 40 | 41 | assert.deepStrictEqual(await pioasm.assemble(testInput, 'python'), { 42 | output: expectedOutput, 43 | exitCode: 0, 44 | }); 45 | 46 | assert.deepStrictEqual(await pioasm.assemble(testInput, 'hex'), { 47 | output: '80a0\n6040', 48 | exitCode: 0, 49 | }); 50 | 51 | assert.deepStrictEqual(await pioasm.assemble(badProgram, 'c-sdk'), { 52 | output: `input.pio:3.7-13: undefined symbol 'unknown' 53 | 3 | jmp unknown 54 | | ^~~~~~~`, 55 | exitCode: 1, 56 | }); 57 | 58 | console.log('Test passed successfully!'); 59 | } 60 | 61 | main().catch((err) => { 62 | console.error(err); 63 | process.exit(1); 64 | }); 65 | --------------------------------------------------------------------------------