Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 03:48 22 Jul 2026 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : CSubs: A new (easier) approach

Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 11636
Posted: 06:26pm 20 Jun 2026
Copy link to clipboard 
Print this post

armcfgen.pdf

armcfgen.zip

We now have a python script that will compile, link and convert the C source for a CSUB to the basic statements required to run it. Full details of the new approach and how to use it are given in the manual. This is essentially a conversion of armcfgenV144.bas with a number of bugs fixed and a number of enhancements.
Specifically, the new version also includes everything needed to allow CSUBs to access constant data defined in the C source - there is an example in the manual
AND
CSUBs produced in this way and run on PicoMite versions 6.03.00RC22 and above will run on both RP2040 and RP2350 without any changes
The zip file contains the python program and the updated header PicoCFunctions.h needed to access the internal firmware functions. These are all described in the manual.

An example of the command to create a CSUB would be:
python armcfgen.py merge.c --compile -n times3 -e scale3 -O s -I d:\Dropbox\PicoMite\PicoMite

This says convert the file merge.c which will need compiling. Name the CSUB times3 and use as the entry point the function scale3, optimise it for size, and find the header file in the directory "d:\Dropbox\PicoMite\PicoMite"

For reference below is the csub that works the magic in the bubble universe demo
/* bubble.c - "bubble universe" inner loop as a PicoMite CSUB.
*
* One frame row (66 points): the sin/cos recurrence + scale + offset, producing
* integer screen coordinates in c() and d(). Moving this out of the BASIC
* interpreter took the demo from 233 ms/frame to ~23 ms.
*
* Build (compute-heavy -> ask for -Os; -I points at PicoCFunctions.h):
*     python armcfgen.py bubble.c --compile -n bubblerow -e bubblerow -O s -I <firmware dir>
*
* Call from BASIC:
*     CSUB bubblerow INTEGER, INTEGER, FLOAT      ' c(), d(), pf()
*     ...
*     pf(0)=i : pf(1)=r*i+t
*     bubblerow c(), d(), pf()
*
* pf(): 0=iang(i)  1=b(r*i+t)  2=v(state)  3=x(state)  4=pi/2
*       5=xs  6=ys  7=xc  8=yc      (v,x persist in pf(2)/pf(3) across calls)
*
* Uses PicoCFunctions.h: all double maths goes through the firmware CallTable
* (Sine/FAdd/FMul/FloatToInt), which the header locates at runtime via VTOR - so
* no CallTable argument and no chip-/build-specific address, one blob for every
* variant and both chips. (The per-call base re-read the wrappers do is lost in
* the noise here - the software Sine calls dominate.)
*/
#include "PicoCFunctions.h"

long long bubblerow(long long *c, long long *d, double *pf)
{
   double iang = pf[0], b = pf[1], v = pf[2], x = pf[3], hp = pf[4];
   double xs = pf[5], ys = pf[6], xc = pf[7], yc = pf[8];
   int j;

   for (j = 0; j < 66; j++) {
       double A   = FAdd(iang, v);
       double siv = Sine(A);
       double civ = Sine(FAdd(A, hp));     /* cos(i+v) = sin(i+v + pi/2) */
       double sx  = Sine(x);
       double cx  = Sine(FAdd(x, hp));     /* cos(x) */
       double uu  = FAdd(siv, sx);
       v = FAdd(civ, cx);
       x = FAdd(uu, b);
       c[j] = FloatToInt(FAdd(FMul(uu, xs), xc));
       d[j] = FloatToInt(FAdd(FMul(v,  ys), yc));
   }
   pf[2] = v;
   pf[3] = x;
   return 0;
}

Edited 2026-06-21 04:41 by matherp
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 645
Posted: 06:12pm 21 Jun 2026
Copy link to clipboard 
Print this post

Wow! This is a great leap forward in simplifying the compilation of CSubs. Thank you  
Edited 2026-06-22 04:14 by PeteCotton
 
Bleep

Guru

