Jump to content
  • 0
rje

Function and other pointers in cc65

Question

I've hit a wall with cc65, and I bet someone here knows what to do.

I've got a set of void functions declared like so:

static void grouping() { /* do nothing for now */ }

 

And I've got an array of function pointers like so:

void (*prefix[MAX_TOKEN_VALUE]) ();

 

I'd love to assign them.  So I try this:

prefix[ 0 ] = grouping;

or 

prefix[ 0 ] = &grouping;

 

In both cases, cc65 complains:

	Robs-Mac-mini-2019:x16-8sh rje$ make
	cl65 -c --cpu 65c02 -t cx16 --create-dep compiler.d -O -o .obj/compiler.o compiler.c
	compiler.c(190): Warning: Implicit 'int' is an obsolete feature
	compiler.c(190): Error: Conflicting types for 'prefix'
	compiler.c(190): Error: '{' expected
	compiler.c(190): Error: '}' expected
	make: *** [.obj/compiler.o] Error 1
	

 

 

Obviously, the compiler thinks I'm ... well I don't know what it thinks I'm doing.  Declaring a new function, perhaps.  Which is weird.

 

Edited by rje

Share this post


Link to post
Share on other sites

15 answers to this question

Recommended Posts

  • 0

I think I may have found a potential "solution".  It looks like I might have to initialize it inside code.

Share this post


Link to post
Share on other sites
  • 0

Well, I tried the following sample code in godbolt.org:

Quote

#include <stdio.h>

static void grouping() { printf("yo"); }

#define MAX_TOKEN_VALUE 10
static void (*prefix[MAX_TOKEN_VALUE]) ();

int main()
{
    prefix[0] = grouping;

    for(int i=0; i<256; ++i) {
        printf("%d%s", (i % 16 ? i : 0), ( i %16 == 15 ? ",\n" : ", "));
        prefix[0]();
    }
    return 0;
}

What I discovered is that both GCC and Clang will work, with and without an ampersand before the function name when assigning to prefix[0].

cc65, however, does not like it.

First off, cc65 insists that variables, including loop variables, be declared at the top of the function. This is an old C-ism that most compilers have left behind.

Second, cc65 insists that functions with void parameter lists be specifically declared with void as the parameter list. This is another old C-ism that most compilers have left behind.

The following code should compile in cc65:

Quote

// Type your code here, or load an example.
#include <stdio.h>

void grouping(void) { printf("yo"); }

#define MAX_TOKEN_VALUE 10
static void (*prefix[MAX_TOKEN_VALUE]) (void);

int main()
{
    int i;

    prefix[0] = grouping;
    for(i=0; i<256; ++i) {
        printf("%d%s", (i % 16 ? i : 0), ( i %16 == 15 ? ",\n" : ", "));
        prefix[0]();
    }
    return 0;
}

At least, it does according to godbolt.org's cc65 2.17 and cc65 trunk compiler versions.

Edited by StephenHorn

Share this post


Link to post
Share on other sites
  • 0

I should probably add, this still seems like a bug in cc65, because my guess is that it implied a lone int as the argument list in one case of the function type declaration, but not in the other. That's where you're getting your warning about "implicit 'int' is an obsolete feature". And that theory jives with why the compiler is subsequently complaining about your assignment between conflicting types. By the time you're getting to the expected { }, however, I suspect that's just the compiler having gotten confused.

Share this post


Link to post
Share on other sites
  • 0

In terms of initializing the array of function pointers you can do that at the time you're declaring the array:

Quote
#include <stdio.h>
 
static void grouping() { printf("yo"); }
 
#define MAX_TOKEN_VALUE 10
static void (*prefix[MAX_TOKEN_VALUE]) () = {
    grouping, grouping, grouping, grouping, grouping, grouping, grouping, grouping, grouping, grouping
};
 
int main()
{
    int i = 0;
 
    for(i=0; i<256; ++i) {
        printf("%d%s", (i % 16 ? i : 0), ( i %16 == 15 ? ",\n" : ", "));
        prefix[0]();
    }
    return 0;
}

 

Share this post


Link to post
Share on other sites
  • 0
44 minutes ago, ChrisL said:

In terms of initializing the array of function pointers you can do that at the time you're declaring the array:

 

That appears to be valid, *if* you include void in the parameter list of the function type declarations, otherwise you run into the same cc65 behavior with mismatched types.

Quote
 
#include <stdio.h>
 
static void grouping(void) { printf("yo"); }
 
#define MAX_TOKEN_VALUE 10
static void (*prefix[MAX_TOKEN_VALUE]) (void) = {
    grouping, grouping, grouping, grouping, grouping, grouping, grouping, grouping, grouping, grouping
};
 
