Update README.md
This commit is contained in:
parent
3d3618876f
commit
b456086ee4
151
README.md
151
README.md
|
@ -3,27 +3,27 @@
|
|||
|
||||
## History and motivation
|
||||
|
||||
It is a sad fact that the 6502 used in the Commodore64 and other home computers of the 80s has a poor code density when it comes to 16 bit code. The C standard requires computations to be made with ints which work best if they have the same size as a pointer.
|
||||
It is a sad fact that the 6502 used in the Commodore64 and other home computers of the 80s is widely believet to have a poor code density when it comes to compiled or wider than eight bit code. The C standard requires computations to be made with ints which work best if they have the same size as a pointer.
|
||||
|
||||
The 6502 also has a very small stack of 256 bytes which cannot be easily addressed and thus cannot be used for local variables. Therefore a second stack for variables has to be maintained, resulting in costly indexing operations.
|
||||
The 6502 also has a very small stack of 256 bytes which cannot be easily addressed and thus cannot be used for local variables. Therefore a second stack for variables has to be maintained, resulting in costly indexing operations. The 6502 is also pretty poor when it comes to indexed operations, it has no index with constant offset addressing mode, and requires the y register to be used for indexing.
|
||||
|
||||
A C compiler for the 6502 thus generates large binaries if it translates to native machine code. The idea for the **oscar64** compiler is to translate the C source to an intermediate 16 bit byte code with the option to use native machine code for crucial functions. Using embedded assembly for runtime libraries or critical code should also be possible.
|
||||
|
||||
The resulting compiler is a frankenstein constructed from a converted javascript parser a intermediate code optimizer based on a 15 year old compiler for 64bit x86 code and some new components for the backend.
|
||||
Most C compilers for the 6502 thus generate large binaries when translating to native machine code. The original idea for the **oscar64** compiler was to translate the C source to an intermediate 16 bit byte code with the option to use native machine code for crucial functions. Using embedded assembly for runtime libraries or critical code should also be possible.
|
||||
|
||||
The performance of interpreted code is clearly not as good as native machine code but the penalty for 16bit code is around 40-50% and less than 10% for floating point. Code that can use 8bit may suffer up to a factor of 10 to 20.
|
||||
|
||||
The goal is to implement the actual C standard and not some subset for performance reasons. So the compiler must support:
|
||||
The goal was also to implement the C99 standard and not some subset for performance reasons. So the compiler must support:
|
||||
|
||||
* Floating point
|
||||
* Recursion
|
||||
* Multi dimensional arrays
|
||||
* Pointer to structs
|
||||
|
||||
After extensive optimizations it turns out, that the interpreted code is not significantly smaller than the native code in most scenarios (although there are cases where the difference is significant).
|
||||
|
||||
|
||||
## Limits and Errors
|
||||
|
||||
There are still several open areas, but most targets have been reached. The current Dhrystone performance is 81 iterations per second with byte code (11108) and 354 iterations with native code (10965 Bytes).
|
||||
There are still several open areas, but most targets have been reached. The current Dhrystone performance is 81 iterations per second with byte code (11108) and 354 iterations with native code (10965 Bytes). This clearly shows that Dhrystone is not a valid benchmark for optimizing compilers, because it puts the 6502 on par with a 4MHz 8088 or 68k, which it clearly is not.
|
||||
|
||||
### Language
|
||||
|
||||
|
@ -32,6 +32,8 @@ There are still several open areas, but most targets have been reached. The cur
|
|||
|
||||
### Linker
|
||||
|
||||
* No external libraries
|
||||
|
||||
### Standard Libraries
|
||||
|
||||
* No standard file functions, but CBM based file ops
|
||||
|
@ -41,24 +43,35 @@ There are still several open areas, but most targets have been reached. The cur
|
|||
* No NaN support for floats
|
||||
* Basic zero page variables not restored on stop/restore
|
||||
|
||||
|
||||
### Intermediate code generation
|
||||
|
||||
* No check for running out of temporary registers
|
||||
|
||||
### Native code generation
|
||||
|
||||
## Compiler arguments
|
||||
## Installation and Usage
|
||||
|
||||
### Installing on windows
|
||||
|
||||
A windows installer is provided with the release, the compiler is installed into "%programfiles(x86)%\oscar64\bin\oscar64". When not using batch or make files, it might be a good idea to add the folder to the path environment variable.
|
||||
|
||||
### Building
|
||||
|
||||
The compiler can also built using MSVC or GCC. A visual studio project and a makefile are part of the source repository. The makefile is in the make folder.
|
||||
|
||||
|
||||
### Compiler arguments
|
||||
|
||||
The compiler is command line driven, and creates an executable .prg file.
|
||||
|
||||
oscar64 {-i=includePath} [-o=output.prg] [-rt=runtime.c] [-e] [-n] [-dSYMBOL[=value]] {source.c}
|
||||
oscar64 {-i=includePath} [-o=output.prg] [-rt=runtime.c] [-tf=format] [-e] [-n] [-dSYMBOL[=value]] {source.c}
|
||||
|
||||
* -v : verbose output for diagnostics
|
||||
* -i : additional include paths
|
||||
* -o : optional output file name
|
||||
* -rt : alternative runtime library, replaces the crt.c
|
||||
* -e : execute the result in the integrated emulator
|
||||
* -ep : execute and profile the result in the integrated emulator
|
||||
* -n : create pure native code for all functions
|
||||
* -d : define a symbol (e.g. NOFLOAT or NOLONG to avoid float/long code in printf)
|
||||
* -O1 or -O : default optimizations
|
||||
|
@ -66,10 +79,34 @@ The compiler is command line driven, and creates an executable .prg file.
|
|||
* -O2: more aggressive speed optimizations including auto inline of small functions
|
||||
* -O3: aggressive optimization for speed
|
||||
* -Os: optimize for size
|
||||
* -tf: target format, may be prg, crt or bin
|
||||
|
||||
A list of source files can be provided.
|
||||
|
||||
## Console input and output
|
||||
## Language extensions
|
||||
|
||||
The compiler has various extensions to simplify developing for the C64.
|
||||
|
||||
### Embedding binary data
|
||||
|
||||
The compiler supports the #embed preprocessor directive to import binary data. It converts a section of an external binary file into a sequence of numbers that can be placed into an initializer of an array.
|
||||
|
||||
byte data[] = {
|
||||
|
||||
#embed "data.bin"
|
||||
|
||||
};
|
||||
|
||||
A section of the file can be selected by providing a limit and or an offset into the file before the file name.
|
||||
|
||||
byte data[] = {
|
||||
|
||||
#embed 4096 126 "data.bin"
|
||||
|
||||
};
|
||||
|
||||
|
||||
### Console input and output
|
||||
|
||||
The C64 does not use ASCII it uses a derivative called PETSCII. There are two fonts, one with uppercase and one with uppercase and lowercase characters. It also used CR (13) as line terminator instead of LF (10). The stdio and conio libaries can perform translations.
|
||||
|
||||
|
@ -88,23 +125,9 @@ Screen codes can be generated similar using "s" or "S" prefix.
|
|||
|
||||
Input from the console will also be translated accordingly.
|
||||
|
||||
## Embedding binary data
|
||||
The character map for string and char constants can be changed with a pragma to match a custon character set or PETSCII.
|
||||
|
||||
The compiler supports the #embed preprocessor directive to import binary data. It converts a section of an external binary file into a sequence of numbers that can be placed into an initializer of an array.
|
||||
|
||||
byte data[] = {
|
||||
|
||||
#embed "data.bin"
|
||||
|
||||
};
|
||||
|
||||
A section of the file can be selected by providing a limit and or an offset into the file before the file name.
|
||||
|
||||
byte data[] = {
|
||||
|
||||
#embed 4096 126 "data.bin"
|
||||
|
||||
};
|
||||
#pragma charmap(char, code [,count])
|
||||
|
||||
|
||||
## Language extensions for optimization
|
||||
|
@ -137,12 +160,72 @@ The linker includes only objects that are referenced, starting by the startup co
|
|||
|
||||
If you need to have a function or variable present regardless, you can specify it with the __export storage class specifier or use the #pragma reference(name) pragma.
|
||||
|
||||
#### Using libraries
|
||||
|
||||
## Inline Assembler
|
||||
The compiler does a full program compile, the linker step is part of the compilation. It knows all functions during the compilation run and includes only reachable code in the output. Source files are added to the build with the help of a pragma:
|
||||
|
||||
#pragma compile("stdio.c")
|
||||
|
||||
This way you do not need a makefile to build your project. All header files of the provided libraries add their implementation to the build using this pragma.
|
||||
|
||||
#### Placement
|
||||
|
||||
The linker uses three levels of objects:
|
||||
|
||||
* Region : A physical region of memory or a bank in a cartridge
|
||||
* Section : A logical region of memory, may span several sections
|
||||
* Object : Generated code or data, either initialized or empty (e.g. stack or bss)
|
||||
|
||||
With the default prg target and no further changes, the compiler creates the following regions and sections:
|
||||
|
||||
* "startup" : **0x0801-0x0900** Basic and assembler startup code and interpreter loop
|
||||
* "startup"
|
||||
* "bytecode" : **0x0900-0x0a00** Interpreter jump table if not all native
|
||||
* "bytecode"
|
||||
* "main": **0x0a00-0xa000** Main region for code, data, bss, heap and stack
|
||||
* "code" : Compiled code
|
||||
* "data" : Constant data
|
||||
* "bss" : Non constant data, initialized to zero on program start
|
||||
* "heap" : Memory available for allocation
|
||||
* "stack" : Data stack
|
||||
|
||||
The layout can be changed using #pragma commands. One may e.g. use all memory up to 0xd000 with the following code:
|
||||
|
||||
#include <c64/memmap.h>
|
||||
|
||||
#pragma region( main, 0x0a00, 0xd000, , , {code, data, bss, heap, stack} )
|
||||
|
||||
int main(void)
|
||||
{
|
||||
mmap_set(MMAP_NO_BASIC)
|
||||
|
||||
Regions can also be used to place assets such as character sets at fixed location in the prg file to avoid copying:
|
||||
|
||||
#pragma region( lower, 0x0a00, 0x2000, , , {code, data} )
|
||||
|
||||
#pragma section( charset, 0)
|
||||
|
||||
#pragma region( charset, 0x2000, 0x2800, , , {charset} )
|
||||
|
||||
#pragma region( main, 0x2800, 0xa000, , , {code, data, bss, heap, stack} )
|
||||
|
||||
#pragma data(charset)
|
||||
|
||||
char charset[2048] = {
|
||||
#embed "../resources/charset.bin"
|
||||
}
|
||||
|
||||
#pragma data(data)
|
||||
|
||||
The #pragma data(), #pragma code() and #pragma bss() control the placement of the generated objects into sections other than the default sections.
|
||||
|
||||
|
||||
|
||||
### Inline Assembler
|
||||
|
||||
Inline assembler can be embedded inside of any functions, regardles of their compilation target of byte code or native.
|
||||
|
||||
### Accessing variables in assembler
|
||||
#### Accessing variables in assembler
|
||||
|
||||
Access to local variables and parameters is done with zero page registers, global variables are accessed using absolute addressing.
|
||||
|
||||
|
@ -212,15 +295,10 @@ The compiler provides two levels of interrupt safe functions. The specifier __i
|
|||
}
|
||||
|
||||
|
||||
|
||||
## Implementation Details
|
||||
|
||||
The compiler does a full program compile, the linker step is part of the compilation. It knows all functions during the compilation run and includes only reachable code in the output. Source files are added to the build with the help of a pragma:
|
||||
|
||||
#pragma compile("stdio.c")
|
||||
|
||||
The character map for string and char constants can be changed with a pragma to match a custon character set or PETSCII.
|
||||
|
||||
#pragma charmap(char, code [,count])
|
||||
|
||||
The byte code interpreter is compiled by the compiler itself and placed in the source file "crt.c". Functions implementing byte codes are marked with a pragma:
|
||||
|
||||
|
@ -254,7 +332,8 @@ The current byte code program counter is (ip),y. The interpreter loop guarantees
|
|||
The intermediate code generator assumes a large number of registers so the zero page is used for this purpose. The allocation is not yet final:
|
||||
|
||||
* **0x02-0x02** spilling of y register
|
||||
* **0x03-0x09** workspace for mul/div and floating point routines
|
||||
* **0x03-0x0c** workspace for mul/div and floating point routines
|
||||
* **0x0d-0x1a** function arguments for leaf functions
|
||||
* **0x19-0x1a** instruction pointer
|
||||
* **0x1b-0x1e** integer and floating point accumulator
|
||||
* **0x1f-0x22** pointers for indirect addressing
|
||||
|
|
Loading…
Reference in New Issue