INSPIN - P2 hosted P2/P1 Spin/Pasm development environment

Timeline
Login

Timeline

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

50 most recent check-ins

2026-08-24
16:41
RESTART.md: the wedge corrupts FAT chains (1630 evidence); blobs rewritten and consumer-verified, rung green on the new image leaf check-in: b7eeab3fba user: ben tags: trunk
16:05
RESTART.md: agree with fossil after the 2026-08-24 reclone + FAT32-everywhere session (inspin-working checkout, provision.py, 1610/1620/1630, MIT pass) check-in: 475c0e6750 user: ben tags: trunk
16:04
ustor: trim the FAT32 name pad in entry_name - book finds its chapters (1620) The FAT32 layer space-pads a short extension when it rebuilds a name from the on-disk 8.3 field: 00.md came back "00.MD " and is_md_name refused every chapter - book ASM said 'no chapters under /BOOKS/ASM' while uls listed all 21 (its two-space column hid the third). Lookups never noticed: the search path pads both sides. One in-place trim_name in ustor covers entry_name and the list() emitter; 40 bytes, hub free 8308 over the 8192 reserve. Flashed and bench-proven: the 13-step book/ref transcript is green on the FAT32 thumb - shelf, TOC, link-follow (the 700 ms two-digit wait), b/n/q, book SPIN2, ref drvnot. A followed link's header shows the link's own case (01.md), a dir-step shows the FAT name (01.MD) - cosmetic, noted here. check-in: 111a4b0d6d user: ben tags: trunk
12:45
a little more house dressing check-in: 4c723119f6 user: ben tags: trunk
12:41
Drop the SPDX-License-Identifier lines; the copyright + LICENSE line stays Owner's call: the machine-readable tag labels what the adjacent line already says. Every stamped file keeps its one-line copyright pointing at LICENSE; inspin_all regenerated from the cleaned parts; vga image byte-identical at 213,768. check-in: 87daadf0d9 user: ben tags: trunk
12:34
MIT license across the inspin-authored sources, with acknowledgements LICENSE (MIT, Shannon Mackey) at the root, scoped to the inspin-authored tree; SPDX + copyright headers stamped on the 43 hand-written sources (src/*.spin2, tools/*.py, the three blob top objects, build.sh, measure.sh). Generated files (inspin_all, asmtab, asmtab_full, asmnames, pincons) keep their GENERATED provenance lines and are covered by the root LICENSE; vendor/ and the linked usbhost tree are untouched and remain under their own authors' terms. README gains the License note and a generous Acknowledgements section: Chip Gracey and Parallax, Stephen M Moraco (PNut-TS, PNut-Term-TS, P2 Knowledge Base), Roger Loh (PSRAM), pik33 (retromachine), Jon McPhalen (jm_i2c/jm_ds3231), Marco Maccaferri (Spin Tools spinc), and the P2 community and OBEX contributors. Verified: vga image rebuilds byte-identical at 213,768 (comments emit nothing), all four tool blobs rebuild at their deployed sizes, every stamped .py still parses. check-in: d71eacb343 user: ben tags: trunk
12:31
FAT32-8.3 everywhere (1620): books serialized to two-digit chapters, /EX/P1 for the P1 sources, one provisioning tool books/asm and books/spin2 chapters are 00..16 + 90/91/92/95 .md and 00..04.md (fossil mv), every intra-book link rewritten to the short names - the ASM TOC's 19 links sit exactly at the pager's 19-link cap. genappx.py emits the new appendix names; goldp1.py pulls EX/P1/. Found on the bench, the predicted bug class: the driver folds .spin and .spin2 to the same 8.3 name, so the flat /EX deploy had silently replaced blink.spin2/hello.spin2 with their P1 namesakes - /EX/P1 is the layout fix. tools/provision.py pushes the complete resource set (EX, EX/P1, BOOKS/ASM, BOOKS/SPIN2, REF, TOOLS by size) over one serial session, refuses any manifest with an 8.3 collision, size-verifies via uls, resumes by verification, and DTR-resets through the drive's machine-silent write wedge (evidence appended to 1620; deploy running on the bench thumb). check-in: b7e97820fd user: ben tags: trunk
06:10
RESTART.md: agree with fossil after the 2026-08-24 session (1540 closed, 0530 fixed upstream, the thumb reprovisioned as P2THUMB, MAXL 588, hub free 8348) check-in: cd7edc84fe user: ben tags: trunk
06:08
USB driver bump: the 0530 field fixes, committed upstream and pinned here The linked P2_USB_HID_CDC_ACM_MSC tree at its 942d33ed4f + csw-poll-once state (their ticket 1230, Fixed): hub-port enumeration backoff (1 s -> 8 s cap, one enum attempt per scan - the 17 s/command defect), the VBUS rescue of a wedged silent root inside rescan() (what only a reboot used to fix), NAK-is-alive liveness (an MSC stick mid-GC was being declared detached), XFER_STALL_MS 20 s zero-progress ceiling on bulk retries, and csw_patient gone poll-once (its 150-turn CSW loop ran each turn through the class's 10000-try NAK budget: ~25 minutes of held cog - THE machine-silent-for- minutes hang, reproduced live deploying to the bench thumb and gone after). Validated on their bench (this same board): top_rootplug P1-P5 PASS - P5 rescues the actual field-drop thumb in 9.9 s by poll() alone; new top_0530drive PASS. This image (flashed) ran the whole inspin regression through the same driver. check-in: 5a93840264 user: ben tags: trunk
06:08
1540 MEDIUM+LOW pass: every remaining quiet cut is loud - 26 findings closed, and MAXL 588 pays the blob's bill The 19 MEDIUM and 9 LOW from the audit technote (1b2d35be24), the whole remaining worklist. Refusals and notices, by home: inspin.spin2: book NAME >17 refuses; chapter names >23 skipped loudly in bk_pick and bk_links/bk_stk widened to 24-byte entries (no more silent 23->20 double-clip); exp chat halves >47 refuse instead of sending a cut command to a live device; the REPL rings the bell at HISTLEN instead of eating keystrokes; run_con_line marks a filled capture in-band (' [cut]'); ref NAME >24 refuses. cmd_tool's two MEDIUMs need no change: the HIGH pass's n>27 refusal already catches every clipped name, and take_arg's 63-char ceiling makes the tool_arg clip unreachable. console.spin2: capture_cut() + bell() (serial-only BEL: the video consoles would draw the byte). tools_target.spin2: 9th breakpoint refusal names the 8-slot cap; tgt_reply and mon_ask set r_cut when a reply hits its buffer and the caller says 'reply cut' instead of parsing the partial with confidence; mon_ask's leftover hard 40-byte quit now honors MONLEN; p1v prints '(48-slot table full)' when the blob's MAXVT vtab fills. editor.spin2 + host: ins_ch/split_line/join_next refuse at their limits with a one-shot note the host shows in the status bar (take_note), bell alongside; join_next REFUSES rather than dropping the second line's tail. ustor.spin2: path[66] so make_path carries a full 63-char argument (the clip is gone, not just loud); enter_dir refuses a >64-char dir part via w_cd = -1 rather than chdir'ing to a truncated one. tools_shared.spin2: CC_FUNS 48 -> 64 = compile.spin2's MAX_FUN, so methods 49-64 keep their debug names; path_join says 'path cut' when an input outgrows it; pasm_run prints '(line map full)' at PA_MAPN; cc/pa_errmsg 56 bytes to match the widened compiler errmsg. lex.spin2: scan_id latches err -6 on a 32+-char identifier (silent aliasing of a shared 31-char head); compile.spin2's scan_unit bridges ANY lex error no consumer caught (-4/-5/-6 with real messages). compile.spin2: errmsg 40 -> 56 (the 39-char clip cut the actionable half of real messages); OBJ alias >23 refuses; unit_for's arbitrary 24-char pre-clip is a >33 refusal (nm has the room, and a clipped name made a misleading 'not found'). p1c.spin2: STRING()/DAT-byte literals check the -5 clip like Spin2's consumers; a child exposing >12 PUBs or CONs refuses (m_iface) instead of silently hiding the 13th from every parent. asm.spin2: copy_word carries 12 chars - IF_NC_AND_NZ, the longest real condition, could never match at 11. pasm2_tool.spin2: an instruction line >127 chars refuses instead of assembling without its tail. tools_audio.spin2: notetab read gets hold_note's 0..127 guard. LOW not changed, on purpose: /INSPIN restore's 31-char clip (closed round-trip, names are ours) and p1c badname's 19-char error-text clip (cosmetic). editor MAXL 580 -> 588: the blob's new refusals cost ~304 bytes and the spin2c interpreter stack after dbase fell to 2,116 bytes - the compiler's recursion walked off the arena end and deeper sources failed with phantom errors (loops.spin2 refused 'OBJ file not found' at a line with no OBJ while bytes/trig compiled). Eight more lines put the stack past 2.6 KB. vga hub free 8,348 over the 8,192 reserve. Regressed on the flashed image against the freshly reprovisioned drive: Spin2 rungs 1-13 INCLUDING 8 all err=0 (8: target HALTED line 38, 10,869 steps), 13/13 P1 gold byte-identical, 8/8 PASM rungs byte-identical. Blobs spin2c 47,528 / pasm2 19,028 / p1c 30,832 / tgtvm 40,656 deployed. check-in: 6778f8fe63 user: ben tags: trunk
00:02
1520 + 1530: the interpreted-VM budgets and the fixed reply/line buffers, raised and bench-proven - and the honest end of the hub 1520 (LIMITS-C): VM_VARLONGS 1024 -> 2048 - an interpreted program owns 8 KB of VAR now (bench: long big[1500], 6 KB, filled and summed to 1124250 err=0 where 4 KB refused it); NFRAME 16 -> 20 (bench: 18-deep recursion returns 18 err=0; the -3 refusal still guards the top). NLOC stays 32 - the bytecode encoding, not a preference. SV_BUF already matches FSIZE at 16 KB since 1500; CC_BCMAX deliberately unchanged (no program has approached 8 KB of object; the 1540 refusal now guards the seam instead of a silent clip). 1530 (LIMITS-D): the target/loader reply buffers get CONs and double - tbuf/treply/tout/plugbuf 64 -> RLEN 128 (a td regs dump pushed 64), monbuf 48 -> MONLEN 96; the REPL line/history go 96 -> HISTLEN 160 so a full drive path fits an argument, and the VM-command refusals track the new bound. arg was already 64 with its loud refusal (3af65537f5). THE BUDGET, shaken out (the owner's ask): vga hub free is 11,444 over the 8,192 reserve after this - about 3.2 K of true slack remains, and it stays as margin. The wish-list items that DON'T fit and why: FIFOSZ 16 K (+8 K), CC_BCMAX 16 K (+8 K), spin2c arena growth for MAX_SYM/ BCMAX/DATMAX (~2 K arena per 64 symbols, and the compiler's own stack shares the arena tail) - each now has a loud refusal at its cap, so the day a real program hits one, the message names it and the trade can be made deliberately then. Regression: rung 12 err=0, blink golds, hello runs; LIMITS.md rows updated. check-in: 9814d0ea07 user: ben tags: trunk
2026-08-23
23:31
RESTART.md: agree with fossil after 1170/1310-begins/1510-free-half/1540-HIGH-pass (evening close) check-in: 5e0270dbe4 user: ben tags: trunk
23:29
1540: the eleven HIGH silent-cut findings, fixed - four audit agents swept the tree, every quiet clip is a named refusal now The sweep (four Sonnet agents over the inspin-owned sources, consolidated report in the session scratchpad: 39 findings - 11 HIGH, 19 MEDIUM, 9 LOW) and the HIGH fixes, each bench-checked: - cmd_tool overflowed ed_ask: TOOLS/ + a 30-char name + .spin2 = 43 bytes into 40, stomping ed_depth - a real out-of-bounds write, not a clip. A name over 27 refuses by name (bench: 28-char name -> the message). - A VM-issued console() command over 95 chars was CLIPPED AND RUN - the cut could execute a different valid command. run_con_line and run_con_raw refuse with an error the program sees. - ed_want copied 30 bytes of a legal 31-char filename - ^D/Alt-Tab reopen then missed the file. All four sites copy 31. - The editor silently dropped every line past MAXL on open, and a save then CLOBBERED the real file to the truncated copy - the file's own header comment describes this exact loss happening once before at the old cap. ed.load returns a verdict now and every open refuses loudly. - dat_resolve matched inline-PASM/DAT labels on their first 11 chars with k==11 an unconditional hit - stage_buffer_a and stage_buffer_b silently resolved to the SAME register. Names compare to their terminators (name_eq); an inline label past its 15-char slot refuses at declaration. - The systemic NAMELEN hole: names stored cut to 23 chars but looked up whole, so a 24-31 char identifier was DECLARED then never resolved - the exact bug the file's comment records fixing once at 16. Every store path (symbols, methods, struct types/members, bitfields) now refuses through one name_fits gate (bench: temperature_threshold_high -> 'name longer than 23 chars' at its line). - p1c's wr_byte dropped bytes past the buffer while compile() reported a clean length - a truncated P1 image would LOAD. It fails loudly. p1c names past 19 chars refuse instead of prefix-aliasing; OBJ filenames past 15 refuse; CT_NAME 12 -> 20, so a child method with 12+ chars in its name (read_status) is callable at all - store and lookup finally agree. 3/3 P1 gold spot-checks incl. pasm and uart (an OBJ child). - spin2c_tool clamped an over-BCMAX object into the shim buffer and reported success - dead code today, a corrupt-program loader the day BCMAX outgrows CC_BCMAX. It refuses with the message naming the fix. The spin2c blob crossed its arena line by 140 bytes carrying the new refusals: MAXL 576 -> 580 (+512 image hub, the sanctioned LIMITS budget) gives the arena 74,240 and the blob 372 bytes of slack - and the editor four more lines. Hub free 17,924 flashed; rung 12 err=0 on the new pair. MEDIUM/LOW findings stay listed on the ticket for future passes. check-in: 27290f60e6 user: ben tags: trunk
23:14
1170: PASM completion and the column Tab - the editor treats PASM as a first-class dialect end to end Inside PASM context - a DAT section, an inline ORG..END block, or any .pasm file (the editor's in_pasm walk mirrors the painter's ps_pasm rule; a .pasm name is PASM from line 0) - Tab now does two new things: - A started word completes from the ASSEMBLER'S OWN tables: the 341 mnemonics of src/asmnames.spin2 (generated from the same table the pasm2 blob is built from - one source, the 1100 doctrine) plus the condition prefixes, matched case-insensitively and completed in the house lower case. Spin2 context keeps the existing bins/kws engine untouched - the routing is the only change there. - Tab with nothing to complete advances to the PASM column stops - opcode 8, operand 16, comment 32 - instead of the two-space indent. Enter already carries the opcode column via the existing auto-indent. BENCH, keys sent raw over the wire: in a new t.pasm, the sequence Tab drvno Tab Tab #56 produced a line that saved and assembled to FD64705F - blink's own drvnot #56 long, one keystroke run proving the column stop, the completion, and the name-context in one; and in a .spin2 file, Tab at the end of a pasted 'drvno' under DAT/org completed through the SECTION WALK and the file compiled (pnut obj 28 bytes). GUIDE's Tab section documents the dialect. Hub free 18,976 flashed. fmt (re-columning a whole DAT block) was the ticket's named stretch goal, not the requirement, and is not included. check-in: 74635ba067 user: ben tags: trunk
22:51
trig.spin2: the multiple-results comment aged out - 0110 made them general (any method), not CORDIC-only; the Spin2 book's ch3 agent tripped over the stale claim and traced the truth through the ticket check-in: b4cc6c7324 user: ben tags: trunk
22:50
1310 begins + 1510's free half: the Spin2 book's first four chapters (bench-verified labs), and every cap that could raise without a hub trade, raised THE SPIN2 BOOK (books/spin2/, per the proven 1200 delivery model - two Sonnet agents drafted, this session verified every claim and RAN every lab): 00-TOC (links 1-4, marks 5-15 coming), 01-why-spin2 (bytecode, the P2-has-no-ROM-interpreter truth the agent caught in MY OWN brief and corrected; the inspin-extras asterisk on byte-identical - print is hub selector $EA and the chapter shows it in a REAL captured dis transcript: compile hello.spin2 -> 40 bytes, dis 8 decoded), 02-values (CON/VAR/DAT; labs benched: ops.spin2 -> 39, P_OE|P_NCO_FREQ -> 76, the byte/word alignment gap -> 2), 03-methods (multiple returns - the agent traced ticket 0110 past a stale in-repo comment; the frame model with vm.spin2's real constants; divmod(47,5) benched -> 9 2), 04-objects (per-instance VAR witnessed by rung 5 - re-run, all ok err=0; the honest gaps read out of parse_obj_call/parse_obj_one: no child constants, no object arrays, no method pointers, uncatchable abort; the five-parts story from 0790 as the real-world cost lesson). Fixed on review: the features-list overclaim (cmd_lang is hand-maintained, not generated), the ops.spin2 claims, NAMELEN 24-includes-terminator. 1510's FREE HALF (blob VAR is arena, no image cost): p1c caps roughly doubled - MAXSYM 192, MAXMET 64, MAXSTR 1024, MAXVT 48, MAXINST 16, MAXCTAB 10 (its blob has ~35 KB arena headroom) - and 13/13 P1 programs re-golded byte-identical to spinc after the raise. lex depth MAX_IND/ MAX_PEND 24; tokstr 128 with the CLIP made a REFUSAL: a >127-char string literal now fails by name at its line (all three TK_STR paths - expression, string(), DAT data), where the old 80-byte buffer silently shipped the head. Bench: a 100-char literal prints whole (the old cap cut at 79); a 140-char one refuses 'string literal longer than 127 chars' at line 2 - delivered via upull because the editor's own 128-col line cap cannot even type the attack. CALLDEPTH tried at 24 and PUT BACK: spin2c's arena margin is ~330 bytes TOTAL (dbo 71,604 + 2,048 stack of 73,728) - the ticket's big spin2c raises (MAX_SYM/BCMAX/ DATMAX/LMAP) are arena-bound and the arena is image hub, so that half waits on the owner's hub call (numbers on the ticket). Rungs 12/13 err=0 on the final blob. check-in: b8a455bbbc user: ben tags: trunk
22:11
RESTART.md: agree with fossil after the >=1100 closeout sweep (1111/1600/1165/1200/1220-1270 Fixed; the encoder, arg and write_file finds; what stays open) check-in: 7d2ca7b9a4 user: ben tags: trunk
22:10
Two silent-cut bugs the book deploy walked into (the 1540 class, live specimens) (1) The shared REPL argument buffer was byte arg[32], and take_arg cut the tail at 31 chars WITHOUT SAYING SO - so 'recv BOOKS/ASM/<17-char name> 5896' lost the last digit of its length and streamed 589 bytes, one decimal order per extra path char (589, then 58 - the digit-eating made the diagnosis: 16-char leaf wrote 5896, 17-char wrote 589, 18-char wrote 58). arg is 64 bytes now and a longer tail REFUSES by name ('argument longer than 63 chars') instead of running on part of itself. cmd_ed's ed_want copy tracked the size. (2) ustor write_file dropped files in the ROOT when the directory chain did not exist: enter_dir mkdir'd ONE level, so BOOKS/ASM (BOOKS absent) failed mkdir AND chdir and the write proceeded anyway - 21 book chapters landed in / silently, 8.3-mangled by the FAT32 layer on top. enter_dir now walks the chain segment by segment (mkdir + chdir each level, FAT32's cwd model) and write_file REFUSES when the chdir failed, with the fs error in w_er, rather than corrupting the root. Bench: recv BOOKS/ASM/00-TOC.md on a drive with neither level -> uls BOOKS shows ASM, uls BOOKS/ASM shows 00-TOC.MD 2239 - both created, the file where it was addressed. (This thumb's FAT32 layer is 8.3-only, so the book itself still lives on the exFAT P2SSD whose 18-char leaves fit its chapter names; redeploy the changed chapters there when it is back on the bench.) Both found deploying the finished book to the bench drive - the doc work forcing the machine correct, again. Flashed; hub free 19,936. check-in: 3af65537f5 user: ben tags: trunk
21:42
1270: the advanced arc's last open edges - ch14's lab is a real transcript, ch16's CORDIC fragments assemble, jmp #$ encodes books/asm/14-lut.md: the REVIEWER placeholder is a verbatim bench transcript - squares.pasm (mul/wrlut loop, rdlut #4) tpasm'd to the target: tpar 0 -> V 16; the file edited to #9 and re-shipped onto a FRESH cog (C 2 then C 3, the chapter's own caveat visible in the numbers): tpar 0 -> V 81. On-device assembly byte-identical to pnut first (F6041010 F6001208 FA001208 FC301208 FB6C11FC FAA41204 FC641300 FD9FFFFC) - mul, wrlut, rdlut, djnz to a .local, and jmp #$. That last one was a find: the chapter's program ends 'jmp #$' and the encoder silently read bare $ as 0 - a jump to address zero instead of here. branch_enc now takes $ (no hex digit after) as the current address in either domain; golded in the squares image above. books/asm/16-cordic.md: both fenced fragments carried '(runs once the on-device assembler lands)' - it landed; notes gone. And the first fragment did NOT assemble under pnut: qrotate #1000 is out of # range (m130) - now ##1000 with the why in the comment, pnut-clean, and the encodings proven on-device (asm qrotate ##1000, 6 -> FF800001 FD4BD006; qmul/getqx/getqy each encode). The drafting pass had only pnut-verified the MIDDLE chapters' fences; ch16's slipped through. check-in: 793162bf46 user: ben tags: trunk
21:35
ch8's transcript is real, td r/w work for the first time, and the reader follows 19 links The 1250 audit agent designed the ch8 bench session around reqresp.pasm (a wait loop a hypothesis can be wrong about) and the FIRST run of its script found td r answering 'r-1 = 83620095': arg_second() skipped a leading DIGIT run, so 'td r 7' - letter first - read no register at all. td r N and td w N V have been broken since they landed; the fix skips the first token whatever it is (tpar's number-first shape still parses; all four callers are token-then-number). tools_target.spin2 carries the why. With the fix flashed, the whole 22-command session ran exactly to the agent's predicted shapes and is pasted VERBATIM into 08-debug.md where the REVIEWER placeholder stood: pc bouncing 0/1 while cmd is 0 (the hypothesis), tpar 1 5 the probe, the fall-through to pc 2 the conclusion, r7 = 5 and r8 = 105 read mid-flight, tpar 0 -> 105 the independent confirmation, td c / td q closing clean. A second caveat the session proved is now in the chapter and on ticket 1160's notes: single-stepping a setq+block pair loses Q across the debug ISR (the transfer moves ONE long under the stepper; free-run keeps blocks whole) - step over block copies with a break address, not into them. The reader (inspin.spin2): links number to [19] now - two digits render from [10], page_key collects '1'+digit within 700 ms (a lone 1 still follows link 1), show_md takes the resolved number - because the ASM TOC has 19 links and digits stopped at 9, leaving chapters 10+ and every appendix unreachable from the book's own front page (1260 audit find). Bench: a 12-link page renders [10]..[12] and typing 12 follows link 12 (the read failure names ch12.md, not ch01.md). Hub free 19.9K flashed. check-in: d3f10fa01b user: ben tags: trunk
21:34
The SETQ block-copy investigation: a real encoder bug, the silicon's index rule, and rung 8 Chasing the book audit's setqcopy bench check pulled a thread worth the afternoon. Three findings, each bench-proven on the target: (1) ENCODER BUG (asm.spin2): PTRA/PTRB parsed as pointer-expression S-codes ($100/$180, I set) in EVERY operand slot - but those codes are only real in the hub RD/WR S slot. 'mov p, ptra' assembled as mov p, #$100 (gold: pnut F60019F8, we said F6041900), so a register- walked block copy marched hub $100 instead of the mailbox. parse_opnd now takes a pok gate - ptr forms only where the shape says operand_dsp/ operand_lsp - and sym_reg grew the WHOLE $1F0..$1FF special map from the silicon doc (IJMP3..INB; the old table had IJMP1 at $1F1, which is IRET3's slot). Golded byte-identical to pnut after the fix; all 8 pasm rungs re-golded on-device, rungs 12/13 err=0 on the rebuilt spin2c. (2) SILICON RULE, confirmed at the bench: in a SETQ block transfer a PTRx INDEX scales by the WHOLE BLOCK, not by one long - setq #4-1 with ptra[4] reads 64 bytes in, not slot 4. The book's ch5 s10 taught the indexed form and its own bench check read back zeros; s10 is rewritten around the register-walked copy and teaches the scaling rule by name (plus the zeros-story as the debugging moral). Also proven: plain-ptra block reads AND writes both work; the failures were all address math. (3) examples/pasm/blkcopy.pasm is rung 8: the correct block copy, gold F60019F8 FD640628 FB00100C F1041820 FD640628 FC60100C FC6CC70F FD9FFFFC, run on the target - slots 0-3 (5/6/7/8) land in 8-11, the ptra[15] marker proves the run. README rows for rungs 7 and 8. check-in: 0f71f616e3 user: ben tags: trunk
20:57
Book audits (1220-1260, 1250): two agent passes over ch1-9 + appendices as the reader's stand-in, every claim re-verified against today's machine Chapters 1-2 and 9 audited clean. Fixed, each defect verified against the source or pnut before touching the text: ch3 taught the GONE tool-pasm workflow and a DAT header the file assembler does not want, and misquoted both output formats (asm prints its longs on one line; pasm prints a 'assembled N longs, M labels:' header then one per line - read from cmd_asm/cmd_pasm); ch4 claimed RET encodes (it is not in the table - the text now says so and shows the jmp workaround); ch5 said count.pasm ticks twice a second (waitx ##2_000_000 at the target's 200 MHz is ~100 Hz) and still said tool pasm; ch6 had blink at 15 Hz from a 300 MHz clock the target does not run (10 Hz at 200 MHz), a QFRAC Y computed for the wrong clock (would make ~667 Hz, not the claimed 1 kHz), and now flags AKPIN as real-silicon-not-yet-encoded; ch7 undercounted ser.spin2's TX producers (three share txlk - the pump's own XON/XOFF is the third) and quoted gfxcon's VAR block in a shape the source does not have; ch8 mischaracterized GETBRK's two readings (both are wcz - context distinguishes them, per p2kb) and now warns the transcript will carry td_line's 'line N:' echo; appendix B/C's Back link skipped appendix A. Every fenced PASM program in the audited chapters assembles under pnut-ts; appendix A regenerates byte-identical from tools/genappx.py. Reports in the session scratchpad (book_audit_A/B.md) carry the evidence trail and the bench scripts; the ch8 REVIEWER placeholder now names the reqresp.pasm session as the preferred transcript - captured next. check-in: 1b3e0532da user: ben tags: trunk
20:47
1600: all 116 P_* smart-pin constants on-device, operand | and + expressions, and cogstop N at the prompt src/pincons.spin2 is GENERATED from pnut-ts's own automatic_symbols table by tools/genpcons.py - the asmtab discipline again: a transcription of the gold compiler, not a re-derivation (the 17 hand-kept add_con values all matched it and are deleted; one source now). Both compilers consult it on symbol MISS, so the names cost no MAX_SYM slots until a program uses one: compile.spin2's findsym falls back (intern goes through the raw findsym0 - the fallback interns, routing intern through it would recurse; the OBJ is pcs because a local already claimed pc, the old reserved-name trap), and the pasm2 blob's resolve falls back after real labels. Case-insensitive, as pnut is. The assembler grew value EXPRESSIONS in operands - terms joined by | and +, spaces allowed - because P_* constants are designed to be OR'd; parse_opnd now leaves the operand end in oend (adv_opnd could not carry a spaced expression) and sym_reg's copy widened to 19 chars (P_COUNTER_PERIODS is 17; copy_word's 12 silently cut it). square.pasm now reads wrpin ##P_NCO_FREQ | P_OE, #56 - same 10 gold longs, teachable names. cogstop N (inspin.spin2): frees an orphaned worker cog from the prompt - only the two worker slots; anything else refuses BY NAME (cog_role), an idle cog answers idle. h and GUIDE say so. Loud refusals on the way (the 1540 rule): a pasm label longer than the table's 11 chars refuses instead of truncating; a symbol in byte/word/long data refuses instead of dropping silently (put_data skips the directive word itself now). Bench, flashed image (hub free 20,484) + both blobs recv'd: pcons.spin2 - P_ASYNC_TX | P_OE = 124, lowercase p_nco_freq = 12, ok err=0. orphan.spin2 leaves cog 6 counting; cogstop 0 -> 'refused - cog 0 is: spin2 interpreter...', cogstop 6 -> stopped (cogs confirms), again -> already idle. All 7 pasm rungs byte-identical to pnut on the new blobs (square via the named expression); rungs 12 and 13 err=0 on the new spin2c. check-in: 681a4c7959 user: ben tags: trunk
20:24
1111 ORGH/FIT: the assembler completes pnut's PASM-only addressing model The pasm2 blob now carries pnut's whole flat-image model, verified against spinResolver and golded byte for byte: the object offset and the cog counter are separate (org resets the cog counter WITHOUT moving the output), orgh switches to hub mode where the address is the offset (an address operand zero-fills up to itself, and cannot decrease), res reserves cog space with no object bytes (pnut-exact now - it used to ship zeros) and is refused after orgh, fit is a real over-budget error per mode, align gaps ship as zeros (they were stale buffer), and every cog-mode label must be long-aligned or the assembly refuses with the line number - the same rule pnut enforces. Labels in hub mode are byte addresses. The encoder (asm.spin2, shared with the spin2c blob's DAT/inline paths): set_hub() puts branch encoding in hub terms - 20-bit branches follow pnut's domain rule, relative only inside one domain (cog < $400, hub >= $400), absolute otherwise, with #\ to force absolute; 9-bit branches get the byte-relative form, the alignment check, the crossing refusal and the -256..255 range check; NOP encodes (it was an unencodable sentinel) and refuses conditions/effects the way pnut does. Gold: tools/goldasm2.py now golds against pnut PASM-ONLY mode - the flat bin IS the expected image, no counting heuristics - and its output is byte-identical to the old DAT-wrap harness on all six prior rungs. New rung examples/pasm/hubdat.pasm (7): orgh fill, hub labels, ##hub-address, alignl, nop, the absolute hub jmp - 13/13 longs match pnut ON THE BENCH, as do the six old rungs re-run (count/ptrs/reqresp now print pnut's exact long count with res excluded). Four refusals proven on-device with pnut's own verdicts: fit over budget, res in orgh, unaligned cog label, orgh decrease. Rungs 12 and 13 (DAT + inline PASM through the same encoder in spin2c) re-run green, err=0. Blob-only - no reflash; pasm2.bin and spin2c.bin rebuilt and recv'd. check-in: df7b615cd0 user: ben tags: trunk
19:32
RESTART.md: agree with fossil after 1111 (blob-only augment fix), the assembly book's middle chapters + appendices, and tickets 1600/1160 from the agents' workflow gaps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 766e54c305 user: ben tags: trunk
19:31
1220-1270: the assembly book's middle and appendices - chapters 4-8, 14-15, and appendices A/B/C, drafted concurrently by four Sonnet-class agents per the 1200 delivery model, reviewed and bench-checked here. CHAPTERS (books/asm/, all pure ASCII, <=300 lines, <=11 KB, <=80 cols): - 04-flags.md Flags and flow: C/Z, WC/WZ as explicit choices, conditional execution on any instruction, JMP/CALL/RET/DJNZ/TJZ (1230) - 05-hub.md Talking to hub memory: cog vs hub, RD/WRLONG, the egg-beater, SETQ blocks, PTRx, the write-data-then-flag race (1240) - 06-pins.md Talking to pins: DIRx/DRVx, then the smart pin as coprocessor, ser.spin2 the witness (1240 depth) - 07-driver.md The driver shape: worker + mailbox + offloaded I/O, datpasm.spin2 the runnable case, ser/gfxcon cited (1250) - 08-debug.md Debugging for real: the debug interrupt, the target td arm/s/q/regs/c walk, hypothesis-probe-conclude; carries a REVIEWER-paste block for a real session (1250) - 14-lut.md LUT RAM and cog-to-cog sharing: RDLUT/WRLUT, SETLUTS pairing quoted verbatim from p2kb, sharing marked coming (1270/3) - 15-mailbox.md Mailboxes and the egg-beater: uproxy's rq() quoted verbatim, the one-producer-vs-many rule, when a lock is really needed (1270/4) APPENDICES: - 90/91/92-appx-a.md the instruction quick table, GENERATED by tools/genappx.py from tools/asmtab.tab (the assembler's own 341-row table) + the same P2-KB one-liners /REF reads, so book and cards cannot drift; three parts, each <=300 lines, chained by Back/Next links and the last to Appendix B (1260 appendix A) - 95-appx-bc.md Appendix B (the /REF cards, ref NAME, the one-table rule) + Appendix C (further reading, real works only) (1260 B/C) TOC (00-TOC.md): chapters 4-8, 14, 15 now LINKED (were "coming"); an Appendices section added. VERIFICATION done here, not taken on faith: - format/limits/ASCII/links checked on every file; one broken inline link fixed (04 pointed at 05-hubmem.md, the file is 05-hub.md). - every fenced PASM PROGRAM in the chapters assembles under pnut-ts (11 blocks); the Spin2 blocks are verbatim quotes of uproxy.spin2 (rq()) confirmed against the source. - ch15's runnable lesson promoted to examples/pasm/reqresp.pasm (rung 6): a request/response mailbox using ptra[1] and wrlong #0, ptra[1] - the 1111 {#}D feature. Assembled ON-DEVICE byte-identical to pnut (FB040F01 FB940FFE F6001007 F1041064 FC641100 FC6C0101 FD9FFFE4), golded by tools/goldasm2.py. - the two ch4 TO-GOLD encodings confirmed on the bench: asm if_z mov 8,#5 -> A6041005; asm cmp 8,#7 wcz -> F21C1007. STILL OPEN (agent reports, worth their own tickets/notes): the on-device compiler predefines only a few P_* smart-pin constants (ser.spin2's P_ASYNC_TX is compiled by pnut on the PC, not on-device); no console `cogstop N` to free an orphaned worker cog by number; the debug ISR's exact hub layout in ch8 is inspin's own account (p2kb had no general-P2 entry), flagged as such; ch8's session transcript is a REVIEWER-paste placeholder awaiting a real bench td run. genappx.py needs tools/asmtab.tab (now committed) and regenerates the three parts deterministically. check-in: ad34997c12 user: ben tags: trunk
19:23
1111: wrpin/wxpin/wypin and the {#}D shapes take ## - the reversed-shape augment, and pnut's # range rule enforced. THE FIX (src/asm.spin2): the D/#,S/# shapes (operand_ls/lsp: wrpin/wxpin/wypin, wrlong/wrbyte/wrword/wrlut, rdfast/wrfast/fblock, xinit/xzero/xcont, coginit, setpat, the q* CORDIC issues) carry the L bit at [19] when their FIRST operand is an immediate, and ## there augments through AUGD - pnut emits AUGD then AUGS then the instruction, and so do we now. is_lshape() names those shapes; a # on D outside them (mov #5,x) is refused ('D must be a register here'). And pnut's real rule for a single #: it must be 0..511, it is NOT silently widened - ## is how the writer asks for an augment; a bare # over 511 now says 'must be 0..511 - use ## for a wider value' (the 0480 refuse-don't-truncate rule) instead of quietly emitting an AUGS pnut never would. The sizer bug that rode with it: a line can carry TWO ## (wrpin ##mode, ##pin -> AUGD + AUGS + instr = 3 longs); pasm2_tool.spin2's per-line buffer was out[2] and its pass-1 sizer added a flat 8 bytes for 'has ##' - now out[3] and n_dd() counts each ##; the Spin2 compiler's DAT and inline walkers (compile.spin2) had the identical out[2]/one-extra-long bug, fixed the same way with n_aug(). Also: `res` in the file assembler now ships zeros for the reserved space instead of leaving the buffer's prior bytes (a stale-scratch leak the gold caught). GOLD, byte-identical to pnut and RUN: examples/pasm/square.pasm (a P_NCO_FREQ smart-pin square wave: wrpin ##C / wxpin ##1000 / wypin ##21475, 10 longs incl three AUGDs) and drv.pasm (the mailbox driver, 7 longs) added to the ladder; tools/goldasm2.py golds all five (blink count ptrs square drv). Bench on the flashed 6x16 image + rebuilt blobs: `asm wrpin ##$10000,##$1000` -> FF800080 FF000008 FC0C0000; `asm mov 8,#512` and `asm waitx #5000` refused; the whole pasm ladder byte-exact; datpasm (rung 12) and inline (rung 13) still err=0 through the rebuilt spin2c.bin; square.pasm shipped with tpasm -> the target's cog 3, tpar 0 -> 1. goldasm ring-1 0 mismatched; the --all sentinels (branch/aug rows) unchanged. Blobs rebuilt by ./build.sh tools and recv'd. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 8804bf5df0 user: ben tags: trunk
18:48
RESTART.md: agree with fossil after 1500 (6 x 16 KB, the read_file refusal, the drive-drop evidence on 0530, the reset-per-rung regression recipe). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 5062cd1587 user: ben tags: trunk
18:46
1500 LIMITS-A: the ramdisk is 6 x 16 KB (was 5 x 12 KB) - the owner's pick over 8 x 12 KB at the same hub cost, so no source that compiles here is slot-bound (dsp_pdm-class 14 KB fits; 16 KB = scratch/SV_BUF = PROGMAX, a whole slot stages or sends). fsys.spin2 NFILE 6 / FSIZE 16384; the 12 KB cut bought the hub the RESIDENT compiler's recursion needed (0560) and that compiler is the /TOOLS/spin2c.bin blob now, its stack in the arena. vga hub free 58,268 -> 21,372 (+36,864 B); hdmi 146,548; both compile; 7 x 16 would leave ~5 K, under the 8 K reserve. Flashed and regressed on the bench: ladder rungs 1-7, 9-13 all err=0 with no BAD (sysinfo's slot check now wants 6); 10 of 10 P1 programs byte-identical to spinc (blink talker vars cogs casey pasm strings par hello montest); a 15,250-byte source pulls into a slot and runs (eval steps=2189 = ptr.spin2 padded). Rung 8: the first run reached the target (compile ok, stub sent, target HALTED line 38 after 10,869 steps - tdrive's final 't c') but the transcript was lost to the status-bar flood; two re-runs hit the drive drop at 'probe' - see the new drive-drop ticket. FOUND ON THE WAY and fixed here (the 0480 rule, 1540's audit): upull of a file larger than a slot answered 'pulled 16384' - read to its head, silently cut mid-method. ustor.read_file now REFUSES a file larger than the buffer (E_BIG -3, distinct from the FS drivers' -80..-96), read_head is the deliberate preview (ucat), file_size gives upull its message ('too big: 17000 bytes, slot holds 16384' - bench); blob_run, the stub read, ref cards, book chapters, tool sources and the /INSPIN restore each say 'larger than' instead of 'read fail' or a quiet skip. Docs: MEMORY.md (ramdisk section + 3a reversal note), LIMITS.md rows 1/2 + the raise list, GUIDE (six slots, 576-line editor), GETTING-STARTED (576 lines / 128 cols / 16 KB), examples/README. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 44c54de016 user: ben tags: trunk
16:54
docs/LIMITS.md: link the five LIMITS tickets (1500-1540) created from the assessment, with the suggested order. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: ca7fb1b32c user: ben tags: trunk
16:48
docs/LIMITS.md: a full assessment of every artificial/truncating/capped/full resource - 30 items in a table with class (freed-by-extraction / blob-VAR / hardware / small-buffer / policy), the value, what it truncates, why it was set, and what to raise now vs leave vs make-loud. Basis for the follow-on tickets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 1d873bf4f6 user: ben tags: trunk
16:46
FAT32 provisioning: a fresh thumbdrive works, after two fixes found by provisioning one (2026-08-23, owner swapped the exFAT SSD for a blank 7680 MB FAT32 stick). (1) enter_dir (ustor.spin2) chdir'd into the target directory but NEVER CREATED it - on FAT32 that just fails and the file lands in the ROOT (SPIN2C.BIN etc. at /, not /TOOLS); exFAT never hit it because it takes full paths and auto-creates. Now enter_dir fs_mkdir's the directory part first (harmless if it exists). (2) target_vm.bin is 9 chars before the dot - past FAT32's 8.3 short-name limit - and the driver has no LFN, so it landed as the mangled "TARGET_V.M.B"; renamed the /TOOLS name to tgtvm.bin (<= 8.3, so FAT32 keeps it verbatim). spin2c/pasm2/p1c already fit 8.3, and FAT32 lookup is case-insensitive so /TOOLS/pasm2.bin finds PASM2.BIN. VERIFIED on the fresh FAT32 stick: recv the four tools to /TOOLS (mkdir'd on the fly); asm add pa,#1 -> F107EC01; compile a program -> object; eval loops.spin2 -> 21 ok (the compiler blob loads and runs a ladder rung end to end); tload datpasm.spin2 -> the target HALTED at step 77 from /TOOLS/tgtvm.bin. So a blank FAT32 drive provisions from the PC with recv, no PC-mount needed. Provisioning steps recorded: ./build.sh tools, then recv TOOLS/{spin2c,pasm2,p1c}.bin and TOOLS/tgtvm.bin<-target_vm.bin; the /EX examples and /REF cards likewise. A board DTR-reset re-enumerates a drive that dropped off the roster after a reflash (seen this session). LFN/8.3 and subdir-create differences between FAT32 and exFAT are otherwise the borrowed usb_app's territory (parked). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 310124f4f6 user: ben tags: trunk
16:13
RESTART.md: the full 2026-08-23 arc - P1 debugger, 1160 stage 1, 1180, 1190; the PASM umbrella through phase D; the earlier checkpoints that silently no-op'd, now written. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 1e1daf0587 user: ben tags: trunk
16:11
asm: ## always augments (pnut forces it), not only when the value exceeds 9 bits - parse_opnd flags ## as imm=2 and enc augments on that or on >9 bits, for both the S operand of a D,S shape and the D operand of the D/#-only group. Verified: asm mov pa,##5000 now emits the AUGS long (FF000009 ...) as pnut does. wrpin/wxpin still don't augment - their first operand is the S field (a reversed shape) the encoder maps as D; that shape-order fix stays open on 1111, and square.pasm waits on it. The four gold rungs (blink/count/ptrs/drv) unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 14a473668e user: ben tags: trunk
16:06
1190: GETTING-STARTED-PASM.md and the PASM ladder. The tutorial (docs/GETTING-STARTED-PASM.md, 150 lines, house style, every claim run on the bench) walks: why the second P2 is the playground (the host's six cogs, three hard-real-time), one instruction (asm add x,#1 -> F107EC01), a whole file (pasm blink.pasm), running on the target (tload/tpasm/tcog), the DAT-driver-with-a-Spin2-face shape, inline ORG/END on a method's frame longs, and live single-stepping (td arm/tcog/td s/td q/td regs). It points at ref, examples/pasm and book asm. THE LADDER, examples/pasm/, each rung gold byte-identical to pnut-ts on the bench (pasm NAME.pasm vs tools/goldasm2.py's pnut longs): blink.pasm (drvnot/waitx ##/jmp, 4 longs), count.pasm (a ptra mailbox loop, 6), ptrs.pasm (all 11 PTRx operand forms, 11), drv.pasm (a full driver, 6). goldasm2.py wraps each .pasm as a Spin2 DAT, assembles with pnut, and prints the cog longs; the board's pasm matches them long for long (the res-reserved tail slot the board counts is uninitialized and not part of the gold). A smart-pin square-wave rung is deferred: wrpin/wxpin with a ##%binary mode hits a ## augment edge in the encoder (noted on 1111). FIXES the ladder forced, both in the pasm2 blob (no hub cost): (1) full-line ' comments and blank lines are skipped - pasm choked on a rung's comment header ("unknown mnemonic line 1"); every example and real source starts with a comment, so this was a real gap. (2) %binary literals in operands (asm.spin2 value_of) - smart-pin modes are written in binary; the encoder did $ hex and decimal only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: b2b67ac8e6 user: ben tags: trunk
15:57
1180: the PASM2 instruction reference on the drive. tools/genref.py generates one small ASCII .md card per instruction the on-device assembler knows (341 of them) from the P2 Knowledge Base's per-instruction YAML - oneliner, syntax, encoding, clocks, flags, description, wrapped to 76 cols, pure ASCII, file names <= 18 chars. The mnemonic list is src/asmtab_full.spin2's own (the assembler's table), so /REF covers exactly what asm/pasm accept - one source, the 1100 doctrine. `ref NAME` on the REPL upulls REF/name.md into the help pager (page_md, the 1210 reader); `ref` alone opens the index. Zero hub cost: the cards live on the SSD, one read into scratch at a time. The cards and the generator and README are in books/ref/ (the [a-z]*.md cards are fossil-ignored - regenerate with genref.py); 00-index.md and README.md are tracked. NOT deployed to the drive this session: the SSD went into its slow/stale-dir state (a single recv took 91 s and the board then could not re-read the file - the [[inspin-usb-storage]] wedge). Bulk deploy of 341 cards needs a healthy drive: re-seat the SSD (a different hub port clears it), then `for f in books/ref/*.md; do tools/recv_send.py REF/$(basename $f) $f; done`, or mount P2SSD on the PC and cp to /REF. The `ref` command and page_md are verified by construction (same path as `book`); one card (waitx.md) was written to the drive but the wedged dir could not re-read it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 989a6d953a user: ben tags: trunk
15:23
blob_run 'no tool' message: point at the drive/build.sh tools, not a stale p1c line (it fired for spin2c too). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 45e4640a8b user: ben tags: trunk
15:17
RESTART.md checkpoint: the P1 debugger and 1160 stage 1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 3051bc9832 user: ben tags: trunk
15:16
1160 stage 1 (the TARGET): live PASM single-stepping over the P2 silicon debug interrupt, on the second P2. The stub installs a debug ISR for cogs 2..7 (td arm -> HUBSET $2000_00FC), so the next cog started with tcog/trun/coginit stops in its first instruction and waits; then td s single-steps, td q shows where it is, td regs the register file, td r/td w one register, td c continues. THE SILICON, from the doc (part3-interrupts / p2kbArchDebugInterrupt): the debug interrupt is the highest-priority hidden interrupt, enabled per-cog by HUBSET %0010_..LW_<16 cog bits>. On COGINIT of an enabled cog it fires with IJMP0=$1F8; the execute-only ROM at $1F8 saves cog regs $000..$00F to hub $FF800+!cog<<7, loads 16 longs from $FF840+!cog<<7 into $000..$00F, and JMP #$000. Our 16-long isr16 (loaded into every cog 2..7 image slot) saves $010..$1EF to $FC000, loads the real debugger dbgcode into cog $010, and jmps to it; dbgcode posts pc/flags/pa/pb/ptra/ptrb to a hub mailbox ($FD000), waits for a command, arms the next BRK - single-step (bit 0), or continue (BRK-instruction bit 4, plus an address match bit 10 when given) - and exits through the ROM at $1FD, which restores $000..$00F and RETI0's. The ISR is entirely cog-resident, so the FIFO and hub-exec state are untouched, exactly as the doc intends. The upper 16 KB the ISR needs is free real estate on the target (the 1100 doctrine: host-side debug would cost the 0560 reserve; the target is the lab). BENCH, on the second P2 with datpasm.spin2's DAT driver: td arm; tcog -> cog 2 stops at its first instruction; td q -> "cog 2 stopped at pc 0 C=0 Z=0 pa=.. ptra=$96EC ptrb=$8F2C"; td s repeatedly -> pc 0->1->2->3->4, the program counter advancing one PASM instruction at a time on real silicon; td regs -> the sixteen registers around the pc. This is the 1160 stage-1 acceptance: stepping a real PASM program with registers live over the wire. TWO BUGS PAID FOR (both in the ISR bring-up, both instructive): (1) two DAT blocks with `org 0` in a standalone image put @isr16 at the wrong hub address (the map showed HUB $00000); merging into one DAT fixed @ to a real hub pointer. (2) `setq #(dbgcode_end - dbgcode) / 4 - 1` divided by 4 a difference that is ALREADY a long count (cog-address labels), so it loaded 9 of 38 longs and dbgcode never ran past isr16's save; the fix is `#dbgcode_end - dbgcode - 1`. The debug interrupt itself was proven to fire with a $BEEF sentinel written by isr16 before either fix landed. pasm2 now also returns a pc->source-line map (mailbox slots 7-9); td shows the source line for a stopped pc when the last tpasm assembled the running blob. NOT in this stage: the continue-to-BRK/address path is armed but not bench-confirmed (the driver has no BRK to hit); individual `td r N` is intermittent on the bench where td regs is solid; the VGA register PANE (0310's UI grown a register view) is the next stage; and stage 2 (host-side user cogs, the hub-top carve-out) is deliberately deferred - the scanout/USB/pump cogs are fenced off as the doctrine requires. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 2ef6e90b34 user: ben tags: trunk
14:43
RESTART.md checkpoint: 0840, tpasm, 1111, 1170, the P1 debugger. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: e3f6f8b8b0 user: ben tags: trunk
14:43
1400 piece 3: A DEBUGGER ON THE TARGET P1 - the honest first form, the ROM-monitor shape the umbrella named. examples/p1/inspmon.spin is a PASM cog (assembled by p1c itself, byte-identical to spinc) a program links in with OBJ mon : "inspmon" / mon.start; it owns P30/P31 at 115200 and answers one line per command while the program runs: I (@P1MON), R hhhh (a hub long), B hhhh (a byte), W hhhh vvvvvvvv, S n (cogstop; its own cog answers NO). p1c hands the host the top object's VAR table (name, offset, size - mailbox slots 10-12) and the image header's VBASE, so the P2 reads variables BY NAME: p1mon, p1v (every VAR live), p1peek NAME, p1poke NAME VALUE (read-modify-write of the containing long for bytes/words), p1r HEX, p1w HEX HEX, p1stop N. BENCH, on the real P1 with montest.spin: @P1MON; ticks = 54 then 59, slow = 5 then 6 (a long and a byte climbing at their rates); p1peek ticks 77; p1poke slow 200 -> OK and it carried on from there (wrapped at 255); p1stop 1 -> NO (the monitor); p1stop 0 -> OK and ticks FROZE at 70 across reads - the program stopped, the variables readable at leisure. mon_ask asks twice because the first exchange after the FTDI opens sometimes gets nothing. GUIDE section 9 documents it; the gold harness now clears the ramdisk before each program, settles after the rms, drops the status-bar repaints from the transcript and re-asks p1hex if the prompt race ate it (13 of 13 gold). Not here: source-level stepping - the P1 Spin interpreter is in ROM; this is peek/poke/stop by name, which is what a monitor can give. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 271bb61b4d user: ben tags: trunk
14:22
1170: PASM smart editing, the paint and the navigation. The painter knows when it is in PASM (a DAT section, or an ORG..END block inside a method - the section walk carries ps_pasm) and paints: a word at column 0 white as a label (.local grey), if_x / _ret_ conditions and wc/wz/wcz/andc.. effects magenta, mnemonics in the keyword ink, org/end/res/fit/byte/word/long/alignl/alignw green, #/##/$/%/numbers yellow, registers and symbols plain, ' comments grey. The mnemonic list is src/asmnames.spin2, GENERATED by build.sh from the same asmtab the assembler blob is built from (one source, the 1100 doctrine) - 348 names, 2.7 K resident. ^N/^P jump to labels inside DAT as they jump PUB/PRI outside it. Verified over the serial seat (24-bit SGR capture of datpasm.spin2's DAT rows). Not yet: mnemonic completion through the tab engine, the column-style Tab. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: db0e13c0e2 user: ben tags: trunk
14:13
1111: the PTRx forms and data in the assembler blob - ptra[n] / ptrb[n] (6-bit index), ++ptra[n] --ptra ptra++[n] ptra-- (5-bit steps; 1 without [n]), all eleven forms byte-identical to pnut (examples/pasm/ptrs.pasm); pasm2's driver counts in bytes like pnut, so BYTE/WORD/LONG data and strings pack across lines and labels get hpc/4; alignl/alignw; negative numbers. And a rule learned from the listing: pnut aligns NOTHING but instructions and res in DAT - byte then word puts the word at byte 1 - so the Spin2 compiler's DAT walker no longer aligns word/long data either (1120 had added it). Bare ptra/ptrb regressed for one build in the rewrite and is back (rung 12 green, asm rdlong pa, ptra). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 9f6207b6e8 user: ben tags: trunk
14:07
1111/1150: tpasm FILE - assemble a PASM2 source here (/TOOLS/pasm2.bin into scratch) and ship the longs to the target's pasmbuf in one command; tcog starts it, tpar reads it. Bench: tload datpasm.spin2 (monitor), tpasm drv.pasm -> OK, tcog -> C 2, tpar 0 -> V 8 then V 15: PASM written on this P2 running on the second one with no hex file. The lab README says so. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 506e9fd999 user: ben tags: trunk
14:02
0840 SOLVED - not the compiler: `tool` read its source with fs.max_file() (12,288, the ramdisk slot) into the 16 KB scratch, so any /TOOLS source over 12 KB was silently truncated and its last methods never existed ('unknown fn: nl'). Bisected on the bench with the original commented repro: 12,219 bytes compiles, 14,435 does not, exactly at the cap; the stripped tool that 'worked' was 12,242. Now the whole of scratch is read and a source at the 16 KB limit is announced. The 660-line fully-commented assembler tool compiles (4,180-byte object) and runs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 810d717212 user: ben tags: trunk
12:13
RESTART.md: the tools-out afternoon (stub, compiler, assembler to /TOOLS), hub 67,600, the four-blob deploy, the arena; build/load notes updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 605d7ca52f user: ben tags: trunk
12:12
TOOLS OUT OF THE IMAGE, step 3: THE PASM2 ASSEMBLER. asm.spin2 and its 341-row table left the image: /TOOLS/pasm2.bin (tools/pasm2_tool.spin2 - the encoder plus the two-pass file driver that was behind HAS_ASMFILE, labels/.locals, org, res, long, ## augments) serves `asm INSTRUCTION` (one indented line) and the new `pasm FILE` (a cog image into scratch, hex listed) through blob_run. The asmenc/enclong console seam and the interpreted tools/pasm.spin2 + pasm1.spin2 + asmtab.tab are gone (their job is the native blob); HAS_ASMFILE is gone; HAS_ASM now only reaches the compiler blob's build (DAT and inline PASM encode inside spin2c.bin, which carries its own copy). Verified: asm add pa,#1 -> F107EC01; asm waitx ##1000 -> FF800001 FD67D01F; pasm drv.pasm -> FB040D00 F1040C01 FC640D00 FF800F42 FD65001F FD9FFFE8 00000005, byte-identical to pnut. Hub free 61,416 -> 67,600 on vga. GUIDE section 2 says where the tools live. THE DAY'S LEDGER: the owner's direction this afternoon - every P2 and P1 development tool to /TOOLS, the image SMALLER - is done for the compiler, the assembler, the P1 compiler and the stub. vga hub free went 17,860 (morning) -> 8,244 (noon, after the resident language work) -> 67,600 (now); the image 276 KB -> 202 KB; the editor 320 -> 576 lines. Still resident of the toolchain: the P1 seam (p1link + commands, 4.8 K - the USB load path is cog 0's), the second-P2 commands (tools_target), the VM and the debugger UI, which are the IDE itself. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: f0bb4be167 user: ben tags: trunk
12:02
TOOLS OUT OF THE IMAGE, step 2: THE SPIN2 COMPILER. compile.spin2 + lex + asm + asmtab are /TOOLS/spin2c.bin now (tools/spin2c_tool.spin2, built by ./build.sh tools with the IMAGE's defines), run by blob_run in the editor's arena; the image keeps a thin seam (ccshim in tools_shared.spin2: the 8 KB object buffer, the line map for dbg/tdbg, the method table by name, the error - same names as before, cc_ instead of cc., every call site rewired; cc_compile builds the ramdisk file table so OBJ children resolve inside the blob). The editor buffer - the arena - grows to 576 x 128 = 73,728 B (the compiler blob is 37 K code + 31 K VAR + its stack): 576 editor lines. Hub free on vga: 45,172 -> 61,416 (+16 K net of the arena growth; the image 276 KB -> 208 KB). With step 1 (the stub) the day's total is 8,244 -> 61,416. Verified on the bench: ladder rungs 2-7, 9-13 + objdemo (OBJ children via the file table) all green; dbg steps by line through the copied map (15->16->17->18); tload datpasm.spin2 -> the target HALTED at step 77, V 56; `tool rep` (an interpreted /TOOLS tool compiled from scratch by the blob); `compile` lists the methods; 11 of 11 P1 gold (p1c shares the arena). `syms` is gone (the symbol table leaves with the compiler; the command says so). THE IMAGE CANNOT COMPILE WITHOUT THE DRIVE NOW - `eval`/`compile` answer `no tool: /TOOLS/spin2c.bin` when the SSD is off. That is the direction: every development tool on /TOOLS, the image smaller. A fresh drive needs three files from ./build.sh tools: p1c.bin, spin2c.bin, target_vm.bin (recv each), built by the SAME pnut-ts as the image. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: 6d6819ad78 user: ben tags: trunk
11:52
TOOLS OUT OF THE IMAGE, step 1: the second-P2 stub. target_vm.bin is no longer embedded (loader.spin2's `file` is gone); `load`/`tload` read /TOOLS/target_vm.bin into the editor's arena, patch the program in (patch_stub now takes the buffer), and send it from there. Hub free 8,244 -> 45,172 on the vga build (+37 K; the stub was 39.6 K, the arena grew 2.5 K: editor LINELEN back to 128 so the 320-line buffer - the tool arena - holds the stub, 40,960 B). Bench: tload datpasm.spin2 -> Prop_Ver G, 39,608 bytes sent, monitor up, t c -> HALTED step 77 on the target as before. Deploy: ./build.sh tools now also rebuilds target_vm.bin; tools/recv_send.py TOOLS/target_vm.bin target_vm.bin. A missing file says so (`no stub: /TOOLS/target_vm.bin`). This is the owner's direction 2026-08-23: every P2 and P1 development tool goes to /TOOLS and the image gets SMALLER - the stub first, the Spin2 compiler next. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kCALspA8e7YWe3CwbXWjf check-in: ed59c86a28 user: ben tags: trunk
11:36
manual cleaning of startup files. addition of Chip's font work for later check-in: 106190a2e1 user: ben tags: trunk