Difference between revisions of "C-compiler use"

From C256 Foenix Wiki
Jump to navigation Jump to search
(C-Compiler pointers)
Line 23: Line 23:
 
#include <stdio.h>
 
#include <stdio.h>
 
#include <sys/types.h>
 
#include <sys/types.h>
 +
 +
void *heap_start = (void * )0x190000, *heap_end = (void * )0x193000;
  
 
#define BORDER_Y_SIZE (*(volatile unsigned char *)0xAF0009)  
 
#define BORDER_Y_SIZE (*(volatile unsigned char *)0xAF0009)  
volatile unsigned char * const textScreen = (unsigned char *)0xAFA000;
+
#define textScreen ((volatile unsigned char *)0xAFA000)
 +
 
  
 
void IRQHandler(void) {             
 
void IRQHandler(void) {             
 +
}
 +
 +
void COPHandler(void) {           
 +
}
 +
 +
void BRKHandler(void) {           
 
}
 
}
  
 
void main(void) {
 
void main(void) {
 
BORDER_Y_SIZE = 0;
 
BORDER_Y_SIZE = 0;
textScreen[0] = 'H';
+
  textScreen[0] = 'H';
 
}
 
}
 
</pre>
 
</pre>
Line 39: Line 48:
  
 
<pre>
 
<pre>
 +
wdc816as -DUSING_816 -DLARGE -V -L fxloader.asm -O fxloader.obj
 
wdc816cc -ML demo.c
 
wdc816cc -ML demo.c
wdcln -HI -V -T -P00 demo.obj c0l.obj -LCL -O demo.hex -C1000
+
wdcln -HIE -V -T -P00 demo.obj fxloader.obj -LCL -O demo.hex -C10000 -D20000
 
</pre>
 
</pre>
  
The first line calls the compiler (wdc816cc) the import swith here is the -M switch. This specifies the memory model the compiler is going to use when compiling code. The example here is using the large model. Choosing the right memory model for your code can have a significant impact on the resulting codes execution performance. The 65816 is a 16-bit cpu but can adress up to 16MB of memory using 32 bit pointers in the large model (the bus is actually 24bit). But this take more CPU cycles than using 16bit pointers. 16 bit pointers are however to small to address all memory. So you can only use those if your code or data fits within a single page of memory (64kb). To make things more confusing. We can specify different models for both data and code. WDC provides the following table to choose a model. For more details refer to the chapter on memory models in the compiler manual (816cc.pdf)
+
The first line calls the assembler (wdc816as) to compile the startup code. This startup code switches the CPU from 6502 mode into 65816 mode. It also sets up the stack pointer and some interrupt handlers. Once this is done it calls into the main function of the C-code. Most of this can be done from the C code itself but its easier this way. As an example the start and end of the heap as used by the WDC library functions (aside from our own code) is set in the C-code and not assembly. It makes little difference.
 +
 
 +
Second the C-compiler (wdc816cc). The import swith here is the -M switch. This specifies the memory model the compiler is going to use when compiling code. The example here is using the large model. Choosing the right memory model for your code can have a significant impact on the resulting codes execution performance. The 65816 is a 16-bit cpu but can adress up to 16MB of memory using 32 bit pointers in the large model (the bus is actually 24bit). But this take more CPU cycles than using 16bit pointers. 16 bit pointers are however to small to address all memory. So you can only use those if your code or data fits within a single page of memory (64kb). To make things more confusing. We can specify different models for both data and code. WDC provides the following table to choose a model. For more details refer to the chapter on memory models in the compiler manual (816cc.pdf)
  
 
{|
 
{|
Line 59: Line 71:
 
|}
 
|}
  
The Second line of the example links to objects the compiler generated into a single hex file that can be uploaded to the foenix. -H specifies the output format. Here it is (I) Intel Hex. This is fine a long as you keep your code in page0. But if you place it elsewhere (recomended) use (-HIE). Basic intel format does not support more that 16bit addresses, but will not complain about that if you enter code segments above that.  
+
The Second line of the example links to objects the compiler generated into a single hex file that can be uploaded to the foenix. -H specifies the output format. Here it is (IE) Intel Extended Hex. Basic intel format does not support more that 16bit addresses, but will not complain about that if you enter code segments above that.  
 
