| Menu | JAQForum Ver 26.09 |
Forum Index : Microcontroller and PC projects : New in V6.03.02b9 : create a CSUB from Basic without writing any C
mmb2csub.pdf Every PicoMite program has at least one routine doing the real work, and most of the time the interpreter spends there goes on *reading* the code rather than running it. `mmb2csub` compiles that routine to machine code and puts it back into your program as a CSUB: ``` :: assuming I am in the user-tools directory after unpacking the zip file python mmb2csub.py ../examples/julia.bas PlotJulia ``` That rewrites the program: the original Basic routine is commented out, the CSUB is appended, and **your call sites do not change** — MMBasic calls a CSUB exactly as it calls a SUB. The Julia set demo included with the tool renders in 5.9 seconds instead of 119 on an RP2040, and 2.5 instead of 86 on an RP2350 - 20x and 35x - and draws a byte-identical image either way. **How it works.** The translation from MMBasic to C is done by `mmb2c.py`, a complete MMBasic-to-C translator — the same one that, converted to C, runs as a native application under the Fuzix port. It already understood MMBasic's scope rules, string semantics and array layouts, which is the hard part. What `mmb2csub` adds is the other half: a driver that picks one routine out of your program and works out what it needs, and a runtime that maps the translated C onto the firmware's *own* routines through the CallTable defined in PicoCFunctions.h. That second part is what makes the result trustworthy. `SIN`, `MID$`, `STR$`, `RGB` and the drawing commands inside a CSUB call the same firmware code the interpreter calls, so a converted routine cannot quietly disagree with the original about what `MID$` means — and the compiled blob stays small, because none of it is duplicated. **One converted program runs on every PicoMite.** The same file — same bytes — works on the RP2040 and the RP2350 and on every firmware variant, because the code is built for the Cortex-M0+, is position-independent, and finds the firmware's routines through a table it locates at run time rather than an address fixed when it was compiled. Tested both ways round: byte-identical output from one file on a PicoMiteVGA (RP2040) and a PicoMiteHDMIWEB (RP2350B). So a converted program can be posted or shipped exactly like any other `.bas`. **What to expect.** Loops, array work and arithmetic run 10–20x faster. Routines that mostly call the firmware already — graphics, `SIN`, string formatting — gain much less, because only the interpreting overhead goes away. The tool tells you which yours is before you commit to anything: `--list` compiles and links every routine in your program and reports what each would cost, without changing a thing. It is also honest about what it cannot do. Anything unsupported is refused by name, before anything is written, rather than producing a CSUB that is subtly wrong. **Getting it:** `mmb2csub-6.03.02b9.zip`, attached to this release. It needs Python, the Arm GNU toolchain (`arm-none-eabi-gcc`) — the same compiler used to build the firmware — and one Python package (`pip install pyelftools`). The manual inside covers setting both up on Windows and Linux, and the recommended workflow. Firmware 6.03.02b9 or later is required on the board. Julia.bas as originally written ' julia.bas - Julia set, with plotjulia taking everything it needs as ' parameters so it is a self-contained routine (the first mmb2csub target). Mode 2 CLS map maximite 'Specify initial values RealOffset = -1.30 ImaginOffset = -1.22 '------------------------------------------------* 'Set the Julia set constant [eg C = -1.2 + 0.8i] CRealVal = -0.78 CImagVal = -0.20 '------------------------------------------------* MAXIT=80 'max iterations PixelWidth = MM.HRes PixelHeight = MM.VRes GAP = PixelHeight / PixelWidth SIZE = 2.50 XDelta = SIZE / PixelWidth YDelta = (SIZE * GAP) / PixelHeight ' MAP() has no CSUB CallTable slot, and in a fixed mode its sixteen values are ' constants - so look them up once here and pass the table in. Dim mp%(15) For i% = 0 To 15 : mp%(i%) = map(i%) : Next i% plotjulia PixelWidth, PixelHeight, XDelta, YDelta, RealOffset, ImaginOffset, CRealVal, CImagVal, MAXIT, mp%() ' the rendered image, for comparing this against the CSUB version byte for byte. ' SAVE IMAGE records the framebuffer and ignores the colour map, so what lands in ' the file is exactly what plotjulia computed. Save Image "julia.bmp" Do a$ = Inkey$ Loop While a$ = "" end ' ' w, h picture size in pixels ' xd, yd the step in the complex plane per pixel ' rOfs, iOfs top-left corner of the view ' cRe, cIm the Julia constant C ' mit iteration limit ' mp%() the sixteen colours, already resolved through MAP() sub plotjulia w, h, xd, yd, rOfs, iOfs, cRe, cIm, mit, mp%() Local X, Y, CX, CY, Zr, Zi, COUNT, new_Zr, new_Zi 'Loop processing - visit every pixel For X = 0 To (w - 1) CX = X * xd + rOfs For Y = 0 To (h - 1) CY = Y * yd + iOfs Zr = CX Zi = CY COUNT = 0 ' Begin Iteration loop Do While (( COUNT <= mit ) And (( Zr * Zr + Zi * Zi ) < 4 )) new_Zr = Zr * Zr - Zi * Zi + cRe new_Zi = 2 * Zr * Zi + cIm Zr = new_Zr Zi = new_Zi COUNT = COUNT + 1 Loop Pixel X,Y,mp%( COUNT Mod 16) Next Y Next X end sub After running the csub generator. Note: the original basic, the C source and comments in the csub are all removed by using the option --lean in the generator command ' julia.bas - Julia set, with plotjulia taking everything it needs as ' parameters so it is a self-contained routine (the first mmb2csub target). Mode 2 CLS map maximite 'Specify initial values RealOffset = -1.30 ImaginOffset = -1.22 '------------------------------------------------* 'Set the Julia set constant [eg C = -1.2 + 0.8i] CRealVal = -0.78 CImagVal = -0.20 '------------------------------------------------* MAXIT=80 'max iterations PixelWidth = MM.HRes PixelHeight = MM.VRes GAP = PixelHeight / PixelWidth SIZE = 2.50 XDelta = SIZE / PixelWidth YDelta = (SIZE * GAP) / PixelHeight ' MAP() has no CSUB CallTable slot, and in a fixed mode its sixteen values are ' constants - so look them up once here and pass the table in. Dim mp%(15) For i% = 0 To 15 : mp%(i%) = map(i%) : Next i% plotjulia PixelWidth, PixelHeight, XDelta, YDelta, RealOffset, ImaginOffset, CRealVal, CImagVal, MAXIT, mp%() ' the rendered image, for comparing this against the CSUB version byte for byte. ' SAVE IMAGE records the framebuffer and ignores the colour map, so what lands in ' the file is exactly what plotjulia computed. Save Image "julia.bmp" Do a$ = Inkey$ Loop While a$ = "" end ' ' w, h picture size in pixels ' xd, yd the step in the complex plane per pixel ' rOfs, iOfs top-left corner of the view ' cRe, cIm the Julia constant C ' mit iteration limit ' mp%() the sixteen colours, already resolved through MAP() /* ' --- mmb2csub: PlotJulia replaced by a CSUB; original follows sub plotjulia w, h, xd, yd, rOfs, iOfs, cRe, cIm, mit, mp%() Local X, Y, CX, CY, Zr, Zi, COUNT, new_Zr, new_Zi 'Loop processing - visit every pixel For X = 0 To (w - 1) CX = X * xd + rOfs For Y = 0 To (h - 1) CY = Y * yd + iOfs Zr = CX Zi = CY COUNT = 0 ' Begin Iteration loop Do While (( COUNT <= mit ) And (( Zr * Zr + Zi * Zi ) < 4 )) new_Zr = Zr * Zr - Zi * Zi + cRe new_Zi = 2 * Zr * Zi + cIm Zr = new_Zr Zi = new_Zi COUNT = COUNT + 1 Loop Pixel X,Y,mp%( COUNT Mod 16) Next Y Next X end sub */ ' --- mmb2csub: generated code for PlotJulia CSUB PlotJulia FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, INTEGER 00000022 '__aeabi_dadd 4C03B510 69E46824 682434A4 BD1047A0 E000ED08 '__aeabi_dsub 4C03B510 69E46824 682434A8 BD1047A0 E000ED08 '__aeabi_dmul 4C03B510 69E46824 682434A0 BD1047A0 E000ED08 '__aeabi_dcmplt 4C04B510 69E46824 682434B0 0FC047A0 46C0BD10 E000ED08 '__aeabi_dcmple 4C05B510 69E46824 682434B0 1E4347A0 0FC04318 46C0BD10 E000ED08 '__aeabi_dcmpge 4C04B510 69E46824 682434B0 43C047A0 BD100FC0 E000ED08 'PlotJulia B097B5F0 22009214 93154C8F 00066823 911369DB 605A6FDB 6823601A 6FDF69DB 4295683D 2080D111 01406B1B 21804798 60386823 014969DB 605D6FDB 69DB6823 685A6FDB D900428A 6823605D 68302200 69DB6871 685B6FDB 4B7C9312 FF96F7FF 23002200 910D900C 93019200 990D980C 69DB6823 9A006FDD F7FF9B01 2800FFB5 2000D105 9B122100 B017606B 68ABBDF0 60AB3301 D103059B 69DB6823 47986A9B 99019800 681A9B14 F7FF685B 9B1CFF79 685B681A FF60F7FF 910F900E 22009913 68496808 F7FF4B61 2200FF61 90102300 92029111 98109303 68239911 6FDD69DB 9B039A02 FF80F7FF D1082800 99019800 4B562200 FF40F7FF 91019000 68ABE7B6 60AB3301 D103059B 69DB6823 47986A9B 99039802 681A9B15 F7FF685B 9B1DFF41 685B681A FF28F7FF 9F0F9E0E 23002200 91059004 93079206 003B0032 00390030 FF2EF7FF 9B059A04 91099008 00190010 FF26F7FF 900A6823 9806910B 69DD9907 681A9B20 F7FF685B 2800FF31 9A0AD00B 98089B0B F7FF9909 2200FEFF F7FF4B34 2800FF19 9800D12D 002B9901 681B33EC 681E3588 4798682B 00056823 99039802 338869DB 4798681B 90046823 980669DB 001A9907 32FC3388 6CD7681B 22104798 47B82300 00C09B21 9904581A 47B00028 99039802 4B1D2200 FECEF7FF 91039002 6FEAE779 33016893 059B6093 6823D103 6A9B69DB 9A0A4798 98089B0B F7FF9909 9B1EFEC5 685B681A FEB6F7FF 003B0032 91099008 00390030 FEAEF7FF 9B059A04 FEBEF7FF 681A9B1F F7FF685B 2200FEA5 91059004 99079806 F7FF4B04 9E08FE9D 90069F09 E7779107 E000ED08 3FF00000 40100000 End CSUB ' The C this was compiled from, for reference. Regenerate with: ' python mmb2csub.py julia.bas PlotJulia ' '/* Generated by mmb2csub.py from ../examples/julia.bas, PlotJulia. ' * Do not edit - regenerate instead. */ '#include "mmcsub.h" ' ' 'static void f_plotjulia(MMFLOAT *p_w, MMFLOAT *p_h, MMFLOAT *p_xd, MMFLOAT *p_yd, MMFLOAT *p_rofs, MMFLOAT *p_iofs, MMFLOAT *p_cre, MMFLOAT *p_cim, MMFLOAT *p_mit, MMINTEGER *p_mp, const MMINTEGER *__b_mp) { ' unsigned __mark = mm_mark(); (void)__mark; ' MMFLOAT v_x = 0; ' MMFLOAT v_y = 0; ' MMFLOAT v_cx = 0; ' MMFLOAT v_cy = 0; ' MMFLOAT v_zr = 0; ' MMFLOAT v_zi = 0; ' MMFLOAT v_count = 0; ' MMFLOAT v_new_zr = 0; ' MMFLOAT v_new_zi = 0; ' { ' MMFLOAT __lim2 = ((((*p_w)) - (1.0))); ' for (v_x = 0.0; v_x <= __lim2; v_x += 1) { ' mm_poll(); ' v_cx = ((((v_x) * ((*p_xd)))) + ((*p_rofs))); ' { ' MMFLOAT __lim3 = ((((*p_h)) - (1.0))); ' for (v_y = 0.0; v_y <= __lim3; v_y += 1) { ' mm_poll(); ' v_cy = ((((v_y) * ((*p_yd)))) + ((*p_iofs))); ' v_zr = v_cx; ' v_zi = v_cy; ' v_count = 0.0; ' while (((((((v_count) <= ((*p_mit)))) & ((((((((v_zr) * (v_zr))) + (((v_zi) * (v_zi)))))) < (4LL)))))) != 0) { ' mm_poll(); ' v_new_zr = ((((((v_zr) * (v_zr))) - (((v_zi) * (v_zi))))) + ((*p_cre))); ' v_new_zi = ((((((2.0) * (v_zr))) * (v_zi))) + ((*p_cim))); ' v_zr = v_new_zr; ' v_zi = v_new_zi; ' v_count = ((v_count) + (1.0)); ' } ' mm_pixel(mm_toint(v_x), mm_toint(v_y), p_mp[(int)(mm_mod(mm_toint(v_count), 16LL))]); ' } ' } ' } ' } ' mm_release(__mark); '} ' '/* CSUB entry - the interpreter hands us one pointer per argument. */ 'long long PlotJulia(void *a0, void *a1, void *a2, void *a3, void *a4, void *a5, void *a6, void *a7, void *a8, void *a9) '{ ' mm_scratch_reset(); ' f_plotjulia((MMFLOAT *)a0, (MMFLOAT *)a1, (MMFLOAT *)a2, (MMFLOAT *)a3, (MMFLOAT *)a4, (MMFLOAT *)a5, (MMFLOAT *)a6, (MMFLOAT *)a7, (MMFLOAT *)a8, (MMINTEGER *)a9, 0); ' return 0; '} Edited 2026-09-17 23:14 by matherp |
||||||
Hi Peter, this is a quantum leap for fast execution of MMBasic programs. Thank you indeed for the new Py tool ! I am not into Unix - as I understand, except for preemptive multitasking and for C programmers, what are the benefits of Unix in comparison to the latest Basic version ? With my respect, best regards Thomas |
||||||
Good evening, firste steps: trying to generate CSUB from julia.bas example with the py tool. Using WIN7 therefore have to install an old python 3.8 instead of newer versions Created a PATH to the compiler and another to python, both accessible PIP pyelftools which copied "readelf.py" into python38/scripts subdir (I have not found more tools installed by PIP pyelftools is this correct ?) Moved into the examples dir and from there I can verify the toolchain, mmb2csub.py julia.bas --list, as this reads julia.bas but simply says "plotjulia did not build", trying one of my own programs lists several subs but all of them "did not build". What am I missing or doing wrong ? Any hints are appreciated, thank you Best: Thomas |
||||||
mmb2csub works on subroutines not complete programs - read the manual My example uses python mmb2csub.py ../examples/julia.bas PlotJulia |
||||||
Peter, thanks for the answer. I understand. I followed the manual. Still I can't see through: Step 3 leaves no indication about costs and says SUB plotjulia is not convertible: C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\examples> mmb2csub.py julia.bas --list checking 1 routines. Convertible, and not already inside another one's blob: routine args blob prog closure also brings in Not convertible: plotjulia did not build Step 5 throws an error (transpiler generates "Julia_PlotJulia.c" file, which looks correct and is located in /examples, I think gcc does not find this file ?) C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\examples> mmb2csub.py julia.bas plotjulia Traceback (most recent call last): File "C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\user-tools\armcfgen.py", line 299, in <module> main() File "C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\user-tools\armcfgen.py", line 260, in main objs = [compile_c(s, args.include, args.opt) for s in objs] File "C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\user-tools\armcfgen.py", line 260, in <listcomp> objs = [compile_c(s, args.include, args.opt) for s in objs] File "C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\user-tools\armcfgen.py", line 211, in compile_c subprocess.run([GCC, *CFLAGS, "-O" + opt, *inc, "-o", obj, src], check=True) File "C:\Program Files\Python38\lib\subprocess.py", line 489, in run with Popen(*popenargs, **kwargs) as process: File "C:\Program Files\Python38\lib\subprocess.py", line 854, in __init__ self._execute_child(args, executable, preexec_fn, close_fds, File "C:\Program Files\Python38\lib\subprocess.py", line 1307, in _execute_child hp, ht, pid, tid = _winapi.CreateProcess(executable, args, FileNotFoundError: [WinError 2] Das System kann die angegebene Datei nicht finden error: the CSUB did not compile. 'undefined reference to mm_xxx' means that MMBasic feature has no CSUB runtime yet; 'undefined reference to __aeabi_xxx' means an arithmetic helper is missing from mmcsub.h. Either way the tool is telling you the truth - do not work around it. Content in transpiled c-File Julia_PlotJulia.c: /* Generated by mmb2csub.py from ../examples/julia.bas, PlotJulia. * Do not edit - regenerate instead. */ #include "mmcsub.h" static void f_plotjulia(MMFLOAT *p_w, MMFLOAT *p_h, MMFLOAT *p_xd, MMFLOAT *p_yd, MMFLOAT *p_rofs, MMFLOAT *p_iofs, MMFLOAT *p_cre, MMFLOAT *p_cim, MMFLOAT *p_mit, MMINTEGER *p_mp, const MMINTEGER *__b_mp) { unsigned __mark = mm_mark(); (void)__mark; MMFLOAT v_x = 0; MMFLOAT v_y = 0; MMFLOAT v_cx = 0; MMFLOAT v_cy = 0; MMFLOAT v_zr = 0; MMFLOAT v_zi = 0; MMFLOAT v_count = 0; MMFLOAT v_new_zr = 0; MMFLOAT v_new_zi = 0; { MMFLOAT __lim2 = ((((*p_w)) - (1.0))); for (v_x = 0.0; v_x <= __lim2; v_x += 1) { mm_poll(); v_cx = ((((v_x) * ((*p_xd)))) + ((*p_rofs))); { MMFLOAT __lim3 = ((((*p_h)) - (1.0))); for (v_y = 0.0; v_y <= __lim3; v_y += 1) { mm_poll(); v_cy = ((((v_y) * ((*p_yd)))) + ((*p_iofs))); v_zr = v_cx; v_zi = v_cy; v_count = 0.0; while (((((((v_count) <= ((*p_mit)))) & ((((((((v_zr) * (v_zr))) + (((v_zi) * (v_zi)))))) < (4LL)))))) != 0) { mm_poll(); v_new_zr = ((((((v_zr) * (v_zr))) - (((v_zi) * (v_zi))))) + ((*p_cre))); v_new_zi = ((((((2.0) * (v_zr))) * (v_zi))) + ((*p_cim))); v_zr = v_new_zr; v_zi = v_new_zi; v_count = ((v_count) + (1.0)); } mm_pixel(mm_toint(v_x), mm_toint(v_y), p_mp[(int)(mm_mod(mm_toint(v_count), 16LL))]); } } } } mm_release(__mark); } /* CSUB entry - the interpreter hands us one pointer per argument. */ long long PlotJulia(void *a0, void *a1, void *a2, void *a3, void *a4, void *a5, void *a6, void *a7, void *a8, void *a9) { mm_scratch_reset(); f_plotjulia((MMFLOAT *)a0, (MMFLOAT *)a1, (MMFLOAT *)a2, (MMFLOAT *)a3, (MMFLOAT *)a4, (MMFLOAT *)a5, (MMFLOAT *)a6, (MMFLOAT *)a7, (MMFLOAT *)a8, (MMINTEGER *)a9, 0); return 0; } |
||||||
Run arm-none-eabi-gcc --version in the same console. It may say "not recognized". Install the Arm GNU toolchain and tick Add path to environment variable, or add its bin folder to PATH by hand. The default location is C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\<version>\bin. If you have only built the firmware through the Pico VS Code extension, the compiler may exist but only on the extension's own PATH. In that case you need to add that folder to PATH. Open a new console, then run --list again. The sizes should appear, and step 5 should work. |
||||||
First of all: I have absolutely no idea! ...however, I've often had problems in the past when the path name contained multiple “.” characters. Frank |
||||||
tnx. The toolchain reports: C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\user-tools> arm-none-eabi-gcc --version arm-none-eabi-gcc.exe (Arm GNU Toolchain 15.3.Rel1 (Build arm-15.149)) 15.3.1 20260627 Copyright (C) 2025 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Python reports: C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\user-tools> python --version Python 3.8.6 I upgraded pip to the latest version for python 3.8 as well. So I think the tools are correctly installed. What to check more ? |
||||||
The recommended compiler for MMbasic is 13.3 but this may not be important YET. The problem is that your console can find arm-none-eabi-gcc, but Python can't. They look up programs slightly differently. The most common reason is that the toolchain folder was added to PATH with quotes around it. The folder name contains spaces, so that's an easy mistake to make. The console accepts a quoted entry, but Python's subprocess ignores it and reports "Das System kann die angegebene Datei nicht finden". Please run these in the same window where mmb2csub.py fails: 1. Check whether Python can see the compiler: python -c "import shutil; print(shutil.which('arm-none-eabi-gcc'))" It prints None: Python can't see it. Go to step 2. It prints a path ending .cmd or .bat: your console is running a wrapper script, not the compiler itself. Put the toolchain's real bin folder on PATH instead. It prints a path ending .exe: this window is fine. Run mmb2csub.py again from here. The earlier failure must have come from a window with a different PATH. 2. List your PATH entries: python -c "import os; print(chr(10).join(os.environ['PATH'].split(';')))" Find the line for the Arm toolchain, something like C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\15.3 rel1\bin. If it has " quotes around it, open System Properties → Environment Variables, edit PATH and remove the quotes. PATH entries must never be quoted, even when they contain spaces. 3. Open a new console (the old one keeps the old PATH) and run: mmb2csub.py julia.bas --list You should now see a size for plotjulia (the blob should be about 730 bytes) instead of "did not build". Step 5 (mmb2csub.py julia.bas plotjulia) should then work too. If it still fails, please send me the output of steps 1 and 2. |
||||||
Thank you Peter ! Nasty little space in the PATH to the compiler was preventing python from seeing the compiler, exactly as you pointed out ! debug script result before correction: C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\user-tools> python -c "import os; print(chr(10).join(os.environ[ 'PATH'].split(';')))" %SystemRoot%\system32\WindowsPowerShell\v1.0\ C:\Program Files\Python38\Scripts\ C:\Program Files\Python38\ C:\Windows\system32 C:\Windows C:\Windows\System32\Wbem C:\Windows\System32\WindowsPowerShell\v1.0\ C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x86 C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x64 C:\Program Files\Arm\GNU Toolchain mingw-w64-x86_64-arm-none-eabi\bin C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\user-tools C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\examples debug script result after correction: C:\Users\tom> python -c "import os; print(chr(10).join(os.environ['PATH'].split(';')))" %SystemRoot%\system32\WindowsPowerShell\v1.0\ C:\Program Files\Python38\Scripts\ C:\Program Files\Python38\ C:\Windows\system32 C:\Windows C:\Windows\System32\Wbem C:\Windows\System32\WindowsPowerShell\v1.0\ C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x86 C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x64 C:\Program Files\Arm\GNU Toolchain mingw-w64-x86_64-arm-none-eabi\bin C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\user-tools C:\ablage\pico\firmware\mmb2csub-6.04.00RC1\mmb2csub\examples CSUB building for Julia.bas has finally succeded. I use this opportunity to thank you sincerely for the splendid work you are offering to all of us in a significant way- for me PicoMite has become a livechanging tool - and thank for your patience and everpresent help assistance - often I feel a bit like me staying in the desert and calling out for help - yet having the confidence that someone is hearing me ! Best regards, Thomas Edited 2026-09-25 19:40 by tom_g |
||||||
| The Back Shed's forum code is written, and hosted, in Australia. |