Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 03:58 03 May 2024 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 : MM2 Playing with CFunctions LTRIM,RTRIM

     Page 1 of 2    
Author Message
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1133
Posted: 12:49am 14 Jul 2017
Copy link to clipboard 
Print this post

As a result of my playing with Cfunctions
I can present some simple code examples.

Each call takes about 0,2ms.

Syntax for CFunction header:
ltrim(STRING) STRING
rtrim(STRING) STRING



Dim x$, a$
x$=" Hello World "
Print
Print "input string:>"x$;"<",Len(x$)
Print

Timer=0
For i = 1 To 1000
a$=rtrim(x$)
Next i
Print "Time for Rtrim:";Timer/1000;"ms"

Print ">";a$;"<",Len(a$)
Print

Timer=0
For i = 1 To 1000
a$=ltrim(x$)
Next i
Print "Time for Ltrim:";Timer/1000;"ms"

Print ">";a$;"<",Len(a$)
End

' File rtrim.bas written 13-Jul-2017 23:05:06
'
CFunction ltrim(STRING) STRING
00000000
27BDFFE0 AFBF001C AFB10018 AFB00014 00808021 90910000 3C029D00 8C420040
0040F809 26240001 1220001A A0400000 00002021 24060020 24830001 306300FF
0223282B 54A00006 02248823 26100001 82050000 50A6FFF8 00602021 02248823
323100FF 5220000B A0510000 24030001 00432021 92050000 A0850000 24630001
306300FF 0223202B 1080FFF9 26100001 A0510000 8FBF001C 8FB10018 8FB00014
03E00008 27BD0020
End CFunction
'
CFunction rtrim(STRING) STRING
00000000
27BDFFE0 AFBF001C AFB10018 AFB00014 00808821 90900000 3C029D00 8C420040
0040F809 26040001 1200001D A0400000 02301821 00002821 10000002 24070020
00802821 24A40001 308400FF 0204302B 54C00005 02058023 80660000 10C7FFF8
2463FFFF 02058023 321000FF 5200000C A0500000 24030001 26310001 00432021
92250000 A0850000 24630001 306300FF 0203202B 1080FFF9 26310001 A0500000
8FBF001C 8FB10018 8FB00014 03E00008 27BD0020
End CFunction


C source:

//--------------------------------------------
//
// LTrim and RTrim (VB style)
// requires MMBasic for MX170 V5.02 or later
// Syntax for CFunction header:
// ltrim(STRING) STRING
// rtrim(STRING) STRING

//
// eg a$=ltrim(" Hello World")
// PRINT a$ ----> "Hello World"
//--------------------------------------------
// For the TBS-Forum 13.07.2017
//--------------------------------------------

#define _SUPPRESS_PLIB_WARNING
#include <plib.h>
#include "cfunctions.h"

//
// strip leading whitespace from a string
//
char *ltrim(char *string){
unsigned char len = (unsigned char) string[0];
unsigned char i = 0, tr_len;
char *s = GetTempMemory(len+1);

s[0] = 0; // for empty string

if (len > 0) {
//find first occurrence of non whitespace char from the left
while(++i <= len && *++string == 32);

i--; // number of spaces
//remaining string length = len - i
tr_len = len-i;
//copy remaining string to s[]
for(i = 1; i <= tr_len; i++) s[i ] = *string++;

s[0] = tr_len;// store BASIC (trimmed) string length
}
return s;
}

//
// strip trailing whitespace from a string
//
char *rtrim(char *string){
char *tr_string = string; // copy pointer
unsigned char len = (unsigned char) string[0];
unsigned char i = 0, tr_len;
char *s = GetTempMemory(len+1);

s[0] = 0; // for empty string

//if we have an empty string, nothing to do
if (len != 0) {
//find first occurrence of non whitespace char from the right
while(++i <= len && *(--string+len+1) == 32);

i--; // number of spaces
//remaining string length = len - i
tr_len = len-i;
//copy remaining string to s[]
for(i = 1; i <= tr_len; i++) s[i ]= *++tr_string;

s[0] = tr_len;// store BASIC (trimmed) string length
}
return s;
}

void main(){}


to be continued ...

