From 80f9c267c7c4d31df5fef7dabfb266119a00317e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sat, 18 Jul 2026 18:48:14 +0200 Subject: [PATCH 1/5] Change assemble -p to -P --- .../org/soundpaint/rp2040pio/monitor/commands/Assemble.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java b/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java index 55a347a..f4733f3 100644 --- a/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java +++ b/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java @@ -77,7 +77,7 @@ public class Assemble extends Command CmdOptions.createStringOption("PATH", false, 't', "tool", null, "path to pioasm tool, if not on PATH"); private static final CmdOptions.StringOptionDeclaration optProgram = - CmdOptions.createStringOption("NAME", false, 'p', "program", null, + CmdOptions.createStringOption("NAME", false, 'P', "program", null, "name of program to use, if any"); private static final CmdOptions.BooleanOptionDeclaration optLoad = CmdOptions.createBooleanOption(false, 'l', "load", false, From f4fef75457cd28c39b054c1b8ed2c8cb36577af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sat, 18 Jul 2026 18:50:36 +0200 Subject: [PATCH 2/5] Add assemble options validation --- .../soundpaint/rp2040pio/monitor/commands/Assemble.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java b/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java index f4733f3..f23c714 100644 --- a/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java +++ b/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java @@ -106,6 +106,14 @@ protected void checkValidity(final CmdOptions options) throw new CmdOptions. ParseException("input file \"-i\" must be specified"); } + if (options.getValue(optLoad) && options.isDefined(optOutput)) { + throw new CmdOptions. + ParseException("output file can't be used with +l/--load"); + } + if (!options.getValue(optLoad) && options.isDefined(optProgram)) { + throw new CmdOptions. + ParseException("program name can only be used with +l/--load"); + } } } From da8576fc1c1694b5a99519c13376586ec44b0f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sat, 18 Jul 2026 18:51:19 +0200 Subject: [PATCH 3/5] Add PIO/SM number to assemble command --- .../rp2040pio/monitor/commands/Assemble.java | 48 ++++++++++++++----- .../rp2040pio/monitor/commands/Load.java | 10 ++-- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java b/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java index f23c714..dc333a0 100644 --- a/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java +++ b/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java @@ -64,8 +64,8 @@ public class Assemble extends Command "%n" + "If the \"-l\" load option is given, then after assembly,%n" + "it it loaded into pio 0, along with wrap and side set %n"+ - "commands for sm 0. If you need further control, manually%n" + - "load and configure the file, or make a PR with code improvements."; + "commands for sm 0. To change the target pio/sm instance,%n" + + "use \"-p\" and \"-s\" options."; private static final CmdOptions.StringOptionDeclaration optInput = CmdOptions.createStringOption("PATH", false, 'i', "input", null, @@ -82,6 +82,12 @@ public class Assemble extends Command private static final CmdOptions.BooleanOptionDeclaration optLoad = CmdOptions.createBooleanOption(false, 'l', "load", false, "If the .pioasm file should be loaded"); + private static final CmdOptions.IntegerOptionDeclaration optPio = + CmdOptions.createIntegerOption("NUMBER", false, 'p', "pio", null, + "PIO number, either 0 or 1"); + private static final CmdOptions.IntegerOptionDeclaration optSm = + CmdOptions.createIntegerOption("NUMBER", false, 's', "sm", null, + "SM number, one of 0, 1, 2 or 3"); private final SDK sdk; @@ -89,7 +95,7 @@ public Assemble(final PrintStream console, final SDK sdk) { super(console, fullName, singleLineDescription, notes, new CmdOptions.OptionDeclaration[] - { optInput, optOutput, optTool, optLoad, optProgram }); + { optInput, optOutput, optTool, optLoad, optProgram, optPio, optSm }); if (sdk == null) { throw new NullPointerException("sdk"); } @@ -114,6 +120,24 @@ protected void checkValidity(final CmdOptions options) throw new CmdOptions. ParseException("program name can only be used with +l/--load"); } + if (!options.getValue(optLoad) && (options.isDefined(optPio) || options.isDefined(optSm))) { + throw new CmdOptions. + ParseException("PIO/SM number can only be used with +l/--load"); + } + if (options.isDefined(optPio)) { + final int pioNum = options.getValue(optPio); + if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) { + throw new CmdOptions. + ParseException("PIO number must be either 0 or 1"); + } + } + if (options.isDefined(optSm)) { + final int smNum = options.getValue(optSm); + if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) { + throw new CmdOptions. + ParseException("SM number must be one of 0, 1, 2 or 3"); + } + } } } @@ -162,6 +186,8 @@ protected boolean execute(final CmdOptions options) throws IOException } catch (InterruptedException e) { return false; } + final int pioNum = options.isDefined(optPio) ? options.getValue(optPio) : 0; + final int smNum = options.isDefined(optSm) ? options.getValue(optSm) : 0; var parser = new JSONParser(); try(var reader = new FileReader(jsonOutput)) { @@ -173,7 +199,7 @@ protected boolean execute(final CmdOptions options) throws IOException if (all_programs.size() != 1 && requestedProgram == null) { - sdk.getConsole().println("pioasm files must have exactly one program when being loaded without -p specified. Please specify -p"); + sdk.getConsole().println("pioasm files must have exactly one program when being loaded without -P specified. Please specify -P"); return false; } int progindex = 0; @@ -203,18 +229,18 @@ protected boolean execute(final CmdOptions options) throws IOException // TODO: use ProgramParser supported directives? hex = "#.program " + program_name + "\n\n" + hex; - if (!new Load(sdk.getConsole(), sdk).loadHexDump(0, new LineNumberReader(new StringReader(hex)), program_name, null)) - return false; + var lineReader = new LineNumberReader(new StringReader(hex)); + var assignedAddress = new Load(sdk.getConsole(), sdk).loadHexDump(pioNum, lineReader, program_name, null); // code loaded, now // Use the JSON output to get set/side set options var wrap = new Wrap(sdk.getConsole(), sdk); - wrap.setWrap(0, 0, sdk, (int)(long)(Long)program.get("wrap")); - wrap.setWrapTarget(0, 0, sdk, (int)(long)(Long)program.get("wrapTarget")); + wrap.setWrap(pioNum, smNum, sdk, assignedAddress + (int)(long)(Long)program.get("wrap")); + wrap.setWrapTarget(pioNum, smNum, sdk, assignedAddress + (int)(long)(Long)program.get("wrapTarget")); var sideset_obj = (JSONObject)program.get("sideset"); var sideset = new SideSet(sdk.getConsole(), sdk); - sideset.setSideSetCount(0, 0, sdk, (int)(long)(Long)sideset_obj.get("size")); - sideset.setSideSetOpt(0, 0, sdk, (Boolean)sideset_obj.get("optional")); - sideset.setSideSetPinDirs(0, 0, sdk, (Boolean)sideset_obj.get("pindirs")); + sideset.setSideSetCount(pioNum, smNum, sdk, (int)(long)(Long)sideset_obj.get("size")); + sideset.setSideSetOpt(pioNum, smNum, sdk, (Boolean)sideset_obj.get("optional")); + sideset.setSideSetPinDirs(pioNum, smNum, sdk, (Boolean)sideset_obj.get("pindirs")); return true; } catch (ParseException e) { diff --git a/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Load.java b/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Load.java index 60a42a4..3c1d597 100644 --- a/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Load.java +++ b/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Load.java @@ -146,7 +146,7 @@ protected void checkValidity(final CmdOptions options) } } - boolean loadHexDump(final int pioNum, + int loadHexDump(final int pioNum, final BufferedReader reader, final String hexDumpId, final Integer address) @@ -159,7 +159,7 @@ boolean loadHexDump(final int pioNum, pioSdk.addProgram(hexDumpId, reader); console.printf("(pio%d:sm*) loaded program %s at address 0x%02x%n", pioNum, hexDumpId, assignedAddress); - return true; + return assignedAddress; } /** @@ -185,11 +185,13 @@ protected boolean execute(final CmdOptions options) throws IOException String.format("/examples/%s.hex", optExampleValue); final LineNumberReader reader = IOUtils.getReaderForResourcePath(resourcePath); - return loadHexDump(pioNum, reader, optExampleValue, optAddressValue); + loadHexDump(pioNum, reader, optExampleValue, optAddressValue); + return true; } else if (optFileValue != null) { final LineNumberReader reader = IOUtils.getReaderForResourcePath(optFileValue); - return loadHexDump(pioNum, reader, optFileValue, optAddressValue); + loadHexDump(pioNum, reader, optFileValue, optAddressValue); + return true; } return false; } From 42886a939479a4af31711074be25dea20bb4ff37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 19 Jul 2026 16:19:44 +0200 Subject: [PATCH 4/5] Adjust initial PC address when using assemble --- .../org/soundpaint/rp2040pio/monitor/commands/Assemble.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java b/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java index dc333a0..369aa49 100644 --- a/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java +++ b/app/src/main/java/org/soundpaint/rp2040pio/monitor/commands/Assemble.java @@ -242,6 +242,10 @@ protected boolean execute(final CmdOptions options) throws IOException sideset.setSideSetOpt(pioNum, smNum, sdk, (Boolean)sideset_obj.get("optional")); sideset.setSideSetPinDirs(pioNum, smNum, sdk, (Boolean)sideset_obj.get("pindirs")); + // set initial PC address + final int address = PIOEmuRegisters.getSMAddress(pioNum, smNum, PIOEmuRegisters.Regs.SM0_PC); + sdk.writeAddress(address, assignedAddress); + return true; } catch (ParseException e) { e.printStackTrace(); From fdc89e414e36e3ab2cbaaa49889738c84ad71bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 19 Jul 2026 21:16:08 +0200 Subject: [PATCH 5/5] Relocate JMP target addresses when loading program --- .../org/soundpaint/rp2040pio/Instruction.java | 5 +++++ .../org/soundpaint/rp2040pio/sdk/PIOSDK.java | 22 +++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/soundpaint/rp2040pio/Instruction.java b/app/src/main/java/org/soundpaint/rp2040pio/Instruction.java index 95ea82c..33e64e4 100644 --- a/app/src/main/java/org/soundpaint/rp2040pio/Instruction.java +++ b/app/src/main/java/org/soundpaint/rp2040pio/Instruction.java @@ -277,6 +277,11 @@ public void setAddress(final int address) this.address = address; } + public void moveTarget(final int offset) + { + this.address = (this.address + offset) % 32; + } + public int encode(final int pinCtrlSidesetCount, final boolean execCtrlSideEn) { diff --git a/app/src/main/java/org/soundpaint/rp2040pio/sdk/PIOSDK.java b/app/src/main/java/org/soundpaint/rp2040pio/sdk/PIOSDK.java index 923776a..e98d90e 100644 --- a/app/src/main/java/org/soundpaint/rp2040pio/sdk/PIOSDK.java +++ b/app/src/main/java/org/soundpaint/rp2040pio/sdk/PIOSDK.java @@ -530,11 +530,25 @@ private void writeProgram(final Program program, final int addressOffset) final int length = program.getLength(); synchronized(memory) { for (int index = 0; index < length; index++) { - final short instruction = program.getInstruction(index); + short instruction = program.getInstruction(index); final int memoryAddress = (addressOffset + index) & 0x1f; - // TODO: FIXME: Code relocation: When (addressOffset != 0), - // JMP commands need their absolute target address to be - // adjusted according to the offset. + + // Code relocation: When (addressOffset != 0), JMP commands need + // their absolute target address to be adjusted according to the offset. + Instruction insn; + try { + // Hardcode delay/side-set configuration, since for this purpose + // it only matters to use the same for decoding and encoding. + insn = + decoder.decode(instruction, 0, false); + } catch (final Decoder.DecodeException e) { + insn = null; + } + if (insn instanceof Instruction.Jmp) { + ((Instruction.Jmp)insn).moveTarget(addressOffset); + instruction = (short)((Instruction.Jmp)insn).encode(0, false); + } + memory.writeAddress(PIORegisters. getMemoryAddress(pioNum, memoryAddress), instruction);