├── .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 |
--------------------------------------------------------------------------------