Michael

edit: The forum software "eats" the [i ]

Download:2017-07-14_142016_LTRIM_RTRIM_CFunctions.zip
Edited by twofingers 2017-07-15
 
led-bloon

Senior Member

Joined: 21/12/2014
Location: Australia
Posts: 203
Posted: 01:20am 14 Jul 2017
Copy link to clipboard 
Print this post

What about the TAB character CHR$(09)?
Miss you George
 
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1133
Posted: 01:37am 14 Jul 2017
Copy link to clipboard 
Print this post

  led-bloon said   What about the TAB character CHR$(09)?


This is only for whitespaces (VB style LTRIM & RTRIM)

It would be easy to change the code to eg LTrim(Inputstring$,chr$(9)). But that's not what I intended.

It should be rather a example HOW to make such CFunctions.
They need MMBasic V5.02 or later!

Regards
Michael
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9066
Posted: 02:31am 14 Jul 2017
Copy link to clipboard 
Print this post

Nice one, Michael.

We need as many Cfunction/Csub writers as possible, to take the workload off matherp

C is a complete mystery to me, and I don't have the time to it, otherwise I would also be learning to write Cfunction/Csub stuff.
Smoke makes things work. When the smoke gets out, it stops!
 
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1133
Posted: 03:05am 14 Jul 2017
Copy link to clipboard 
Print this post

  Grogster said   We need as many Cfunction/Csub writers as possible, to take the workload off matherp


Thanks!
And we need also more examples. CFunctions are fantastic!

Kind regards
Michael
 
paceman
Guru

Joined: 07/10/2011
Location: Australia
Posts: 1328
Posted: 03:18am 14 Jul 2017
Copy link to clipboard 
Print this post

Yes, +1 from me too, as Grogster said Michael - nice job! Now where is that library?

Greg
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3165
Posted: 12:28pm 14 Jul 2017
Copy link to clipboard 
Print this post

Excellent, would it be OH if I included it in the Micromite MMBasic distribution?

BTW, you do not have to worry about tab characters. MMBasic automatically converts them to spaces on INPUT, LINE INPUT, program load, etc.

Geoff
Geoff Graham - http://geoffg.net
 
piclover
Senior Member

Joined: 14/06/2015
Location: France
Posts: 134
Posted: 03:48pm 14 Jul 2017
Copy link to clipboard 
Print this post

There are obvious optimizations to make, the first two being of great benefit:

- You should test for the case when there is nothing for ltrim() and rtrim() to do, and return 'string' itself in these cases (thus sparing a memory reservation and string copy).

- When there is something to do (spaces to remove), you should reserve memory for the final string only after having calculated the amount of bytes actually needed (thus using less memory during the function call).

- You could avoid entirely the use of 'tr_len', by reusing 'len', avoid incrementing 'i' when not needed, in the while loop, etc...

E.g. for ltrim:

char* ltrim(char* string) {
char* ret = string;

// When either the string is empty, or contains no leading space,
// return the same string.
unsigned char len = (unsigned char)*string;
if (!len || *++string != ' ') {
return ret;
}

// At this point, we already know that first character in string is a space...
unsigned char spaces = 1;
// Search first non-space character
while (spaces < len && *++string == ' ') {
++spaces;
}

len -= spaces; // Remaining string length = len - spaces

// Allocate our return string
ret = GetTempMemory(len + 1);

// Copy the rest of the string
char* s = ret;
*s++ = (char)len; // Store BASIC (trimmed) string length
while (len--) {
*s++ = *string++;
}

return ret;
}
Edited by piclover 2017-07-16
 
CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 1985
Posted: 02:45am 15 Jul 2017
Copy link to clipboard 
Print this post

  paceman said   Yes, +1 from me too, as Grogster said Michael - nice job! Now where is that library?

Greg


here

includes all of the old MMbasic library file and a a growing number of articles from various contributors.
 
paceman
Guru

Joined: 07/10/2011
Location: Australia
Posts: 1328
Posted: 09:01pm 15 Jul 2017
Copy link to clipboard 
Print this post

