Add guides to documentation

This commit is contained in:
Karol Stasiak 2020-07-24 19:13:46 +02:00
parent d5367cc1fe
commit c6c021cf85
6 changed files with 235 additions and 0 deletions

View File

@ -79,6 +79,14 @@
* [Game Boyonly modules](stdlib/gb.md)
* [X16only 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

View File

@ -81,6 +81,14 @@
* [Game Boyonly modules](stdlib/gb.md)
* [X16only 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

34
docs/various/asmdiff.md Normal file
View 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
View 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.

View File

@ -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.
### 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?
It stands for **MI**ddle **L**evel **L**anguage **FOR** **K**ommodore computers.

View 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.