mirror of
https://github.com/KarolS/millfork.git
synced 2024-05-29 04:41:30 +00:00
Add guides to documentation
This commit is contained in:
parent
d5367cc1fe
commit
c6c021cf85
|
@ -79,6 +79,14 @@
|
||||||
* [Game Boy–only modules](stdlib/gb.md)
|
* [Game Boy–only modules](stdlib/gb.md)
|
||||||
|
|
||||||
* [X16–only modules](stdlib/x16.md)
|
* [X16–only modules](stdlib/x16.md)
|
||||||
|
|
||||||
|
## Guides
|
||||||
|
|
||||||
|
* [Differences from C](various/cdiff.md)
|
||||||
|
|
||||||
|
* [Differences from other assemblers](various/asmdiff.md)
|
||||||
|
|
||||||
|
* [Optimization guide](various/optimization.md)
|
||||||
|
|
||||||
## Implementation details
|
## Implementation details
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,14 @@
|
||||||
* [Game Boy–only modules](stdlib/gb.md)
|
* [Game Boy–only modules](stdlib/gb.md)
|
||||||
|
|
||||||
* [X16–only modules](stdlib/x16.md)
|
* [X16–only modules](stdlib/x16.md)
|
||||||
|
|
||||||
|
## Guides
|
||||||
|
|
||||||
|
* [Differences from C](various/cdiff.md)
|
||||||
|
|
||||||
|
* [Differences from other assemblers](various/asmdiff.md)
|
||||||
|
|
||||||
|
* [Optimization guide](various/optimization.md)
|
||||||
|
|
||||||
## Implementation details
|
## Implementation details
|
||||||
|
|
||||||
|
|
34
docs/various/asmdiff.md
Normal file
34
docs/various/asmdiff.md
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
[< back to index](../doc_index.md)
|
||||||
|
|
||||||
|
# Differences in assembly
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
* High and low bytes of an 16-bit value are acquired using the `hi` and `lo` functions, not the `>` and `<` operators.
|
||||||
|
|
||||||
|
* Anonymous labels and local labels are not supported.
|
||||||
|
All labels defined in assembly are global.
|
||||||
|
Colons are required in label declarations.
|
||||||
|
|
||||||
|
* Macros are inserted using the `+` operator.
|
||||||
|
|
||||||
|
* Raw bytes are inserted using the Millfork array syntax, not with any pseudoopcode (like `!byte`, `db` or `fcb`)
|
||||||
|
|
||||||
|
* Assembly blocks cannot contain definitions of constants or variables.
|
||||||
|
|
||||||
|
* 6502: To enforce zero-page addressing, wrap the argument in the `lo` function: `lo(arg)`
|
||||||
|
|
||||||
|
* 6502: To enforce absolute addressing, add a 16-bit zero to the argument: `0000 + arg`
|
||||||
|
|
||||||
|
* GameBoy: The $FF page loads/stores are written `LDH (C),A`, not `LD ($FF00+C),A`.
|
||||||
|
|
||||||
|
* GameBoy: The loads/stores that postincrement/postdecrement HL write the HL register as `HLI` or `HLD`, not `HL+` or `HL-`
|
||||||
|
|
||||||
|
* Z80: Indexing using the index register uses the `IX(1)` syntax, not `(IX+1)` or `1(IX)`.
|
||||||
|
|
||||||
|
* Z80: Most undocumented instructions are not supported. The only one supported is `SLL`.
|
||||||
|
|
||||||
|
* 6809: `0,X` is assembled the same as `,X`.
|
||||||
|
|
||||||
|
|
||||||
|
|
78
docs/various/cdiff.md
Normal file
78
docs/various/cdiff.md
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
[< back to index](../doc_index.md)
|
||||||
|
|
||||||
|
# Differences from C
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
* Block comments `/* */` are not supported, use line comments `//`.
|
||||||
|
|
||||||
|
* You cannot put multiple statements on one line.
|
||||||
|
Semicolons are allowed at the end of the line, but no code can follow them.
|
||||||
|
|
||||||
|
* There are no `++` or `--` operators. Use `+= 1` and `-= 1`.
|
||||||
|
|
||||||
|
* Pointer types are declared using the `pointer.` prefix, not `*` suffix. To dereference them, use `p[0]` instead of `*p`.
|
||||||
|
|
||||||
|
* There is no unary `&` operator.
|
||||||
|
Pointers to an object are acquired using the `.pointer` suffix.
|
||||||
|
Raw addresses are acquired using the `.addr` suffix.
|
||||||
|
The numeric values of the pointer and of the raw address may differ.
|
||||||
|
|
||||||
|
* Operator precedence works differently.
|
||||||
|
Bitwise and bitshift operators have the same precedence as arithmetic operators,
|
||||||
|
and mixing different operators with the same precedence is usually forbidden.
|
||||||
|
This prevents most ambiguities in bit-twiddling code, but requires care when porting code from or to C.
|
||||||
|
|
||||||
|
* There is no `!` operator. The negation is expressed as the `not` function.
|
||||||
|
|
||||||
|
* The modulo operator is written `%%`. As with `/`, it's only defined for unsigned division.
|
||||||
|
|
||||||
|
* The `for` loops are range-based. Arbitrary, C-like `for` loops are not supported.
|
||||||
|
|
||||||
|
* Variable declaration and initialization have to be separate.
|
||||||
|
|
||||||
|
* Integer literals starting with zero and containing just digits are decimal, not octal.
|
||||||
|
For octal literals, use the `0o` prefix.
|
||||||
|
|
||||||
|
* String literals are not null-terminated by default. Use the `z` suffix for null-terminated strings.
|
||||||
|
|
||||||
|
* In `if`, `do/while`, `while` and `for` statements, parentheses are not required, but braces are.
|
||||||
|
The `else` branch also requires braces, unless the only statement in the `else` block is an `if` statement.
|
||||||
|
|
||||||
|
* There has to be a space between many operators and character literals,
|
||||||
|
as the parser may treat the apostrophe as a part of the operator.
|
||||||
|
|
||||||
|
## Preprocessor
|
||||||
|
|
||||||
|
* The preprocessor cannot expand symbols to something more complex than an identifier or a literal.
|
||||||
|
|
||||||
|
* The preprocessor cannot define symbols for use in other files.
|
||||||
|
|
||||||
|
* The closest to C's `#define` is `#use`.
|
||||||
|
Unlike `#define`, such definitions are not visible within the preprocessor.
|
||||||
|
|
||||||
|
* Directives like `#use` and `#pragma` affect the entire file, not just the code after the directive.
|
||||||
|
|
||||||
|
* The preprocessor cannot include files.
|
||||||
|
|
||||||
|
## Semantics
|
||||||
|
|
||||||
|
* There is no automatic integer promotion. An operation of two bytes will yield a byte. For example:
|
||||||
|
|
||||||
|
byte a
|
||||||
|
byte b
|
||||||
|
word w
|
||||||
|
w = a * b
|
||||||
|
|
||||||
|
may yield unexpected results. To prevent this, cast at least one argument:
|
||||||
|
|
||||||
|
w = word(a) * b
|
||||||
|
|
||||||
|
This issue applies mostly to the `*` and `<<` operators.
|
||||||
|
|
||||||
|
* There is no padding in structs.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,10 @@ or generate a static binary and link it manually using the `file` directive.
|
||||||
|
|
||||||
Since the compiler is a work-in-progress, some of the mentioned issues might be improved upon in the future.
|
Since the compiler is a work-in-progress, some of the mentioned issues might be improved upon in the future.
|
||||||
|
|
||||||
|
### I have experience with C and/or assembly. What should I keep in mind?
|
||||||
|
|
||||||
|
See the [differences from C](./cdiff.md) and the [differences from assembly](./asmdiff.md).
|
||||||
|
|
||||||
### Why is it called Millfork?
|
### Why is it called Millfork?
|
||||||
|
|
||||||
It stands for **MI**ddle **L**evel **L**anguage **FOR** **K**ommodore computers.
|
It stands for **MI**ddle **L**evel **L**anguage **FOR** **K**ommodore computers.
|
||||||
|
|
103
docs/various/optimization.md
Normal file
103
docs/various/optimization.md
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
[< back to index](../doc_index.md)
|
||||||
|
|
||||||
|
# Optimization guidelines
|
||||||
|
|
||||||
|
## Command-line options
|
||||||
|
|
||||||
|
* The default options provide literally no optimizations.
|
||||||
|
Consider using at least `-O1` for quick compilation and `-O4` for release builds.
|
||||||
|
|
||||||
|
* Inlining can drastically improve performance. Add `-finline` to the command line.
|
||||||
|
|
||||||
|
* If you're not using self-modifying code or code generation,
|
||||||
|
enabling interprocedural optimizations (`-fipo`) and stdlib optimizations (`-foptimize-stdlib`) can also help.
|
||||||
|
|
||||||
|
* For convenience, all options useful for debug builds can be enabled with `-Xd`,
|
||||||
|
and for release builds with `-Xr`.
|
||||||
|
|
||||||
|
* 6502 only: If you are sure the target will have a CPU that supports so-called illegal/undocumented 6502 instructions,
|
||||||
|
consider adding the `-fillegals` option. Good examples of such targets are NES and C64.
|
||||||
|
|
||||||
|
## Alignment
|
||||||
|
|
||||||
|
* Consider adding `align(fast)` or even `align(256)` to arrays which you want to access quickly.
|
||||||
|
|
||||||
|
* 6502 only: Consider adding `align(fast)` to the hottest functions.
|
||||||
|
|
||||||
|
* If you have an array of structs, consider adding `align(X)` to the definition of the struct,
|
||||||
|
where `X` is a power of two. Even if this makes the struct 12 bytes instead of 11, it can still improve performance.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
* Use the smallest type you need. Note that Millfork supports integers of any size from 1 to 16 bytes.
|
||||||
|
|
||||||
|
* Consider using multiple arrays instead of arrays of structs.
|
||||||
|
|
||||||
|
* Avoid reusing temporary variables.
|
||||||
|
It makes it easier for the optimizer to eliminate the variable entirely.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
* Write many functions with no parameters and use `-finline`.
|
||||||
|
This will simplify the job for the optimizer and increase the chances of certain powerful optimizations to apply.
|
||||||
|
|
||||||
|
* Avoid passing many parameters to functions.
|
||||||
|
Try to minimize the number of bytes passed as parameters and returned as return values.
|
||||||
|
|
||||||
|
## Loops
|
||||||
|
|
||||||
|
* For `for` loops that use a byte-sized variable and whose body does not involve function calls or further loops,
|
||||||
|
use a unique iteration variable. Such variable will have a bigger chance of being stored in a CPU register.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
byte i
|
||||||
|
byte j
|
||||||
|
for i,0,until,30 { .... }
|
||||||
|
for j,0,until,40 { .... }
|
||||||
|
|
||||||
|
is usually better than:
|
||||||
|
|
||||||
|
byte i
|
||||||
|
for i,0,until,30 { .... }
|
||||||
|
for i,0,until,40 { .... }
|
||||||
|
|
||||||
|
* 8080/Z80 only: The previous tip applies also for loops using word-sized variables.
|
||||||
|
|
||||||
|
* When the iteration order is not important, use `paralleluntil` or `parallelto`.
|
||||||
|
The compiler will try to choose the optimal iteration order.
|
||||||
|
|
||||||
|
* Since 0.3.18: When the iteration order is not important,
|
||||||
|
use `for ix,ptr:array` to iterate over arrays of structs.
|
||||||
|
|
||||||
|
* 6502 only: When iterating over an array larger than 256 bytes, whose element count is a composite number,
|
||||||
|
consider splitting it into less-than-256-byte sized slices and use them within the same iteration.
|
||||||
|
For example, instead of:
|
||||||
|
|
||||||
|
word i
|
||||||
|
for i,0,paralleluntil,1000 {
|
||||||
|
screen[i] = ' 'scr
|
||||||
|
}
|
||||||
|
|
||||||
|
consider:
|
||||||
|
|
||||||
|
byte i
|
||||||
|
for i,0,paralleluntil,250 {
|
||||||
|
screen[i+000] = ' 'scr
|
||||||
|
screen[i+250] = ' 'scr
|
||||||
|
screen[i+500] = ' 'scr
|
||||||
|
screen[i+750] = ' 'scr
|
||||||
|
}
|
||||||
|
|
||||||
|
Note that the compiler might do this optimization automatically
|
||||||
|
for simpler loops with certain iteration ranges, but it is not guaranteed.
|
||||||
|
|
||||||
|
# Arithmetic
|
||||||
|
|
||||||
|
* Avoid 16-bit arithmetic. Try to keep calculations 8-bit for as long as you can.
|
||||||
|
If you can calculate the upper and lower byte of a 16-bit value separately, it's usually better to do so.
|
||||||
|
|
||||||
|
* Avoid arithmetic larger than 16-bit.
|
||||||
|
|
||||||
|
* Use `nonet` if you are sure that the result of shifting will fit into 9 bits.
|
||||||
|
Use `nonet` when doing byte addition that you want to promote to a word.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user