KickC Optimizing C-Compiler now supports Commander X16

All aspects of programming on the Commander X16.
TomXP411
Posts: 1783
Joined: Tue May 19, 2020 8:49 pm

KickC Optimizing C-Compiler now supports Commander X16

Post by TomXP411 »



38 minutes ago, kktos said:




Tom, I guess that's related to what Jesper told you already about the compiler no "seeing" the var in the asm.

Therefore, here, as it doesn't see any use of R0, it simply removes it.

You need to use volatile in the declaration so the complier won't optimize it



Yeah, I tried that... still no joy.  The error I get is "unknown addressing mode". The compiler doesn't understand that STA (R0),Y should be an indirect , Y indexed instruction with the address of R0. 

The compiler does generate code for STA R0, which generates STA $02, but not for STA ($02),Y

 

 

SlithyMatt
Posts: 913
Joined: Tue Apr 28, 2020 2:45 am

KickC Optimizing C-Compiler now supports Commander X16

Post by SlithyMatt »


Sounds like R0 is not getting recognized as an 8-bit value or there's a bug in cc65. Did you try just STA ($02),y?

For zero page addresses, you may need to have separate identifiers for zero page address values and usable pointers.

Jesper Gravgaard
Posts: 16
Joined: Mon Dec 14, 2020 9:18 pm

KickC Optimizing C-Compiler now supports Commander X16

Post by Jesper Gravgaard »



12 hours ago, TomXP411 said:




I am working on it now... part of the problem is I really don't understand your assembly/c interface very well, and your calling convention is not something I've seen before. 



For example, I wanted to fill a buffer in the data segment, with the pointer stored in RO (address 2 in zero page). I'd expect something like this to work...



in my global section:



  __address($02) word R0;



char* buffer ..... 



...



R0 = (word) buffer;



asm {



  STA (R0),Y



...



}



}



But the compiler just completely removed the assignment to R0, then failed to assemble the STA line because R0 didn't exist. 



I ended up using a really obtuse syntax with a double pointer that I pulled out of your LOAD example, but I don't understand why the code above doesn't work. 



 



I have tried to create a minimal example re-creating your problem - but it seems my programs cannot reproduce your problem.

The following is my C-program  


__address($02) word R0;

 


void main() {



  char* buffer = 0x0400;



  R0 = (word) buffer;



  asm {



    sta (R0),y



  }



}


And this is the resulting ASM of the main function, when compiling with version 0.8.5:


main: {



  .label buffer = $400



  lda #<buffer



  sta.z R0



  lda #>buffer



  sta.z R0+1



  sta (R0),y



  rts



}


I believe this is what you wanted to see.

Could you show a little more of your code (or share the entire file). Then I will look into the problem you are facing.

/Jesper

Greg King
Posts: 162
Joined: Wed Jul 08, 2020 1:14 pm

KickC Optimizing C-Compiler now supports Commander X16

Post by Greg King »


Does the assembler understand capital letters for the register names?

TomXP411
Posts: 1783
Joined: Tue May 19, 2020 8:49 pm

KickC Optimizing C-Compiler now supports Commander X16

Post by TomXP411 »



Quote




Does the assembler understand capital letters for the register names?



Thanks for the tip: that's exactly what it was. The compiler fails two different ways if you:


  1. Write the mnemonics in upper case. 


  2. Write register operands in upper case


Now that I know, I'll make sure to lower-case mnemonics and register operands. 

And now that I've done that, I can now read 256 bytes from a file directly into a memory buffer. I've actually got two more extensions to make to that method: stop on null, and stop on CR. 

On that note @Jesper Gravgaard, I'm considering the naming convention for the CMDR File I/O routines. 

I started using fopen, fgetc, and fgets, but the signature of all those does not match the conventions in the ANSI C stdio library.

for example, my fopen looks like this:

void fopen(byte channel, byte device, byte secondaryAddress, char *filename)

while ANSI's stdio looks like

FILE * fopen ( const char * filename, const char * mode );

Would it be better to rename the routines, or just overload the standard names with the platform-specific signatures? 

The names I'm considering are: cxopen, cxgetc, cxgets, cxseek, cxputc, cxputs, and cxclose. This keeps the original fopen() and related function names free, in case someone decides to implement a standardized version of those functions in the future. 

 

 

    

Jesper Gravgaard
Posts: 16
Joined: Mon Dec 14, 2020 9:18 pm

KickC Optimizing C-Compiler now supports Commander X16

Post by Jesper Gravgaard »


I am glad you got everything to compile! How ASM and C works together can still use some work - both improvements of the features allowing for for inlining ASM easily and the documentation of how you use it.


6 hours ago, TomXP411 said:




The names I'm considering are: cxopen, cxgetc, cxgets, cxseek, cxputc, cxputs, and cxclose. This keeps the original fopen() and related function names free, in case someone decides to implement a standardized version of those functions in the future. 