Though not required -V make the linker output the locations of functions and data/code segments and symbols. In this output you will notice that the compiler prefixes every function/symbol with a combination of (~) and (_). This is used to signify the memory model used by that part of the code. Unfortunately these prefixes turn up in error messages. So when you see this in an error just ignore them and find the function or such without these prefixes in your code. -T this generates a map file. -P specifies the fill characters for the output hex file. 00 will be fine here. -L is used to specify one or more libraries. Choose the correct libraries depending again on your choosen memory model. The linker manaual documents this. -O for the output hex file name. You can tell the linker to load code and data (initialized and unintialized) to specfic locations. In this example I only specify a location for the Code segment (-C1000 start code at hex 1000). The linker will layout other sections using the code segment as a starting point.  
 
Though not required -V make the linker output the locations of functions and data/code segments and symbols. In this output you will notice that the compiler prefixes every function/symbol with a combination of (~) and (_). This is used to signify the memory model used by that part of the code. Unfortunately these prefixes turn up in error messages. So when you see this in an error just ignore them and find the function or such without these prefixes in your code. -T this generates a map file. -P specifies the fill characters for the output hex file. 00 will be fine here. -L is used to specify one or more libraries. Choose the correct libraries depending again on your choosen memory model. The linker manaual documents this. -O for the output hex file name. You can tell the linker to load code and data (initialized and unintialized) to specfic locations. In this example I only specify a location for the Code segment (-C1000 start code at hex 1000). The linker will layout other sections using the code segment as a starting point.  
The objects linked together in this example are demo.obj and col.obj. demo.obj is the output from the C-Compiler. Normally this is enough, however with the foenix we are talking a system that (excluding the kernel) does not have a formal operating system. So you need some startup code to actually set some thing up like the stack and interrupt handlers and the proper vectors. You can create the needed assembly code for this yourself. But WDC provides a few default options. C0S.OBJ, C0C.OBJ, C0M.OBJ and C0L.OBJ. Again choose the proper one based on your choise of memory model. The manual actually states that the ASM files of these are included to tweak to desire. This however is not the case. If one is interested, they can be requested by mailing WDC and asking for them. These example startup code objects do not know anything about the foenix kernel and thus assume your program is the only one running. In time proper starup objects and a format for binaries to work along with the foenix kernel should appear, but have not materialized yet. Considering that this is really a plaform to experiment the author feels this is not a significant problem at the moment.
+
The objects linked together in this example are demo.obj and fxloader.obj. demo.obj is the output from the C-Compiler.  
 +
 
 +
Normally this is enough, however with the foenix we are talking a system that (excluding the kernel) does not have a formal operating system. So you need some startup code to actually set some thing up like the stack and interrupt handlers and the proper vectors. You can create the needed assembly code for this yourself, but an example is fxloader and can downloaded from the wiki. The WDC provides a few default options. C0S.OBJ, C0C.OBJ, C0M.OBJ and C0L.OBJ. But these do not work properly with the foenix. The stack they configure is small and placed such that it is bound to crash into the interrupt hardware registers. The manual actually states that the ASM files of these are included to tweak to desire. This however is not the case. If one is interested, they can be requested by mailing WDC and asking for them. These example startup code objects do not know anything about the foenix kernel and thus assume your program is the only one running. In time proper starup objects and a format for binaries to work along with the foenix kernel should appear, but have not materialized yet. Considering that this is really a platform to experiment the author feels this is not a significant problem at the moment.

Revision as of 11:34, 23 February 2020

C-Compiler pointers

Warning this document is rather a work in progress, take it for what it is... :)

The compiler

Though assembly at the moment seems to be the most popular programming language for the IDE, it is not the only one. Assembly (when done right) will produce the most speedy and compact code. But it is not exactly portable nor does everyone want to learn every seperate cpu its specific opcodes. For those Western Design Center (WDC) provides an C-Compiler. It has a few quircks but if you are familier with C on other platforms most things will seem quite familier to you.

Where to get it
You can download the compiler at https://wdc65xx.com/. I cannot provide a direct link as it requires you to provide an email address. You will be send the download URL in an email. I have not noticed any spam from them so I guess its not a problem. Here you can also download several manuals for thing like the compiler, the linker and the assembler.
Installing
The compiler is a windows program, but runs fine under wine on linux. It is command line based, but the setup is graphical. Just do the usual next next finish steps. It installs to c:\wdc by default and I advise to keep this location. It sets the path variables properly so after installation you can call it from any location. For those installing it under wine run wine installer.exe from your favorite shell or wine installer utility.


Using
For those working on Linux use wineconsole to get a command prompt and from that you can use it the same as windows. Wine provides a z-drive by default that links to your linux file system and PATH variables to the compiler executables should work fine.
You can write your code in any text editor you like. The auther likes VSCode as it runs both on linux and windows and it folder based (contrary to for example Visual Studio which is solution based). So you can just create a new folder for your project and at the a new c-file to it. VSCode has extension for c syntax highlighting to make life a little easier. But this is not about talking up VSCode, use whatever you like for editing. Once you have a folder and a C-file, you can create eiter a batch file or make file to run the compilation. The author prefers a simple batch file as they are native to windows and are very simple to use.

a basic c file looks like this:

#include <stdio.h>
#include <sys/types.h>

void *heap_start = (void * )0x190000, *heap_end = (void * )0x193000;

#define BORDER_Y_SIZE (*(volatile unsigned char *)0xAF0009) 
#define textScreen ((volatile unsigned char *)0xAFA000) 


void IRQHandler(void) {             
}

void COPHandler(void) {             
}

void BRKHandler(void) {             
}

void main(void) {
	BORDER_Y_SIZE = 0;
  	textScreen[0] = 'H';
}

To the batch file add the following lines

wdc816as -DUSING_816 -DLARGE -V -L fxloader.asm -O fxloader.obj
wdc816cc -ML demo.c
wdcln -HIE -V -T -P00 demo.obj fxloader.obj -LCL -O demo.hex -C10000 -D20000

The first line calls the assembler (wdc816as) to compile the startup code. This startup code switches the CPU from 6502 mode into 65816 mode. It also sets up the stack pointer and some interrupt handlers. Once this is done it calls into the main function of the C-code. Most of this can be done from the C code itself but its easier this way. As an example the start and end of the heap as used by the WDC library functions (aside from our own code) is set in the C-code and not assembly. It makes little difference.

Second the C-compiler (wdc816cc). The import swith here is the -M switch. This specifies the memory model the compiler is going to use when compiling code. The example here is using the large model. Choosing the right memory model for your code can have a significant impact on the resulting codes execution performance. The 65816 is a 16-bit cpu but can adress up to 16MB of memory using 32 bit pointers in the large model (the bus is actually 24bit). But this take more CPU cycles than using 16bit pointers. 16 bit pointers are however to small to address all memory. So you can only use those if your code or data fits within a single page of memory (64kb). To make things more confusing. We can specify different models for both data and code. WDC provides the following table to choose a model. For more details refer to the chapter on memory models in the compiler manual (816cc.pdf)

<64K Code >64K Code
<64K data Small Medium
>64K data Compact Large

The Second line of the example links to objects the compiler generated into a single hex file that can be uploaded to the foenix. -H specifies the output format. Here it is (IE) Intel Extended Hex. Basic intel format does not support more that 16bit addresses, but will not complain about that if you enter code segments above that. Though not required -V make the linker output the locations of functions and data/code segments and symbols. In this output you will notice that the compiler prefixes every function/symbol with a combination of (~) and (_). This is used to signify the memory model used by that part of the code. Unfortunately these prefixes turn up in error messages. So when you see this in an error just ignore them and find the function or such without these prefixes in your code. -T this generates a map file. -P specifies the fill characters for the output hex file. 00 will be fine here. -L is used to specify one or more libraries. Choose the correct libraries depending again on your choosen memory model. The linker manaual documents this. -O for the output hex file name. You can tell the linker to load code and data (initialized and unintialized) to specfic locations. In this example I only specify a location for the Code segment (-C1000 start code at hex 1000). The linker will layout other sections using the code segment as a starting point. The objects linked together in this example are demo.obj and fxloader.obj. demo.obj is the output from the C-Compiler.

Normally this is enough, however with the foenix we are talking a system that (excluding the kernel) does not have a formal operating system. So you need some startup code to actually set some thing up like the stack and interrupt handlers and the proper vectors. You can create the needed assembly code for this yourself, but an example is fxloader and can downloaded from the wiki. The WDC provides a few default options. C0S.OBJ, C0C.OBJ, C0M.OBJ and C0L.OBJ. But these do not work properly with the foenix. The stack they configure is small and placed such that it is bound to crash into the interrupt hardware registers. The manual actually states that the ASM files of these are included to tweak to desire. This however is not the case. If one is interested, they can be requested by mailing WDC and asking for them. These example startup code objects do not know anything about the foenix kernel and thus assume your program is the only one running. In time proper starup objects and a format for binaries to work along with the foenix kernel should appear, but have not materialized yet. Considering that this is really a platform to experiment the author feels this is not a significant problem at the moment.