int main()
{
    int i = 0;
 
    for(i=0; i<256; ++i) {
        printf("%d%s", (i % 16 ? i : 0), ( i %16 == 15 ? ",\n" : ", "));
        prefix[0]();
    }
    return 0;
}

 

Edited by StephenHorn

Share this post


Link to post
Share on other sites
  • 0
6 hours ago, StephenHorn said:

First off, cc65 insists that variables, including loop variables, be declared at the top of the function. That's an old C-ism that most compilers have left behind.

Second, cc65 insists that functions with void parameter lists be specifically declared with void as the parameter list. That's another old C-ism that most compilers have left behind.

First off, I doubt that cc65 ever will implement the C99 rules about where variables can be defined.

Second, it's the other way around.  "Empty parameter lists" is the old-fashioned K & R style.  "void parameter lists" is the modern, C Standard style.  The current version of cc65 accepts both styles.  Version 2.17 might not accept the old-fashioned style.

The current cc65 compiles those examples -- in either style, with assignment or with initiation -- without any complaints.

Edited by Greg King
  • Like 2

Share this post


Link to post
Share on other sites
  • 0
7 hours ago, Greg King said:

First off, I doubt that cc65 ever will implement the C99 rules about where variables can be defined.

In fact the docs state clearly that it will never be c99 compliant.

 

  • Like 1

Share this post


Link to post
Share on other sites
  • 0
8 hours ago, Greg King said:

Second, it's the other way around.  "Empty parameter lists" is the old-fashioned K & R style.  "void parameter lists" is the modern, C Standard style.  The current version of cc65 accepts both styles.  Version 2.17 might not accept the old-fashioned style.

My bad, I could have sworn I've worked with C in older compilers that insisted on void parameter lists, but that's digging some decades back and my memory could easily be faulty here.

Share this post


Link to post
Share on other sites
  • 0

Here's a related question.  I've got a struct that holds a const char *.  When I run the code on the X16, the address looks correct (it's in the 40,000 range.. hmm I wonder if that's too high?   Well maybe but for now I'll pretend it's ok).  

But, then, the struct gets handled by another function, and the address looks positively truncated... as in, down to an 8-bit number kind of truncated, as in under 256.  I am suspecting that there is truncation going on behind my back. 

But I am not sure.

I will try a small bit of test code and see if I can replicate in a very contained environment.

 

...and I found the problem.  The method that creates that structure should return a POINTER to that structure.  Returning the ACTUAL structure causes indigestion for CC65.  I wonder if it causes indigestion for c99 in general?  Let's test that too.

NOPE!  That works fine.  So there are stack frame limitations when passing data around for 8 bit machines, I suspect, and I seem to remember CC65 docs talking about that.  So maybe I need to return a pointer.

Edited by rje

Share this post


Link to post
Share on other sites
  • 0
51 minutes ago, rje said:

So there are stack frame limitations when passing data around for 8 bit machines, I suspect, and I seem to remember CC65 docs talking about that. 

Indeed there are! Passing structs by value is generally a bad-to-impossible idea on a 6502 system. The stack is (at most) 256 bytes, and each subroutine layer requires at least two bytes taken up to store the return address, plus you should expect A, X, Y and the status register to also be pushed to the stack for each call (that is the default behavior of C functions in cc65), so that's 6 bytes right there. We are conditioned through "modern" Computer Science studies to avoid global data, do proper OOP and encapsulate and compartmentalize. Going back to 8-bit, you gotta throw that stuff out the window.

Share this post


Link to post
Share on other sites
  • 0

Mmmm.  That clarifies my brain at least.

So I'm working on an on-demand scanner, that looks at the current and previous tokens only and makes decisions based on them alone.  For the sake of memory efficiency, it uses the input string for some of the token storage as well.  So we find a token, store its type, point to its start position in the input array, and store its length.

The token was being built and returned "by value", instead of a pointer.  Boom.

I'll have to look very carefully at my code to see other potential pitfalls here.

There is potential recursion as well, and it sounds like I need to be careful of that.

 

* * *

 

I was hoping to load scripts into RAM bank 1 (as a set of null-terminated lines, processed one line at a time, to build up a list of output bytecodes...), and use that as my input stream.  (Can't see a downside there).

 

Edited by rje

Share this post


Link to post
Share on other sites
  • 0
26 minutes ago, rje said:

There is potential recursion as well, and it sounds like I need to be careful of that.

To say the least! Recursion is not your friend with a limited stack. Another reason why Lisp is not the greatest fit for this platform. Kinda hits the other, Functional, side of "modern" Computer Science, striving to make everything an elegant function tree, which is not the way to go, either.

Share this post


Link to post
Share on other sites
  • 0

In this case, recursion is for handling nested expressions.  This just means one should not nest expressions more than one has to... and preferably never, but these things happen with programming languages.

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

Please review our Terms of Use