Thanks CaptainB - I have previously had a browse of the code from Hugh's Maximite library that you'd already added but I'm just setting up an account now.
How do you intend new code or snippets to be added to the Wiki? Is this going to be up to each account holder or will you be putting useful things like this Cfunction of Michael's into it?

Greg

BTW: I'm not familiar with how Wiki's operate but I guess I'll learn. Do you have a link to a good primer, preferably less than 20 pages long.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8592
Posted: 09:59pm 15 Jul 2017
Copy link to clipboard 
Print this post

  Quote  - When there is something to do (spaces to remove), you should reserve memory for the final string only after having calculated the amount of bytes actually needed (thus using less memory during the function call).


Memory is reserved in 256-byte chunks so whatever you request it is rounded up to the next amount divisible by 256
 
piclover
Senior Member

Joined: 14/06/2015
Location: France
Posts: 134
Posted: 10:46pm 15 Jul 2017
Copy link to clipboard 
Print this post

  matherp said  
  Quote  - When there is something to do (spaces to remove), you should reserve memory for the final string only after having calculated the amount of bytes actually needed (thus using less memory during the function call).


Memory is reserved in 256-byte chunks so whatever you request it is rounded up to the next amount divisible by 256

Good to know. In this case, always reserving 256 bytes in ltrim()/rtrim() would be simpler.
However, this is an implementation detail that could change in future a Micromite firmware, so the future-proof way to do it is to reserve the amount necessary for the return string...Edited by piclover 2017-07-17
 
piclover
Senior Member

Joined: 14/06/2015
Location: France
Posts: 134
Posted: 10:47pm 15 Jul 2017
Copy link to clipboard 
Print this post

Please ignore this message; I meant to "Edit" and wrongly pushed "Quote"... and since there's no "Delete post" option on this forum...Edited by piclover 2017-07-17
 
CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 1985
Posted: 07:28am 16 Jul 2017
Copy link to clipboard 
Print this post

  paceman said  
How do you intend new code or snippets to be added to the Wiki? Is this going to be up to each account holder or will you be putting useful things like this Cfunction of Michael's into it?

Greg

BTW: I'm not familiar with how Wiki's operate but I guess I'll learn. Do you have a link to a good primer, preferably less than 20 pages long.


Hi Greg. Anyone can browse the code etc with no restriction. Having an account allows you to add your own code, modify existing pages (to improve/discuss/correct) etc. I have added code as administrator (and *always* credit) but it is nicer if people add their own because:

1. contributions can be seen by user (by clicking on the user name) which means the credit is given automatically
2. it promotes the idea of community growing the library with no single person as gatekeeper.

The problem with libraries is everyone wants them but the push to keep adding fades over time.

Wikis are really easy to drive. Posting on TBS has already got you in the mindset of using Markup to make sections stand-out. (One thing that always catches new users is uploading images and attachments - they have to be done before you need them in a page - this allows images to be shared between articles as they are not compartmentalized to a single page.)

Here is a nice <20 page primer that gets your pages looking pro - but the idea of a wiki is "do it your way". Have a read of the namespace home-pages. They contain hints for a good experience and any "rules" such as they are.

As a very quick crib; choose the namespace (zone) of interest from the drop-down top right, click "Create New Page" on the navi-pane on the left and you are into your new article. Give it a nice title and then write your article in the main body. Markup tags here will make your page look snazzy Tip:Select either of the "code" types to add verbatim code (I like block to make it stand out). You can choose categories at the bottom from the list (or make a new one), add any keywords for searching (not essential) and a brief comment for any changes you make.

It will always be non-commercial, free to use and advert free.

Edited by CaptainBoing 2017-07-17
 
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1133
Posted: 04:02am 17 Jul 2017
Copy link to clipboard 
Print this post

@paceman,

thanks for the kind words!


@Geoffg

  Geoffg said   Excellent, would it be OH if I included it in the Micromite MMBasic distribution?Geoff

Of course! The code is not perfect but simple & short. It's a honor to be in the MM distribution.

BTW. Is there a way for a CFunction implemented to inform Basic about a error?
Some kind of error handling/trapping? E.G. to set a error number?


@piclover

Thanks for your contribution! This is meant as a discussion thread therefore your example is highly welcome.