I believe that is a good starting point! 

If they are common enough to be easily portable to the other commodore platforms maybe they should be called cbmopen, cbmgetc, ...

 

TomXP411
Posts: 1783
Joined: Tue May 19, 2020 8:49 pm

KickC Optimizing C-Compiler now supports Commander X16

Post by TomXP411 »



50 minutes ago, Jesper Gravgaard said:




I am glad you got everything to compile! How ASM and C works together can still use some work - both improvements of the features allowing for for inlining ASM easily and the documentation of how you use it.



I believe that is a good starting point! 



If they are common enough to be easily portable to the other commodore platforms maybe they should be called cbmopen, cbmgetc, ...



Thanks. I'm moving forward, slowly but surely. My next step is to implement the command channel, so I can issue disk commands. Once that's done, I can start working on the UI code.

 

TomXP411
Posts: 1783
Joined: Tue May 19, 2020 8:49 pm

KickC Optimizing C-Compiler now supports Commander X16

Post by TomXP411 »


Just another question, and I know this one comes out of left field...

I was talking about a windowing library on another thread, and I am wondering if there's a way in KickC to generate an object file that does NOT optimize away unused functions. I'm thinking about how to create a function library that I can burn into ROM and make available to other programs later. Right now, your compiler seems to simply ignore functions that aren't called from another function, and when they are called, there's a lot of coupling at the assembly level. 

For example, if I have a routine named print(char* text), the text variable isn't always passed as a distinct value; instead, the compiler simply grabs the original declaration in the calling function and references that directly in the callee. I don't see evidence of using the stack or heap to marshal parameters, like in the C calling convention. 

It seems easy enough to devise my own marshaling subsystem, by using __address variables to directly access the R0-R15 byte pairs in zero page (the first 32 bytes are set aside for parameter passing.)  But I don't see a simple way to force the compiler to include a non-referenced function. 

I guess what I'm asking is... is there a way to force a function to be compiled, even if it's not used, and is there a way to force the C calling convention of putting parameters on the stack or a stack-like structure?

 

Jesper Gravgaard
Posts: 16
Joined: Mon Dec 14, 2020 9:18 pm

KickC Optimizing C-Compiler now supports Commander X16

Post by Jesper Gravgaard »



17 hours ago, TomXP411 said:




I was talking about a windowing library on another thread, and I am wondering if there's a way in KickC to generate an object file that does NOT optimize away unused functions. I'm thinking about how to create a function library that I can burn into ROM and make available to other programs later. Right now, your compiler seems to simply ignore functions that aren't called from another function, and when they are called, there's a lot of coupling at the assembly level. 



It seems easy enough to devise my own marshaling subsystem, by using __address variables to directly access the R0-R15 byte pairs in zero page (the first 32 bytes are set aside for parameter passing.)  But I don't see a simple way to force the compiler to include a non-referenced function. 



I guess what I'm asking is... is there a way to force a function to be compiled, even if it's not used, and is there a way to force the C calling convention of putting parameters on the stack or a stack-like structure?



 



There is no direct support (like a keyword) for this at the moment. However, it can be achieved, with a little linker trick.

By creating a linker specification that declares a segment that is not included in the generated binary file then we can use the main() function to call all the ROM functions - but ensure that the main function itself is not included in the binary.

I have created an example C-file and linker configuration file that creates a ROM at $F000 with three functions.

The example also shows how to pass parameters and return values in three different ways:

- through the stack

- through zeropage 

- through registers (and/or zeropage)

You can see the code here: https://gitlab.com/camelot/kickc/-/tree/master/src/test/kc/examples/rom

Let me know if this makes sense, or if it leads to new questions.

TomXP411
Posts: 1783
Joined: Tue May 19, 2020 8:49 pm

KickC Optimizing C-Compiler now supports Commander X16

Post by TomXP411 »



5 hours ago, Jesper Gravgaard said:




There is no direct support (like a keyword) for this at the moment. However, it can be achieved, with a little linker trick.



By creating a linker specification that declares a segment that is not included in the generated binary file then we can use the main() function to call all the ROM functions - but ensure that the main function itself is not included in the binary.



I have created an example C-file and linker configuration file that creates a ROM at $F000 with three functions.



The example also shows how to pass parameters and return values in three different ways:



- through the stack



- through zeropage 



- through registers (and/or zeropage)



You can see the code here: https://gitlab.com/camelot/kickc/-/tree/master/src/test/kc/examples/rom



Let me know if this makes sense, or if it leads to new questions.



That's perfect. I had come to a similar conclusion, but was unsure how to prevent main() from being included or executed. 

So, looking at the calls in main, where you call each function twice with two different parameters -  I suspect that's to fool the optimizer into using proper parameter passing, rather than converting the parameter into a shared variable?

 

Thanks!

 

Post Reply