Llvm-mos: Difference between revisions

From Foenix F256 / Wildbits/K2 Wiki
Jump to navigationJump to search
(Added link to latest llvm tools for the F256.)
(new llvm-mos page separate from the rest of the C compilers)
 
Line 2: Line 2:


Foenix F256 support, programming libraries, examples, and installers for Windows, MacOS, and Linux can be found at [https://kangaroopunch.com/view/ShowSoftware?id=13 Kangaroo Punch Studios].
Foenix F256 support, programming libraries, examples, and installers for Windows, MacOS, and Linux can be found at [https://kangaroopunch.com/view/ShowSoftware?id=13 Kangaroo Punch Studios].
==== Documentation ====
You can find the [https://kangaroopunch.com/files/download/software/f256lib.pdf PDF manual linked] at the [https://kangaroopunch.com/view/ShowSoftware?id=13 Kangaroo Punch distribution] of llvm-mos specifically tailored to the F256/Wildbits.
=== Installation procedure ===
Very easy, can be done for Windows running a single batch file, or linux running a single script. Follow the instructions found in the PDF manual linked above.
=== Noteworthy files and folders ===
(some of these are merely suggestions in how to organize your local installation of llvm-mos)
assuming your '''BASEPATH''' = the folder where you do your installation,
'''BASEPATH\f256dev\foenixmgr''' a copy of foenixmgr to interact with your machine with the usb debug cable
'''BASEPATH\f256dev\llvm-mos\code\''' is a good place to store your projects
'''BASEPATH\f256dev\llvm-mos\code\myproject\''' is the root folder of your project called 'myproject' and where the program and linker produced files end up in
'''BASEPATH\f256dev\llvm-mos\code\myproject\src\''' is the folder containing all of your 'myproject' source code
'''BASEPATH\f256dev\llvm-mos\mos-platform\f256\lib\link.ld''' a parameter file for the linker options. It's where you can set the optimization level to -O1, -O2, -O3 or -Os. You can clone this file inside your project folder to let the clone override this main one.
=== How to manage multiple source code files in a project ===
* Create .h + .c pairs of source code files by themes.<br>
* Use '''#include "../src/whatever.h"''' so that '''BASEPATH\f256dev\llvm-mos\code\myproject\src\whatever.c''' and its corresponding whatever.h are referred to<br>
* Put the entirety of whatever.h's content between
<pre>#ifndef WHATEVER_H
#define WHATEVER_H
</pre>
and
<pre>
#endif
</pre>
to avoid complaints of multiple definitions if multiple files need this reference.
=== Compiling a program, scripts from Mu0n ===
I use a few batch files that are located in '''BASEPATH\f256dev\llvm-mos\'''
Here's the batch file "c.bat" (compile) I use to simply compile a program, adjust your absolute paths to what you have on your modern computer. "projectname" is the folder of your project inside the "code" folder. Run it using the command '''c projectname'''
<pre>
call D:\F256\llvm-mos\f256build.bat code/%1
</pre>
Here's the batch file "car.bat" (compile and run) so that I also execute it to the real machine, using foenixmgr and the usb debug cable. Run it using the command '''car projectname'''
<pre>
call D:\F256\llvm-mos\f256build.bat code/%1
call D:\F256\llvm-mos\f256run.bat code/%1
</pre>
Here's the batch file "cas.bat" (compile and send) so that I also send it to the real machine, using foenixmgr's pcopy routine to write it to the root of the SD card. Run it using the command '''cas projectname'''
<pre>
call D:\F256\llvm-mos\f256build.bat code/%1
copy .\code\%1\%1.pgz D:\F256\FoenixMgr\tools\cmd\
cd D:\F256\FoenixMgr\tools\cmd\
call pcopy.bat %1.pgz
cd D:\F256\llvm-mos
</pre>
=== Embedding asset files ===
Assuming your assets are in '''BASEPATH\f256dev\llvm-mos\code\myproject\assets\''', use:
<pre>
EMBED(palgrudge, "../assets/grudge.pal", 0x10000);//1kb
EMBED(boy1, "../assets/boy1.bin",  0x10400);//1kb
EMBED(thing, "../assets/thing.bin", 0x10800);//1kb
EMBED(girl1, "../assets/girl1.bin", 0x10C00);//1kb
EMBED(cath, "../assets/cath.bin",  0x11000);//1kb
EMBED(cat, "../assets/cat.bin",    0x11400);//1kb
EMBED(dash, "../assets/dash.bin",  0x13000); //2kb
</pre>
=== How to use higher memory and the MMU to store code ===
(this section is credited to jbaker8935)
what you can do is pick a function in one of your projects that isnt time critical and create a trampoline function for it using this pattern:
void FAR8_video_init(void);
<pre>
#pragma clang optimize off
__attribute__((noinline))
void video_init(void) {
    volatile unsigned char ___mmu = (unsigned char)*(volatile unsigned char *)0x000d;
    *(volatile unsigned char *)0x000d = 8;
    FAR8_video_init();
    *(volatile unsigned char *)0x000d = ___mmu;
}
#pragma clang optimize on
__attribute__((noinline, section(".block8")))
void FAR8_video_init(void)
</pre>
note: that FAR8_video_init is the renamed function that is being moved.  It's just a convention.
in your main you can call video_init as you do normally and the trampoline will take care of swapping the code into A000 in cpu memory.
For linking you need to create file f256.ld that sits in the project directory.  it is a copy of "${ROOT}/llvm-mos/mos-platform/f256/lib/link.ld" but has additional linker instructions to explain how to deal with block8.  à
Around line 100 or so you need to add instructions after the include output.ld statement.  like so:
<pre>
/* Overlay Segments and Binary Data */
  INCLUDE output.ld
/* Hand Coded output.ld records */
  SHORT(8*0x2000)
  BYTE(8/8)
  SHORT(end_block8 - __block8_lma)
  BYTE(0x00)
  TRIM(block8)
</pre>
i'm using f256build.sh and the normal 'everything in src' structure.  Also I didnt fiddle with moving the default stack or swap page,  so this example trampoline is using A000  (writing to 0x000d).  Note that the mmu is left in edit mode, so the trampolines can just write to 000d with no disabling of interrupts.</blockquote>
=== Accessing the extra 2x core banks of 512kb memory ===
Since the launch of the 2x core, the full 2MB of SRAM is accessible if you set a bit right in register 0x0000. For more details on this feature of the 2x core, see this page.
To peek a byte at these regions of memory, use this asm block routine as a function:
PEEK24 accepts a 32bit long address, stores it temporarily into a few bytes near the beginning of zero page. It then activates bit 3 of register 0x0000 to access the new 2x core higher memory banks. Finally, it fetches the content of that byte as a returned char.
<pre>
asm (
    ".text                  \n"
    ".global PEEK24        \n"
    "PEEK24:                \n"
    "  sta    $5          \n"
    "  stx    $6          \n"
    "  lda    __rc2      \n"
    "  sta    $7          \n"
    "  ldx    $0          \n"
    "  ldy    $1          \n"
    "  txa                \n"
    "  ora    #$8        \n"
    "  php                \n"
    "  sei                \n"
    "  sta    $0          \n"
    "  tya                \n"
    "  ora    #48        \n"
    "  sta    $1          \n"
    "  .byte  0xa7,0x05  \n"
    "  stx    $0          \n"
    "  sty    $1          \n"
    "  plp                \n"
    "  ldx    #0          \n"
    "  rts                \n"
);
</pre>
To write something to these higher memory banks, you can use POKE24, which goes the other way:
<pre>
asm (
    ".text                  \n"
    ".global POKE24        \n"
    "POKE24:                \n"
    "  sta    $5          \n"
    "  stx    $6          \n"
    "  lda    __rc2      \n"
    "  sta    $7          \n"
    "  ldx    $0          \n"
    "  ldy    $1          \n"
    "  txa                \n"
    "  ora    #$8        \n"
    "  php                \n"
    "  sei                \n"
    "  sta    $0          \n"
    "  tya                \n"
    "  ora    #$48        \n"
    "  sta    $1          \n"
    "  lda    __rc4      \n"
    "  .byte  0x87,0x05  \n"
    "  stx    $0          \n"
    "  sty    $1          \n"
    "  plp                \n"
    "  ldx    #0          \n"
    "  rts                \n"
);
</pre>
=== "Troubleshoot Gotchas" during compilation ===
1) When trying to use the EMBED directive in order to put data at specific memory locations, keep in mind that you CAN'T comment those lines out with <code>/*</code> and <code>*/</code>, the compiler will try to follow them whether they're in them or not. The only way to comment them is to use <code>//</code>.
2) In the f256dev/ folder, the f256build.bat file contains all you need to compile and link your project. In the "call mos-f256-clang.bat" line, one important switch is the optimization level you let the compiler use. Experiment with -O1 (no optimization), -O2 (some) and -O3 (most). Some operations and repetitions work with some and fail with others. One prominent example of where it failed was a repetitive fileRead operation that read and parsed little chunks of data from a file, byte by byte and while it compiled and linked, it failed during execution. By reducing -O3 to -O2, it all worked again.
3) One linking error kept giving me "<code>ld.lld: error: ld-temp.o <inline asm>:2:1: operand must be a 16-bit address sta (mos8(.LpickAudioFile_zp_stk+4)</code>" despite not having written any inline assembly in my project. You can skirt around the issue for the problematic function by adding a compiler attribute: <code>__attribute__((optnone))</code> right before your function definition.
4) When your code nears a size of 0x9400 and above, you may get in trouble in terms of code not being fully loaded into RAM while executing. Either deliberately put some code into far memory and devise a scheme with the MMU to swap that code in and out of reach for the CPU, or you can carve yourself a bit more space by editing f256dev/llvm-mos/mos-platform/f256/lib/link.ld and change:
<code>/* fake C Stack */</code>
<code>PROVIDE(__stack = 0xA000);</code>
<code>to</code>
<code>/* fake C Stack */</code>
<code>PROVIDE(__stack = 0xC000);</code>
to get yourself an extra 8k of code space before it collides with the stack
5) The compiler will often complain about a missing closing curly bracket '}' at the end of your source files. Just add the one it requires at the very end even though it's an extra one, or, finish your source file by putting your last function with this notation:
<code>void myLastFunction(uint8_t whatever) {</code>
<code>//bunchacode</code>
<code>}</code></blockquote><blockquote>

Latest revision as of 01:51, 30 April 2026

The llvm-mos project is a suite of development tools aimed at 65xx processors. It comes with optimizing C and C++ compilers that are mostly compatible with the C99 and C++11 standards, allowing for modern programming styles.

Foenix F256 support, programming libraries, examples, and installers for Windows, MacOS, and Linux can be found at Kangaroo Punch Studios.

Documentation

You can find the PDF manual linked at the Kangaroo Punch distribution of llvm-mos specifically tailored to the F256/Wildbits.

Installation procedure

Very easy, can be done for Windows running a single batch file, or linux running a single script. Follow the instructions found in the PDF manual linked above.

Noteworthy files and folders

(some of these are merely suggestions in how to organize your local installation of llvm-mos)

assuming your BASEPATH = the folder where you do your installation,

BASEPATH\f256dev\foenixmgr a copy of foenixmgr to interact with your machine with the usb debug cable

BASEPATH\f256dev\llvm-mos\code\ is a good place to store your projects

BASEPATH\f256dev\llvm-mos\code\myproject\ is the root folder of your project called 'myproject' and where the program and linker produced files end up in

BASEPATH\f256dev\llvm-mos\code\myproject\src\ is the folder containing all of your 'myproject' source code

BASEPATH\f256dev\llvm-mos\mos-platform\f256\lib\link.ld a parameter file for the linker options. It's where you can set the optimization level to -O1, -O2, -O3 or -Os. You can clone this file inside your project folder to let the clone override this main one.


How to manage multiple source code files in a project

  • Create .h + .c pairs of source code files by themes.
  • Use #include "../src/whatever.h" so that BASEPATH\f256dev\llvm-mos\code\myproject\src\whatever.c and its corresponding whatever.h are referred to
  • Put the entirety of whatever.h's content between
#ifndef WHATEVER_H
#define WHATEVER_H

and

#endif

to avoid complaints of multiple definitions if multiple files need this reference.

Compiling a program, scripts from Mu0n

I use a few batch files that are located in BASEPATH\f256dev\llvm-mos\

Here's the batch file "c.bat" (compile) I use to simply compile a program, adjust your absolute paths to what you have on your modern computer. "projectname" is the folder of your project inside the "code" folder. Run it using the command c projectname

call D:\F256\llvm-mos\f256build.bat code/%1

Here's the batch file "car.bat" (compile and run) so that I also execute it to the real machine, using foenixmgr and the usb debug cable. Run it using the command car projectname

call D:\F256\llvm-mos\f256build.bat code/%1
call D:\F256\llvm-mos\f256run.bat code/%1

Here's the batch file "cas.bat" (compile and send) so that I also send it to the real machine, using foenixmgr's pcopy routine to write it to the root of the SD card. Run it using the command cas projectname

call D:\F256\llvm-mos\f256build.bat code/%1
copy .\code\%1\%1.pgz D:\F256\FoenixMgr\tools\cmd\
cd D:\F256\FoenixMgr\tools\cmd\
call pcopy.bat %1.pgz
cd D:\F256\llvm-mos


Embedding asset files

Assuming your assets are in BASEPATH\f256dev\llvm-mos\code\myproject\assets\, use:

EMBED(palgrudge, "../assets/grudge.pal", 0x10000);//1kb
EMBED(boy1, "../assets/boy1.bin",   0x10400);//1kb
EMBED(thing, "../assets/thing.bin", 0x10800);//1kb
EMBED(girl1, "../assets/girl1.bin", 0x10C00);//1kb
EMBED(cath, "../assets/cath.bin",   0x11000);//1kb
EMBED(cat, "../assets/cat.bin",     0x11400);//1kb
EMBED(dash, "../assets/dash.bin",   0x13000); //2kb

How to use higher memory and the MMU to store code

(this section is credited to jbaker8935)

what you can do is pick a function in one of your projects that isnt time critical and create a trampoline function for it using this pattern:

void FAR8_video_init(void);

#pragma clang optimize off
__attribute__((noinline))
void video_init(void) {
    volatile unsigned char ___mmu = (unsigned char)*(volatile unsigned char *)0x000d;
    *(volatile unsigned char *)0x000d = 8;
    FAR8_video_init();
    *(volatile unsigned char *)0x000d = ___mmu;
}
#pragma clang optimize on

__attribute__((noinline, section(".block8")))
void FAR8_video_init(void)

note: that FAR8_video_init is the renamed function that is being moved. It's just a convention. in your main you can call video_init as you do normally and the trampoline will take care of swapping the code into A000 in cpu memory. For linking you need to create file f256.ld that sits in the project directory. it is a copy of "${ROOT}/llvm-mos/mos-platform/f256/lib/link.ld" but has additional linker instructions to explain how to deal with block8. à Around line 100 or so you need to add instructions after the include output.ld statement. like so:

 /* Overlay Segments and Binary Data */
  INCLUDE output.ld

/* Hand Coded output.ld records */
  SHORT(8*0x2000)
  BYTE(8/8)
  SHORT(end_block8 - __block8_lma)
  BYTE(0x00)
  TRIM(block8)

i'm using f256build.sh and the normal 'everything in src' structure. Also I didnt fiddle with moving the default stack or swap page, so this example trampoline is using A000 (writing to 0x000d). Note that the mmu is left in edit mode, so the trampolines can just write to 000d with no disabling of interrupts.

Accessing the extra 2x core banks of 512kb memory

Since the launch of the 2x core, the full 2MB of SRAM is accessible if you set a bit right in register 0x0000. For more details on this feature of the 2x core, see this page.

To peek a byte at these regions of memory, use this asm block routine as a function:

PEEK24 accepts a 32bit long address, stores it temporarily into a few bytes near the beginning of zero page. It then activates bit 3 of register 0x0000 to access the new 2x core higher memory banks. Finally, it fetches the content of that byte as a returned char.

asm (
    ".text                  \n"
    ".global PEEK24         \n"
    "PEEK24:                \n"
    "   sta     $5          \n"
    "   stx     $6          \n"
    "   lda     __rc2       \n"
    "   sta     $7          \n"
    "   ldx     $0          \n"
    "   ldy     $1          \n"
    "   txa                 \n"
    "   ora     #$8         \n"
    "   php                 \n"
    "   sei                 \n"
    "   sta     $0          \n"
    "   tya                 \n"
    "   ora     #48         \n"
    "   sta     $1          \n"
    "   .byte   0xa7,0x05   \n"
    "   stx     $0          \n"
    "   sty     $1          \n"
    "   plp                 \n"
    "   ldx     #0          \n"
    "   rts                 \n"
);

To write something to these higher memory banks, you can use POKE24, which goes the other way:

asm (
    ".text                  \n"
    ".global POKE24         \n"
    "POKE24:                \n"
    "   sta     $5          \n"
    "   stx     $6          \n"
    "   lda     __rc2       \n"
    "   sta     $7          \n"
    "   ldx     $0          \n"
    "   ldy     $1          \n"
    "   txa                 \n"
    "   ora     #$8         \n"
    "   php                 \n"
    "   sei                 \n"
    "   sta     $0          \n"
    "   tya                 \n"
    "   ora     #$48         \n"
    "   sta     $1          \n"
    "   lda     __rc4       \n"
    "   .byte   0x87,0x05   \n"
    "   stx     $0          \n"
    "   sty     $1          \n"
    "   plp                 \n"
    "   ldx     #0          \n"
    "   rts                 \n"
);

"Troubleshoot Gotchas" during compilation

1) When trying to use the EMBED directive in order to put data at specific memory locations, keep in mind that you CAN'T comment those lines out with /* and */, the compiler will try to follow them whether they're in them or not. The only way to comment them is to use //.

2) In the f256dev/ folder, the f256build.bat file contains all you need to compile and link your project. In the "call mos-f256-clang.bat" line, one important switch is the optimization level you let the compiler use. Experiment with -O1 (no optimization), -O2 (some) and -O3 (most). Some operations and repetitions work with some and fail with others. One prominent example of where it failed was a repetitive fileRead operation that read and parsed little chunks of data from a file, byte by byte and while it compiled and linked, it failed during execution. By reducing -O3 to -O2, it all worked again.

3) One linking error kept giving me "ld.lld: error: ld-temp.o <inline asm>:2:1: operand must be a 16-bit address sta (mos8(.LpickAudioFile_zp_stk+4)" despite not having written any inline assembly in my project. You can skirt around the issue for the problematic function by adding a compiler attribute: __attribute__((optnone)) right before your function definition.

4) When your code nears a size of 0x9400 and above, you may get in trouble in terms of code not being fully loaded into RAM while executing. Either deliberately put some code into far memory and devise a scheme with the MMU to swap that code in and out of reach for the CPU, or you can carve yourself a bit more space by editing f256dev/llvm-mos/mos-platform/f256/lib/link.ld and change:

/* fake C Stack */

PROVIDE(__stack = 0xA000);

to

/* fake C Stack */

PROVIDE(__stack = 0xC000);

to get yourself an extra 8k of code space before it collides with the stack

5) The compiler will often complain about a missing closing curly bracket '}' at the end of your source files. Just add the one it requires at the very end even though it's an extra one, or, finish your source file by putting your last function with this notation:

void myLastFunction(uint8_t whatever) {

//bunchacode

}