![]() |
Forum Index : Microcontroller and PC projects : CMM2: V5.05.04b3a: CSUBs - not for the faint hearted!!!!
Page 1 of 5 ![]() ![]() |
|||||
Author | Message | ||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10066 |
I've managed to make a very initial implementation of CSUBs on the CMM2 ![]() http://geoffg.net/Downloads/Maximite/CMM2_Beta.zip Because of limitations on command slots I can't implement CSUBs and CFunctions so I have chosen the former as parameters are always passed by reference so a CSUB can return an answer and in any case MMBasic functions don't support arrays as return values which is likely one of the major uses of CSUBs. To use the Cfunction you need to install the GCC ARM compiler I'm using gcc-arm-none-eabi-9-2020-q2-update-win32.exe The commands for compiling and linking are: ![]() You need the linker file arm-gcc-link.zip You then need to convert the .elf file to the Basic code. I'm doing this using a MMBASIC for DOS program armcfgenV142.zip As with the PIC there are two variants for the conversion merge and join. Use merge if you need one C function to call another in which case the entry is always to main{}. Use join if the c function is self contained (read the PIC docs for more info) I have developed a first cut header file that allows the CSUB to call some internal firmware routines - same concept as the PIC ARMCFunctions.zip Below is my test C source and test programs. The GCC compiler will give warnings as we are using main{} in a non-standard way. You can convert the the code as either joined, in which case each function can be called separately, or merged, in which case the call is to main{} First tests appear to suggest that floats can be used as CFunction parameters - this is much better than the situation on the PICs. Of course it is up to the programmer to make sure the C code matches the calling Basic in terms of parameters. If you are new to CFunctions and CSubs then read the various tutorials I posted. There has been no testing so far of the structure used in the PICs for defining and using constant data. WARNING: CSUBs are extremely difficult to debug and can crash and even overwrite the firmware - buyer beware! #include "ARMCFunctions.h" /* * This CFunction will reverse the characters in an MMBasic string * eg if the string is "ABCDEFG" then on return the string will * be "GFEDCBA" */ void strrev(unsigned char in[], unsigned char out[]) { unsigned int i; unsigned int len; //MMBasic stores the length of the string in the //first,0th,byte len=in[0]; //Set the length of the output string out[0]=len; //Do the reverse copy for(i=1;i<(len+1);i++){ out[i]=in[len-i+1]; } //and return the length of the string } void waitabit(long long int *w){ uSec(*w); } void doubleit(long long int *c, long long int *b){ char a[10]; IntToStr(a,*c,10); MMPrintString(a); putConsole(13); putConsole(10); *b= *c + *c; } void main(long long int *c, long long int *b){ char a[10]; doubleit(c,b); IntToStr(a,*c,10); MMPrintString(a); putConsole('X'); putConsole(13); putConsole(10); } a%=7 b%=2 test a%,b% print b% end CSUB test 0000003A 'strrev B085B480 6078AF00 687B6039 60BB781B B2DA68BB 701A683B 60FB2301 68BAE00D 1AD368FB 687A3301 6839441A 440B68FB 701A7812 330168FB 68BB60FB 68FA3301 D3EC429A BF00BF00 46BD3714 7B04F85D 00004770 'waitabit B082B580 6078AF00 681B4B06 687B4619 2300E9D3 46184613 BF004788 46BD3708 BF00BD80 080002A0 'doubleit B089B5F0 6078AF02 4B156039 461E681B E9D3687B F1072300 200A010C 46089000 4B1047B0 461A681B 030CF107 47904618 681B4B0D 4798200D 681B4B0B 4798200A E9D3687B 18942300 0503EB43 462B4622 E9C16839 BF002300 46BD371C BF00BDF0 080002C4 080002C0 080002A4 'main B089B590 6078AF02 68396039 F7FF6878 4B11FFC1 461C681B E9D3687B F1072300 200A010C 46089000 4B0C47A0 461A681B 030CF107 47904618 681B4B09 47982058 681B4B07 4798200D 681B4B05 4798200A 371CBF00 BD9046BD 080002C4 080002C0 080002A4 End CSUB CSUB doubleit 00000000 B089B5F0 6078AF02 4B176039 461E681B E9D3687B F1072300 200A010C 46089000 4B1247B0 461A681B 030CF107 47904618 681B4B0F 47982058 681B4B0D 4798200D 681B4B0B 4798200A E9D3687B 18942300 0503EB43 462B4622 E9C16839 BF002300 46BD371C BF00BDF0 080002C4 080002C0 080002A4 End CSUB a%=3 b%=7 doubleit a%,b% print "b=";b% end |
||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
Yes, Yes, Yes!!! I mean, Thank You, Peter this is awesome! |
||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
Also, the biggest issue I see with this is that with CSUBs, Mauro Xavier will somehow figure out how to run Crysis on this thing and make our efforts look lame in comparison ![]() |
||||
thwill![]() Guru ![]() Joined: 16/09/2019 Location: United KingdomPosts: 4251 |
I welcome the addition with exactly the same reservations. Thank you Peter. Tom MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
disco4now![]() Guru ![]() Joined: 18/12/2014 Location: AustraliaPosts: 971 |
Hi Peter, Good result. A thought about saving some tokens. Is there a possibility Define Font, End Font, CSUB ,END CSUB, CFUNCTION ,END CFUNCTION could all share the same token. They all have similar format when stored, only ever happen in pairs and are never nested. Whenever the token is encounter another pointer is looked up to see which particular one it is based on which occurrence of the token you are at. At a minimum could each set share its start and end token, if you get the token a second time you know it must be an END. Just thinking about possibility of future CSUBs for the H7. Regards Gerry Latest F4 Latest H7 FotS |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10066 |
I'm really happy with the way CSUBs are working out. The structures for constant data work as per the PIC and floating point just works - much better than the PIC. NB: you cannot use standard C library calls so things like strcpy will need coding into your program. The source of all the standard libraries is widely available online My latest test program needs a tiny firmware change to add FloatToStr to the list of internal functions supported by the CMM2 firmware but I'll be going through this list in the next days to add lots of stuff. These also have to go into the header file that you use to complie. Note, the getFPC function is very slightly different from the PIC in that the address has a 1 subtracted. This is a feature of the ARM processor. Functions are called with an address that isn't on a 32-bit word boundary but 1 byte offset. This is some internal mechanism which tells the processor which instruction set to use. In our case this is the Thumb instruction set. Minor revision to the complilation options to get rid of the warning on main arm-none-eabi-gcc -c -mcpu=cortex-m7 -mfpu=fpv5-d16 -mfloat-abi=hard -mthumb -Wall -Wno-main -ffunction-sections -O0 -fPIC -I. main.c -o main.o arm-none-eabi-ld -nostartfiles -T arm-gcc-link.ld -o main.elf main.o /* * This CFunction will reverse the characters in an MMBasic string * eg if the string is "ABCDEFG" then on return the string will * be "GFEDCBA" */ #include "ARMCFunctions.h" void strrev(unsigned char in[], unsigned char out[]) { unsigned int i; unsigned int len; //MMBasic stores the length of the string in the //first,0th,byte len=in[0]; //Set the length of the output string out[0]=len; //Do the reverse copy for(i=1;i<(len+1);i++){ out[i]=in[len-i+1]; } //and return the length of the string } void waitabit(long long int *w){ uSec(*w); } MMFLOAT doubleit(MMFLOAT *c){ return *c *2.0; } __attribute__((noinline)) void getFPC(void *a, void *b, volatile unsigned int *c) { *c = (unsigned int) (__builtin_return_address (0) - (b -a) - 1) ; } void p_string(const char *s){ volatile unsigned int libAddr ; getFPC(0,&&getFPCLab,&libAddr) ; getFPCLab: { } unsigned char * testData = (unsigned char *)((void *)s + libAddr); MMPrintString(testData); } void p_int(int a,int base){ char b[64]; IntToStr(b,a,base); MMPrintString(b); } void p_float(float a,int m, int n, char c){ // m is the nbr of chars before the decimal point // n is the nbr chars after the point // ch is the leading pad char char b[14]; FloatToStr(b,a, m, n ,c); MMPrintString(b); } void main(long long int *c, MMFLOAT *b){ volatile unsigned int libAddr ; getFPC(0,&&getFPCLab,&libAddr) ; getFPCLab: { } static const char crlf[]="\r\n"; static const char s1[]="First variable = "; static const char s2[]="Second variable = "; p_string(s1); p_int(*c,10); p_string(crlf); p_string(s2); p_float(doubleit(b),4,2,' '); p_string(crlf); } Edited 2020-07-05 23:30 by matherp |
||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
Is there a way to call any form of BLIT form within a CSUB? There is no BLIT provided in the ARMCFunctions.h header. If not available what's the most efficient way to copy a block of pixels from one video page to another? |
||||
Sasquatch![]() Guru ![]() Joined: 08/05/2020 Location: United StatesPosts: 376 |
Take a look at ReadBufferFast(),DrawBufferFast() and MoveBuffer() in the latest version of ARMCFunctions.h -Carl |
||||
MauroXavier Guru ![]() Joined: 06/03/2016 Location: BrazilPosts: 303 |
I appreciate your comment ![]() Crysis not, but who knows Doom? Or even Quake if I didn't have a job ![]() Joking apart, I intent to port the Wolfenstein 3D at maximum using only MMBasic until my patience comes to the limit, then I will see something with the CSUBs if the performance proves to be unsustainable at all. I'm thinking how CSUBs can enhance the raycasting without rewrite a big part of the code because what turns it slow in MMBasic is the excessive loop count. If I rewrite all the loops then practically I'm making a C port of a raycasting engine with MMBasic running only the game core logic, which is not my purpose. My goal is to any user touch the code and learn with it (even though it is considered complex), and C language with compiling involved (If anyone wants to change something) can put away the beginners, and mainly, kills that famous phrase "it's made only in BASIC?". For sure I will use CSUBs, but I will hold my will for a while. |
||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
I have the same reservations about CSUBs in their present state: - it's no longer pure BASIC so bragging rights are dimminished - CSUBs are even more opaque than ASM blocks in BASIC code thus harder for anyone to learn from the code - I'd rather if MMBasic had an option to compile or JIT the running code instead That said, the 10x or so speed up might be tempting whenever one hits a performance limit that seems unsurmountable otherwise. But if all you're doing is coding in C and then uploading to Maximite with just a thin MMBasic wrapper around the CSUB where's the fun in that? |
||||
Sasquatch![]() Guru ![]() Joined: 08/05/2020 Location: United StatesPosts: 376 |
I think you make some valid points! Here are my thoughts: 1. Keep in mind that CSub code is essentially an ASM block reduced to hexidecimal OP-codes. It just isn't in a mnemonic form that is more easily human readable. CSubs are usually small and the source code might be included as a comment block in the .bas file where it is used. Granted this is not as convenient as compiling or assembling the code inline. 2. MMBasic itself is compiled C code and somewhat "opaque" to the user. (although the source code is available) Perhaps it might be better to think of a CSub as an extension of the MMBasic environment. For performance reasons, MMBasic is out of tokens for new features that might be added otherwise, CSubs can fill the gap in some special cases where extreme performance is needed. To each his own! I am happy to have the ability to use a CSub if needed. I feel it may also open up a new world of possibilities to those who wish to learn something new. -Carl |
||||
CircuitGizmos![]() Guru ![]() Joined: 08/09/2011 Location: United StatesPosts: 1427 |
The point of the CSUB capability isn't as much for writing a full program as it is for adding timing-sensitive or speed-improving code to the MMBasic program that is being written. For example I have to interface a Glonk 6000 retro-encabulator to the 'mite. Writing it in BASIC is tough because of some timing issues. So I write basically a "driver" in C and build a CSUB. Then the rest of the code is written in MMBasic. Other people can use the CSUB driver for their own code. CSUB isn't meant for everyone using the CMM2 to be able to create/write, but can be used by anyone that needs the result. Micromites and Maximites! - Beginning Maximite |
||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
Yeah, that I know. The slight trepidation is that it might be tempting for the more competitive folks to write the bulk of performance sensitive code in a CSUB thus nullifying all the fun of working on a simple self contained computer. That said, I'm not at all against having them. In fact I'm really grateful they've been added by Pete although I'd be even happier if they were available on a "pure" Maximite i.e. without a cross compiler sitting on a separate PC getting involved. The pinnacle would be the ability to edit a .C file in the Maximite editor and have a command to generate the ELF bytecode right there on the Maximite with the ability to paste into your .bas code. The friction of CSUBs is currently their biggest drawback in my opinion. Edited 2020-07-09 03:28 by abraxas |
||||
JohnS Guru ![]() Joined: 18/11/2011 Location: United KingdomPosts: 3998 |
It's not ELF bytecode. It's Arm ASM (in binary), i.e. you'd need a C-to-Arm-binary compiler, one that's not hosted on any OS (as there isn't one). Doable but not easy. Don't hold your breath for it becoming available. John |
||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
Hmm... not sure why you recon that's not easy. As long as there is a CC compiler on the Maximite it should be quite simple to create a command that invokes it on a .CC file on the SSD. The output of CC will be a .obj file which could be statically linked with whatever necessary dependencies the CSUB is pulling. Then it's just a matter of converting the hex data from the compiler to a string of chars. Yes, I misspoke I know it's just ARM machine code and not bytecode like in a JVM or CLR. I just misspoke - I actually meant ARM binary code. Doesn't really change my line of thinking though. As long as it's possible for the CMM2 firmware to bundle in a C compiler and a linker (even if it's hidden to us, CMM2 users) it's entirely possible to run it on the Maximite without having to have an x86 PC involved. So the workflow would be like: 1. you start your Maximite 2. you edit a mycsub.c file 3. you save mycsub.c file and get out of the editor 4. on the command line you issue something like MAKECSUB mycsub.c 5. step 4 outputs mycsub.inc file that looks like this: CSUB mycsub arg1 as Integer, arg2 as String 9548 8576 3987 .... END CSUB 6. You use mycsub.inc as any other .inc file in MMBasic What kind of problems am I overlooking? Edited 2020-07-09 05:32 by abraxas |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10066 |
Its not and never will be. Way to big for the available memory |
||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
Its not and never will be. Way to big for the available memory Yeah, that is a showstopper for sure. Unless you used something like the Tiny C Compiler. I assume perhaps wrongly that you can still use gcc to develop the firmware but bundle TCC into the firmware? C symbols are ANSI standardised, right? TCC is allegedly only around 100KB in size. |
||||
MauroXavier Guru ![]() Joined: 06/03/2016 Location: BrazilPosts: 303 |
I'm not worried about "competition" as for me the CMM2 is a hobby and I see a good community born around it. If anyone will use a CSUB to make incredible things, I respect it, but for me isn't pure BASIC anymore. For me the most fun part of the CMM and CMM2 that it is always a self-contained computer. I use a PC only for editing images and sounds, the rest I used only the editor since the first CMM. As I said before, the CSUB inserts new possibilities and maybe is a must for some programmers, but as my purpose is to make cool games and demos only in BASIC, opening a world to beginners learn with the code, I will close the CSUB in my toolchains. |
||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
I'm not worried about "competition" as for me the CMM2 is a hobby and I see a good community born around it. If anyone will use a CSUB to make incredible things, I respect it, but for me isn't pure BASIC anymore. For me the most fun part of the CMM and CMM2 that it is always a self-contained computer. I use a PC only for editing images and sounds, the rest I used only the editor since the first CMM. As I said before, the CSUB inserts new possibilities and maybe is a must for some programmers, but as my purpose is to make cool games and demos only in BASIC, opening a world to beginners learn with the code, I will close the CSUB in my toolchains. Would it change your mind if CSUBs worked in the way I described in my reply to JohnS here? |
||||
thwill![]() Guru ![]() Joined: 16/09/2019 Location: United KingdomPosts: 4251 |
This is just idle stirring of the pot ... Why would this have to be in the firmware? If you have the skills and tenacity then the compiler and linker could be written in BASIC with or without CSUBs as you desire. However in general people don't just knock up a C compiler overnight and as yet nobody has volunteered ![]() The addition of CSUBs offers other possibilities. I believe a FORTH or for that matter Z-machine interpreter could be implemented in the form of a CSUB and embedded in a BASIC program and used to run code faster than interpreted BASIC alone without the need to supply ARM machine-code. If you want something editable inline with BASIC (and you don't want a pre-processing or transcompiling step) then I would imagine it would probably need a very simple syntax and ~1:1 relationship with ARM machinecode so that the compiler/assembler could be squeezed into the firmware, i.e. either (a subset of) ARM assembler itself or some "made up assembler-like" language. Also given that MMBasic is already pushing on its design limit in terms of number of tokens and other complexity you would probably end up having to do a significant rewrite with all the problems with compatibility and adoption that would entail. Peter if you are reading this is there any significant space left for firmware or is it tinkering room only? the answer to that might close down the conversation pretty quickly. Regards, Tom Edited 2020-07-09 07:34 by thwill MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
Page 1 of 5 ![]() ![]() |
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |