Hmmm, that was an interesting bug -- the bytes[] array was being kept in a register because it was only 4 bytes long, instead of being put on the stack. I think that should be fixed now; the listing looks more reasonable, at least. For me I'm currently seeing that variables aren't being set; when I do "a=4" it doesn't seem to change the value of a.
Thanks,
Eric
BTW, even though it is possible to type immediate statements and have them executed, I don't think that feature is completely implemented yet. So if you type "a=1" as an immediate statement followed by "print a" you'll likely see a zero printed since the value of "a" was not remembered from the previous immediate statement. I should fix that or remove the immediate execution feature.
The latest version of fastspin in GitHub seems to work quite well! I've pushed the latest sources to GitHub.
Nice progress.
Do you have a simple summary of ebasic3 - like lines of code compiled, compile times, code binary size, memory footprints, and speeds for the PC-targeting version, and the P2-targeting version ?
( I expect those may change/improve over time, so maybe in a new thread ?)
Could be useful to compare P2 vs PC code size/speeds.
Excellent! I'm pretty happy with the fastspin C coverage so far. Eric, your craftmanship is very much appreciated. I have most of a rudimentary SPI function working as of a couple of hours ago. David Betz, thank you as well for your concise feedback to Eric - bugs are being slayed left and right. Good stuff.
Is this a legal BASIC program? I wanted to print a value followed by a tab but not end with a newline. This works with basic but it seems like it should generate a syntax error. Is comma followed immediately by a semicolon allowed in other BASICs?
list
10 for y=1 to 5
20 for x=1 to 5
30 print x*y,;
40 next x
45 print
50 next y
OK
run
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
OK
Excellent! I'm pretty happy with the fastspin C coverage so far. Eric, your craftmanship is very much appreciated. I have most of a rudimentary SPI function working as of a couple of hours ago. David Betz, thank you as well for your concise feedback to Eric - bugs are being slayed left and right. Good stuff.
Thanks! David's bug reports have been great and very helpful.
Is this a legal BASIC program? I wanted to print a value followed by a tab but not end with a newline. This works with basic but it seems like it should generate a syntax error. Is comma followed immediately by a semicolon allowed in other BASICs?
It looks like freebasic accepts it if you give it the -lang qb option (otherwise it wants all the variables dim'd).
fastspin doesn't like it. Both fastspin and freebasic will do what you want if you end the print with just a comma:
print x*y,
which is like ending it with a semicolon in that no newline is produced, but it does produce a tab.
I guess I should update the fastspin BASIC parser to accept having both comma and semicolon at the end of a line.
Is this a legal BASIC program? I wanted to print a value followed by a tab but not end with a newline. This works with basic but it seems like it should generate a syntax error. Is comma followed immediately by a semicolon allowed in other BASICs?
It looks like freebasic accepts it if you give it the -lang qb option (otherwise it wants all the variables dim'd).
fastspin doesn't like it. Both fastspin and freebasic will do what you want if you end the print with just a comma:
print x*y,
which is like ending it with a semicolon in that no newline is produced, but it does produce a tab.
I guess I should update the fastspin BASIC parser to accept having both comma and semicolon at the end of a line.
I just tried it and ebasic3 also accepts the line with just a comma at the end and does the right thing.
The problem seems to be in compiling a function call. It doesn't look like this code works as expected. It seems to generate an improperly terminated argument list.
/* ParseCall - parse a function or subroutine call */
static ParseTreeNode *ParseCall(ParseContext *c, ParseTreeNode *functionNode)
{
ParseTreeNode *node = NewParseTreeNode(c, NodeTypeFunctionCall);
ExprListEntry **pLast;
int tkn;
/* intialize the function call node */
node->u.functionCall.fcn = functionNode;
pLast = &node->u.functionCall.args;
/* parse the argument list */
if ((tkn = GetToken(c)) != ')') {
SaveToken(c, tkn);
do {
ExprListEntry *actual;
actual = (ExprListEntry *)LocalAlloc(c, sizeof(ExprListEntry));
actual->expr = ParseExpr(c);
actual->next = NULL;
*pLast = actual;
pLast = &actual->next;
++node->u.functionCall.argc;
} while ((tkn = GetToken(c)) == ',');
Require(c, tkn, ')');
}
/* return the function call node */
return node;
}
ersmith,
I grabbed version 1.3.9 of spin2gui, and started attempting to compile simpletools inside of the Simple-Libraries set. Here's the initial things I ran into:
// need __attribute__() on functions, and format attribute
error from spin2gui compile:
C:/Github/Simple-Libraries/Learn/Simple Libraries/TextDevices/libsimpletext/simpletext.h(309) error: syntax error, unexpected identifier `__attribute__', expecting ',' or ';'
Line 309 of simpletext.h:
int print(const char *format, ...) __attribute__((format (printf, 1, 2)));
Additionally, everything assumes SimpleIDE's feature that does automatic pathing/finding of Simple-Libraries includes and also auto linking of any libraries.
So doing this in the source file:
#include "simpletext.h"
Automatically finds simpletext.h in the Simple-Libraries/TextDevices/libsimpletext/ folder. All of the includes are buried in folders like this, there isn't one path with all the includes.
That include also causes the compile to automatically link in libsimpletext.a which is prebuilt in the library folders along with the includes.
The source for the libraries are there also, but they are built separately and individually. Also, note, that most of the Simple-Libraries implementations are split into many c files, almost but not quite one c file per function for some of them. Sadly, it's not consistent.
So, if fastspin allows it, I could give it all the separate include paths ( all 47 of them ), but that still leaves the linking issue. I'm not sure I want to go edit every single include file with the implementation info for each function in them. I assume your _IMPL() thing can handle the cases where one c file has several functions in it and all of them are in an include so need _IMPL()'s to the same file? I mean I am pretty sure this would work, but... I think I need to make a script or something that will do this for me.
I can't remember if you were planning on having linking in the future or not?
The problem seems to be in compiling a function call. It doesn't look like this code works as expected. It seems to generate an improperly terminated argument list.
I'm not sure what could be wrong there. Do you think one of the unions might not be working? In general I would suspect the C specific parts of fastspin (struct and union handling, nested local variables, and varargs, for example) before other parts, since those are better tested. Although it might not hurt to try compiling with -O0 to turn off the optimizer; that's probably the second most suspicious part of fastspin.
I'm guessing that you could comment those out (or put #ifndef __FLEXC__ around them). I doubt very many of the simple libraries use those features, so you could probably make progress on, say, the text libraries without them. (I suspect it's only the SD card that will need sys/sd.h and dirent.h, for example).
// need __attribute__() on functions, and format attribute
__attribute__ is a GCC specific extension which most compilers don't have, so we should add something like:
#ifndef __GNUC__
#define __attribute__(x)
#endif
to the top of files that use it.
Additionally, everything assumes SimpleIDE's feature that does automatic pathing/finding of Simple-Libraries includes and also auto linking of any libraries.
So doing this in the source file:
#include "simpletext.h"
Automatically finds simpletext.h in the Simple-Libraries/TextDevices/libsimpletext/ folder. All of the includes are buried in folders like this, there isn't one path with all the includes.
So the simple libraries only work with SimpleIDE, and rely on SimpleIDE to find the paths? That's unfortunate. How does SimpleIDE pass the include and library paths to the compiler? Does it just add appropriate -I and -L command line options?
I wonder how hard it would be to get SimpleIDE to work with fastspin instead of GCC?
Alternatively, I guess another path forward would be to first get the libraries to work with the command line version of PropGCC. Normally the way one would do this kind of thing would be to have a top level include file like "simplelibrary.h" located in the Simple-Libraries folder, which would then have relative includes like:
Then the app would just include that one header file, and the compiler only needs one include path (at the root of Simple-Libraries).
I'm not sure I want to go edit every single include file with the implementation info for each function in them. I assume your _IMPL() thing can handle the cases where one c file has several functions in it and all of them are in an include so need _IMPL()'s to the same file? I mean I am pretty sure this would work, but... I think I need to make a script or something that will do this for me.
Yes, having multiple functions in the same file is fine, _IMPL will only load the file once in that case (and fastspin will remove any unused functions).
The whole _IMPL() mechanism is kind of an attempt to have the automatic linking feature that newbies seem to expect -- that is, as long as you #include the appropriate header file the library functions will automatically get linked to the application, rather than PropGCC's way of requiring both the header file and the library file to be given. Kind of like what SimpleIDE does, but at the compiler level instead of the IDE level. I agree it is a pain to add the _IMPL() lines, but I hope it only has to be done once.
I can't remember if you were planning on having linking in the future or not?
I wrote a Makefile to build Simple Libraries a while ago. I'm sure it isn't up to date but it worked at the time. I've attached it here.
Eric: I wonder if you might consider adding a feature to fastspin to accept a separate file that lists the mappings between function names and source files. This would be sort of a poor man's library facility. The code itself wouldn't be in the file, just information about where to find it. Something like this:
Roy, now I understand the "why" of having to explicitly include every include and library path when using the Simple libs, thank you. Eric,
How does SimpleIDE pass the include and library paths to the compiler? Does it just add appropriate -I and -L command line options?
in order to use VS Code as my editor of choice a batch file needed to be created. After much hair pulling and dead ends I ended up just copy/pasting the command line issued by SimpleIDE for the compile step and slightly modding it. But it does appear that each -I and -L path needs to be passed to the compiler.
I think Tubular is already in contact with the author of MicroPython. There is talk about creating a port that makes use of XBYTE and friends to create a fast interpreter. I've looked at the source myself but decided to start with ebasic3 since I already know how it works and it is much smaller than MicroPython. I figure if I can get it working with fastspin then I can attempt MicroPython as a second step. That is unless Tubular gets it done before then!
I think Tubular is already in contact with the author of MicroPython. There is talk about creating a port that makes use of XBYTE and friends to create a fast interpreter. I've looked at the source myself but decided to start with ebasic3 since I already know how it works and it is much smaller than MicroPython. I figure if I can get it working with fastspin then I can attempt MicroPython as a second step. That is unless Tubular gets it done before then!
Well, yes, smaller steps are always a good idea, as is working with something you already know... At least the ROM/RAM they report are useful pegs in the sand..
I think Tubular is already in contact with the author of MicroPython. There is talk about creating a port that makes use of XBYTE and friends to create a fast interpreter. I've looked at the source myself but decided to start with ebasic3 since I already know how it works and it is much smaller than MicroPython. I figure if I can get it working with fastspin then I can attempt MicroPython as a second step. That is unless Tubular gets it done before then!
Well, yes, smaller steps are always a good idea, as is working with something you already know... At least the ROM/RAM they report are useful pegs in the sand..
It will be quite cool and very interesting to Parallax if someone were to get MicroPython working on the P2!
I'm still trying to track down what's going wrong with my ParseCall function. I added some debug output and got some very strange results. Here is the instrumented code. The idea is to print out the argument list right after constructing it. It's a simple linked list with a structure containing two fields: expr and next.
/* ParseCall - parse a function or subroutine call */
static ParseTreeNode *ParseCall(ParseContext *c, ParseTreeNode *functionNode)
{
ParseTreeNode *node = NewParseTreeNode(c, NodeTypeFunctionCall);
ExprListEntry **pLast;
int tkn;
/* intialize the function call node */
node->u.functionCall.fcn = functionNode;
pLast = &node->u.functionCall.args;
/* parse the argument list */
if ((tkn = GetToken(c)) != ')') {
SaveToken(c, tkn);
do {
ExprListEntry *actual;
actual = (ExprListEntry *)LocalAlloc(c, sizeof(ExprListEntry));
actual->expr = ParseExpr(c);
actual->next = NULL;
*pLast = actual;
pLast = &actual->next;
++node->u.functionCall.argc;
} while ((tkn = GetToken(c)) == ',');
Require(c, tkn, ')');
}
{ ExprListEntry *e = node->u.functionCall.args;
while (e) {
VM_printf("entry %08x, expr %08x, next %08x\n", e, e->expr, e->next);
e = e->next;
VM_printf("e %08x\n", e);
}
}
/* return the function call node */
return node;
}
Here is what was printed when I typed "print foo(3)" into the interpreter:
entry 0000B8F0, expr 0000B8E0, next 00000000
e 0000000d
entry 0000000d, expr 00000000, next 0
p %%%%
e 0
p 00entry 0
p 00e 0CD4E48D
entry 0CD4E48D, expr 0DC301E0, next 0C458573
e 0C45856F
entry 0C45856F, expr 02576C2C, next 2A932226
e 2A932222
entry 2A932222, expr 6BF08DA4, next 480F2923
e 480F291F
entry 480F291F, expr 00000000, next 00000000
e 0000000d
It then repeats that over and over. The odd thing is that the first entry looks pretty good. The "next" pointer is zero indicating the end of the list. The odd part though is that "e" is printed out as 0000000d. But if you look at the code, e is just set to the next pointer that printed as zero in the immediately preceding printf call. It looks like "e = e->next" is not working as expected.
ersmith,
Did some of your suggestions to get farther along in compiling things, and ran into this:
This is the fastspin error:
C:/Github/Simple-Libraries/Learn/Simple Libraries/TextDevices/libsimpletext/simpletext.h(1064) error: Internal error: unknown type 125 passed to TypeSize
This is a function declared near the end of simpletext.h
static inline void simpleterm_set(text_t *ptr)
{
extern text_t *dport_ptr; // <-- this is line 1064 in simpletext.h
simpleterm_close();
dport_ptr = ptr;
}
dport_ptr is instanciated in another c file.
This is text_t defined near the top of simpletext.h
typedef struct text_struct
{
/** Pointer to text device library's character receiving function. */
int (*rxChar)(struct text_struct *p);
/** Pointer to text device library's character transmitting function. */
int (*txChar)(struct text_struct *p, int ch);
/** Pointer to text device library's cog variable(s). */
int cogid[TERM_COG_LEN];
/** Pointer to text device library's info. */
volatile void *devst;
/** Echo setting, typically for usage with a terminal. */
volatile int terminalEcho;
/** List of end characters. */
//char ec[3];
/** End character sequence when an end character is encountered. */
//char ecs[3];
volatile char ecA;
volatile char ecB;
volatile char ecsA;
volatile char ecsB;
} text_t;
I assume it's related to the "extern" keyword. Since you don't link, you don't really have extern stuff, right? Piles of this Simple Libraries code assumes things built in libs and using extern to "see" them and it all gets fixed up when linking.
I exchanged some messages with Tubular and it seems the MicroPython project is already in progress. I think I need to come up with a different plan for my P2 work. There is really no point in getting ebasic3 working since it was only supposed to be a stepping stone to MicroPython.
I exchanged some messages with Tubular and it seems the MicroPython project is already in progress. I think I need to come up with a different plan for my P2 work. There is really no point in getting ebasic3 working since it was only supposed to be a stepping stone to MicroPython.
Anything that speeds knocking bugs out of the P2 C compiler path, has to be very useful for all other P2 work
I exchanged some messages with Tubular and it seems the MicroPython project is already in progress. I think I need to come up with a different plan for my P2 work. There is really no point in getting ebasic3 working since it was only supposed to be a stepping stone to MicroPython.
Anything that speeds knocking bugs out of the P2 C compiler path, has to be very useful for all other P2 work
Yeah but I think ebasic3 is too complicated to be useful as a test program. There must be a C test suite somewhere we could run.
@"David Betz" : weird that it's able to print e->next correctly (using a varargs call even) but is getting e = e->next wrong. That's very odd indeed. I haven't had a chance t look further at it, sorry, but if you're feeling adventurous you could try looking at the generated assembly. I guess one slightly odd thing is that the structs don't seem to be aligned on longword boundaries. I know this isn't necessary on P2, but it is on P1 and so fastspin generally pads structs out to multiples of 4 bytes. Perhaps these structs are dynamically allocated, but again, one would expect the code to use sizeof() to figure out the size of the structures.
@"Roy Eltham" : You've found a bug in the parsing of variable declarations inside functions. I'll try to fix that soon, but in the meantime hoisting any "extern" declarations outside of the function should get you going further.
Nope. No calls to malloc in ebasic3. It's all static allocation. I'll take your advice and look at the generated code. It will give me some practice understanding P2 assembly code.
Nope. No calls to malloc in ebasic3. It's all static allocation.
Sorry, I wasn't clear there... I meant dynamically allocated by ebasic3's internal memory allocation routines (I'm pretty sure it does have some kind of allocator acting on a static heap, doesn't it?)
Comments
Do you have a simple summary of ebasic3 - like lines of code compiled, compile times, code binary size, memory footprints, and speeds for the PC-targeting version, and the P2-targeting version ?
( I expect those may change/improve over time, so maybe in a new thread ?)
Could be useful to compare P2 vs PC code size/speeds.
Mike
This compiles in FreeBasic
produces
fastspin doesn't like it. Both fastspin and freebasic will do what you want if you end the print with just a comma: which is like ending it with a semicolon in that no newline is produced, but it does produce a tab.
I guess I should update the fastspin BASIC parser to accept having both comma and semicolon at the end of a line.
I grabbed version 1.3.9 of spin2gui, and started attempting to compile simpletools inside of the Simple-Libraries set. Here's the initial things I ran into:
Additionally, everything assumes SimpleIDE's feature that does automatic pathing/finding of Simple-Libraries includes and also auto linking of any libraries.
So doing this in the source file: Automatically finds simpletext.h in the Simple-Libraries/TextDevices/libsimpletext/ folder. All of the includes are buried in folders like this, there isn't one path with all the includes.
That include also causes the compile to automatically link in libsimpletext.a which is prebuilt in the library folders along with the includes.
The source for the libraries are there also, but they are built separately and individually. Also, note, that most of the Simple-Libraries implementations are split into many c files, almost but not quite one c file per function for some of them. Sadly, it's not consistent.
So, if fastspin allows it, I could give it all the separate include paths ( all 47 of them ), but that still leaves the linking issue. I'm not sure I want to go edit every single include file with the implementation info for each function in them. I assume your _IMPL() thing can handle the cases where one c file has several functions in it and all of them are in an include so need _IMPL()'s to the same file? I mean I am pretty sure this would work, but... I think I need to make a script or something that will do this for me.
I can't remember if you were planning on having linking in the future or not?
I'm not sure what could be wrong there. Do you think one of the unions might not be working? In general I would suspect the C specific parts of fastspin (struct and union handling, nested local variables, and varargs, for example) before other parts, since those are better tested. Although it might not hurt to try compiling with -O0 to turn off the optimizer; that's probably the second most suspicious part of fastspin.
Thanks,
Eric
__attribute__ is a GCC specific extension which most compilers don't have, so we should add something like: to the top of files that use it.
So the simple libraries only work with SimpleIDE, and rely on SimpleIDE to find the paths? That's unfortunate. How does SimpleIDE pass the include and library paths to the compiler? Does it just add appropriate -I and -L command line options?
I wonder how hard it would be to get SimpleIDE to work with fastspin instead of GCC?
Alternatively, I guess another path forward would be to first get the libraries to work with the command line version of PropGCC. Normally the way one would do this kind of thing would be to have a top level include file like "simplelibrary.h" located in the Simple-Libraries folder, which would then have relative includes like: Then the app would just include that one header file, and the compiler only needs one include path (at the root of Simple-Libraries).
Yes, having multiple functions in the same file is fine, _IMPL will only load the file once in that case (and fastspin will remove any unused functions).
The whole _IMPL() mechanism is kind of an attempt to have the automatic linking feature that newbies seem to expect -- that is, as long as you #include the appropriate header file the library functions will automatically get linked to the application, rather than PropGCC's way of requiring both the header file and the library file to be given. Kind of like what SimpleIDE does, but at the compiler level instead of the IDE level. I agree it is a pain to add the _IMPL() lines, but I hope it only has to be done once.
No plans to, although we may find we have to.
Thanks,
Eric
Eric: I wonder if you might consider adding a feature to fastspin to accept a separate file that lists the mappings between function names and source files. This would be sort of a poor man's library facility. The code itself wouldn't be in the file, just information about where to find it. Something like this:
The advantage of this would be to not have to edit every header file to add the _IMPL macro calls.
another.
I like your method better for the "auto linking". I especially like David Betz idea above to have a separate file with the mappings.
https://github.com/OpenNuvoton/NuMicroPy/tree/master/M48x
Could be a good c-source test for the compiler ?
Says:
So it is large, but should? fit into a P2 ?
Well, yes, smaller steps are always a good idea, as is working with something you already know... At least the ROM/RAM they report are useful pegs in the sand..
Did some of your suggestions to get farther along in compiling things, and ran into this:
This is the fastspin error:
This is a function declared near the end of simpletext.h
dport_ptr is instanciated in another c file.
This is text_t defined near the top of simpletext.h
I assume it's related to the "extern" keyword. Since you don't link, you don't really have extern stuff, right? Piles of this Simple Libraries code assumes things built in libs and using extern to "see" them and it all gets fixed up when linking.
Anything that speeds knocking bugs out of the P2 C compiler path, has to be very useful for all other P2 work
@"Roy Eltham" : You've found a bug in the parsing of variable declarations inside functions. I'll try to fix that soon, but in the meantime hoisting any "extern" declarations outside of the function should get you going further.
Thanks,
Eric