Is your code also free for anybody - without any restrictions - like mine?

I converted the code into a CFunction and it seems your code is significant faster than mine (for longer strings about 10%) . On the other hand my code takes less memory. This just as information. I learnt from your code and wrote a RTrim() CFunction in your style (hopefully! ).

I always appreciate any improvements (any kind, any time)!

This is my new C code for RTrim (piclovers style)
char* rtrim(char* string) {
char* ret = string; // Save string pointer

// When either the string is empty, or contains no trailing space,
// return the same string.
unsigned char len = (unsigned char)*string;
if (!len || *(--string+len+1) != ' ') {
return ret;
}
// At this point, we already know that
// the last character in string is a space...
unsigned char spaces = 1;

char* sr=++string+len; // point to the end of the string
// Search first non-space character
while (spaces <= len && *--sr == ' ') {
++spaces;
}

len -= spaces; // Remaining string length = len - spaces

// Allocate our return string
ret = GetTempMemory(256);
*ret = (char)len; // Store BASIC (trimmed) string length

sr = ret; // Copy pointer to ret string
while (len--) {// Copy the string, starting from the first char
*++sr = *++string;
}
return ret;
}


CFunctions:
'
' piclovers LTrim version
'
CFunction ltrim1(STRING) STRING
00000000
27BDFFE0 AFBF001C AFB20018 AFB10014 AFB00010 90920000 1240002C 00801021
80830001 24020020 14620027 24900001 2E420002 1440000F 24020020 26100001
82030000 1462000C 24110001 24030020 26310001 323100FF 12510022 26100001
82020000 5043FFFB 26310001 10000003 02518823 24110001 02518823 323100FF
3C029D00 8C420040 0040F809 24040100 1220000E A0510000 24430001 26040001
2631FFFF 323100FF 00918821 92050000 A0650000 26100001 1611FFFC 24630001
10000003 8FBF001C 00801021 8FBF001C 8FB20018 8FB10014 8FB00010 03E00008
27BD0020 3C029D00 8C420040 0040F809 24040100 02518823 1000FFF4 A0510000
End CFunction

'
' RTrim like piclovers LTrim
'
CFunction rtrim1(STRING) STRING
00000000
27BDFFE0 AFBF001C AFB10018 AFB00014 90820000 1040002B 00808021 00821821
80640000 24030020 54830027 02001021 02022021 2483FFFF 8085FFFF 24040020
14A4000C 24110001 24050020 26310001 323100FF 0051202B 14800006 2463FFFF
80640000 5085FFFA 26310001 10000002 00518823 00518823 323100FF 3C029D00
8C420040 0040F809 24040100 1220000E A0510000 26030001 2631FFFF 323100FF
00718821 00401821 24630001 26100001 92040000 1611FFFC A0640000 10000003
8FBF001C 00801021 8FBF001C 8FB10018 8FB00014 03E00008 27BD0020
End CFunction


I also wrote a empty CFunction for speed comparison purposes
char* empty(char* string) {
char* ret = string;
return ret;
}


'
' empty function, only returns the input string - for speed test
'
CFunction empty(STRING) STRING
00000000
03E00008 00801021
End CFunction


The result is amazing: the empty function takes (0.186ms on a MX170/28 V5.04.05) about as long as the TRIM() functions. If I put the CFunction at the end of the Basic code then I get 0.203ms and if I use a huge function name (eg "empty_longname()") then takes it 0.22ms.

Compared to 0.199ms for a LTrim(" Hello World ") is this a lot.

Something to play with:
2017-07-17_132814_CFunction_Trim_tests.zip


@CaptainBoing

It's of course a great project but I could not find out how to browse. Is there a table of content, kind of?


@Peter

Thanks for your hint about the GetTempMemory(256)!
My fault!

Michael
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3165
Posted: 05:15am 17 Jul 2017
Copy link to clipboard 
Print this post

  twofingers said   Is there a way for a CFunction implemented to inform Basic about a error?

Yes, call the error() function with a string. However defining a string in a CFunction is difficult. Probably the best way to see how it is done is to refer to Peter's tutorials.

Geoff
Geoff Graham - http://geoffg.net
 
CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 1985
Posted: 05:30am 17 Jul 2017
Copy link to clipboard 
Print this post

  twofingers said  
@CaptainBoing

Is there a table of content, kind of?

Michael


Navigation on the left, "All Pages" or "Categories" - make sure you are in the namespace you want (drop-down top right)Edited by CaptainBoing 2017-07-18
 
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1133
Posted: 07:57am 17 Jul 2017
Copy link to clipboard 
Print this post

@Geoff

Yes, that's what I saw.
I think I can do something like this:
char *error_str = GetTempMemory(256);

error_str[0]=15;
error_str[1]='m';
error_str[2]='i';
error_str[3]='s';
error_str[4]='s';
error_str[5]='i';
error_str[6]='n';
error_str[7]='g';
error_str[8]=' ';
error_str[9]='s';
error_str[10]='t';
error_str[11]='r';
error_str[12]='i';
error_str[13]='n';
error_str[14]='g';
error_str[15]='!';

// missing arg[0] + arg[1]?
if (string != NULL){
len = (unsigned char) string[0];
}
else {
error(error_str);
}

Basic output:
  Quote  [4] a$=rtrim()
Error: missing string!


The string argument for the error() function is a Basic string (Byte[0] = strlen)?

It seems to work as it should, I can even use the "ON ERROR SKIP".
Is there a list of MMBasic error messages? I thought I could somehow call some of them from the CFunction.


Here is again a list of Peters tutorials for CFunctions:

CFunctions - learning by example (1)

CFunctions - learning by example (2)

CFunctions - learning by example (3)

CFunctions - learning by example (4)

I scanned all of this and found nothing about the error().
Anyway many thanks!


@CaptainBoing,

it works, thanks! Seems I'm sometimes blind or too old!

Michael
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8592
Posted: 08:35am 17 Jul 2017
Copy link to clipboard 
Print this post

  Quote  I scanned all of this and found nothing about the error().


You can call error in the same way as I call MMPrintString in tutorial 4. Make sure all the compiler and linker switches are set exactly as in the tutorial.

Here is the error code I use in the MM2 SDcard routines. In the main code I can then just call errstring(error_number);

__attribute__((noinline)) void getFPC(void *a, void *b, volatile unsigned int *c)
{
*c = (unsigned int) (__builtin_return_address (0) - (b -a)) ;
}
void errstring(int a){
volatile unsigned int libAddr ;
getFPC(NULL,&&getFPCLab,&libAddr) ; // warning can be ignored, stupid editor
getFPCLab: { }
unsigned char *s;
static const char err0[]="Unknown error\r\n";
static const char err1[]="Disk Error\r\n";
static const char err2[]="Not Ready\r\n";
static const char err3[]="No File\r\n";
static const char err4[]="Not Opened\r\n";
static const char err5[]="Not Enabled\r\n";
static const char err6[]="No File System\r\n";
static const char err7[]="Not mounted\r\n";
s=(unsigned char *)((void *)err0 + libAddr );
if(a){
if(a==1)s=(unsigned char *)((void *)err1 + libAddr );
if(a==2)s=(unsigned char *)((void *)err2 + libAddr );
if(a==3)s=(unsigned char *)((void *)err3 + libAddr );
if(a==4)s=(unsigned char *)((void *)err4 + libAddr );
if(a==5)s=(unsigned char *)((void *)err5 + libAddr );
if(a==6)s=(unsigned char *)((void *)err6 + libAddr );
if(a==7)s=(unsigned char *)((void *)err7 + libAddr );
error(s);
}
}


 
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1133
Posted: 09:30am 17 Jul 2017
Copy link to clipboard 
Print this post

  matherp said   You can call error in the same way as I call MMPrintString in tutorial 4.

Here is the error code I use in the MM2 SDcard routines. In the main code I can then just call errstring(error_number);

Thanks, Peter! I think that should help. I will try it.

Michael


EDIT:
Found the link to the MM2 SDcard routines

EDIT2:
Nathan's post helps to understand Peters C code above (Posted: 17 July 2017 at 6:35pm).Edited by twofingers 2017-07-20
 
     Page 1 of 2    
Print this page
© JAQ Software 2024