Calypsi C compiler toolchain for 6502 and 65816
- akohlbecker
- Posts: 282
- Joined: 24 Jul 2021
- Contact:
Re: Calypsi C compiler toolchain for 6502 and 65816
I've been reading the docs for the 65816 and this might be a dumb question but I haven't seen it mentioned there: Is Calypsi running in 16bit mode or 8bit mode? Does it switch automatically as needed? Or is this something you have to define yourself, if so how?
- BigDumbDinosaur
- Posts: 9425
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: Calypsi C compiler toolchain for 6502 and 65816
akohlbecker wrote:
Is Calypsi running in 16bit mode or 8bit mode? Does it switch automatically as needed? Or is this something you have to define yourself, if so how?
I'll interject here.
The 65C816 has no “16-bit mode” or “8-bit mode.” The only modes are emulation and native. In native mode, registers may be set to 8- or 16-bits width.
x86? We ain't got no x86. We don't NEED no stinking x86!
- GARTHWILSON
- Forum Moderator
- Posts: 8773
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: Calypsi C compiler toolchain for 6502 and 65816
BigDumbDinosaur wrote:
akohlbecker wrote:
Is Calypsi running in 16bit mode or 8bit mode? Does it switch automatically as needed? Or is this something you have to define yourself, if so how?
I'll interject here.
The 65C816 has no “16-bit mode” or “8-bit mode.” The only modes are emulation and native. In native mode, registers may be set to 8- or 16-bits width.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
- akohlbecker
- Posts: 282
- Joined: 24 Jul 2021
- Contact:
Re: Calypsi C compiler toolchain for 6502 and 65816
Thanks. I'll rephrase my question in case there is doubt about what I meant: Is Calypsi generally executing code by default using 16 bit memory accesses or 8 bit accesses, is it switching the indexes or accumulator sizes itself automatically (depending on type size for example?), or do you have to manage this yourself. If so, how?
Re: Calypsi C compiler toolchain for 6502 and 65816
akohlbecker wrote:
Thanks. I'll rephrase my question in case there is doubt about what I meant: Is Calypsi generally executing code by default using 16 bit memory accesses or 8 bit accesses, is it switching the indexes or accumulator sizes itself automatically (depending on type size for example?), or do you have to manage this yourself. If so, how?
The code generator tries to stay in 16 bit mode as much as possible. When reading 8 bit (or 24 bit) data, it will access it as 16 bits if it can to reduce data mode changes. 24 bit pointers are treated as 32 bit objects for this reason. Volatile accesses are done in their specified size and there is a __far24 attribute for the situations where you must have a 24 bit type.
If you call your own assembly routines, you can rely on that the index and data size are 16 bit when called. You can change it internally but you need to give control back with 16 bit index and data size enabled.
- akohlbecker
- Posts: 282
- Joined: 24 Jul 2021
- Contact:
Re: Calypsi C compiler toolchain for 6502 and 65816
Thank you for your answer, much appreciated.
Is there an optimization where it sees multiple 8-bit accesses in succession and decides to switch M to 8-bit? Considering 16-bit accesses have a one-cycle penalty. You said "when required" but it is unclear to me what those cases are?
This comes to mind because I'm currently working (in assembly, haven't switched to C yet) on hardware drivers where most of the accesses and data manipulated are 8-bit (things like reading keyboard keys and writing to an LCD), and so they would benefit from a mode change at the beginning of the subroutine.
Is there an optimization where it sees multiple 8-bit accesses in succession and decides to switch M to 8-bit? Considering 16-bit accesses have a one-cycle penalty. You said "when required" but it is unclear to me what those cases are?
This comes to mind because I'm currently working (in assembly, haven't switched to C yet) on hardware drivers where most of the accesses and data manipulated are 8-bit (things like reading keyboard keys and writing to an LCD), and so they would benefit from a mode change at the beginning of the subroutine.
Re: Calypsi C compiler toolchain for 6502 and 65816
akohlbecker wrote:
Thank you for your answer, much appreciated.
Is there an optimization where it sees multiple 8-bit accesses in succession and decides to switch M to 8-bit? Considering 16-bit accesses have a one-cycle penalty. You said "when required" but it is unclear to me what those cases are?
This comes to mind because I'm currently working (in assembly, haven't switched to C yet) on hardware drivers where most of the accesses and data manipulated are 8-bit (things like reading keyboard keys and writing to an LCD), and so they would benefit from a mode change at the beginning of the subroutine.
Is there an optimization where it sees multiple 8-bit accesses in succession and decides to switch M to 8-bit? Considering 16-bit accesses have a one-cycle penalty. You said "when required" but it is unclear to me what those cases are?
This comes to mind because I'm currently working (in assembly, haven't switched to C yet) on hardware drivers where most of the accesses and data manipulated are 8-bit (things like reading keyboard keys and writing to an LCD), and so they would benefit from a mode change at the beginning of the subroutine.
If you are concerned about 8 bit access and performance, I would suggest grouping such accesses next to each other. Then try it out and study what the compiler makes out of it. You can always contact me if there is specific code generation you want to discuss, but preferably do that on Github as I am not here on a regular basis. If the access code is very critical, you may consider writing that routine in assembly.
- akohlbecker
- Posts: 282
- Joined: 24 Jul 2021
- Contact:
Re: Calypsi C compiler toolchain for 6502 and 65816
Thanks again!
Maybe I missed it in the docs, but if not, a paragraph describing this behaviour might be a worthwhile addition.
Maybe I missed it in the docs, but if not, a paragraph describing this behaviour might be a worthwhile addition.
Re: Calypsi C compiler toolchain for 6502 and 65816
akohlbecker wrote:
Thanks again!
Maybe I missed it in the docs, but if not, a paragraph describing this behaviour might be a worthwhile addition.
Maybe I missed it in the docs, but if not, a paragraph describing this behaviour might be a worthwhile addition.
Re: Calypsi C compiler toolchain for 6502 and 65816
i'm trying out this compiler again, after finding out that WDC's Compiler doesn't support binary notation... which seemed insane.
but after a quick round of google, the C standard just doesn't have binary notation because aparently "hex is good enough", and it just so happend that GCC/LLVM and cc65 just add binary prefixes for convenience so i had no idea it wasn't an "official" feature.
anyways i've been trying for some time to get something working, but i'm getting kinda hung up on the whole segements thing.
for example: you can have "code" and "data" in the same memory, you can have "data" and "bss" in the same memory, so why can you not have "code" and "bss" in the same memory too? am i missing some setting that allows this?
other compilers like WDC's and cc65 don't care what memory you place segments in, they just stack them one after another.
speaking of "one after another", what determines the order of segments in the output file? i need a custom header at the start of the output binary that has some information about the program (ie total size of the file, entry point for program execution, and where to place the file in memory).
with cc65 i just defined a custom segment that gets placed before any of the other segments to make sure it's the first in the output binary.
with WDC's Compiler i just placed it at the start of the crt0.s file and made sure that file got linked first so it's always at the start of the output binary.
so, how can i do that with this compiler? can i just make a custom segment? do i have to place that in the startup code? (where is the startup assembly file anyways? i was not able to find one) does the linker even generate the symbols required for this kind of thing?
and lastly, on a completely different note: is the DP just used like the ZP on the 65C02? or can it be moved during runtime?
i really liked how the WDC Compiler uses the DP, after a function has been called the stack is adjusted by some amount of bytes for temporary variables (if any), and then the DP is placed ontop of the stack. so you got really fast access to the input arguments and temporary variables, and the whole thing is nest-able so you can't accidentally clobber up the DP from some parent function. so i was hoping to be able to do the same here when writting assembly routines (or just have the compiler do that automatically).
i assume it should be fine as long as i restore the DP back to where it was before the function was called.
i'm sorry if any of these are answered inside the user guide, i have been scrolling back and forth in that thing trying to find what i need and just thought directly asking would probably be better.
but after a quick round of google, the C standard just doesn't have binary notation because aparently "hex is good enough", and it just so happend that GCC/LLVM and cc65 just add binary prefixes for convenience so i had no idea it wasn't an "official" feature.
anyways i've been trying for some time to get something working, but i'm getting kinda hung up on the whole segements thing.
for example: you can have "code" and "data" in the same memory, you can have "data" and "bss" in the same memory, so why can you not have "code" and "bss" in the same memory too? am i missing some setting that allows this?
other compilers like WDC's and cc65 don't care what memory you place segments in, they just stack them one after another.
speaking of "one after another", what determines the order of segments in the output file? i need a custom header at the start of the output binary that has some information about the program (ie total size of the file, entry point for program execution, and where to place the file in memory).
with cc65 i just defined a custom segment that gets placed before any of the other segments to make sure it's the first in the output binary.
with WDC's Compiler i just placed it at the start of the crt0.s file and made sure that file got linked first so it's always at the start of the output binary.
so, how can i do that with this compiler? can i just make a custom segment? do i have to place that in the startup code? (where is the startup assembly file anyways? i was not able to find one) does the linker even generate the symbols required for this kind of thing?
and lastly, on a completely different note: is the DP just used like the ZP on the 65C02? or can it be moved during runtime?
i really liked how the WDC Compiler uses the DP, after a function has been called the stack is adjusted by some amount of bytes for temporary variables (if any), and then the DP is placed ontop of the stack. so you got really fast access to the input arguments and temporary variables, and the whole thing is nest-able so you can't accidentally clobber up the DP from some parent function. so i was hoping to be able to do the same here when writting assembly routines (or just have the compiler do that automatically).
i assume it should be fine as long as i restore the DP back to where it was before the function was called.
i'm sorry if any of these are answered inside the user guide, i have been scrolling back and forth in that thing trying to find what i need and just thought directly asking would probably be better.
Re: Calypsi C compiler toolchain for 6502 and 65816
Håkan thanks for the Calypsi toolchain. Do you have any plans to make it available on Ubuntu Linux ARM64?
Re: Calypsi C compiler toolchain for 6502 and 65816
Proxy wrote:
anyways i've been trying for some time to get something working, but i'm getting kinda hung up on the whole segements thing.
for example: you can have "code" and "data" in the same memory, you can have "data" and "bss" in the same memory, so why can you not have "code" and "bss" in the same memory too? am i missing some setting that allows this?
other compilers like WDC's and cc65 don't care what memory you place segments in, they just stack them one after another.
for example: you can have "code" and "data" in the same memory, you can have "data" and "bss" in the same memory, so why can you not have "code" and "bss" in the same memory too? am i missing some setting that allows this?
other compilers like WDC's and cc65 don't care what memory you place segments in, they just stack them one after another.
Calyspi is based on ROM/RAM separation, but can do the other kind too (--hosted given to the linker). The main difference between the two is that a hosted system loads initialized data into place, while on an embedded/ROM based system such data initializers are placed in ROM and copied to RAM during C startup, which is done automatically.
To have a lot of flexibility Calypsi uses many sections with different properties. They cannot just be stack after each others with no regard of memory type for reasons outlined above. There are also sections based on different address spaces, e.g. bank 00 memory, far semantics, as well as some others.
With the current release you can do more placement without specifying where sections are placed as the linker is fairly capable of figuring it out and generate a suitable placement for you. You still need to tell what memory ranges you have and the kind of memory it is (this is the type attribute, which can be ANY, so everything goes, if that is what you want). See section 6.1.2 Placing sectons in memory.
If you want your own sections you can just create them using a #pragma or with the .section directive in the assembler. They will end up with the linker who will tell you that need to mention them in the placement, by a diagnostic message.
DP is fixed in the run-time. It will get set at startup and does not move. This allows for having a couple of pseudo registers and it can also act as a short address area (tiny) for frequently addressed static data.
You can move it as you see fit in your own assembly routines, but you need to restore it before you give control back.
The reasons I decided to make it fixed is that the 65816 already provide fast access to the stack. It also avoids code to adjust it back and forth while still having a couple of scratch locations in the DP area. Another reason is to avoid complexity that could arise in very deranged cases if you have a large stack frame (beyond 256 bytes) and need to use addressing modes only on DP and they happen to be out of range. I think the WDC compiler limits the stack frame to 256 bytes, so it cannot run into this problem.
It is a tradeoff. As mentioned, you get fast access static (tiny) data area which is not provided by WDC.
Re: Calypsi C compiler toolchain for 6502 and 65816
zuliani wrote:
Håkan thanks for the Calypsi toolchain. Do you have any plans to make it available on Ubuntu Linux ARM64?
Upgrading also affects some libraries and sadly one library has rather dramatic API changes, so it sadly is not just a simple recompile. I also need a suitable build machine and I think the newer Mac with a Linux Docker would be useful for this. I still need to get such machine and then find time to look into this.
Re: Calypsi C compiler toolchain for 6502 and 65816
hth313 wrote:
If you have a bare metal system with ROM and RAM then you have to separate code from modifiable data. If you load a program into a RAM based system by some kernel or operating system based environment you can place things more or less how you see fit.
Calyspi is based on ROM/RAM separation, but can do the other kind too (--hosted given to the linker). The main difference between the two is that a hosted system loads initialized data into place, while on an embedded/ROM based system such data initializers are placed in ROM and copied to RAM during C startup, which is done automatically.
To have a lot of flexibility Calypsi uses many sections with different properties. They cannot just be stack after each others with no regard of memory type for reasons outlined above. There are also sections based on different address spaces, e.g. bank 00 memory, far semantics, as well as some others.
Calyspi is based on ROM/RAM separation, but can do the other kind too (--hosted given to the linker). The main difference between the two is that a hosted system loads initialized data into place, while on an embedded/ROM based system such data initializers are placed in ROM and copied to RAM during C startup, which is done automatically.
To have a lot of flexibility Calypsi uses many sections with different properties. They cannot just be stack after each others with no regard of memory type for reasons outlined above. There are also sections based on different address spaces, e.g. bank 00 memory, far semantics, as well as some others.
you would just need to trust that the user knows the hardware it's compiling for.
but thanks for the commandline parameter, that seems to have worked. it stopped complaining about the sections.
hth313 wrote:
With the current release you can do more placement without specifying where sections are placed as the linker is fairly capable of figuring it out and generate a suitable placement for you. You still need to tell what memory ranges you have and the kind of memory it is (this is the type attribute, which can be ANY, so everything goes, if that is what you want). See section 6.1.2 Placing sectons in memory.
hth313 wrote:
If you want your own sections you can just create them using a #pragma or with the .section directive in the assembler. They will end up with the linker who will tell you that need to mention them in the placement, by a diagnostic message.
but what about the data i mentioned that is needed for the header? what symbols do i need to import to know the size of the file, or where to jump to once everything is loaded into RAM?
hth313 wrote:
DP is fixed in the run-time. It will get set at startup and does not move. This allows for having a couple of pseudo registers and it can also act as a short address area (tiny) for frequently addressed static data.
You can move it as you see fit in your own assembly routines, but you need to restore it before you give control back.
The reasons I decided to make it fixed is that the 65816 already provide fast access to the stack. It also avoids code to adjust it back and forth while still having a couple of scratch locations in the DP area. Another reason is to avoid complexity that could arise in very deranged cases if you have a large stack frame (beyond 256 bytes) and need to use addressing modes only on DP and they happen to be out of range. I think the WDC compiler limits the stack frame to 256 bytes, so it cannot run into this problem.
It is a tradeoff. As mentioned, you get fast access static (tiny) data area which is not provided by WDC.
You can move it as you see fit in your own assembly routines, but you need to restore it before you give control back.
The reasons I decided to make it fixed is that the 65816 already provide fast access to the stack. It also avoids code to adjust it back and forth while still having a couple of scratch locations in the DP area. Another reason is to avoid complexity that could arise in very deranged cases if you have a large stack frame (beyond 256 bytes) and need to use addressing modes only on DP and they happen to be out of range. I think the WDC compiler limits the stack frame to 256 bytes, so it cannot run into this problem.
It is a tradeoff. As mentioned, you get fast access static (tiny) data area which is not provided by WDC.
you can basically see the 16-bit index registers as the base address, and the 1 byte operand as the offset to that address.
it's functionally similar to the stack addressing mode, but without the 256 byte limit as the stack addressing mode only uses a single byte as offset instead of an index register.
on a different note i still can't get anything to compile even with the --hosted commandline parameter, here the error:
Code: Select all
symbol '__program_start' referenced from section 'reset' at offset 000000 in cstartup.o: value 147945 is out of range, allowed range is -32768 to 65535Code: Select all
(define memories'(
(memory DirectPage (address (#x000100 . #x0001FF))
(section (registers ztiny)))
(memory LoRAM (address (#x000200 . #x00EFFF))
(section stack heap))
(memory IO (address (#x00F000 . #x00F0FF)))
(memory VRAM (address (#x010000 . #x01FFFF)))
(memory HiRAM (address (#x020000 . #x08FFFF))
(section reset data_init_table farcode code farswitch far huge cfar chuge zfar zhuge cstack))
(block cstack (size #x004000))
(block stack (size #x004000))
(block heap (size #x004000))
(base-address _DirectPageStart DirectPage 0)
(base-address _NearBaseAddress LoRAM 0)
))Code: Select all
CC65816 --64bit-doubles --code-model large --data-model large -I Include -O2 -l -o Temp\test.o test.c
LN65816 --hosted Config\SBC_816.scm clib-lc-ld-double64.a Temp\test.oRe: Calypsi C compiler toolchain for 6502 and 65816
The startup code is provided with the product, both in precompiled form in the library and as source code. This code is short and cruical to get the system initialized properly before you enter the main() function. It is not only doing data copying, it is also clearing variables that are 0 initialized. There may also be multiple areas that needs to be initialized.
What output format do you use? Many formats (e.g. ELF, intel-hex, S-records) contain a concept of an entry point that is supported. If you are doing Commodore PRG then https://github.com/hth313/Calypsi-6502-Commodore may offer some inspiration, though it is for 6502.
The linker error is because __program_start which is the first executable address must reside in bank 00 as the reset vector is 16 bits, but it seems you have placed it in bank 02. This should be section 'code'.
What output format do you use? Many formats (e.g. ELF, intel-hex, S-records) contain a concept of an entry point that is supported. If you are doing Commodore PRG then https://github.com/hth313/Calypsi-6502-Commodore may offer some inspiration, though it is for 6502.
The linker error is because __program_start which is the first executable address must reside in bank 00 as the reset vector is 16 bits, but it seems you have placed it in bank 02. This should be section 'code'.