Joined: 09/01/2022
Location: United Kingdom
Posts: 817
Posted: 09:20pm 21 Jun 2026
Copy link to clipboard 
Print this post

Hi Peter, I'd really like to give this a try, but I'm away till a week Tuesday, :-( but I'll give it test run asap after that.
Regards Kevin.
 
Bleep

Guru

Joined: 09/01/2022
Location: United Kingdom
Posts: 817
Posted: 08:30pm 05 Jul 2026
Copy link to clipboard 
Print this post

Hi Peter,
After tearing my hair out to get the environment set up to be able to compile, I've now got a compile error I don't know how to fix, it seems to be a problem with MOD or %, if I remove it the program compiles, I've tried declaring i as a standard int, just in case, but I still get an error.
undefined reference to `__aeabi_ldivmod or _idivmod. I have checked and % is a fundimental part of C, so in theory no extra libraries needed. Any help appreciated.
#include "PicoCFunctions.h"

long long pixie(double *xsq, double *ysq, double *x0, double *y0, long long *a, long long *mi, long long *c)
{
long long i;
double x=0, y=0;
//Do :xsq=x*x:ysq=y*y:y=2*x*y+y0:x=xsq-ysq+x0:Inc i:Loop While xsq+ysq<=4And i<mi
//'If i=mi Then:c=0:Else :c=i Mod 15:EndIf
//c=(i<mi)*i Mod 15
while((FloatToInt(FAdd(*xsq,*ysq)) <= 4) && (i < *mi))
{
 *xsq=FMul(x,x);
 *ysq=FMul(y,y);
 y=FAdd(FMul(FMul(2,x),y),*y0);
 x=FAdd(FSub(*xsq,*ysq),*x0);
}
if(i<*mi)
{
  *c=i % 15;
}
else
{
  *c=0;
}
return 0;
}

Error
PS C:\Users\Kevin\pico\mandelcsub> python armcfgen.py pixie.c --compile -n pixie -e pixie -I C:\Users\Kevin\pico\mandelcsub
C:/Program Files (x86)/Arm GNU Toolchain arm-none-eabi/14.2 rel1/bin/../lib/gcc/arm-none-eabi/14.2.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\Kevin\AppData\Local\Temp\tmpo6fktbvd.o: in function `pixie':
pixie.c:(.text.pixie+0x168): undefined reference to `__aeabi_ldivmod'
collect2.exe: error: ld returned 1 exit status
Traceback (most recent call last):
 File "C:\Users\Kevin\pico\mandelcsub\armcfgen.py", line 274, in <module>
   main()
   ~~~~^^
 File "C:\Users\Kevin\pico\mandelcsub\armcfgen.py", line 239, in main
   elf = link(objs, args.entry)
 File "C:\Users\Kevin\pico\mandelcsub\armcfgen.py", line 198, in link
   subprocess.run([GCC, "-nostartfiles", "-nostdlib",
   ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                   "-Wl,-T," + ld.name, "-Wl,-e," + entry, "-Wl,--no-warn-rwx-segments",
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                   "-o", elf, *objs], check=True)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "C:\Program Files\Python313\Lib\subprocess.py", line 579, in run
   raise CalledProcessError(retcode, process.args,
                            output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['arm-none-eabi-gcc', '-nostartfiles', '-nostdlib', '-Wl,-T,C:\\Users\\Kevin\\AppData\\Local\\Temp\\tmpwhf0biob.ld', '-Wl,-e,pixie', '-Wl,--no-warn-rwx-segments', '-o', 'C:\\Users\\Kevin\\AppData\\Local\\Temp\\tmp4842apy3.elf', 'C:\\Users\\Kevin\\AppData\\Local\\Temp\\tmpo6fktbvd.o']' returned non-zero exit status 1.
PS C:\Users\Kevin\pico\mandelcsub>

Edited 2026-07-06 06:33 by Bleep
 
disco4now

Guru

Joined: 18/12/2014
Location: Australia
Posts: 1133
Posted: 07:52am 06 Jul 2026
Copy link to clipboard 
Print this post

undefined reference to `__aeabi_ldivmod' means you are trying to access a function in __aeabi library which is not available. The MOD function is not available in the CallTable functions. You might be able to do it without 64bit functions as below.
i.e. Use the IDiv(a,b) function in the CallTable.

//*c=i % ;
long long j;
j=IDiv(i,15)
j=(j<<4)-j   //Multiply by 16 (shift left 4) then -j for 15x
*c=i-j
F4 H7FotSF4xGT
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 11636
Posted: 08:27am 06 Jul 2026
Copy link to clipboard 
Print this post

Assuming I do a maintenance build sometime, I'll include IMod(a,b) as a new function in the header and code. At the moment you will have to use Gerry's method. The IDiv function was added sometime ago for exactly the same reason.
 
Bleep

Guru

Joined: 09/01/2022
Location: United Kingdom
Posts: 817
Posted: 08:47am 06 Jul 2026
Copy link to clipboard 
Print this post

Hi, ok thanks for the info. Presumably the good old AI is wrong again, % isn't a fundamental part of C? it's in a math library. :-(

Thanks for adding to the function library.
Regards Kevin
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 11636
Posted: 08:53am 06 Jul 2026
Copy link to clipboard 
Print this post

The RP chips don't have 64-bit native integer support (the CMM2 chip does). For simple maths like add, subtract and multiply the compiler generates in-line code to execute them. 64 bit divide is more complex so it invokes a library routine. CSUBs do not have access to library routines unless they are in the header file because there is no way for them to know where they are in the current build.
 
disco4now

Guru

Joined: 18/12/2014
Location: Australia
Posts: 1133
Posted: 10:36am 06 Jul 2026
Copy link to clipboard 
Print this post

  matherp said   For simple maths like add, subtract and multiply the compiler generates in-line code to execute them

Peter,
Is this for long long i.e. 64bit integers or do you need to convert to 32bit int as shown in the example in the  PDF manual.

long long sqr(long long *a)
{
int v = (int)(*a); /* 32-bit multiply, avoids __aeabi_lmul */
*a = (long long)(v * v);
return 0;
}
x% = 5 : sqr x% : Print x% ' 25

If GCC also generates code for the long long 64bit integers I don't think the PDF spells this out.
F4 H7FotSF4xGT
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 4352
Posted: 10:46am 06 Jul 2026
Copy link to clipboard 
Print this post

  Bleep said  Hi, ok thanks for the info. Presumably the good old AI is wrong again, % isn't a fundamental part of C? it's in a math library. :-(

Regards Kevin

Er, % is definitely part of C - but how it's implemented on a particular CPU etc is up for grabs (same as for any other part of C).

Happening to call a library function is normal and very much allowed (even if frustrating!).

John
Edited 2026-07-06 20:46 by JohnS
 
Bleep

Guru

Joined: 09/01/2022
Location: United Kingdom
Posts: 817
Posted: 03:06pm 06 Jul 2026
Copy link to clipboard 
Print this post

Hi again,
Here is a recursive, MMBasic implimentation of a Mandelbrot, using a CSub for the innermost tight loop.
It speeds up the calculation from using pure Basic by about 3X or 7X if you don't use TraceCache, I could go further, but for now I just wanted to be able to know how to use CSubs and what the constraints were. :-)
It runs best on a VGA screen, but will also work on a LCD, it doesn't scale itself.
Regards Kevin.

'Option Base 0
Option Default None
Option Explicit
'Option Cache Debug On
'Option Profiling On
Option TraceCache On 32

Dim Float y0,x0,x1,y1,x2,y2
'Dim Float x,y,xsq,ysq,xmin=-2.0,xmax=-1.5,ymin=-0.12,ymax=0.12
Dim Float x,y,xsq,ysq,xmin=-2.0,xmax=0.5,ymin=-1.2,ymax=1.2
Dim Integer i,j,k,l,p,pp,c
Dim Integer tcx,tcy,bs,nfi,mi=32
Dim Integer col(15)=(0,RGB(0,63,0),RGB(0,127,0),RGB(0,255,0),RGB(0,0,255),RGB(0,63,255),RGB(0,127,255),RGB(0,255,255),RGB(255,0,0),RGB(255,63,0),RGB(255,127,0),RGB(255,255,0),RGB(255,0,255),RGB(255,63,255),RGB(255,127,255),&HFFFFFF)

If MM.DEVICE$="PicoMiteVGA" Or MM.DEVICE$="PicoMiteHDMIUSB RP2350B" Then
 Mode 2  '320x240 PicoMiteVGA
EndIf

Timer =0
'8,16,32,64,128,256
'CLS RGB(White)
bs=256
For j=0 To 239 Step bs
tcy=j
For k=0 To 239 Step bs
 tcx=k
 fil(tcx,tcy,bs-1)',nfi)
 If nfi Then
  quarter(tcx,tcy,bs-2)
 EndIf
Next
Next
Print Timer
End

Sub quarter(tcx1 As Integer, tcy1 As Integer, bs1 As Integer)
Local Integer ss,s1,tx1,ty1
If bs1<3 Then fil(tcx1+1,tcy1+1,1):nfi=0:Exit Sub ' Finished square
tx1=tcx1+1:ty1=tcy1+1
ss=(bs1>>1)-1:s1=ss+2
fil(tx1,ty1,ss)',nfi)
If nfi Then quarter(tx1,ty1,ss)
'
fil(tcx1+s1,ty1,ss)',nfi)
If nfi Then quarter(tcx1+s1,ty1,ss)
'
fil(tx1,tcy1+s1,ss)',nfi)
If nfi Then quarter(tx1,tcy1+s1,ss)
'
fil(tcx1+s1,tcy1+s1,ss)',nfi)
If nfi Then quarter(tcx1+s1,tcy1+s1,ss)
End Sub

Sub fil(tx1 As Integer,ty1 As Integer, s As Integer)', nfi As Integer)
Local Integer cc,p,pp,cp
Local Float x1,y1,x2,y2
x1=xmin+(xmax-xmin)*tx1/239
y1=ymin+(ymax-ymin)*ty1/239
'single pixie
If s=1 Then x0=x1:y0=y1:Pixie x0,y0,mi,cp,cc,col():Pixel tx1,ty1,cp:End Sub
x2=xmin+(xmax-xmin)*(tx1+s)/239
y2=ymin+(ymax-ymin)*(ty1+s)/239
pp=0:nfi=1
'four sides of a square
For p=ty1 To ty1+s
x0=x1:y0=ymin+(ymax-ymin)*p/239:Pixie x0,y0,mi,cp,cc,col():If p=ty1 Then cc=cp
Pixel tx1,p,cp
x0=x2:Pixie x0,y0,mi,cp,cc,col()
Pixel tx1+s,p,cp
x0=xmin+(xmax-xmin)*(pp+tx1)/239:y0=y1:Pixie x0,y0,mi,cp,cc,col()
Pixel tx1+pp,ty1,cp
y0=y2:Pixie x0, y0,mi,cp,cc,col()
Pixel tx1+pp,ty1+s,cp
Inc pp
Next
If cc>-1 Then p=s-1:Box tx1+1,ty1+1,p,p,,cc,cc:nfi=0'Finished square
End Sub

'Sub Pixie
'i=0:x=0:y=0
'Do :xsq=x*x:ysq=y*y:y=2*x*y+y0:x=xsq-ysq+x0:Inc i:Loop While xsq+ysq<=4And i<mi
''If i=mi Then:c=0:Else :c=i Mod 15:EndIf
'c=(i<mi)*i Mod 15
'End Sub

CSub PIXIE Float, Float, Integer, Integer, Integer, Integer
       00000000
       'pixie
       B08FB5F0 60F8AF00 607A60B9 2200603B 633A2300 2200637B 62BA2300 220062FB
       623A2300 4B65627B 331C681B 33A0681B 001C681B 6AFB6ABA 6AF96AB8 000247A0
       61BA000B 4B5D61FB 331C681B 33A0681B 001C681B 6A7B6A3A 6A796A38 000247A0
       613A000B 4B55617B 331C681B 33A4681B 001C681B 681B4B51 681B331C 681B33A0
       4B4E001D 331C681B 33A0681B 001E681B 6AFB6ABA 21802000 47B005C9 6A7B6A3A
       68BB47A8 685B681A 000247A0 623A000B 4B42627B 331C681B 33A4681B 001C681B
       681B4B3E 681B331C 681B33A8 693A001D 69B8697B 47A869F9 681A68FB 47A0685B
       000B0002 62FB62BA 681B4B34 681B331C 681B3388 4B31001C 331C681B 33A4681B
       001D681B 697B693A 69F969B8 000247A8 0010000B 47A00019 000B0002 DC182B00
       2A04D101 6B3AD815 20016B7B 18122100 633A414B 687B637B 685B681A 42996B79
       E770DA00 42996B79 6B39D103 D2004291 687BE769 685B681A 42996B79 6B79DB05
       D10E4299 42916B39 6B3BD20B 4013220F 6D7A00DB 681A18D3 6839685B 604B600A
       6839E004 23002200 604B600A 68186D3B 683B6859 685B681A D1014290 D0054299
       22016D39 17D34252 604B600A 23002200 00190010 B00F46BD 46C0BDF0 E000ED08
End CSub


This is the C for the CSub above.
#include "PicoCFunctions.h"

long long pixie(double *x0, double *y0, long long *mi, long long *cp, long long *cc, long long *col)
{
long long i=0,c;
double xsq,ysq,x=0, y=0;
//Do :xsq=x*x:ysq=y*y:y=2*x*y+y0:x=xsq-ysq+x0:Inc i:Loop While xsq+ysq<=4And i<mi
//'If i=mi Then:c=0:Else :c=i Mod 15:EndIf
//c=(i<mi)*i Mod 15
do {
     xsq=FMul(x,x);
     ysq=FMul(y,y);
     y=FAdd(FMul(FMul(2,x),y),*y0);
     x=FAdd(FSub(xsq,ysq),*x0);
   } while((FloatToInt(FAdd(xsq,ysq)) <= 4) && (++i < *mi));
// Set up pixel colour
if(i<*mi)
{ *cp=col[i & 15]; }
else
{ *cp=0; }
// Check for colour change
if(*cc != *cp)
{ *cc = -1; }
return 0;
}

'Use the command line to build, see documentation.
'python armcfgen.py pixie.c --compile -n pixie -e pixie -I C:\Users\Kevin\pico\mandelcsub
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 11636
Posted: 04:10pm 06 Jul 2026
Copy link to clipboard 
Print this post

Excellent, glad you got it all to work. Windows, Mac or Linux?
 
Bleep

Guru

Joined: 09/01/2022
Location: United Kingdom
Posts: 817
Posted: 05:23pm 06 Jul 2026
Copy link to clipboard 
Print this post

For now Windows, because I know that's what you use, so I thought I'd stand a better chance of getting it going, now I 'sort of' know what to do I might give Linux a go, just for a bit of extra punishment. :-( Especially as the rest of this week is looking to be uncomfortably hot....
 
Bleep

Guru

Joined: 09/01/2022
Location: United Kingdom
Posts: 817
Posted: 11:44am 07 Jul 2026
Copy link to clipboard 
Print this post

I've also now tested it on a 2040 with LCD and the above code works without change, in other words the CSub is, as you said, fully compatible with both types of Pico processor. I also tried the steroids version of Bubble Universe on the 2040 & that worked well as well.
Thanks again.
Regards Kevin.
Edited 2026-07-07 22:39 by Bleep
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2026