mirror of
https://github.com/KarolS/millfork.git
synced 2024-06-12 22:29:33 +00:00
Compare commits
83 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d8c11a9c50 | ||
|
ee47ccef5a | ||
|
24de2f7530 | ||
|
1beb695151 | ||
|
9229092309 | ||
|
ca7166d1ae | ||
|
1594d63a9a | ||
|
75cc34663c | ||
|
f4d2fdd370 | ||
|
29c1e3f2a6 | ||
|
e9cfec54b5 | ||
|
1bc1ab3539 | ||
|
6f294d6dec | ||
|
9866c974ad | ||
|
ef2f5b5918 | ||
|
a70a1c0e6b | ||
|
790c836771 | ||
|
6af84d1628 | ||
|
7b205a2754 | ||
|
d23fe1756e | ||
|
3b3ee1bc55 | ||
|
fb30075ea5 | ||
|
b5084cd180 | ||
|
b1a2be5574 | ||
|
a260d0a806 | ||
|
38ad919ed9 | ||
|
34ef8b8de9 | ||
|
f676e74e38 | ||
|
c9313e5dbe | ||
|
73a223b9b6 | ||
|
b168818bab | ||
|
effa723863 | ||
|
b2ab3dbeab | ||
|
c9ef5e636b | ||
|
fc2f0782c5 | ||
|
f0e22f02a6 | ||
|
b2291d1cb2 | ||
|
166acf2b18 | ||
|
7530b382a8 | ||
|
b66435d06b | ||
|
0c8951d015 | ||
|
84dde8589c | ||
|
3b0aa9a425 | ||
|
1b8de990a5 | ||
|
b060fc599b | ||
|
90e5360bfd | ||
|
bbf430a1c7 | ||
|
7f6a0c6b0d | ||
|
7f0def54bc | ||
|
faf97cee1f | ||
|
da862069a7 | ||
|
431a25d325 | ||
|
73beafd65e | ||
|
307ad90ecf | ||
|
2e592a2331 | ||
|
91c9b42f3d | ||
|
144da70594 | ||
|
4f6eefab79 | ||
|
ca35367974 | ||
|
2065c3b4ac | ||
|
9028d55a7e | ||
|
21d4d3252f | ||
|
45ad049a51 | ||
|
0172e29bb2 | ||
|
ffb46c4250 | ||
|
c51c08ad56 | ||
|
fcdad413b0 | ||
|
63edce28c4 | ||
|
1f318a2a0e | ||
|
1bcb6d5010 | ||
|
510f85960c | ||
|
8412075175 | ||
|
e25df3d1b3 | ||
|
b71d058c6a | ||
|
bf273456a3 | ||
|
062483971a | ||
|
1e4a193741 | ||
|
2468d8cca5 | ||
|
58b5b6ff28 | ||
|
8aac3bc329 | ||
|
24eac6708b | ||
|
66fc1d3984 | ||
|
0bbdc348e7 |
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -31,9 +31,12 @@ issue*.mfk
|
|||
*.seq
|
||||
*.asm
|
||||
*.lbl
|
||||
*.labels
|
||||
*.nl
|
||||
*.fns
|
||||
*.sym
|
||||
*.mlb
|
||||
*.dbg
|
||||
*.deb
|
||||
*.xex
|
||||
*.nes
|
||||
|
|
64
CHANGELOG.md
64
CHANGELOG.md
|
@ -1,5 +1,69 @@
|
|||
# Change log
|
||||
|
||||
## 0.3.30 (2021-12-15)
|
||||
|
||||
* Added volatile structure fields (#112).
|
||||
|
||||
* Added `this.function` as the alias for the current function (#118).
|
||||
|
||||
* Added support for constant evaluation in `file` expressions (#114).
|
||||
|
||||
* Allowed declaring local constants and passing untyped parameters for macros.
|
||||
|
||||
* Allowed treating bare function name as a pointer to it.
|
||||
|
||||
* Added Mesen, ld65 and "raw" label file formats (#128).
|
||||
|
||||
* Commodore: the address used by SYS is now determined automatically instead of hardcoded (#111).
|
||||
|
||||
* C64: Fixed address for `sid_v1_sr` (#115).
|
||||
|
||||
* EasyFlash: Fixed address for `switch_hirom` (#121).
|
||||
|
||||
* GB: Fixed standard library (thanks to @retrac0).
|
||||
|
||||
* Commander X16: Updated platform definition file (thanks to @mookiexl).
|
||||
|
||||
* 65CE02: Full assembly support.
|
||||
|
||||
* R800: Full assembly support.
|
||||
|
||||
* Various miscompilation fixes (#123, #125) and parser fixes (e.g. #120).
|
||||
|
||||
* 6809: Various optimizations.
|
||||
|
||||
* Improvements related to constant evaluation.
|
||||
|
||||
## 0.3.28 (2021-05-24)
|
||||
|
||||
* Officially deprecated decimal operators with apostrophes.
|
||||
|
||||
* Added optimization hints.
|
||||
|
||||
* Added `utf32be`, `utf32le`, `cp1253`, `cp1254`, `cp1257`, `geos_de` encodings.
|
||||
|
||||
* Allowed for underscores in numeric literals for readability purposes, similar to several other programming languages.
|
||||
|
||||
* Added a warning for comparisons between bytes and pointers (#110).
|
||||
|
||||
* Fixed escape sequences in many encodings.
|
||||
|
||||
* Fixed and documented absolute module imports (#106)
|
||||
|
||||
* Fixed and optimized sign extension.
|
||||
|
||||
* Fixed optimizations involving unused labels.
|
||||
|
||||
* Fixed pointer types to type aliases.
|
||||
|
||||
* Fixed parsing of Intel hex literals of the form `0BH`, `0B0H` etc.
|
||||
|
||||
* 6809: Fixed flow analysis in optimization.
|
||||
|
||||
* Optimization of certain bitmask operations.
|
||||
|
||||
* Parsing optimizations.
|
||||
|
||||
## 0.3.26 (2021-03-01)
|
||||
|
||||
* Array fields in structs.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
A middle-level programming language targeting 6502-based, 8080-based, Z80-based and 6809-based microcomputers.
|
||||
|
||||
For binary releases, see: [https://github.com/KarolS/millfork/releases](https://github.com/KarolS/millfork/releases)
|
||||
(latest: 0.3.24).
|
||||
(latest: 0.3.30).
|
||||
For build instructions, see [Build instructions](./COMPILING.md).
|
||||
|
||||
## Features
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name := "millfork"
|
||||
|
||||
version := "0.3.26"
|
||||
version := "0.3.31-SNAPSHOT"
|
||||
|
||||
// keep it at 2.12.11 for GraalVM native image compatibility!
|
||||
scalaVersion := "2.12.11"
|
||||
|
|
|
@ -14,6 +14,8 @@ It implies the following:
|
|||
|
||||
* cannot contain variable or array declarations
|
||||
|
||||
* but can contain scalar constant declarations; the constants are scoped to the particular macro invocation
|
||||
|
||||
* can be `asm` - in this case, they should **not** end with a return instruction
|
||||
|
||||
* do not have an address
|
||||
|
@ -35,7 +37,13 @@ It implies the following:
|
|||
* `call` parameters exceptionally can have their type declared as `void`;
|
||||
such parameters accept expressions of any type, including `void`, however, you cannot assign from those expressions
|
||||
|
||||
* macros do not have their own scope (they reuse the scope from their invocations) – exceptions: the parameters and the local labels defined in assembly
|
||||
* macros do not have their own scope (they reuse the scope from their invocations) – exceptions:
|
||||
|
||||
* the parameters
|
||||
|
||||
* the local labels defined in assembly
|
||||
|
||||
* the local constants
|
||||
|
||||
* control-flow statements (`break`, `continue`, `return`, `goto`, `label`) are run as if places in the caller function
|
||||
|
||||
|
|
|
@ -47,6 +47,12 @@ The extension and the file format are platform-dependent.
|
|||
* `-G sym` – format used by the WLA DX assembler. The extension is `.sym`.
|
||||
|
||||
* `-G fceux` – multi-file format used by the FCEUX emulator. The extension is `.nl`.
|
||||
|
||||
* `-G mesen` – format used by the Mesen emulator. The extension is `.mlb`.
|
||||
|
||||
* `-G ld65` – a simplified version of the format used by the `ld65` linker (used by CC65 and CA65). The extension is `.dbg`.
|
||||
|
||||
* `-G raw` – Millfork-specific format. The extension is '.labels'. Each row contains bank number, start address, end address (if known), object type, and Millfork-specific object identifier.
|
||||
|
||||
* `-fbreakpoints`, `-fno-breakpoints` –
|
||||
Whether the compiler should use the `breakpoint` macro.
|
||||
|
@ -206,6 +212,10 @@ The compiler doesn't support accessing the stack variables via the S stack point
|
|||
|
||||
* `-O9` – Optimize code using superoptimizer (experimental). Computationally very expensive, decent results.
|
||||
|
||||
* `-fhints`, `-fno-hints` –
|
||||
Whether optimization hints should be used.
|
||||
Default: yes.
|
||||
|
||||
* `-finline`, `-fno-inline` – Whether should inline functions automatically.
|
||||
See the [documentation about inlining](../abi/inlining.md). Computationally easy, can give decent gains.
|
||||
`.ini` equivalent: `inline`.
|
||||
|
@ -289,6 +299,10 @@ You can also enable or disable warnings individually:
|
|||
Whether should warn about deprecated aliases.
|
||||
Default: enabled.
|
||||
|
||||
* `-Wcomparisons`, `-Wno-comparisons` –
|
||||
Whether should warn about comparisons between bytes and pointers.
|
||||
Default: enabled.
|
||||
|
||||
* `-Wextra-comparisons`, `-Wno-extra-comparisons` –
|
||||
Whether should warn about simplifiable unsigned integer comparisons.
|
||||
Default: disabled.
|
||||
|
@ -312,3 +326,7 @@ You can also enable or disable warnings individually:
|
|||
* `-Wuseless`, `-Wno-useless` –
|
||||
Whether should warn about code that does nothing.
|
||||
Default: enabled.
|
||||
|
||||
* `-Whints`, `-Wno-hints` –
|
||||
Whether should warn about unsupported optimization hints.
|
||||
Default: enabled.
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
### A note about Commodore 64
|
||||
|
||||
#### Unusual main function location
|
||||
|
||||
If you're creating a prg file and your `main` function may be located at address $2710 hex (10000 decimal) or higher,
|
||||
you might need to define the `DISPLACED_MAIN=1` preprocessor feature.
|
||||
|
||||
#### Multifile programs
|
||||
|
||||
A multifile program is a program stored on a disk that consists of the main program file that is executed first
|
||||
|
|
|
@ -37,6 +37,8 @@ if a line ends with a backslash character, the value continues to the next line.
|
|||
* `z80` (Zilog Z80)
|
||||
|
||||
* `strictz80` (Z80 without illegal instructions)
|
||||
|
||||
* `r800` (R800)
|
||||
|
||||
* `z80next` (Z80 core from ZX Spectrum Next)
|
||||
Note: Millfork version 0.3.18 and earlier uses the name `zx80next` for this architecture.
|
||||
|
@ -80,6 +82,8 @@ This list cannot contain module template instantiations.
|
|||
* `emit_x80` – whether the compiler should emit instructions present on Sharp LR35902 and Z80, but absent on Intel 8080, default is `true` on compatible processors and `false` elsewhere
|
||||
|
||||
* `emit_z80` – whether the compiler should emit Zilog Z80 instructions not covered by `emit_x80`, default is `true` on compatible processors and `false` elsewhere
|
||||
|
||||
* `emit_r800` – whether the compiler should emit R800 instructions, default is `true` on compatible processors and `false` elsewhere
|
||||
|
||||
* `prevent_jmp_indirect_bug` – whether the compiler should try to avoid the indirect JMP bug,
|
||||
default is `false` on 65C02-compatible or non-6502 processors and `true` elsewhere
|
||||
|
@ -263,3 +267,7 @@ Default: `main,*`
|
|||
* `sym` – format used by the WLA/DX assembler. The extension is `.sym`.
|
||||
|
||||
* `fceux` – multi-file format used by the FCEUX emulator. The extension is `.nl`.
|
||||
|
||||
* `mesen` – format used by the Mesen emulator. The extension is `.mlb`.
|
||||
|
||||
* `ld65` – format used by the `ld65` linker. The extension is `.dbg`.
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
|
||||
* [Important guidelines regarding reentrancy](lang/reentrancy.md)
|
||||
|
||||
* [Optimization hints](lang/hints.md)
|
||||
|
||||
* [List of keywords](lang/keywords.md)
|
||||
|
||||
## Library reference
|
||||
|
|
|
@ -16,7 +16,7 @@ No other lines are allowed in the file.
|
|||
* `NAME=<name>` defines the name for this encoding. Required.
|
||||
|
||||
* `BUILTIN=<internal name>` defines this encoding to be a UTF-based encoding.
|
||||
`<internal name>` may be one of `UTF-8`, `UTF-16LE`, `UTF-16BE`.
|
||||
`<internal name>` may be one of `UTF-8`, `UTF-16LE`, `UTF-16BE`, `UTF-32LE`, `UTF-32BE`.
|
||||
If this directive is present, the only other allowed directive in the file is the `NAME` directive.
|
||||
|
||||
* `EOT=<xx>` where `<xx>` are two hex digits, defines the string terminator byte.
|
||||
|
|
|
@ -4,17 +4,17 @@
|
|||
|
||||
Syntax:
|
||||
|
||||
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [@ <address>] { <body> }`
|
||||
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [<optimization hints>] [@ <address>] { <body> }`
|
||||
|
||||
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [@ <address>] = <expression>`
|
||||
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [<optimization hints>] [@ <address>] = <expression>`
|
||||
|
||||
`[segment (<segment>)] asm <return_type> <name> ( <params> ) @ <address> extern`
|
||||
`[segment (<segment>)] asm <return_type> <name> ( <params> ) [<optimization hints>] @ <address> extern`
|
||||
|
||||
Examples:
|
||||
|
||||
void do_nothing() { }
|
||||
inline byte two() = 2
|
||||
asm void chkout(byte register(a) char) @ $FFD2 extern
|
||||
asm void chkout(byte register(a) char) !preserves_x !preserves_y @ $FFD2 extern
|
||||
segment(prgrom0) void main_loop(word w, byte x) align(fast) { // body omitted
|
||||
|
||||
|
||||
|
@ -68,6 +68,8 @@ For assembly functions, certain parameter names are interpreted as CPU registers
|
|||
* on 6502, it means that the function will not cross a page boundary if possible
|
||||
* on Z80, it is ignored
|
||||
|
||||
* `<optimization hints>` is a list of [optimization hints](./hints.md), separated by spaces
|
||||
|
||||
* `<address>` is a constant expression that defines where in the memory the function is or will be located.
|
||||
|
||||
* `extern` is a keyword than marks functions that are not defined in the current program,
|
||||
|
|
84
docs/lang/hints.md
Normal file
84
docs/lang/hints.md
Normal file
|
@ -0,0 +1,84 @@
|
|||
[< back to index](../doc_index.md)
|
||||
|
||||
# Optimization hints
|
||||
|
||||
Optimization hints are optional annotations for functions and variables
|
||||
that allow the compiler to make extra assumptions that help with code optimization.
|
||||
|
||||
The general idea is that removing or disabling optimization hints will not break the code,
|
||||
but adding invalid optimization hints may break the code.
|
||||
|
||||
Every optimization hint's name starts with an exclamation mark.
|
||||
|
||||
Optimization hints marked with **(X)** currently do nothing and are planned to be implemented in the future.
|
||||
|
||||
## Hints for functions
|
||||
|
||||
* `!preserves_memory` – the function does not write to memory
|
||||
|
||||
* `!idempotent` – calling the function multiple times in succession has no effect
|
||||
|
||||
* `!hot` – the function is hot and should be optimized for speed at the cost of increased size
|
||||
|
||||
* `!cold` **(X)** – the function is cold and should be optimized for size at the cost of increased run time
|
||||
|
||||
* `!odd` – the function returns an odd value
|
||||
|
||||
* `!even` – the function returns an even value
|
||||
|
||||
## Hints for 6502 assembly functions
|
||||
|
||||
These hints have only effect when used on an assembly function on a 6502 target:
|
||||
|
||||
* `!preserves_a` – the function preserves the contents of the A register
|
||||
|
||||
* `!preserves_x` – the function preserves the contents of the X register
|
||||
|
||||
* `!preserves_y` – the function preserves the contents of the Y register
|
||||
|
||||
* `!preserves_c` – the function preserves the contents of the carry flag
|
||||
|
||||
## Hints for 8080/Z80/LR35902 assembly functions
|
||||
|
||||
These hints have only effect when used on an assembly function on a 8080-like target:
|
||||
|
||||
* `!preserves_a` – the function preserves the contents of the A register
|
||||
|
||||
* `!preserves_bc` – the function preserves the contents of the B and C registers
|
||||
|
||||
* `!preserves_de` – the function preserves the contents of the D and E registers
|
||||
|
||||
* `!preserves_hl` – the function preserves the contents of the H and L registers
|
||||
|
||||
* `!preserves_cf` – the function preserves the contents of the carry flag
|
||||
|
||||
## Hints for 6809 assembly functions
|
||||
|
||||
These hints have only effect when used on an assembly function on a 6809 target:
|
||||
|
||||
* `!preserves_a` – the function preserves the contents of the A register
|
||||
|
||||
* `!preserves_b` – the function preserves the contents of the B register
|
||||
|
||||
* `!preserves_d` – the function preserves the contents of the A and B register
|
||||
|
||||
* `!preserves_dp` **(X)** – the function preserves the contents of the DP register; exceptionally this can also be used on non-assembly functions
|
||||
|
||||
* `!preserves_x` – the function preserves the contents of the X register
|
||||
|
||||
* `!preserves_y` – the function preserves the contents of the Y register
|
||||
|
||||
* `!preserves_u` – the function preserves the contents of the U register
|
||||
|
||||
* `!preserves_c` – the function preserves the contents of the carry flag
|
||||
|
||||
## Hints for variables
|
||||
|
||||
* `!odd` **(X)** – the variable can only contain odd values
|
||||
|
||||
* `!even` **(X)** – the variable can only contain even values
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -14,7 +14,14 @@ Octal: `0o172`
|
|||
|
||||
Hexadecimal: `$D323`, `0x2a2`
|
||||
|
||||
When using Intel syntax for inline assembly, another hexadecimal syntax is available: `0D323H`, `2a2h`.
|
||||
Digits can be separated by underscores for readability. Underscores are also allowed between the radix prefix and the digits:
|
||||
|
||||
123_456
|
||||
0x01_ff
|
||||
0b_0101_1111__1100_0001
|
||||
$___________FF
|
||||
|
||||
When using Intel syntax for inline assembly, another hexadecimal syntax is available: `0D323H`, `2a2h`, `3e_h`.
|
||||
It is not allowed in any other places.
|
||||
|
||||
The type of a literal is the smallest type of undefined signedness
|
||||
|
@ -65,7 +72,7 @@ and the byte constant `nullchar_scr` is defined to be equal to the string termin
|
|||
You can override the values for `nullchar` and `nullchar_scr`
|
||||
by defining preprocessor features `NULLCHAR` and `NULLCHAR_SCR` respectively.
|
||||
|
||||
Warning: If you define UTF-16 to be you default or screen encoding, you will encounter several problems:
|
||||
Warning: If you define UTF-16 or UTF-32 to be you default or screen encoding, you will encounter several problems:
|
||||
|
||||
* `nullchar` and `nullchar_scr` will still be bytes, equal to zero.
|
||||
* the `string` module in the Millfork standard library will not work correctly
|
||||
|
@ -75,21 +82,26 @@ Warning: If you define UTF-16 to be you default or screen encoding, you will enc
|
|||
You can also prepend `p` to the name of the encoding to make the string length-prefixed.
|
||||
|
||||
The length is measured in bytes and doesn't include the zero terminator, if present.
|
||||
In all encodings except for UTF-16 the prefix takes one byte,
|
||||
In all encodings except for UTF-16 and UTF-32 the prefix takes one byte,
|
||||
which means that length-prefixed strings cannot be longer than 255 bytes.
|
||||
|
||||
In case of UTF-16, the length prefix contains the number of code units,
|
||||
so the number of bytes divided by two,
|
||||
which allows for strings of practically unlimited length.
|
||||
The length is stores as two bytes and is always little endian,
|
||||
The length is stored as two bytes and is always little endian,
|
||||
even in case of the `utf16be` encoding or a big-endian processor.
|
||||
|
||||
In case of UTF-32, the length prefix contains the number of Unicode codepoints,
|
||||
so the number of bytes divided by four.
|
||||
The length is stored as four bytes and is always little endian,
|
||||
even in case of the `utf32be` encoding or a big-endian processor.
|
||||
|
||||
"this is a Pascal string" pascii
|
||||
"this is also a Pascal string"p
|
||||
"this is a zero-terminated Pascal string"pz
|
||||
|
||||
Note: A string that's both length-prefixed and zero-terminated does not count as a normal zero-terminated string!
|
||||
To pass it to a function that expects a zero-terminated string, add 1 (or, in case of UTF-16, 2):
|
||||
To pass it to a function that expects a zero-terminated string, add 1 (or, in case of UTF-16, 2, or UTF-32, 4):
|
||||
|
||||
pointer p
|
||||
p = "test"pz
|
||||
|
|
|
@ -11,7 +11,8 @@ Each module has a name, which is its unique identifier.
|
|||
A module name is a sequence of slash-separated valid Millfork identifiers.
|
||||
The name also defines where the module is located:
|
||||
a module named `a/b` is presumed to exist in `a/b.mfk`
|
||||
and it's looked up first in the current working directory,
|
||||
and it's looked up first in the directory that contains the current source file,
|
||||
then in the current working directory,
|
||||
and then in the include directories.
|
||||
|
||||
A module can import other modules, using the `import` statement.
|
||||
|
|
|
@ -18,9 +18,9 @@ Millfork has different operator precedence compared to most other languages. Fro
|
|||
|
||||
* `->` and `[]`
|
||||
|
||||
* `*`, `*'`, `/`, `%%`
|
||||
* `*`, `$*`, `/`, `%%`
|
||||
|
||||
* `+`, `+'`, `-`, `-'`, `|`, `&`, `^`, `>>`, `>>'`, `<<`, `<<'`, `>>>>`
|
||||
* `+`, `$+`, `-`, `$-`, `|`, `&`, `^`, `>>`, `$>>`, `<<`, `$<<`, `>>>>`
|
||||
|
||||
* `:`
|
||||
|
||||
|
@ -34,16 +34,16 @@ Millfork has different operator precedence compared to most other languages. Fro
|
|||
|
||||
You cannot use two different operators at the same precedence levels without using parentheses to disambiguate.
|
||||
It is to prevent confusion about whether `a + b & c << d` means `(a + b) & (c << d)` `((a + b) & c) << d` or something else.
|
||||
The only exceptions are `+` and `-`, and `+'` and `-'`.
|
||||
They are interpreted as expected: `5 - 3 + 2 == 4` and `5 -' 3 +' 2 == 4`.
|
||||
Note that you cannot mix `+'` and `-'` with `+` and `-`.
|
||||
The only exceptions are `+` and `-`, and `$+` and `$-`.
|
||||
They are interpreted as expected: `5 - 3 + 2 == 4` and `5 $- 3 $+ 2 == 4`.
|
||||
Note that you cannot mix `$+` and `$-` with `+` and `-`.
|
||||
|
||||
Certain operators (`/`, `%%`, `<<`, `>>`, `<<'`, `>>'`, `>>>>`, `:`, `!=`) cannot have more than 2 parameters,
|
||||
Certain operators (`/`, `%%`, `<<`, `>>`, `$<<`, `$>>`, `>>>>`, `:`, `!=`) cannot have more than 2 parameters,
|
||||
i.e. `x / y / z` will not compile.
|
||||
|
||||
The decimal operators have two different forms:
|
||||
|
||||
* apostrophe form (e.g. `+'`) – the original one, to be deprecated in the future, may be removed in Millfork 0.4
|
||||
* apostrophe form (e.g. `+'`) – the original one, deprecated, will be removed in Millfork 0.4
|
||||
|
||||
* dollar form (e.g. `$+`) – available since Millfork 0.3.22
|
||||
|
||||
|
@ -135,20 +135,20 @@ These operators work using the decimal arithmetic (packed BCD).
|
|||
|
||||
On Ricoh-based targets (e.g. Famicom) they require the zeropage register to have size at least 4
|
||||
|
||||
* `+'`, `-'`: decimal addition/subtraction
|
||||
`$+`, `$-`: (since Millfork 0.3.22)
|
||||
`byte +' byte`
|
||||
`constant word +' constant word`
|
||||
`constant long +' constant long`
|
||||
`word +' word` (zpreg)
|
||||
* `$+`, `$-`: decimal addition/subtraction
|
||||
`+'`, `-'`: (deprecated form)
|
||||
`byte $+ byte`
|
||||
`constant word $+ constant word`
|
||||
`constant long $+ constant long`
|
||||
`word $+ word` (zpreg)
|
||||
|
||||
* `*'`: decimal multiplication
|
||||
`$*`: (since Millfork 0.3.22)
|
||||
`constant *' constant`
|
||||
* `$*`: decimal multiplication
|
||||
`*'`: (deprecated form)
|
||||
`constant $* constant`
|
||||
|
||||
* `<<'`, `>>'`: decimal multiplication/division by power of two
|
||||
`$<<`, `$>>`: (since Millfork 0.3.22)
|
||||
`byte <<' constant byte`
|
||||
* `$<<`, `$>>`: decimal multiplication/division by power of two
|
||||
`<<'`, `>>'`: (deprecated form)
|
||||
`byte $<< constant byte`
|
||||
|
||||
## Comparison operators
|
||||
|
||||
|
@ -201,8 +201,8 @@ An expression of form `a[f()] += b` may call `f` an undefined number of times.
|
|||
`mutable word = word`
|
||||
`mutable long = long`
|
||||
|
||||
* `+=`, `+'=`, `|=`, `^=`, `&=`: modification in place
|
||||
`$+=` (since Millfork 0.3.22)
|
||||
* `+=`, `$+=`, `|=`, `^=`, `&=`: modification in place
|
||||
`+'=` (deprecated form)
|
||||
`mutable byte += byte`
|
||||
`mutable word += word`
|
||||
`mutable trivial long += long`
|
||||
|
@ -212,14 +212,14 @@ An expression of form `a[f()] += b` may call `f` an undefined number of times.
|
|||
`mutable word <<= byte`
|
||||
`mutable trivial long <<= byte`
|
||||
|
||||
* `<<'=`, `>>'=`: decimal shift in place
|
||||
`$<<=`, `$>>=` (since Millfork 0.3.22)
|
||||
`mutable byte <<'= constant byte`
|
||||
`mutable word <<'= constant byte`
|
||||
`mutable trivial long <<'= constant byte`
|
||||
* `$<<=`, `$>>=`: decimal shift in place
|
||||
`<<'=`, `>>'=` (deprecated form)
|
||||
`mutable byte $<<= constant byte`
|
||||
`mutable word $<<= constant byte`
|
||||
`mutable trivial long $<<= constant byte`
|
||||
|
||||
* `-=`, `-'=`: subtraction in place
|
||||
`$-=` (since Millfork 0.3.22)
|
||||
* `-=`, `$-=`: subtraction in place
|
||||
`-'=` (deprecated form)
|
||||
`mutable byte -= byte`
|
||||
`mutable word -= simple word`
|
||||
`mutable trivial long -= simple long`
|
||||
|
@ -230,9 +230,9 @@ An expression of form `a[f()] += b` may call `f` an undefined number of times.
|
|||
`mutable word *= unsigned byte` (zpreg)
|
||||
`mutable word *= word` (zpreg)
|
||||
|
||||
* `*'=`: decimal multiplication in place
|
||||
`$*=` (since Millfork 0.3.22)
|
||||
`mutable byte *'= constant byte`
|
||||
* `$*=`: decimal multiplication in place
|
||||
`*'=` (deprecated form)
|
||||
`mutable byte $*= constant byte`
|
||||
|
||||
* `/=`, `%%=`: unsigned division and modulo in place
|
||||
`mutable unsigned byte /= unsigned byte` (zpreg)
|
||||
|
@ -291,9 +291,9 @@ but you can access its fields or take its pointer:
|
|||
|
||||
* `nonet`: expansion of an 8-bit operation to a 9-bit operation
|
||||
`nonet(byte + byte)`
|
||||
`nonet(byte +' byte)`
|
||||
`nonet(byte $+ byte)`
|
||||
`nonet(byte << constant byte)`
|
||||
`nonet(byte <<' constant byte)`
|
||||
`nonet(byte $<< constant byte)`
|
||||
Other kinds of expressions than the above (even `nonet(byte + byte + byte)`) will not work as expected.
|
||||
|
||||
* `hi`, `lo`: most/least significant byte of a word
|
||||
|
|
|
@ -25,3 +25,5 @@
|
|||
* `byte segment.N.bank` – the value of `segment_N_bank` from the platform definition
|
||||
|
||||
* `byte segment.N.fill` – the value of `segment_N_fill` from the platform definition
|
||||
|
||||
* `this.function` – the alias of the current function (in macros, it resolves to the actual non-macro function that called the macro)
|
||||
|
|
|
@ -103,6 +103,8 @@ Some libraries may require that some of these be defined.
|
|||
|
||||
* `KEYBOARD` – 1 if the target has a keyboard, 0 otherwise
|
||||
|
||||
* `DISPLACED_MAIN` – set this to 1 if the `main` function is in a very unusual location for the target
|
||||
|
||||
* `USE_MOUSE_MBM` – set this to 1 if you want to enable middle button support for the mouse.
|
||||
|
||||
* `JOYSTICKS` – the maximum number of joysticks using standard hardware configurations, may be 0
|
||||
|
@ -151,11 +153,40 @@ The `if` function returns its second parameter if the first parameter is defined
|
|||
// prints 500:
|
||||
#infoeval if(0, 400, 500)
|
||||
|
||||
TODO
|
||||
`not`, `lo`, `hi`, `min`, `max` `+`, `-`, `*`, `|`, `&`, `^`, `||`, `&&`, `<<`, `>>`,`==`, `!=`, `>`, `>=`, `<`, `<=`
|
||||
The `min` and `max` functions return the smallest or largest parameter respectively. They support any number of arguments:
|
||||
|
||||
// prints 400:
|
||||
#infoeval min(400, 500, 600)
|
||||
// prints 500:
|
||||
#infoeval max(400, 500)
|
||||
|
||||
The following Millfork operators and functions are also available in the preprocessor:
|
||||
`not`, `lo`, `hi`, `+`, `-`, `*`, `|`, `&`, `^`, `||`, `&&`, `<<`, `>>`,`==`, `!=`, `>`, `>=`, `<`, `<=`
|
||||
|
||||
The following Millfork operators and functions are not available in the preprocessor:
|
||||
`+'`, `-'`, `*'`, `<<'`, `>>'`, `:`, `>>>>`, `nonet`, all the assignment operators
|
||||
`$+`, `$-`, `$*`, `$<<`, `$>>`, `:`, `>>>>`, `nonet`, all the assignment operators
|
||||
|
||||
|
||||
### Character literals
|
||||
|
||||
Preprocessor supports character literals. By default, they are interpreted in the default encoding,
|
||||
but you can suffix them with other encodings.
|
||||
|
||||
// usually prints 97:
|
||||
#infoeval 'a'
|
||||
// prints 97:
|
||||
#infoeval 'a'ascii
|
||||
|
||||
Exceptionally, you can suffix the character literal with `utf32`.
|
||||
This gives the literal the value of the Unicode codepoint of the character:
|
||||
|
||||
// may print 94, 96, 112, 173, 176, 184, 185, 222, 227, 234, 240, something else, or even fail to compile:
|
||||
#infoeval 'π'
|
||||
// prints 960:
|
||||
#infoeval 'π'utf32
|
||||
|
||||
Escape sequences are supported, as per encoding. `utf32` pseudoencoding supports the same escape sequences as `utf8`.
|
||||
|
||||
|
||||
### `#template`
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ These suffixes can be only applied to arithmetic or pointer variables:
|
|||
|
||||
* `.hi` – the most significant byte of a two-byte variable (word, pointer) (use `hi(_)` for arbitrary expressions)
|
||||
|
||||
* `.loword` – the least significant byte of a three- or four-byte variable
|
||||
* `.loword` – the least significant word of a three- or four-byte variable
|
||||
|
||||
* `.hiword` – the most significant byte of a three- or four-byte variable
|
||||
* `.hiword` – the most significant word of a three- or four-byte variable
|
||||
|
||||
* `.b0`, `.b1` etc. – the given byte of the multi-byte arithmetic variable, with `.b0` being the least significant byte
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ or a top level of a function (*local* variables).
|
|||
|
||||
Syntax:
|
||||
|
||||
`[segment(<segment>)] [volatile] [<storage>] <type> <name> [@<address>] [= <initial_value>]`
|
||||
`[segment(<segment>)] [volatile] [<storage>] <type> <name> [<optimization hints>] [@<address>] [= <initial_value>]`
|
||||
|
||||
Examples:
|
||||
|
||||
|
@ -69,6 +69,8 @@ Volatile variables cannot be declared as `register` or `stack`.
|
|||
* `<storage>` can be only specified for local variables. It can be either `stack`, `static`, `register` or nothing.
|
||||
`register` is only a hint for the optimizer.
|
||||
See [the description of variable storage](../abi/variable-storage.md).
|
||||
|
||||
* `<optimization hints>` is a list of [optimization hints](./hints.md), separated by spaces
|
||||
|
||||
* `<address>` is a constant expression that defines where in the memory the variable will be located.
|
||||
If not specified, it will be located according to the usual allocation rules.
|
||||
|
|
|
@ -26,6 +26,8 @@ TODO: document the file format.
|
|||
|
||||
* `oldpetscii` or `oldpet` – old PETSCII (Commodore PET with newer ROMs)
|
||||
|
||||
* `geos_de` – text encoding used by the German version of GEOS for C64
|
||||
|
||||
* `cbmscr` or `petscr` – Commodore screencodes
|
||||
|
||||
* `cbmscrjp` or `petscrjp` – Commodore screencodes as used on Japanese versions of Commodore 64
|
||||
|
@ -89,7 +91,7 @@ DOS codepages 437, 850, 851, 852, 855, 858, 866
|
|||
|
||||
* `kamenicky` – Kamenický encoding
|
||||
|
||||
* `cp1250`, `cp1251`, `cp1252` – Windows codepages 1250, 1251, 1252
|
||||
* `cp1250`, `cp1251`, `cp1252`, `cp1253`, `cp1254`, `cp1257` – Windows codepages 1250, 1251, 1252, 1253, 1254, 1257
|
||||
|
||||
* `msx_intl`, `msx_jp`, `msx_ru`, `msx_br` – MSX character encoding, International, Japanese, Russian and Brazilian respectively
|
||||
|
||||
|
@ -132,6 +134,8 @@ English, Japanese, Spanish/Italian and French/German respectively
|
|||
|
||||
* `utf16be`, `utf16le` – UTF-16BE and UTF-16LE
|
||||
|
||||
* `utf32be`, `utf32le` – UTF-32BE and UTF-32LE
|
||||
|
||||
When programming for Commodore,
|
||||
use `petscii` for strings you're printing using standard I/O routines
|
||||
and `petsciiscr` for strings you're copying to screen memory directly.
|
||||
|
@ -163,10 +167,12 @@ The exact value of `{nullchar}` is encoding-dependent:
|
|||
* in the `zx80` encoding it's `{x01}`,
|
||||
* in the `zx81` encoding it's `{x0b}`,
|
||||
* in the `petscr` and `petscrjp` encodings it's `{xe0}`,
|
||||
* in the `apple2e` encoding it's `{x7f}`,
|
||||
* in the `atasciiscr` encoding it's `{xdb}`,
|
||||
* in the `pokemon1*` encodings it's `{x50}`,
|
||||
* in the `cocoscr` encoding it's exceptionally two bytes: `{xd0}`
|
||||
* in the `utf16be` and `utf16le` encodings it's exceptionally two bytes: `{x00}{x00}`
|
||||
* in the `utf32be` and `utf32le` encodings it's exceptionally four bytes: `{x00}{x00}{x00}{x00}`
|
||||
* in other encodings it's `{x00}` (this may be a subject to change in future versions).
|
||||
|
||||
##### Available only in some encodings
|
||||
|
@ -211,6 +217,7 @@ Encoding | lowercase letters | backslash | currencies | intl | card suits
|
|||
`petscr` | yes¹ | no | £ | none | yes¹
|
||||
`petjp` | no | no | ¥ | katakana³ | yes³
|
||||
`petscrjp` | no | no | ¥ | katakana³ | yes³
|
||||
`geos_de` | yes | no | | | no
|
||||
`sinclair`, `bbc` | yes | yes | £ | none | no
|
||||
`zx80`, `zx81` | no | no | £ | none | no
|
||||
`apple2` | no | yes | | none | no
|
||||
|
@ -273,6 +280,7 @@ Encoding | new line | braces | backspace | cursor movement | text colour | rever
|
|||
`origpet` | yes | no | no | yes | no | yes | no
|
||||
`oldpet` | yes | no | no | yes | no | yes | no
|
||||
`petscr`, `petscrjp`| no | no | no | no | no | no | no
|
||||
`geos_de` | no | no | no | no | no | yes | no
|
||||
`sinclair` | yes | yes | no | yes | yes | yes | yes
|
||||
`zx80`,`zx81` | yes | no | yes | yes | no | no | no
|
||||
`ascii`, `iso_*` | yes | yes | yes | no | no | no | no
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
The examples showcased here are designed to compile with a compiler built from the newest sources.
|
||||
If you are using a release version of the compiler, consider browsing the older versions of the examples:
|
||||
|
||||
* [for version 0.3.28](https://github.com/KarolS/millfork/tree/v0.3.28/examples)
|
||||
|
||||
* [for version 0.3.26](https://github.com/KarolS/millfork/tree/v0.3.26/examples)
|
||||
|
||||
* [for version 0.3.24](https://github.com/KarolS/millfork/tree/v0.3.24/examples)
|
||||
|
||||
* [for version 0.3.22](https://github.com/KarolS/millfork/tree/v0.3.22/examples)
|
||||
|
||||
* [for version 0.3.18](https://github.com/KarolS/millfork/tree/v0.3.18/examples)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// Input: A = Byte to write.
|
||||
asm void chrout(byte register(a) char) @$FFD2 extern
|
||||
asm void chrout(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
|
||||
|
||||
asm void putchar(byte register(a) char) {
|
||||
JSR chrout
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// Input: A = Byte to write.
|
||||
asm void chrout(byte register(a) char) @$FFD2 extern
|
||||
asm void chrout(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
|
||||
|
||||
asm void putchar(byte register(a) char) {
|
||||
JSR chrout
|
||||
|
|
|
@ -2,7 +2,7 @@ import err
|
|||
|
||||
inline asm void switch_hirom(byte register(a) bank) {
|
||||
? and #$3F
|
||||
! sta $DE01
|
||||
! sta $DE00
|
||||
? rts
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// Input: A = Byte to write.
|
||||
asm void chrout(byte register(a) char) @$FFD2 extern
|
||||
asm void chrout(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
|
||||
|
||||
// CHRIN. Read byte from default input (for keyboard, read a line from the screen). (If not keyboard, must call OPEN and CHKIN beforehands.)
|
||||
// Output: A = Byte read.
|
||||
|
|
|
@ -10,7 +10,7 @@ word sid_v1_freq @$D400
|
|||
word sid_v1_pulse @$D402
|
||||
byte sid_v1_cr @$D404
|
||||
byte sid_v1_ad @$D405
|
||||
byte sid_v1_sr @$D409
|
||||
byte sid_v1_sr @$D406
|
||||
|
||||
word sid_v2_freq @$D407
|
||||
word sid_v2_pulse @$D409
|
||||
|
|
31
include/cbm/basic_loader.mfk
Normal file
31
include/cbm/basic_loader.mfk
Normal file
|
@ -0,0 +1,31 @@
|
|||
#template $ADDR$
|
||||
|
||||
#if not(CBM)
|
||||
#warn cbm/basic_loader module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @ $ADDR$ = [
|
||||
#if DISPLACED_MAIN
|
||||
@word_le [
|
||||
$ADDR$ + 0xB
|
||||
],
|
||||
#else
|
||||
@word_le [
|
||||
$ADDR$ + if(main.addr >= 10000, 1/0, 0xA) // use -D DISPLACED_MAIN=1 if you get an error here
|
||||
],
|
||||
#endif
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
#if DISPLACED_MAIN
|
||||
$30 + (main.addr/10000)%%10,
|
||||
#endif
|
||||
$30 + (main.addr/1000)%%10,
|
||||
$30 + (main.addr/100)%%10,
|
||||
$30 + (main.addr/10)%%10,
|
||||
$30 + (main.addr/1)%%10,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
|
@ -7,7 +7,7 @@ C0-DF=@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
|||
|
||||
a-z=C1
|
||||
|
||||
{q}=02
|
||||
{apos}=07
|
||||
{q}=A2
|
||||
{apos}=A7
|
||||
{nbsp}=40
|
||||
{n}=8D
|
||||
|
|
|
@ -13,3 +13,4 @@ EOT=00
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pound}=5c
|
||||
|
|
|
@ -7,8 +7,8 @@ EOT=7F
|
|||
20-3f=@abcdefghijklmnopqrstuvwxyz[\]^_
|
||||
60-7e=πABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~
|
||||
|
||||
{q}=22
|
||||
{apos}=27
|
||||
{q}=02
|
||||
{apos}=07
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pi}=60
|
||||
|
|
|
@ -9,4 +9,4 @@ E0-FE=`abcdefghijklmnopqrstuvwxyz{\}~
|
|||
{q}=A2
|
||||
{apos}=A7
|
||||
{lbrace}=FB
|
||||
{rbrace}=FC
|
||||
{rbrace}=FD
|
||||
|
|
|
@ -26,6 +26,7 @@ f0-ff=đńňóôőö÷řůúűüýţ˙
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{euro}=80
|
||||
{copy}=a9
|
||||
{ss}=df
|
||||
{nbsp}=A0
|
||||
|
|
|
@ -23,6 +23,7 @@ f0-ff=рстуфхцчшщъыьэюя
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{euro}=88
|
||||
{copy}=a9
|
||||
{nbsp}=A0
|
||||
{shy}=AD
|
||||
|
|
|
@ -25,6 +25,7 @@ f0-ff=ðñòóôõö÷øùúûüýþÿ
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{euro}=80
|
||||
{cent}=a2
|
||||
{pound}=a3
|
||||
{yen}=a5
|
||||
|
|
39
include/encoding/cp1253.tbl
Normal file
39
include/encoding/cp1253.tbl
Normal file
|
@ -0,0 +1,39 @@
|
|||
NAME=CP1253
|
||||
EOT=00
|
||||
|
||||
20=U+0020
|
||||
21-3f=!"#$%&'()*+,-./0123456789:;<=>?
|
||||
40-5f=@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
||||
60-7e=`abcdefghijklmnopqrstuvwxyz{|}~
|
||||
80=€
|
||||
82-87=‚ƒ„…†‡
|
||||
89=‰
|
||||
8b=‹
|
||||
91-97=‘’“”•–—
|
||||
99=™
|
||||
9b=›
|
||||
a1-ac=΅Ά£¤¥¦§¨©ͺ«¬
|
||||
ae-af=®―
|
||||
b0-bf=°±²³΄µ¶·ΈΉΊ»Ό½ΎΏ
|
||||
c0-cf=ΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟ
|
||||
d0-d1=ΠΡ
|
||||
d3-df=ΣΤΥΦΧΨΩΪΫάέήί
|
||||
e0-ef=ΰαβγδεζηθικλμνξο
|
||||
f0-fe=πρςστυφχψωϊϋόύώ
|
||||
|
||||
|
||||
|
||||
{b}=08
|
||||
{t}=09
|
||||
{n}=0d0a
|
||||
{q}=22
|
||||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{euro}=80
|
||||
{pound}=a3
|
||||
{yen}=a5
|
||||
{copy}=a9
|
||||
{pi}=f0
|
||||
{nbsp}=A0
|
||||
{shy}=AD
|
34
include/encoding/cp1254.tbl
Normal file
34
include/encoding/cp1254.tbl
Normal file
|
@ -0,0 +1,34 @@
|
|||
NAME=CP1254
|
||||
EOT=00
|
||||
|
||||
20=U+0020
|
||||
21-3f=!"#$%&'()*+,-./0123456789:;<=>?
|
||||
40-5f=@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
||||
60-7e=`abcdefghijklmnopqrstuvwxyz{|}~
|
||||
80=€
|
||||
82-8c=‚ƒ„…†‡ˆ‰Š‹Œ
|
||||
91-9c=‘’“”•–—˜™š›œ
|
||||
9f=Ÿ
|
||||
a1-ac=¡¢£¤¥¦§¨©ª«¬
|
||||
ae-af=®¯
|
||||
b0-bf=°±²³´µ¶·¸¹º»¼½¾¿
|
||||
c0-cf=ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ
|
||||
d0-df=ĞÑÒÓÔÕÖ×ØÙÚÛÜİŞß
|
||||
e0-ef=àáâãäåæçèéêëìíîï
|
||||
f0-ff=ğñòóôõö÷øùúûüışÿ
|
||||
|
||||
{b}=08
|
||||
{t}=09
|
||||
{n}=0d0a
|
||||
{q}=22
|
||||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{euro}=80
|
||||
{cent}=a2
|
||||
{pound}=a3
|
||||
{yen}=a5
|
||||
{copy}=a9
|
||||
{ss}=df
|
||||
{nbsp}=A0
|
||||
{shy}=AD
|
36
include/encoding/cp1257.tbl
Normal file
36
include/encoding/cp1257.tbl
Normal file
|
@ -0,0 +1,36 @@
|
|||
NAME=CP1257
|
||||
EOT=00
|
||||
|
||||
20=U+0020
|
||||
21-3f=!"#$%&'()*+,-./0123456789:;<=>?
|
||||
40-5f=@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
||||
60-7e=`abcdefghijklmnopqrstuvwxyz{|}~
|
||||
80=€
|
||||
82-8c=‚ƒ„…†‡ˆ‰Š‹Œ
|
||||
8d-8f=¨ˇ¸
|
||||
91-9c=‘’“”•–—˜™š›œ
|
||||
9e-9f=¯˛
|
||||
a1-ac=¡¢£¤¥¦§Ø©Ŗ«¬
|
||||
ae-af=®Æ
|
||||
b0-bf=°±²³´µ¶·ø¹ŗ»¼½¾æ
|
||||
c0-cf=ĄĮĀĆÄÅĘĒČÉŹĖĢĶĪĻ
|
||||
d0-df=ŠŃŅÓŌÕÖ×ŲŁŚŪÜŻŽß
|
||||
e0-ef=ąįāćäåęēčéźėģķīļ
|
||||
f0-ff=šńņóōõö÷ųłśūüżž˙
|
||||
|
||||
|
||||
{b}=08
|
||||
{t}=09
|
||||
{n}=0d0a
|
||||
{q}=22
|
||||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{euro}=80
|
||||
{cent}=a2
|
||||
{pound}=a3
|
||||
{yen}=a5
|
||||
{copy}=a9
|
||||
{ss}=df
|
||||
{nbsp}=A0
|
||||
{shy}=AD
|
|
@ -27,4 +27,5 @@ F0-FE=≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
|
|||
{pound}=9c
|
||||
{yen}=9d
|
||||
{ss}=e1
|
||||
{pi}=e3
|
||||
{nbsp}=FF
|
||||
|
|
|
@ -23,6 +23,7 @@ F1-FE=±‗¾¶§÷¸°¨·¹³²■
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{copy}=b8
|
||||
{cent}=BD
|
||||
{pound}=9c
|
||||
{yen}=BE
|
||||
|
|
|
@ -24,6 +24,7 @@ F1-FE=±υφχ§ψ¸°¨ωϋΰώ■
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pound}=9c
|
||||
{ss}=D7
|
||||
{nbsp}=FF
|
||||
{shy}=F0
|
||||
|
|
|
@ -25,6 +25,7 @@ F1-FE=±‗¾¶§÷¸°¨·¹³²■
|
|||
{rbrace}=7d
|
||||
{cent}=BD
|
||||
{pound}=9c
|
||||
{copy}=b8
|
||||
{yen}=BE
|
||||
{euro}=D5
|
||||
{ss}=e1
|
||||
|
|
|
@ -19,6 +19,9 @@ fe-ff=↕↔
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pound}=a3
|
||||
{copy}=a4
|
||||
{pi}=b8
|
||||
{AE}=5b
|
||||
{OE}=5c
|
||||
{AA}=5d
|
||||
|
|
|
@ -19,3 +19,6 @@ fe-ff=↕↔
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pound}=a3
|
||||
{copy}=a4
|
||||
{pi}=b8
|
||||
|
|
|
@ -19,3 +19,5 @@ fe-ff=↕↔
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{copy}=a4
|
||||
{pi}=b8
|
||||
|
|
|
@ -20,3 +20,6 @@ fe-ff=↕↔
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pound}=a3
|
||||
{copy}=a4
|
||||
{pi}=b8
|
||||
|
|
|
@ -24,3 +24,4 @@ F0-F9=0123456789
|
|||
{q}=7F
|
||||
{lbrace}=C0
|
||||
{rbrace}=D0
|
||||
{cent}=4A
|
||||
|
|
20
include/encoding/geos_de.tbl
Normal file
20
include/encoding/geos_de.tbl
Normal file
|
@ -0,0 +1,20 @@
|
|||
NAME=GEOS-DE
|
||||
EOT=00
|
||||
|
||||
20=U+0020
|
||||
21-3f=!"#$%&'()*+,-./0123456789:;<=>?
|
||||
40-5f=§ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ^_
|
||||
60-7e=`abcdefghijklmnopqrstuvwxyzäöüß
|
||||
|
||||
{b}=08
|
||||
{t}=09
|
||||
{n}=0d0a
|
||||
{q}=22
|
||||
{apos}=27
|
||||
{AE}=5b
|
||||
{OE}=5c
|
||||
{UE}=5d
|
||||
{ae}=7b
|
||||
{oe}=7c
|
||||
{ue}=7d
|
||||
{ss}=7e
|
|
@ -27,3 +27,4 @@ f1-ff=ñòóôġö÷ĝùúûüŭŝ˙
|
|||
{ss}=df
|
||||
{nbsp}=A0
|
||||
{shy}=AD
|
||||
{pound}=A3
|
||||
|
|
|
@ -24,4 +24,5 @@ F0-FE=≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{ss}=e1
|
||||
{pi}=e3
|
||||
{nbsp}=FF
|
||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{nbsp}=a0
|
||||
{copy}=98
|
||||
•=95
|
||||
|
||||
b0=№
|
||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{nbsp}=9a
|
||||
{copy}=bf
|
||||
|
||||
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
|
||||
90-99=░▒▓⌠■∙√≈≤≥
|
||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{nbsp}=9a
|
||||
{copy}=bf
|
||||
|
||||
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
|
||||
90-99=░▒▓“■∙”—№™
|
||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{shy}=8d
|
||||
{copy}=bf
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{nbsp}=9a
|
||||
{copy}=bf
|
||||
|
||||
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
|
||||
90-99=░▒▓⌠■∙√≈≤≥
|
||||
|
|
|
@ -29,5 +29,6 @@ f1-ff=ÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ
|
|||
{ss}=a7
|
||||
{nbsp}=CA
|
||||
{euro}=DB
|
||||
{pi}=b9
|
||||
€=DB
|
||||
U+F8FF=F0
|
||||
|
|
|
@ -26,4 +26,5 @@ F0-FE=≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
|
|||
{cent}=9b
|
||||
{yen}=9d
|
||||
{ss}=e1
|
||||
{pi}=e3
|
||||
{nbsp}=FF
|
||||
|
|
|
@ -55,3 +55,8 @@ f0-ff=äëïöüçæåøñãõ⇒⇐⇔≡
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pi}=1b18
|
||||
{pound}=a3
|
||||
{copy}=a4
|
||||
{yen}=bd
|
||||
{cent}=b1
|
||||
{ss}=ba
|
||||
|
|
|
@ -23,6 +23,7 @@ F0-FF=₽×./,♀0123456789
|
|||
{ot}=BE
|
||||
{'v}=BF
|
||||
{hav}=BF
|
||||
{apos}=E0
|
||||
{'r}=E4
|
||||
{ar}=E4
|
||||
{'m}=E5
|
||||
|
|
|
@ -22,6 +22,7 @@ F0-FF=₽×./,♀0123456789
|
|||
{'t}=DD
|
||||
{'v}=DE
|
||||
|
||||
{apos}=E0
|
||||
{PK}=E1
|
||||
{pk}=E1
|
||||
{MN}=E2
|
||||
|
|
|
@ -13,6 +13,7 @@ E6-E8=?!.
|
|||
EC-EF=▷▶▼♂
|
||||
F0-FF=₽×./,♀0123456789
|
||||
|
||||
{ss}=be
|
||||
{c'}=d4
|
||||
{ce}=d4
|
||||
{d'}=d5
|
||||
|
@ -37,6 +38,7 @@ F0-FF=₽×./,♀0123456789
|
|||
{u'}=de
|
||||
{ue}=de
|
||||
{y'}=df
|
||||
{apos}=e0
|
||||
|
||||
{PK}=E1
|
||||
{pk}=E1
|
||||
|
|
2
include/encoding/utf32be.tbl
Normal file
2
include/encoding/utf32be.tbl
Normal file
|
@ -0,0 +1,2 @@
|
|||
NAME=UTF-32BE
|
||||
BUILTIN=UTF-32BE
|
2
include/encoding/utf32le.tbl
Normal file
2
include/encoding/utf32le.tbl
Normal file
|
@ -0,0 +1,2 @@
|
|||
NAME=UTF-32LE
|
||||
BUILTIN=UTF-32LE
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#pragma zilog_syntax
|
||||
|
||||
array __header @ $100 = [
|
||||
const array __header @ $100 = [
|
||||
$00, $C3, $50, $01, $CE, $ED, $66, $66, $CC, $0D, $00, $0B, $03, $73, $00, $83,
|
||||
$00, $0C, $00, $0D, $00, $08, $11, $1F, $88, $89, $00, $0E, $DC, $CC, $6E, $E6,
|
||||
$DD, $DD, $D9, $99, $BB, $BB, $67, $63, $6E, $0E, $EC, $CC, $DD, $DC, $99, $9F,
|
||||
|
|
|
@ -38,9 +38,9 @@ void read_joy() {
|
|||
ld a,(reg_joypad)
|
||||
ld a,(reg_joypad)
|
||||
}
|
||||
byte tmp
|
||||
|
||||
tmp = reg_joypad ^ $ff
|
||||
input_a = (tmp & 1) >> 0
|
||||
input_a = (tmp & 1) // >> 0
|
||||
input_b = (tmp & 2) >> 1
|
||||
input_select = (tmp & 4) >> 2
|
||||
input_start = (tmp & 8) >> 3
|
||||
|
|
|
@ -11,6 +11,12 @@ inline asm byte __mul_u8u8u8() {
|
|||
? LD A, E
|
||||
? RET
|
||||
}
|
||||
#elseif CPUFEATURE_R800
|
||||
inline asm byte __mul_u8u8u8() {
|
||||
? MULUB A,D
|
||||
? LD A,L
|
||||
? RET
|
||||
}
|
||||
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||
//A = A * D
|
||||
noinline asm byte __mul_u8u8u8() {
|
||||
|
@ -89,6 +95,16 @@ __divmod_u16u8u16u8_skip:
|
|||
? RET
|
||||
}
|
||||
|
||||
|
||||
#if CPUFEATURE_R800
|
||||
inline asm word __mul_u16u8u16() {
|
||||
? LD L,A
|
||||
? LD H,0
|
||||
? MULUW HL,DE
|
||||
? RET
|
||||
}
|
||||
#else
|
||||
// HL=A*DE
|
||||
noinline asm word __mul_u16u8u16() {
|
||||
? LD HL,0
|
||||
? LD B,8
|
||||
|
@ -113,8 +129,17 @@ __mul_u16u8u16_skip:
|
|||
#endif
|
||||
? RET
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||
|
||||
#if CPUFEATURE_R800
|
||||
inline asm word __mul_u16u16u16() {
|
||||
? EX DE,HL
|
||||
? MULUW HL,BC
|
||||
? RET
|
||||
}
|
||||
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||
// HL=BC*DE
|
||||
noinline asm word __mul_u16u16u16() {
|
||||
LD HL,0
|
||||
LD A,16
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_0401 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$401 = [
|
||||
$0b,
|
||||
4,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$31,
|
||||
$30,
|
||||
$33,
|
||||
$37,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$401>
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_0801 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$801 = [
|
||||
$0b,
|
||||
$08,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$32,
|
||||
$30,
|
||||
$36,
|
||||
$31,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$801>
|
||||
|
|
|
@ -22,4 +22,5 @@ asm void __init_16bit() @$80D {
|
|||
clc
|
||||
xce
|
||||
sep #$30
|
||||
}
|
||||
jmp main
|
||||
}
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_1001 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$1001 = [
|
||||
$0b,
|
||||
$10,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$34,
|
||||
$31,
|
||||
$30,
|
||||
$39,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$1001>
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_1201 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$1201 = [
|
||||
$0b,
|
||||
$12,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$34,
|
||||
$36,
|
||||
$32,
|
||||
$31,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$1201>
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_1c01 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$1C01 = [
|
||||
$0b,
|
||||
$1C,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$37,
|
||||
$31,
|
||||
$38,
|
||||
$31,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$1c01>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#warn m6809_math module should be used only on 6809-like targets
|
||||
#endif
|
||||
|
||||
noinline asm word __mul_u16u16u16(word register(x) x, word register(d) d) {
|
||||
noinline asm word __mul_u16u16u16(word register(x) x, word register(d) d) !preserves_y !preserves_u {
|
||||
pshs d,x
|
||||
exg d,x
|
||||
lda ,s
|
||||
|
@ -26,7 +26,7 @@ noinline asm word __mul_u16u16u16(word register(x) x, word register(d) d) {
|
|||
}
|
||||
|
||||
// returns p/q: quotient in A, remainder in B
|
||||
noinline asm word __divmod_u8u8u8u8(word register(a) p, word register(b) q) {
|
||||
noinline asm word __divmod_u8u8u8u8(word register(a) p, word register(b) q) !preserves_x !preserves_y !preserves_u {
|
||||
pshs b,cc
|
||||
ldb #8
|
||||
stb ,-s
|
||||
|
@ -47,7 +47,7 @@ noinline asm word __divmod_u8u8u8u8(word register(a) p, word register(b) q) {
|
|||
}
|
||||
|
||||
// returns p/q: quotient in X, remainder in D
|
||||
noinline asm word __divmod_u16u16u16u16(word register(x) p, word register(d) q) {
|
||||
noinline asm word __divmod_u16u16u16u16(word register(x) p, word register(d) q) !preserves_y !preserves_u {
|
||||
pshs x,d,cc
|
||||
ldb #16
|
||||
pshs b
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// Input: A = Byte to write.
|
||||
asm void putchar(byte register(a) char) @$FFD2 extern
|
||||
asm void putchar(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
|
||||
|
||||
inline void new_line() {
|
||||
putchar(13)
|
||||
|
|
|
@ -11,14 +11,21 @@ modules=loader_0801,x16_kernal,x16_hardware,c64_panic,stdlib
|
|||
|
||||
[allocation]
|
||||
; Let's not use the BASIC:
|
||||
zp_pointers=0-$7F
|
||||
segments=default,himem_00,himem_ff
|
||||
; $00-$01 are used for bank switching
|
||||
zp_pointers=$02-$7F
|
||||
segments=default,user,himem_00,himem_ff
|
||||
default_code_segment=default
|
||||
segment_default_start=$80D
|
||||
segment_default_codeend=$9eff
|
||||
segment_default_datastart=after_code
|
||||
segment_default_end=$9eff
|
||||
|
||||
;1KB user space
|
||||
segment_user_start=$0400
|
||||
segment_user_codeend=$07ff
|
||||
segment_user_datastart=after_code
|
||||
segment_user_end=$07ff
|
||||
|
||||
segment_himem_00_start=$a000
|
||||
segment_himem_00_codeend=$bfff
|
||||
segment_himem_00_datastart=after_code
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// Input: A = Byte to write.
|
||||
asm void chrout(byte register(a) char) @$FFD2 extern
|
||||
asm void chrout(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
|
||||
|
||||
asm void putchar(byte register(a) char) {
|
||||
JSR chrout
|
||||
|
|
|
@ -5,16 +5,16 @@
|
|||
|
||||
#pragma zilog_syntax
|
||||
|
||||
inline asm void putchar(byte register(a) char) {
|
||||
inline asm void putchar(byte register(a) char) !preserves_bc !preserves_de !preserves_hl {
|
||||
rst $10
|
||||
? ret
|
||||
}
|
||||
|
||||
inline void new_line() {
|
||||
inline void new_line() !preserves_bc !preserves_de !preserves_hl {
|
||||
putchar(13)
|
||||
}
|
||||
|
||||
inline asm void set_border(byte register(a) colour) {
|
||||
inline asm void set_border(byte register(a) colour) !preserves_bc !preserves_de !preserves_hl {
|
||||
out (254),a
|
||||
? ret
|
||||
}
|
||||
|
|
|
@ -19,11 +19,15 @@
|
|||
+= -= *= >>= <<=
|
||||
+' -' *' <<' >>'
|
||||
+'= -'= *'= <<'= >>'=
|
||||
$+ $- $* $<< $>>
|
||||
$+= $-= $*= $<<= $>>=
|
||||
= ?
|
||||
/ %% /= %%=
|
||||
> >= < <= != ==
|
||||
| || |= ^ ^= & && &=
|
||||
( ) { } @ [ ] : # </Keywords>
|
||||
( ) { } @ [ ] : #
|
||||
; .
|
||||
</Keywords>
|
||||
<Keywords name="Operators2"></Keywords>
|
||||
<Keywords name="Folders in code1, open">{</Keywords>
|
||||
<Keywords name="Folders in code1, middle"></Keywords>
|
||||
|
@ -38,10 +42,14 @@
|
|||
void bool byte sbyte ubyte word farword pointer farpointer long word_be word_le long_be long_le file
|
||||
int8 int16 int24 int32 int40 int48 int56 int64
|
||||
int72 int80 int88 int96 int104 int112 int120 int128
|
||||
signed8
|
||||
signed8 signed16 signed24 signed32 signed40 signed48 signed56 signed64
|
||||
signed72 signed80 signed88 signed96 signed104 signed112 signed120 signed128
|
||||
unsigned8 unsigned16 unsigned24 unsigned32 unsigned40 unsigned48 unsigned56 unsigned64
|
||||
unsigned72 unsigned80 unsigned88 unsigned96 unsigned104 unsigned112 unsigned120 unsigned128
|
||||
array addr fast</Keywords>
|
||||
array addr fast
|
||||
clear_carry clear_zero clear_overflow clear_negative
|
||||
set_carry set_zero set_overflow set_negative
|
||||
</Keywords>
|
||||
<Keywords name="Keywords2">
|
||||
import segment
|
||||
if else for return while do asm extern break continue default goto label
|
||||
|
@ -54,9 +62,11 @@
|
|||
utf8 utf16le utf16be latin0 latin9 iso8859_15 zx80 zx81 vectrex koi7n2 short_koi msx_intl msx_us msx_uk msx_de msx_fr msx_es msx_ru msx_jp msx_br
|
||||
utf8z utf16lez utf16bez latin0z latin9z iso8859_15z zx80z zx81z vectrexz koi7n2z short_koiz msx_intlz msx_usz msx_ukz msx_dez msx_frz msx_esz msx_ruz msx_jpz msx_brz
|
||||
until to downto parallelto paralleluntil
|
||||
function
|
||||
static stack ref const volatile inline noinline macro register kernal_interrupt interrupt align reentrant
|
||||
hi lo sin cos tan call nonet
|
||||
false true nullptr</Keywords>
|
||||
false true nullptr nullchar nullchar_scr
|
||||
</Keywords>
|
||||
<Keywords name="Keywords4">"sta " "lda " "jmp " "bit " "eor " "adc " "sbc " "ora " "and " "ldx " "ldy " "stx " "sty " "tax" "tay" "tya" "txa" "txs" "tsx" "sei" "cli" "clv" "clc" "cld" "sed" "sec" "bra " "beq " "bne " "bmi " "bpl " "bcc " "bcs " "bvs " bvc " "jsr " rts" "rti" "brk" "rol" "ror" "asl" "lsr" "inc " "dec " "cmp " "cpx " "cpy " inx iny dex dey pla pha plp hp phx plx phy ply "stz " "ldz " tza taz "tsb " "trb " ra txy tyx pld plb phb phd phk xce

"STA " "LDA " "JMP " "BIT " "EOR " "ADC " "SBC " "ORA " "AND " "LDX " "LDY " "STX " "STY " "TAX" "TAY" "TYA" "TXA" "TXS" "TSX" "SEI" "CLI" "CLV" "CLC" "CLD" "SED" "SEC" "BEQ " "BRA " "BNE " "BMI " "BPL " "BCC " "BCS " "BVS " BVC " "JSR " RTS" "RTI" "BRK" "ROL" "ROR" "ASL" "LSR" "INC " "DEC " "CMP " "CPX " "CPY " INX INY DEX DEY PLA PHA PLP HP PHX PLX PHY PLY "STZ " "LDZ " TZA TAZ "TSB " "TRB " RA TXY TYX PLD PLB PHB PHD PHK XCE</Keywords>
|
||||
<Keywords name="Keywords5">"sbx " "isc " "dcp " "lax " "sax " "anc " "alr " "arr " "rra " "rla " "lxa " "ane " "xaa "
"SBX " "ISC " "DCP " "LAX " "SAX " "ANC " "ALR " "ARR " "RRA " "RLA " "LXA " "ANE " "XAA "</Keywords>
|
||||
<Keywords name="Keywords6"></Keywords>
|
||||
|
|
|
@ -35,6 +35,7 @@ nav:
|
|||
- Inline 8080/LR35902/Z80 assembly: lang/assemblyz80.md
|
||||
- Inline 6809 assembly: lang/assembly6809.md
|
||||
- Reentrancy guidelines: lang/reentrancy.md
|
||||
- Optimization hints: lang/hints.md
|
||||
- List of keywords: lang/keywords.md
|
||||
- Library reference:
|
||||
- stdlib module: stdlib/stdlib.md
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
"allDeclaredFields":true,
|
||||
"allPublicMethods":true
|
||||
},
|
||||
{
|
||||
"name":"millfork.assembly.m6809.MOpcode$",
|
||||
"allDeclaredFields":true,
|
||||
"allPublicMethods":true
|
||||
},
|
||||
{
|
||||
"name":"org.apache.commons.logging.LogFactory"
|
||||
},
|
||||
|
|
|
@ -45,7 +45,7 @@ case class CompilationOptions(platform: Platform,
|
|||
EmitIntel8085Opcodes, EmitIntel8080Opcodes, UseIxForStack, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
||||
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(
|
||||
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes,
|
||||
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes, EmitR800Opcodes,
|
||||
UseIyForStack, UseIxForScratch, UseIyForScratch, UseShadowRegistersForInterrupts)
|
||||
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6809) invalids ++= Set(
|
||||
|
@ -172,6 +172,11 @@ case class CompilationOptions(platform: Platform,
|
|||
log.error("Extended 8080-like opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
}
|
||||
if (flags(EmitR800Opcodes)) {
|
||||
if (platform.cpu != R800) {
|
||||
log.error("R800 opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
}
|
||||
if (flags(EmitIntel8080Opcodes)) {
|
||||
if (!Intel8080Compatible(platform.cpu)) {
|
||||
log.error("Intel 8080 opcodes enabled for architecture that doesn't support them")
|
||||
|
@ -220,6 +225,7 @@ case class CompilationOptions(platform: Platform,
|
|||
"OPTIMIZE_IPO" -> toLong(flag(CompilationFlag.InterproceduralOptimization)),
|
||||
"CPUFEATURE_DECIMAL_MODE" -> toLong(flag(CompilationFlag.DecimalMode)),
|
||||
"CPUFEATURE_Z80" -> toLong(flag(CompilationFlag.EmitZ80Opcodes)),
|
||||
"CPUFEATURE_R800" -> toLong(flag(CompilationFlag.EmitR800Opcodes)),
|
||||
"CPUFEATURE_EZ80" -> toLong(flag(CompilationFlag.EmitEZ80Opcodes)),
|
||||
"CPUFEATURE_8080" -> toLong(flag(CompilationFlag.EmitIntel8080Opcodes)),
|
||||
"CPUFEATURE_8085" -> toLong(flag(CompilationFlag.EmitIntel8085Opcodes)),
|
||||
|
@ -290,7 +296,7 @@ object CpuFamily extends Enumeration {
|
|||
import Cpu._
|
||||
cpu match {
|
||||
case Mos | StrictMos | Ricoh | StrictRicoh | Cmos | SC02 | Rockwell | Wdc | HuC6280 | CE02 | Sixteen => M6502
|
||||
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | EZ80 | Z80Next => I80
|
||||
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | R800 | EZ80 | Z80Next => I80
|
||||
case Intel8086 | Intel80186 => I86
|
||||
case Cpu.Motorola6809 => M6809
|
||||
}
|
||||
|
@ -368,6 +374,10 @@ object Cpu extends Enumeration {
|
|||
* The Zilog Z80 processor, without illegal instructions
|
||||
*/
|
||||
val StrictZ80: Cpu.Value = Value
|
||||
/**
|
||||
* The R800 CPU (used in MSX Turbo-R)
|
||||
*/
|
||||
val R800: Cpu.Value = Value
|
||||
/**
|
||||
* The Zilog eZ80 processor
|
||||
*/
|
||||
|
@ -400,11 +410,11 @@ object Cpu extends Enumeration {
|
|||
/**
|
||||
* Processors that can run code for Zilog Z80
|
||||
*/
|
||||
val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, EZ80, Z80Next)
|
||||
val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, R800, EZ80, Z80Next)
|
||||
/**
|
||||
* Processors that can run code for Intel 8080
|
||||
*/
|
||||
val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, EZ80, Z80Next)
|
||||
val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, R800, EZ80, Z80Next)
|
||||
/**
|
||||
* Processors that can run code for Intel 8085
|
||||
*/
|
||||
|
@ -419,14 +429,18 @@ object Cpu extends Enumeration {
|
|||
RegisterVariables,
|
||||
FunctionDeduplication,
|
||||
EnableBreakpoints,
|
||||
UseOptimizationHints,
|
||||
GenericWarnings,
|
||||
ByteOverflowWarning,
|
||||
UselessCodeWarning,
|
||||
BuggyCodeWarning,
|
||||
FallbackValueUseWarning,
|
||||
BytePointerComparisonWarning,
|
||||
DeprecationWarning,
|
||||
NonZeroTerminatedLiteralWarning,
|
||||
CallToOverlappingBankWarning,
|
||||
DataMissingInOutputWarning,
|
||||
UnsupportedOptimizationHintWarning,
|
||||
)
|
||||
|
||||
private val mosAlwaysDefaultFlags = alwaysDefaultFlags
|
||||
|
@ -464,6 +478,8 @@ object Cpu extends Enumeration {
|
|||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitIntel8085Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
||||
case StrictZ80 | Z80 =>
|
||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts)
|
||||
case R800 =>
|
||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitR800Opcodes)
|
||||
case Z80Next =>
|
||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitIllegals, EmitZ80NextOpcodes)
|
||||
case EZ80 =>
|
||||
|
@ -510,6 +526,7 @@ object Cpu extends Enumeration {
|
|||
case "strict2a07" => StrictRicoh
|
||||
case "z80" => Z80
|
||||
case "strictz80" => Z80
|
||||
case "r800" => R800
|
||||
case "zx80next" => Z80Next
|
||||
case "z80next" => Z80Next
|
||||
// disabled for now:
|
||||
|
@ -558,9 +575,9 @@ object CompilationFlag extends Enumeration {
|
|||
EmitIllegals, DecimalMode, LenientTextEncoding, LineNumbersInAssembly, SourceInAssembly, EnableBreakpoints,
|
||||
// compilation options for MOS:
|
||||
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitWdcOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack,
|
||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack, IdentityPage,
|
||||
// compilation options for I80
|
||||
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
|
||||
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitR800Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
|
||||
UseShadowRegistersForInterrupts,
|
||||
UseIxForStack, UseIyForStack,
|
||||
UseIxForScratch, UseIyForScratch,
|
||||
|
@ -582,15 +599,18 @@ object CompilationFlag extends Enumeration {
|
|||
SingleThreaded,
|
||||
// warning options
|
||||
GenericWarnings,
|
||||
ByteOverflowWarning,
|
||||
UselessCodeWarning,
|
||||
BuggyCodeWarning,
|
||||
DeprecationWarning,
|
||||
FallbackValueUseWarning,
|
||||
BytePointerComparisonWarning,
|
||||
ExtraComparisonWarnings,
|
||||
RorWarning,
|
||||
NonZeroTerminatedLiteralWarning,
|
||||
CallToOverlappingBankWarning,
|
||||
DataMissingInOutputWarning,
|
||||
UnsupportedOptimizationHintWarning,
|
||||
FatalWarnings,
|
||||
// special options for internal compiler use
|
||||
EnableInternalTestSyntax,
|
||||
|
@ -598,14 +618,17 @@ object CompilationFlag extends Enumeration {
|
|||
|
||||
val allWarnings: Set[CompilationFlag.Value] = Set(
|
||||
GenericWarnings,
|
||||
ByteOverflowWarning,
|
||||
UselessCodeWarning,
|
||||
BuggyCodeWarning,
|
||||
DeprecationWarning,
|
||||
FallbackValueUseWarning,
|
||||
BytePointerComparisonWarning,
|
||||
ExtraComparisonWarnings,
|
||||
NonZeroTerminatedLiteralWarning,
|
||||
CallToOverlappingBankWarning,
|
||||
DataMissingInOutputWarning,
|
||||
UnsupportedOptimizationHintWarning,
|
||||
)
|
||||
|
||||
val fromString: Map[String, CompilationFlag.Value] = Map(
|
||||
|
@ -618,6 +641,7 @@ object CompilationFlag extends Enumeration {
|
|||
"emit_65ce02" -> Emit65CE02Opcodes,
|
||||
"emit_huc6280" -> EmitHudsonOpcodes,
|
||||
"emit_z80" -> EmitZ80Opcodes,
|
||||
"emit_r800" -> EmitR800Opcodes,
|
||||
"emit_ez80" -> EmitEZ80Opcodes,
|
||||
"emit_x80" -> EmitExtended80Opcodes,
|
||||
"emit_8080" -> EmitIntel8080Opcodes,
|
||||
|
@ -630,6 +654,7 @@ object CompilationFlag extends Enumeration {
|
|||
"u_stack" -> UseUForStack,
|
||||
"y_stack" -> UseYForStack,
|
||||
"software_stack" -> SoftwareStack,
|
||||
"identity_page" -> IdentityPage,
|
||||
"use_shadow_registers_for_irq" -> UseShadowRegistersForInterrupts,
|
||||
"output_intel_syntax" -> UseIntelSyntaxForOutput,
|
||||
"input_intel_syntax" -> UseIntelSyntaxForInput,
|
||||
|
|
|
@ -46,10 +46,24 @@ case class Context(errorReporting: Logger,
|
|||
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)) {
|
||||
addons += CompilationFlag.EmitZ80Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.EmitZ80Opcodes) || isFlagSet(CompilationFlag.EmitSharpOpcodes)) {
|
||||
if (isFlagSet(CompilationFlag.EmitZ80NextOpcodes)) {
|
||||
addons += CompilationFlag.EmitZ80Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.EmitR800Opcodes)) {
|
||||
addons += CompilationFlag.EmitZ80Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitZ80NextOpcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitR800Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitZ80Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitSharpOpcodes)) {
|
||||
addons += CompilationFlag.EmitExtended80Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.EmitZ80Opcodes) || isFlagSet(CompilationFlag.EmitIntel8085Opcodes)) {
|
||||
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitZ80NextOpcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitR800Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitZ80Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitIntel8085Opcodes)) {
|
||||
addons += CompilationFlag.EmitIntel8080Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.OptimizeForSpeed)) {
|
||||
|
|
|
@ -1,23 +1,37 @@
|
|||
package millfork
|
||||
|
||||
import millfork.output.{BankLayoutInFile, FormattableLabel}
|
||||
|
||||
import java.util.regex.Pattern
|
||||
import scala.collection.mutable
|
||||
import scala.util.control.Breaks.{break, breakable}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
object DebugOutputFormat {
|
||||
val map: Map[String, DebugOutputFormat] = Map(
|
||||
"raw" -> RawDebugOutputFormat,
|
||||
"vice" -> ViceDebugOutputFormat,
|
||||
"nesasm" -> NesasmDebugOutputFormat,
|
||||
"fns" -> NesasmDebugOutputFormat,
|
||||
"fceux" -> FceuxDebugOutputFormat,
|
||||
"nl" -> FceuxDebugOutputFormat,
|
||||
"mlb" -> MesenOutputFormat,
|
||||
"mesen" -> MesenOutputFormat,
|
||||
"asm6f" -> MesenOutputFormat,
|
||||
"ld65" -> Ld65OutputFormat,
|
||||
"ca65" -> Ld65OutputFormat,
|
||||
"cc65" -> Ld65OutputFormat,
|
||||
"dbg" -> Ld65OutputFormat,
|
||||
"sym" -> SymDebugOutputFormat)
|
||||
}
|
||||
|
||||
sealed trait DebugOutputFormat {
|
||||
|
||||
def formatAll(labels: Seq[(String, (Int, Int))], breakpoints: Seq[(Int, Int)]): String = {
|
||||
val labelPart = labelsHeader + labels.map(formatLineTupled).mkString("\n") + "\n"
|
||||
def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
|
||||
val labelPart = labelsHeader + labels.map(formatLine).mkString("\n") + "\n"
|
||||
if (breakpoints.isEmpty) {
|
||||
labelPart
|
||||
} else {
|
||||
|
@ -25,9 +39,7 @@ sealed trait DebugOutputFormat {
|
|||
}
|
||||
}
|
||||
|
||||
final def formatLineTupled(labelAndValue: (String, (Int, Int))): String = formatLine(labelAndValue._1, labelAndValue._2._1, labelAndValue._2._2)
|
||||
|
||||
def formatLine(label: String, bank: Int, value: Int): String
|
||||
def formatLine(label: FormattableLabel): String
|
||||
|
||||
final def formatBreakpointTupled(value: (Int, Int)): Seq[String] = formatBreakpoint(value._1, value._2).toSeq
|
||||
|
||||
|
@ -45,10 +57,25 @@ sealed trait DebugOutputFormat {
|
|||
def breakpointsHeader: String = ""
|
||||
}
|
||||
|
||||
object RawDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
f"${label.bankNumber}%02X:${label.startValue}%04X:${label.endValue.fold("")(_.formatted("%04X"))}%s:${label.category}%s:$label%s"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".labels"
|
||||
|
||||
override def filePerBank: Boolean = false
|
||||
|
||||
override def addOutputExtension: Boolean = false
|
||||
|
||||
override def formatBreakpoint(bank: Int, value: Int): Option[String] =
|
||||
Some(f"$bank%02X:$value%04X::b:<breakpoint@$value%04X>")
|
||||
}
|
||||
|
||||
object ViceDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
val normalized = label.replace('$', '_').replace('.', '_')
|
||||
s"al ${value.toHexString} .$normalized"
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
val normalized = label.labelName.replace('$', '_').replace('.', '_')
|
||||
s"al ${label.startValue.toHexString} .$normalized"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".lbl"
|
||||
|
@ -61,8 +88,8 @@ object ViceDebugOutputFormat extends DebugOutputFormat {
|
|||
}
|
||||
|
||||
object NesasmDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
label + " = $" + value.toHexString
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
label.labelName + " = $" + label.startValue.toHexString
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".fns"
|
||||
|
@ -75,8 +102,8 @@ object NesasmDebugOutputFormat extends DebugOutputFormat {
|
|||
}
|
||||
|
||||
object SymDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
f"$bank%02x:$value%04x $label%s"
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
f"${label.bankNumber}%02x:${label.startValue}%04x ${label.labelName}%s"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".sym"
|
||||
|
@ -93,8 +120,8 @@ object SymDebugOutputFormat extends DebugOutputFormat {
|
|||
}
|
||||
|
||||
object FceuxDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
f"$$$value%04x#$label%s#"
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
f"$$${label.startValue}%04x#${label.labelName}%s#"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = if (bank == 0xff) ".ram.nl" else s".$bank.nl"
|
||||
|
@ -105,3 +132,97 @@ object FceuxDebugOutputFormat extends DebugOutputFormat {
|
|||
|
||||
override def formatBreakpoint(bank: Int, value: Int): Option[String] = None
|
||||
}
|
||||
|
||||
object MesenOutputFormat extends DebugOutputFormat {
|
||||
|
||||
override def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
|
||||
val allStarts = labels.groupBy(_.bankName).mapValues(_.map(_.startValue).toSet)
|
||||
labels.flatMap{ l =>
|
||||
val mesenShift = b.getMesenShift(l.bankName)
|
||||
val shiftedStart = l.startValue + mesenShift
|
||||
var shiftedEnd = l.endValue.map(_ + mesenShift).filter(_ != shiftedStart)
|
||||
l.endValue match {
|
||||
case None =>
|
||||
case Some(e) =>
|
||||
// Mesen does not like labels of form XXX-XXX, where both ends are equal
|
||||
breakable {
|
||||
for (i <- l.startValue.+(1) to e) {
|
||||
if (allStarts.getOrElse(l.bankName, Set.empty).contains(i)) {
|
||||
shiftedEnd = None
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shiftedStart >= 0 && shiftedEnd.forall(_ >= 0)) {
|
||||
val normalized = l.labelName.replace('$', '_').replace('.', '_')
|
||||
val comment = (l.category match {
|
||||
case 'F' => "function "
|
||||
case 'A' => "initialized array "
|
||||
case 'V' => "initialized variable "
|
||||
case 'a' => "array "
|
||||
case 'v' => "variable "
|
||||
case _ => ""
|
||||
}) + l.labelName
|
||||
Some(f"${l.mesenSymbol}%s:${shiftedStart}%04X${shiftedEnd.fold("")(e => f"-$e%04X")}%s:$normalized%s:$comment%s")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}.mkString("\n")
|
||||
}
|
||||
|
||||
override def formatLine(label: FormattableLabel): String = throw new UnsupportedOperationException()
|
||||
|
||||
override def formatBreakpoint(bank: Int, value: Int): Option[String] = None
|
||||
|
||||
override def fileExtension(bank: Int): String = ".mlb"
|
||||
|
||||
override def filePerBank: Boolean = false
|
||||
|
||||
override def addOutputExtension: Boolean = false
|
||||
}
|
||||
|
||||
object Ld65OutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: FormattableLabel): String = throw new UnsupportedOperationException()
|
||||
|
||||
override def formatBreakpoint(bank: Int, value: Int): Option[String] = None
|
||||
|
||||
override def fileExtension(bank: Int): String = ".dbg"
|
||||
|
||||
override def filePerBank: Boolean = false
|
||||
|
||||
override def addOutputExtension: Boolean = true
|
||||
|
||||
override def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
|
||||
val q = '"'
|
||||
val allBanksInFile = b.allBanks()
|
||||
val allBanks = (labels.map(_.bankName).distinct ++ allBanksInFile).distinct
|
||||
val result = mutable.ListBuffer[String]()
|
||||
result += "version\tmajor=2,minor=0"
|
||||
result += s"info\tcsym=0,file=0,lib=0,line=0,mod=0,scope=1,seg=${allBanks.size},span=0,sym=${labels.size},type=4"
|
||||
for ((bank, ix) <- allBanks.zipWithIndex) {
|
||||
val common = s"seg\tid=${ix},name=$q${bank}$q,start=0x${b.getStart(bank).toHexString},size=0x0,addrsize=absolute"
|
||||
if (allBanksInFile.contains(bank)) {
|
||||
result += common + s",ooffs=${b.ld65Offset(bank)}"
|
||||
} else {
|
||||
result += common
|
||||
}
|
||||
}
|
||||
result += "scope\tid=0,name=\"\""
|
||||
for ((label, ix) <- labels.sortBy(l => (l.bankName, l.startValue, l.labelName)).zipWithIndex) {
|
||||
val name = label.labelName.replace('$', '@').replace('.', '@')
|
||||
val sb = new StringBuilder
|
||||
sb ++= s"sym\tid=${ix},name=$q${name}$q,addrsize=absolute,"
|
||||
label.endValue match {
|
||||
case Some(e) =>
|
||||
if (!labels.exists(l => l.ne(label) && l.startValue >= label.startValue && l.startValue <= e)) {
|
||||
sb ++= s"size=${ e - label.startValue + 1 },"
|
||||
}
|
||||
case None =>
|
||||
}
|
||||
sb ++= s"scope=0,val=0x${label.startValue.toHexString},seg=${allBanks.indexOf(label.bankName)},type=lab"
|
||||
result += sb.toString
|
||||
}
|
||||
result.mkString("\n")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,25 +119,35 @@ object Main {
|
|||
else if (l.startsWith("__")) 7
|
||||
else 0
|
||||
}
|
||||
val sortedLabels = result.labels.groupBy(_._2).values.map(_.minBy(a => labelUnimportance(a._1) -> a._1)).toSeq.sortBy(_._2)
|
||||
val sortedLabels: Seq[FormattableLabel] =
|
||||
result.labels.groupBy(_._2).values
|
||||
.map(_.minBy(a => labelUnimportance(a._1) -> a._1)).toSeq.sortBy(_._2)
|
||||
.map { case (l, (b, s)) =>
|
||||
val bankNumber = options.platform.bankNumbers.getOrElse(b, 0)
|
||||
val mesenCategory = options.platform.getMesenLabelCategory(b, s)
|
||||
result.endLabels.get(l) match {
|
||||
case Some((c, e)) => FormattableLabel(l, b, bankNumber, s, Some(e), c, mesenCategory)
|
||||
case _ => FormattableLabel(l, b, bankNumber, s, None, 'x', mesenCategory)
|
||||
}
|
||||
}
|
||||
val sortedBreakpoints = result.breakpoints
|
||||
val format = c.outputLabelsFormatOverride.getOrElse(platform.outputLabelsFormat)
|
||||
val basename = if (format.addOutputExtension) output + platform.fileExtension else output
|
||||
if (format.filePerBank) {
|
||||
val banks = sortedLabels.map(_._2._1).toSet ++ sortedBreakpoints.map(_._2).toSet
|
||||
val banks: Set[Int] = sortedLabels.map(_.bankNumber).toSet ++ sortedBreakpoints.map(_._1).toSet
|
||||
banks.foreach{ bank =>
|
||||
val labels = sortedLabels.filter(_._2._1.==(bank))
|
||||
val labels = sortedLabels.filter(_.bankNumber.==(bank))
|
||||
val breakpoints = sortedBreakpoints.filter(_._1.==(bank))
|
||||
val labelOutput = basename + format.fileExtension(bank)
|
||||
val path = Paths.get(labelOutput)
|
||||
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
|
||||
Files.write(path, format.formatAll(labels, breakpoints).getBytes(StandardCharsets.UTF_8))
|
||||
Files.write(path, format.formatAll(result.bankLayoutInFile, labels, breakpoints).getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
} else {
|
||||
val labelOutput = basename + format.fileExtension(0)
|
||||
val path = Paths.get(labelOutput)
|
||||
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
|
||||
Files.write(path, format.formatAll(sortedLabels, sortedBreakpoints).getBytes(StandardCharsets.UTF_8))
|
||||
Files.write(path, format.formatAll(result.bankLayoutInFile, sortedLabels, sortedBreakpoints).getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
}
|
||||
val defaultPrgOutput = if (output.endsWith(platform.fileExtension)) output else output + platform.fileExtension
|
||||
|
@ -165,7 +175,7 @@ object Main {
|
|||
f"${path.getFileName}%s ${start}%04X ${start}%04X ${codeLength}%04X".getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
}
|
||||
errorReporting.debug(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
|
||||
errorReporting.info(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
|
||||
c.runFileName.foreach{ program =>
|
||||
if (File.separatorChar == '\\') {
|
||||
if (!new File(program).exists() && !new File(program + ".exe").exists()) {
|
||||
|
@ -241,10 +251,10 @@ object Main {
|
|||
log.debug(s"Failed to find the default include path: $err")
|
||||
case Right(path) =>
|
||||
log.debug(s"Automatically detected include path: $path")
|
||||
return c.copy(includePath = List(path) ++ c.extraIncludePath)
|
||||
return c.copy(includePath = List(System.getProperty("user.dir"), path) ++ c.extraIncludePath)
|
||||
}
|
||||
}
|
||||
c.copy(includePath = c.includePath ++ c.extraIncludePath)
|
||||
c.copy(includePath = System.getProperty("user.dir") :: (c.includePath ++ c.extraIncludePath))
|
||||
}
|
||||
|
||||
private def assembleForMos(c: Context, platform: Platform, options: CompilationOptions): AssemblerOutput = {
|
||||
|
@ -283,6 +293,7 @@ object Main {
|
|||
if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.All else Nil,
|
||||
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForEmulation else Nil,
|
||||
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) SixteenOptimizations.AllForNative else Nil,
|
||||
if (options.flag(CompilationFlag.IdentityPage)) IdentityPageOptimizations.All else Nil,
|
||||
if (options.flag(CompilationFlag.DangerousOptimizations)) DangerousOptimizations.All else Nil
|
||||
).flatten
|
||||
val goodCycle = List.fill(optLevel - 2)(OptimizationPresets.Good ++ goodExtras).flatten
|
||||
|
@ -321,7 +332,9 @@ object Main {
|
|||
val assemblyOptimizations = optLevel match {
|
||||
case 0 => Nil
|
||||
case _ =>
|
||||
if (options.flag(CompilationFlag.EmitZ80Opcodes))
|
||||
if (options.flag(CompilationFlag.EmitR800Opcodes))
|
||||
Z80OptimizationPresets.GoodForR800
|
||||
else if (options.flag(CompilationFlag.EmitZ80Opcodes))
|
||||
Z80OptimizationPresets.GoodForZ80
|
||||
else if (options.flag(CompilationFlag.EmitIntel8080Opcodes))
|
||||
Z80OptimizationPresets.GoodForIntel8080
|
||||
|
@ -427,7 +440,7 @@ object Main {
|
|||
p.toLowerCase(Locale.ROOT),
|
||||
errorReporting.fatal("Invalid label file format: " + p))
|
||||
c.copy(outputLabels = true, outputLabelsFormatOverride = Some(f))
|
||||
}.description("Generate also the label file in the given format. Available options: vice, nesasm, sym.")
|
||||
}.description("Generate also the label file in the given format. Available options: vice, nesasm, sym, raw.")
|
||||
|
||||
boolean("-fbreakpoints", "-fno-breakpoints").action((c,v) =>
|
||||
c.changeFlag(CompilationFlag.EnableBreakpoints, v)
|
||||
|
@ -487,7 +500,7 @@ object Main {
|
|||
} else {
|
||||
errorReporting.fatal("Invalid syntax for -D option")
|
||||
}
|
||||
}.description("Define a feature value for the preprocessor.")
|
||||
}.description("Define a feature value for the preprocessor.").maxCount(Integer.MAX_VALUE)
|
||||
|
||||
boolean("-finput_intel_syntax", "-finput_zilog_syntax").repeatable().action((c,v) =>
|
||||
c.changeFlag(CompilationFlag.UseIntelSyntaxForInput, v)
|
||||
|
@ -650,7 +663,7 @@ object Main {
|
|||
c.changeFlag(CompilationFlag.CompactReturnDispatchParams, v)
|
||||
}.description("Whether parameter values in return dispatch statements may overlap other objects. Enabled by default.")
|
||||
boolean("-fbounds-checking", "-fno-bounds-checking").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.VariableOverlap, v)
|
||||
c.changeFlag(CompilationFlag.CheckIndexOutOfBounds, v)
|
||||
}.description("Whether should insert bounds checking on array access.")
|
||||
boolean("-flenient-encoding", "-fno-lenient-encoding").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.LenientTextEncoding, v)
|
||||
|
@ -712,6 +725,9 @@ object Main {
|
|||
}.description("Optimize code even more.")
|
||||
if (i == 1 || i > 4) f.hidden()
|
||||
}
|
||||
boolean("-fhints", "-fnohints").action{ (c,v) =>
|
||||
c.changeFlag(CompilationFlag.UseOptimizationHints, v)
|
||||
}.description("Whether optimization hints should be used.")
|
||||
flag("--inline").repeatable().action { c =>
|
||||
c.changeFlag(CompilationFlag.InlineFunctions, true)
|
||||
}.description("Inline functions automatically.").hidden()
|
||||
|
@ -742,6 +758,9 @@ object Main {
|
|||
boolean("-fregister-variables", "-fno-register-variables").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.RegisterVariables, v)
|
||||
}.description("Allow moving local variables into CPU registers. Enabled by default.")
|
||||
boolean("-fidentity-page", "-fno-identity-page").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.IdentityPage, v)
|
||||
}.description("Whether should use an identity page to optimize certain operations.")
|
||||
flag("-Os", "--size").repeatable().action { c =>
|
||||
c.changeFlag(CompilationFlag.OptimizeForSize, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSpeed, false).
|
||||
|
@ -754,6 +773,7 @@ object Main {
|
|||
}.description("Prefer faster code even if it is slightly bigger (experimental). Implies -finline.")
|
||||
flag("-Ob", "--blast-processing").repeatable().action { c =>
|
||||
c.changeFlag(CompilationFlag.OptimizeForSize, false).
|
||||
changeFlag(CompilationFlag.IdentityPage, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSpeed, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSonicSpeed, true)
|
||||
}.description("Prefer faster code even if it is much bigger (experimental). Implies -finline.")
|
||||
|
@ -791,6 +811,10 @@ object Main {
|
|||
c.changeFlag(CompilationFlag.DeprecationWarning, v)
|
||||
}.description("Whether should warn about deprecated aliases. Default: enabled.")
|
||||
|
||||
boolean("-Wcomparisons", "-Wno-comparisons").repeatable().action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.BytePointerComparisonWarning, v)
|
||||
}.description("Whether should warn about comparisons between bytes and pointers. Default: enabled.")
|
||||
|
||||
boolean("-Wextra-comparisons", "-Wno-extra-comparisons").repeatable().action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.ExtraComparisonWarnings, v)
|
||||
}.description("Whether should warn about simplifiable unsigned integer comparisons. Default: disabled.")
|
||||
|
@ -811,10 +835,18 @@ object Main {
|
|||
c.changeFlag(CompilationFlag.RorWarning, v)
|
||||
}.description("Whether should warn about the ROR instruction (6502 only). Default: disabled.")
|
||||
|
||||
boolean("-Woverflow", "-Wno-overflow").repeatable().action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.ByteOverflowWarning, v)
|
||||
}.description("Whether should warn about byte overflow. Default: enabled.")
|
||||
|
||||
boolean("-Wuseless", "-Wno-useless").repeatable().action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.UselessCodeWarning, v)
|
||||
}.description("Whether should warn about code that does nothing. Default: enabled.")
|
||||
|
||||
boolean("-Whints", "-Wno-hints").repeatable().action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.UnsupportedOptimizationHintWarning, v)
|
||||
}.description("Whether should warn about unsupported optimization hints. Default: enabled.")
|
||||
|
||||
fluff("", "Other options:", "")
|
||||
|
||||
expansion("-Xd")("-O1", "-s", "-fsource-in-asm", "-g").description("Do a debug build. Equivalent to -O1 -s -fsource-in-asm -g")
|
||||
|
|
|
@ -42,6 +42,7 @@ class Platform(
|
|||
val outputLabelsFormat: DebugOutputFormat,
|
||||
val outputStyle: OutputStyle.Value
|
||||
) {
|
||||
|
||||
def hasZeroPage: Boolean = cpuFamily == CpuFamily.M6502
|
||||
|
||||
def cpuFamily: CpuFamily.Value = CpuFamily.forType(this.cpu)
|
||||
|
@ -60,6 +61,18 @@ class Platform(
|
|||
val e2 = bankEndBefore(bank2)
|
||||
e2 > s1 && s2 < e1
|
||||
}
|
||||
|
||||
lazy val banksExplicitlyWrittenToOutput: Set[String] = bankNumbers.keySet.filter(b => defaultOutputPackager.writes(b)).toSet
|
||||
|
||||
def getMesenLabelCategory(bank: String, address: Int): Char = {
|
||||
// see: https://www.mesen.ca/docs/debugging/debuggerintegration.html#mesen-label-files-mlb
|
||||
val start = bankStart(bank)
|
||||
val end = bankEndBefore(bank)
|
||||
if (start > address || address >= end) 'G'
|
||||
else if (banksExplicitlyWrittenToOutput(bank)) 'P'
|
||||
else if (bank == "default") 'R'
|
||||
else 'W' // TODO: distinguish between W and S
|
||||
}
|
||||
}
|
||||
|
||||
object Platform {
|
||||
|
|
|
@ -2,7 +2,7 @@ package millfork.assembly
|
|||
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.compiler.LabelGenerator
|
||||
import millfork.env.{NormalFunction, ThingInMemory}
|
||||
import millfork.env.{Constant, NormalFunction, ThingInMemory}
|
||||
import millfork.error.Logger
|
||||
import millfork.node.NiceFunctionProperty
|
||||
|
||||
|
@ -10,8 +10,9 @@ import millfork.node.NiceFunctionProperty
|
|||
* @author Karol Stasiak
|
||||
*/
|
||||
case class OptimizationContext(options: CompilationOptions,
|
||||
labelMap: Map[String, (Int, Int)],
|
||||
labelMap: Map[String, (String, Int)],
|
||||
zreg: Option[ThingInMemory],
|
||||
identityPage: Constant,
|
||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)]) {
|
||||
@inline
|
||||
def log: Logger = options.log
|
||||
|
@ -25,4 +26,6 @@ trait AssemblyOptimization[T <: AbstractCode] {
|
|||
def optimize(f: NormalFunction, code: List[T], context: OptimizationContext): List[T]
|
||||
|
||||
def requiredFlags: Set[CompilationFlag.Value] = Set.empty
|
||||
|
||||
def minimumRequiredLines: Int
|
||||
}
|
||||
|
|
|
@ -168,6 +168,7 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
|||
|
||||
def changesRegister(reg: M6809Register.Value): Boolean = {
|
||||
import M6809Register._
|
||||
if (MOpcode.NotActualOpcodes(opcode)) return false
|
||||
def overlaps(other: M6809Register.Value): Boolean = {
|
||||
if (reg == D && (other == A || other == B)) true
|
||||
else if (other == D && (reg == A || reg == B)) true
|
||||
|
@ -202,12 +203,13 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
|||
}
|
||||
}
|
||||
|
||||
def changesCarryFlag: Boolean = !MOpcode.PreservesC(opcode)
|
||||
def changesCarryFlag: Boolean = !MOpcode.NotActualOpcodes(opcode) && !MOpcode.PreservesC(opcode)
|
||||
|
||||
|
||||
|
||||
def readsRegister(reg: M6809Register.Value): Boolean = {
|
||||
import M6809Register._
|
||||
if (MOpcode.NotActualOpcodes(opcode)) return false
|
||||
def overlaps(other: M6809Register.Value): Boolean = {
|
||||
if (reg == D && (other == A || other == B)) true
|
||||
else if (other == D && (reg == A || reg == B)) true
|
||||
|
@ -252,15 +254,17 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
|||
case MUL => overlaps(D)
|
||||
case ABX => reg == X || overlaps(B)
|
||||
case NOP | SWI | SWI2 | SWI3 | SYNC => false
|
||||
case LDB | LDA | LDX | LDY | LDU => false
|
||||
case INC | DEC | ROL | ROR | ASL | ASR | LSR | CLR | COM | NEG | TST => false // variants for A and B handled before
|
||||
case op if Branching(op) => false
|
||||
case JMP => false
|
||||
case JMP | RTS => false
|
||||
case _ => true // TODO
|
||||
}
|
||||
}
|
||||
|
||||
def readsMemory(): Boolean = {
|
||||
import MOpcode._
|
||||
if (MOpcode.NotActualOpcodes(opcode)) return false
|
||||
val opcodeIsForReading = opcode match {
|
||||
case LDA | LDB | LDD | LDX | LDY | LDU | LDS | PULU | PULS => true
|
||||
case ADDA | SUBA | ADCA | SBCA | ORA | EORA | ANDA | CMPA | BITA => true
|
||||
|
@ -289,6 +293,7 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
|||
|
||||
def changesMemory(): Boolean = {
|
||||
import MOpcode._
|
||||
if (NotActualOpcodes(opcode)) return false
|
||||
val opcodeIsForWriting = opcode match {
|
||||
case LDA | LDB | LDD | LDX | LDY | LDU | LDS | PULU | PULS => false
|
||||
case ADDA | SUBA | ADCA | SBCA | ORA | EORA | ANDA | CMPA | BITA => false
|
||||
|
|
|
@ -32,9 +32,10 @@ object MOpcode extends Enumeration {
|
|||
TFR, TST,
|
||||
DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL = Value
|
||||
|
||||
val NotActualOpcodes: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL)
|
||||
|
||||
private val toMap: Map[String, MOpcode.Value] = {
|
||||
val notActualOpcodes: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL)
|
||||
values.filterNot(notActualOpcodes).map(o => o.toString -> o).toMap ++ Map("BHS" -> BCC, "BLO" -> BCS, "LSL" -> ASL)
|
||||
values.filterNot(NotActualOpcodes).map(o => o.toString -> o).toMap ++ Map("BHS" -> BCC, "BLO" -> BCS, "LSL" -> ASL)
|
||||
}
|
||||
val NoopDiscard: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC)
|
||||
val PrefixedBy10: Set[MOpcode.Value] = Set(CMPD, CMPY, LDS, LDY, SWI2, STS, STY) // TODO: branches
|
||||
|
|
|
@ -3,7 +3,8 @@ package millfork.assembly.m6809.opt
|
|||
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, LongRelative, MAddrMode, MLine, MState, PostIncremented, RegisterSet}
|
||||
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, Indexed, InherentA, InherentB, LongRelative, MAddrMode, MLine, MState, PostIncremented, RegisterSet}
|
||||
import millfork.env.{CompoundConstant, Constant, MathOperator}
|
||||
import millfork.node.M6809Register
|
||||
|
||||
/**
|
||||
|
@ -19,15 +20,38 @@ object AlwaysGoodMOptimizations {
|
|||
(Elidable & HasOpcodeIn(LDX, LEAX) & DoesntMatterWhatItDoesWith(MState.X, MState.NF, MState.ZF, MState.VF)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(LDY, LEAY) & DoesntMatterWhatItDoesWith(MState.Y, MState.NF, MState.ZF, MState.VF)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcode(STB) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesB) & DoesntChangeIndexingInAddrMode(0)).* ~
|
||||
(Linear & Not(ChangesB) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDB) & MatchAddrMode(0) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> (_.init),
|
||||
(Elidable & HasOpcode(STD) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesB) & DoesntChangeIndexingInAddrMode(0)).* ~
|
||||
(Linear & Not(ChangesB) & Not(ChangesA) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDD) & MatchAddrMode(0) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> (_.init),
|
||||
)
|
||||
|
||||
val PointlessCompare = new RuleBasedAssemblyOptimization("Pointless compare",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(HasOpcodeIn(LDA, ANDA, ORA, EORA, ADDA, ADCA, SUBA, SBCA)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasImmediate(0) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> {code => code.init},
|
||||
(HasOpcodeIn(LDB, ANDB, ORB, EORB, ADDB, ADCB, SUBB, SBCB)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasImmediate(0) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> {code => code.init},
|
||||
|
||||
(Elidable & HasOpcode(EORA) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.A, MState.NF, MState.VF, MState.CF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(EORB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.B, MState.NF, MState.VF, MState.CF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
)
|
||||
|
||||
val SimplifiableZeroStore = new RuleBasedAssemblyOptimization("Simplifiable zero store",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
(Elidable & HasOpcode(LDA) & HasImmediate(0) & DoesntMatterWhatItDoesWith(MState.CF)) ~~> {
|
||||
|
@ -50,6 +74,75 @@ object AlwaysGoodMOptimizations {
|
|||
},
|
||||
)
|
||||
|
||||
val SimplifiableComparison = new RuleBasedAssemblyOptimization("Simplifiable comparison",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
|
||||
(Elidable & HasOpcode(EORB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(ADDB) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(SUBB) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(INC) & HasAddrMode(InherentB)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(DEC) & HasAddrMode(InherentB)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(EORA) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(ADDA) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(SUBA) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(INC) & HasAddrMode(InherentA)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(DEC) & HasAddrMode(InherentA)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
|
||||
)
|
||||
|
||||
val PointlessRegisterTransfers = new RuleBasedAssemblyOptimization("Pointless register transfers",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & IsTfr(M6809Register.D, M6809Register.X)) ~
|
||||
|
@ -144,14 +237,67 @@ object AlwaysGoodMOptimizations {
|
|||
(_.map(_.copy(opcode = LDD))),
|
||||
(Elidable & HasImmediate(0) & HasOpcodeIn(ORB, EORB, ADDB, ORA, EORA, ADDA, ADDD) & DoesntMatterWhatItDoesWith(MState.VF, MState.ZF, MState.NF, MState.CF, MState.HF)) ~~>
|
||||
(_ => Nil),
|
||||
(HasOpcode(LDB)) ~
|
||||
(Elidable & HasOpcode(ANDB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(BNE) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(LDB)) ~
|
||||
(HasOpcode(ANDB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(HasOpcode(BEQ)) ~
|
||||
(HasOpcode(LABEL) & MatchParameter(1)) ~~> { code =>
|
||||
List(code.head, code(3).copy(opcode = ORB)) ++ code.drop(4)
|
||||
},
|
||||
(Elidable & HasOpcode(ADDB) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentB(INC)) },
|
||||
(Elidable & HasOpcode(ADDA) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentA(INC)) },
|
||||
(Elidable & HasOpcode(SUBB) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentB(DEC)) },
|
||||
(Elidable & HasOpcode(SUBA) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentA(DEC)) },
|
||||
|
||||
)
|
||||
|
||||
val SimplifiableAddressing = new RuleBasedAssemblyOptimization("Simplifiable addressing",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
(NotFixed & MatchX(0) & HasAddrMode(Indexed(M6809Register.X, indirect = false)) & Not(HasOpcodeIn(LEAX, LEAY, LEAS, LEAU))) ~~> { (code, ctx) =>
|
||||
val x = ctx.get[Constant](0)
|
||||
code.map(l => l.copy(addrMode = Absolute(indirect = false), parameter = (x + l.parameter).quickSimplify))
|
||||
},
|
||||
(NotFixed & MatchY(0) & HasAddrMode(Indexed(M6809Register.Y, indirect = false)) & Not(HasOpcodeIn(LEAX, LEAY, LEAS, LEAU))) ~~> { (code, ctx) =>
|
||||
val y = ctx.get[Constant](0)
|
||||
code.map(l => l.copy(addrMode = Absolute(indirect = false), parameter = (y + l.parameter).quickSimplify))
|
||||
},
|
||||
(NotFixed & MatchX(0) & HasAddrMode(Indexed(M6809Register.X, indirect = false)) & HasOpcodeIn(LEAX, LEAY, LEAS, LEAU)) ~~> { (code, ctx) =>
|
||||
val x = ctx.get[Constant](0)
|
||||
code.map(l => l.copy(opcode = l.opcode match {
|
||||
case LEAX => LDX
|
||||
case LEAY => LDY
|
||||
case LEAU => LDU
|
||||
case LEAS => LDS
|
||||
}, addrMode = Immediate, parameter = (x + l.parameter).quickSimplify))
|
||||
},
|
||||
(NotFixed & MatchY(0) & HasAddrMode(Indexed(M6809Register.X, indirect = false)) & HasOpcodeIn(LEAX, LEAY, LEAS, LEAU)) ~~> { (code, ctx) =>
|
||||
val y = ctx.get[Constant](0)
|
||||
code.map(l => l.copy(opcode = l.opcode match {
|
||||
case LEAX => LDX
|
||||
case LEAY => LDY
|
||||
case LEAU => LDU
|
||||
case LEAS => LDS
|
||||
}, addrMode = Immediate, parameter = (y + l.parameter).quickSimplify))
|
||||
},
|
||||
)
|
||||
|
||||
val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal",
|
||||
needsFlowInfo = FlowInfoRequirement.JustLabels,
|
||||
(Elidable & HasOpcode(LABEL) & HasCallerCount(0) & ParameterIsLocalLabel) ~~> (_ => Nil)
|
||||
)
|
||||
|
||||
|
||||
val All: Seq[AssemblyOptimization[MLine]] = Seq(
|
||||
PointlessLoad,
|
||||
PointlessCompare,
|
||||
PointlessRegisterTransfers,
|
||||
SimplifiableAddressing,
|
||||
SimplifiableArithmetics,
|
||||
SimplifiableComparison,
|
||||
SimplifiableJumps,
|
||||
SimplifiableZeroStore
|
||||
SimplifiableZeroStore,
|
||||
UnusedLabelRemoval
|
||||
)
|
||||
}
|
||||
|
|
|
@ -35,6 +35,11 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
|||
def nzB(i: Long): CpuStatus =
|
||||
this.copy(n = SingleStatus((i & 0x80) != 0), z = SingleStatus((i & 0xff) == 0))
|
||||
|
||||
def nzB(i: Status[Int]): CpuStatus = i match {
|
||||
case SingleStatus(j) => this.copy(n = SingleStatus((j & 0x80) != 0), z = SingleStatus((j & 0xff) == 0))
|
||||
case _ => this.copy(n = AnyStatus, z = AnyStatus)
|
||||
}
|
||||
|
||||
def nzW(i: Long): CpuStatus =
|
||||
this.copy(n = SingleStatus((i & 0x8000) != 0), z = SingleStatus((i & 0xffff) == 0))
|
||||
|
||||
|
@ -44,6 +49,11 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
|||
case _ => this.nz
|
||||
}
|
||||
|
||||
def nzW(i: Status[Int]): CpuStatus = i match {
|
||||
case SingleStatus(j) => this.copy(n = SingleStatus((j & 0x8000) != 0), z = SingleStatus((j & 0xffff) == 0))
|
||||
case _ => this.copy(n = AnyStatus, z = AnyStatus)
|
||||
}
|
||||
|
||||
def ~(that: CpuStatus) = new CpuStatus(
|
||||
a = this.a ~ that.a,
|
||||
b = this.b ~ that.b,
|
||||
|
|
|
@ -52,7 +52,7 @@ object FlowAnalyzer {
|
|||
}
|
||||
val labelMap: () => Option[Map[String, Int]] = () => req match {
|
||||
case FlowInfoRequirement.NoRequirement => None
|
||||
case _ => Some(code.flatMap(_.parameter.extractLabels).groupBy(identity).mapValues(_.size).view.force)
|
||||
case _ => Some(code.filter(m => m.opcode != MOpcode.LABEL).flatMap(_.parameter.extractLabels).groupBy(identity).mapValues(_.size).view.force)
|
||||
}
|
||||
val holder = new FlowHolder(forwardFlow, reverseFlow)
|
||||
code.zipWithIndex.map{ case (line, i) => FlowInfo(holder, i, labelMap) -> line}
|
||||
|
|
|
@ -2,10 +2,11 @@ package millfork.assembly.m6809.opt
|
|||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.OptimizationContext
|
||||
import millfork.assembly.m6809.{Immediate, MLine, MLine0}
|
||||
import millfork.assembly.m6809.{Absolute, Immediate, Inherent, InherentA, InherentB, MLine, MLine0, TwoRegisters}
|
||||
import millfork.assembly.opt.Status.SingleFalse
|
||||
import millfork.assembly.opt.{AnyStatus, FlowCache, SingleStatus, Status}
|
||||
import millfork.env._
|
||||
import millfork.node.M6809NiceFunctionProperty.{DoesntChangeA, DoesntChangeB, DoesntChangeU, DoesntChangeX, DoesntChangeY}
|
||||
import millfork.node.{M6809Register, NiceFunctionProperty}
|
||||
|
||||
import scala.util.control.Breaks._
|
||||
|
@ -62,33 +63,108 @@ object ForwardFlowAnalysis {
|
|||
case _ => None
|
||||
}).fold(currentStatus)(_ ~ _)
|
||||
|
||||
case MLine0(JSR, _, MemoryAddressConstant(th)) =>
|
||||
case MLine0(JSR, am, MemoryAddressConstant(th)) =>
|
||||
var prU = bpInU
|
||||
var prY = bpInY
|
||||
var prA = false
|
||||
var prB = false
|
||||
var prX = false
|
||||
(am, th) match {
|
||||
case (Absolute(false), fun: FunctionInMemory) =>
|
||||
val nfp = optimizationContext.niceFunctionProperties
|
||||
val fn = fun.name
|
||||
if (nfp(DoesntChangeX -> fn)) prX = true
|
||||
if (nfp(DoesntChangeY -> fn)) prY = true
|
||||
if (nfp(DoesntChangeU -> fn)) prU = true
|
||||
if (nfp(DoesntChangeA -> fn)) prA = true
|
||||
if (nfp(DoesntChangeB -> fn)) prB = true
|
||||
case _ =>
|
||||
}
|
||||
currentStatus = initialStatus.copy(
|
||||
memStack = currentStatus.memStack,
|
||||
u = if (bpInU) currentStatus.u else AnyStatus,
|
||||
y = if (bpInY) currentStatus.y else AnyStatus
|
||||
u = if (prU) currentStatus.u else AnyStatus,
|
||||
x = if (prX) currentStatus.x else AnyStatus,
|
||||
y = if (prY) currentStatus.y else AnyStatus,
|
||||
a = if (prA) currentStatus.a else AnyStatus,
|
||||
b = if (prB) currentStatus.b else AnyStatus
|
||||
)
|
||||
|
||||
case MLine0(JSR | BYTE, _, _) =>
|
||||
currentStatus = initialStatus
|
||||
|
||||
case MLine0(NOP, _, _) =>
|
||||
()
|
||||
case MLine0(op, Immediate, constant) if ForwardFlowAnalysisForImmediate.hasDefinition(op) =>
|
||||
ForwardFlowAnalysisForImmediate.get(op)(constant, currentStatus)
|
||||
case MLine0(op, Inherent, _) if ForwardFlowAnalysisForInherent.hasDefinition(op) =>
|
||||
ForwardFlowAnalysisForInherent.get(op)(currentStatus)
|
||||
case MLine0(op, InherentA, _) if ForwardFlowAnalysisForInherentA.hasDefinition(op) =>
|
||||
ForwardFlowAnalysisForInherentA.get(op)(currentStatus)
|
||||
case MLine0(op, InherentB, _) if ForwardFlowAnalysisForInherentB.hasDefinition(op) =>
|
||||
ForwardFlowAnalysisForInherentB.get(op)(currentStatus)
|
||||
|
||||
case MLine0(LDA, Immediate, NumericConstant(n, _)) =>
|
||||
currentStatus = currentStatus.copy(a = SingleStatus(n.toInt & 0xff), v = SingleFalse).nzB(n)
|
||||
case MLine0(LDB, Immediate, NumericConstant(n, _)) =>
|
||||
currentStatus = currentStatus.copy(b = SingleStatus(n.toInt & 0xff), v = SingleFalse).nzB(n)
|
||||
case MLine0(LDD, Immediate, NumericConstant(n, _)) =>
|
||||
currentStatus = currentStatus.copy(
|
||||
a = SingleStatus(n.toInt.>>(8) & 0xff),
|
||||
b = SingleStatus(n.toInt & 0xff),
|
||||
v = SingleFalse
|
||||
).nzW(n)
|
||||
case MLine0(LDX, Immediate, c) =>
|
||||
currentStatus = currentStatus.copy(x = SingleStatus(c), v = SingleFalse).nzW(c)
|
||||
case MLine0(LDY, Immediate, c) =>
|
||||
currentStatus = currentStatus.copy(y = SingleStatus(c), v = SingleFalse).nzW(c)
|
||||
case MLine0(LDA, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||
case MLine0(LDB, _, _) =>
|
||||
currentStatus = currentStatus.copy(b = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||
case MLine0(LDD, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = AnyStatus, b = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||
case MLine0(LDX, _, _) =>
|
||||
currentStatus = currentStatus.copy(x = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||
case MLine0(LDY, _, _) =>
|
||||
currentStatus = currentStatus.copy(y = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||
case MLine0(STA | STB | STD | STX | STU | STY | STS, _, _) =>
|
||||
// don't change
|
||||
case MLine0(TFR, TwoRegisters(source, target), _) =>
|
||||
import M6809Register._
|
||||
(source, target) match {
|
||||
case (A, B) => currentStatus = currentStatus.copy(b = currentStatus.a)
|
||||
case (B, A) => currentStatus = currentStatus.copy(a = currentStatus.b)
|
||||
case (CC, A) => currentStatus = currentStatus.copy(a = AnyStatus)
|
||||
case (CC, B) => currentStatus = currentStatus.copy(b = AnyStatus)
|
||||
case (A, CC) | (B, CC) => currentStatus = currentStatus.copy(c = AnyStatus, z = AnyStatus, n = AnyStatus, v = AnyStatus)
|
||||
case (S, D) => currentStatus = currentStatus.copy(a = AnyStatus, b = AnyStatus)
|
||||
case (S, X) => currentStatus = currentStatus.copy(x = AnyStatus)
|
||||
case (S, Y) => currentStatus = currentStatus.copy(y = AnyStatus)
|
||||
case (S, U) => currentStatus = currentStatus.copy(u = AnyStatus)
|
||||
case (D, X) => currentStatus = currentStatus.copy(x = currentStatus.d.map(n => NumericConstant(n, 2)))
|
||||
case (D, Y) => currentStatus = currentStatus.copy(y = currentStatus.d.map(n => NumericConstant(n, 2)))
|
||||
case (D, U) => currentStatus = currentStatus.copy(u = currentStatus.d.map(n => NumericConstant(n, 2)))
|
||||
case (X, Y) => currentStatus = currentStatus.copy(y = currentStatus.x)
|
||||
case (X, U) => currentStatus = currentStatus.copy(u = currentStatus.x)
|
||||
case (Y, X) => currentStatus = currentStatus.copy(x = currentStatus.y)
|
||||
case (Y, U) => currentStatus = currentStatus.copy(u = currentStatus.y)
|
||||
case (U, X) => currentStatus = currentStatus.copy(x = currentStatus.u)
|
||||
case (U, Y) => currentStatus = currentStatus.copy(y = currentStatus.u)
|
||||
case (X, D) =>
|
||||
val (h, l) = currentStatus.x.toHiLo
|
||||
currentStatus = currentStatus.copy(a = h, b = l)
|
||||
case (Y, D) =>
|
||||
val (h, l) = currentStatus.y.toHiLo
|
||||
currentStatus = currentStatus.copy(a = h, b = l)
|
||||
case (U, D) =>
|
||||
val (h, l) = currentStatus.u.toHiLo
|
||||
currentStatus = currentStatus.copy(a = h, b = l)
|
||||
case _ => currentStatus = initialStatus
|
||||
}
|
||||
case MLine0(EXG, TwoRegisters(source, target), _) =>
|
||||
import M6809Register._
|
||||
(source, target) match {
|
||||
case (A, B) | (B, A) => currentStatus = currentStatus.copy(a = currentStatus.b, b = currentStatus.a)
|
||||
case (A, CC) | (CC, A) => currentStatus = currentStatus.copy(a = AnyStatus, c = AnyStatus, v = AnyStatus, n = AnyStatus, z = AnyStatus)
|
||||
case (B, CC) | (CC, B) => currentStatus = currentStatus.copy(b = AnyStatus, c = AnyStatus, v = AnyStatus, n = AnyStatus, z = AnyStatus)
|
||||
case (X, Y) | (Y, X) => currentStatus = currentStatus.copy(y = currentStatus.x, x = currentStatus.y)
|
||||
case (U, Y) | (Y, U) => currentStatus = currentStatus.copy(y = currentStatus.u, u = currentStatus.y)
|
||||
case (X, U) | (U, X) => currentStatus = currentStatus.copy(u = currentStatus.x, x = currentStatus.u)
|
||||
case (X, D) | (D, X) =>
|
||||
val (h, l) = currentStatus.x.toHiLo
|
||||
currentStatus = currentStatus.copy(a = h, b = l, x = currentStatus.d.map(n => NumericConstant(n, 2)))
|
||||
case (Y, D) | (D, Y) =>
|
||||
val (h, l) = currentStatus.y.toHiLo
|
||||
currentStatus = currentStatus.copy(a = h, b = l, y = currentStatus.d.map(n => NumericConstant(n, 2)))
|
||||
case (U, D) | (D, U) =>
|
||||
val (h, l) = currentStatus.u.toHiLo
|
||||
currentStatus = currentStatus.copy(a = h, b = l, u = currentStatus.d.map(n => NumericConstant(n, 2)))
|
||||
case _ => currentStatus = initialStatus
|
||||
}
|
||||
|
||||
case MLine0(opcode, addrMode, _) =>
|
||||
// TODO
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package millfork.assembly.m6809.opt
|
||||
|
||||
import millfork.assembly.m6809.MOpcode
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.assembly.opt.{AnyStatus, SingleStatus, Status}
|
||||
import millfork.assembly.opt.Status.SingleFalse
|
||||
import millfork.env.Constant
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object ForwardFlowAnalysisForImmediate {
|
||||
private val map: Map[MOpcode.Value, (Constant, CpuStatus) => CpuStatus] = Map(
|
||||
LDX -> {(c, currentStatus) =>
|
||||
currentStatus.copy(x = SingleStatus(c), v = SingleFalse).nzW(c)
|
||||
},
|
||||
LDY -> {(c, currentStatus) =>
|
||||
currentStatus.copy(y = SingleStatus(c), v = SingleFalse).nzW(c)
|
||||
},
|
||||
LDU -> {(c, currentStatus) =>
|
||||
currentStatus.copy(u = SingleStatus(c), v = SingleFalse).nzW(c)
|
||||
},
|
||||
LDA -> {(c, currentStatus) =>
|
||||
val value = Status.fromByte(c)
|
||||
currentStatus.copy(a = value, v = SingleFalse).nzB(value)
|
||||
},
|
||||
LDB -> {(c, currentStatus) =>
|
||||
val value = Status.fromByte(c)
|
||||
currentStatus.copy(a = value, v = SingleFalse).nzB(value)
|
||||
},
|
||||
LDD -> {(c, currentStatus) =>
|
||||
val value = Status.fromByte(c)
|
||||
currentStatus.copy(a = value.hi, b = value.lo, v = SingleFalse).nzW(value)
|
||||
},
|
||||
ORA -> { (c, currentStatus) =>
|
||||
val newValue = (currentStatus.a <*> Status.fromByte(c)) { (x, y) => (x | y) & 0xff }
|
||||
currentStatus.copy(a = newValue, v = SingleFalse).nzB(newValue)
|
||||
},
|
||||
ORB -> { (c, currentStatus) =>
|
||||
val newValue = (currentStatus.b <*> Status.fromByte(c)) { (x, y) => (x | y) & 0xff }
|
||||
currentStatus.copy(b = newValue, v = SingleFalse).nzB(newValue)
|
||||
},
|
||||
EORA -> { (c, currentStatus) =>
|
||||
val newValue = (currentStatus.a <*> Status.fromByte(c)) { (x, y) => (x ^ y) & 0xff }
|
||||
currentStatus.copy(a = newValue, v = SingleFalse).nzB(newValue)
|
||||
},
|
||||
EORB -> { (c, currentStatus) =>
|
||||
val newValue = (currentStatus.b <*> Status.fromByte(c)) { (x, y) => (x ^ y) & 0xff }
|
||||
currentStatus.copy(b = newValue, v = SingleFalse).nzB(newValue)
|
||||
},
|
||||
ANDA -> { (c, currentStatus) =>
|
||||
val newValue = (currentStatus.a <*> Status.fromByte(c)) { (x, y) => (x & y) & 0xff }
|
||||
currentStatus.copy(a = newValue, v = SingleFalse).nzB(newValue)
|
||||
},
|
||||
ANDB -> { (c, currentStatus) =>
|
||||
val newValue = (currentStatus.b <*> Status.fromByte(c)) { (x, y) => (x & y) & 0xff }
|
||||
currentStatus.copy(b = newValue, v = SingleFalse).nzB(newValue)
|
||||
},
|
||||
ADDA -> { (c, currentStatus) =>
|
||||
val (newValue, newCarry) = currentStatus.a.adc(Status.fromByte(c), SingleFalse)
|
||||
currentStatus.copy(a = newValue, v = AnyStatus, c = newCarry).nzB(newValue)
|
||||
},
|
||||
ADDB -> { (c, currentStatus) =>
|
||||
val (newValue, newCarry) = currentStatus.b.adc(Status.fromByte(c), SingleFalse)
|
||||
currentStatus.copy(b = newValue, v = AnyStatus, c = newCarry).nzB(newValue)
|
||||
},
|
||||
ADCA -> { (c, currentStatus) =>
|
||||
val (newValue, newCarry) = currentStatus.a.adc(Status.fromByte(c), currentStatus.c)
|
||||
currentStatus.copy(a = newValue, v = AnyStatus, c = newCarry).nzB(newValue)
|
||||
},
|
||||
ADCB -> { (c, currentStatus) =>
|
||||
val (newValue, newCarry) = currentStatus.b.adc(Status.fromByte(c), currentStatus.c)
|
||||
currentStatus.copy(b = newValue, v = AnyStatus, c = newCarry).nzB(newValue)
|
||||
},
|
||||
)
|
||||
|
||||
def hasDefinition(opcode: MOpcode.Value): Boolean = map.contains(opcode)
|
||||
|
||||
def get(opcode: MOpcode.Value): (Constant, CpuStatus) => CpuStatus = map(opcode)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package millfork.assembly.m6809.opt
|
||||
|
||||
import millfork.assembly.m6809.MOpcode
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.assembly.opt.Status.SingleFalse
|
||||
import millfork.assembly.opt.{AnyStatus, SingleStatus, Status}
|
||||
import millfork.env.Constant
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object ForwardFlowAnalysisForInherent {
|
||||
private val map: Map[MOpcode.Value, CpuStatus => CpuStatus] = Map(
|
||||
NOP -> identity,
|
||||
MUL -> { currentStatus =>
|
||||
val newD = (currentStatus.a <*> currentStatus.b) { (a, b) => ((a & 0xff) * (b & 0xff)) & 0xff }
|
||||
currentStatus.copy(c = AnyStatus, z = AnyStatus, a = newD.hi, b = newD.hi)
|
||||
},
|
||||
SEX -> { currentStatus =>
|
||||
val newA = currentStatus.b.map{ n => if (n.&(0x80) == 0) 0 else 0xff }
|
||||
currentStatus.copy(v = Status.SingleFalse, n = AnyStatus, z = AnyStatus, a = newA)
|
||||
},
|
||||
)
|
||||
|
||||
def hasDefinition(opcode: MOpcode.Value): Boolean = map.contains(opcode)
|
||||
|
||||
def get(opcode: MOpcode.Value): CpuStatus => CpuStatus = map(opcode)
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package millfork.assembly.m6809.opt
|
||||
|
||||
import millfork.assembly.m6809.MOpcode
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.assembly.opt.{AnyStatus, SingleStatus, Status}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object ForwardFlowAnalysisForInherentA {
|
||||
private val map: Map[MOpcode.Value, CpuStatus => CpuStatus] = Map(
|
||||
ASL -> { currentStatus =>
|
||||
val newValue = currentStatus.a.map(n => n.<<(1).&(0xff))
|
||||
currentStatus.copy(a = newValue, c = currentStatus.a.bit7, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
LSR -> { currentStatus =>
|
||||
val newValue = currentStatus.a.map(n => n.>>(1).&(0x7f))
|
||||
currentStatus.copy(a = newValue, c = currentStatus.a.bit0, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
CLR -> { currentStatus =>
|
||||
currentStatus.copy(a = Status.SingleZero, c = Status.SingleFalse, v = Status.SingleFalse, n = Status.SingleFalse, z = Status.SingleFalse)
|
||||
},
|
||||
COM -> { currentStatus =>
|
||||
val newValue = currentStatus.a.map(n => n.^(0xff).&(0xff))
|
||||
currentStatus.copy(a = newValue, c = Status.SingleTrue, v = Status.SingleFalse).nzB(newValue)
|
||||
},
|
||||
DEC -> { currentStatus =>
|
||||
val newValue = currentStatus.a.map(n => n.+(1).&(0xff))
|
||||
currentStatus.copy(a = newValue, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
INC -> { currentStatus =>
|
||||
val newValue = currentStatus.a.map(n => n.-(1).&(0xff))
|
||||
currentStatus.copy(a = newValue, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
NEG -> { currentStatus =>
|
||||
val newValue = currentStatus.a.map(n => (-n).&(0xff))
|
||||
currentStatus.copy(a = newValue, c = AnyStatus, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
TST -> { currentStatus =>
|
||||
currentStatus.copy(v = Status.SingleFalse).nzB(currentStatus.a)
|
||||
},
|
||||
)
|
||||
|
||||
def hasDefinition(opcode: MOpcode.Value): Boolean = map.contains(opcode)
|
||||
|
||||
def get(opcode: MOpcode.Value): CpuStatus => CpuStatus = map(opcode)
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package millfork.assembly.m6809.opt
|
||||
|
||||
import millfork.assembly.m6809.MOpcode
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.assembly.opt.{AnyStatus, Status}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object ForwardFlowAnalysisForInherentB {
|
||||
private val map: Map[MOpcode.Value, CpuStatus => CpuStatus] = Map(
|
||||
ASL -> { currentStatus =>
|
||||
val newValue = currentStatus.b.map(n => n.<<(1).&(0xff))
|
||||
currentStatus.copy(b = newValue, c = currentStatus.b.bit7, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
LSR -> { currentStatus =>
|
||||
val newValue = currentStatus.b.map(n => n.>>(1).&(0x7f))
|
||||
currentStatus.copy(b = newValue, c = currentStatus.b.bit0, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
CLR -> { currentStatus =>
|
||||
currentStatus.copy(b = Status.SingleZero, c = Status.SingleFalse, v = Status.SingleFalse, n = Status.SingleFalse, z = Status.SingleFalse)
|
||||
},
|
||||
COM -> { currentStatus =>
|
||||
val newValue = currentStatus.b.map(n => n.^(0xff).&(0xff))
|
||||
currentStatus.copy(b = newValue, c = Status.SingleTrue, v = Status.SingleFalse).nzB(newValue)
|
||||
},
|
||||
DEC -> { currentStatus =>
|
||||
val newValue = currentStatus.b.map(n => n.+(1).&(0xff))
|
||||
currentStatus.copy(b = newValue, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
INC -> { currentStatus =>
|
||||
val newValue = currentStatus.b.map(n => n.-(1).&(0xff))
|
||||
currentStatus.copy(b = newValue, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
NEG -> { currentStatus =>
|
||||
val newValue = currentStatus.b.map(n => (-n).&(0xff))
|
||||
currentStatus.copy(b = newValue, c = AnyStatus, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
TST -> { currentStatus =>
|
||||
currentStatus.copy(v = Status.SingleFalse).nzB(currentStatus.b)
|
||||
},
|
||||
)
|
||||
|
||||
def hasDefinition(opcode: MOpcode.Value): Boolean = map.contains(opcode)
|
||||
|
||||
def get(opcode: MOpcode.Value): CpuStatus => CpuStatus = map(opcode)
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package millfork.assembly.m6809.opt
|
||||
|
||||
import jdk.jfr.BooleanFlag
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly._
|
||||
import millfork.assembly.m6809.{Absolute, MLine, MLine0, MOpcode, MState}
|
||||
import millfork.assembly.opt.FlowCache
|
||||
|
@ -81,6 +83,7 @@ object ReverseFlowAnalyzer {
|
|||
val readsB: Set[String] = Set("call")
|
||||
val readsX: Set[String] = Set("call")
|
||||
val readsY: Set[String] = Set("")
|
||||
val preservesX: Set[String] = Set("__divmod_u8u8u8u8")
|
||||
|
||||
val cache = new FlowCache[MLine, CpuImportance]("m6809 reverse")
|
||||
private val importanceBeforeJsr: CpuImportance = CpuImportance(
|
||||
|
@ -96,8 +99,8 @@ object ReverseFlowAnalyzer {
|
|||
hf = Unimportant)
|
||||
private val finalImportance: CpuImportance = CpuImportance(
|
||||
a = Important, b = Important,
|
||||
x = Important, y = Important, u = Important,
|
||||
cf = Important, vf = Important, hf = Important, zf = Important, nf = Important)
|
||||
x = Important, y = Important, u = Important, // TODO: correctly check which registers are important given the function and the compilation options
|
||||
cf = Unimportant, vf = Unimportant, hf = Unimportant, zf = Unimportant, nf = Unimportant)
|
||||
|
||||
//noinspection RedundantNewCaseClass
|
||||
def analyze(f: NormalFunction, code: List[MLine], optimizationContext: OptimizationContext): List[CpuImportance] = {
|
||||
|
@ -108,9 +111,27 @@ object ReverseFlowAnalyzer {
|
|||
|
||||
var changed = true
|
||||
changed = true
|
||||
val actualFinalImportance = {
|
||||
var tmp = f.returnType match {
|
||||
case FlagBooleanType(_, _, _) => finalImportance.copy(cf = Important, zf = Important, nf = Important, vf = Important)
|
||||
case t if t.size == 1 => finalImportance.copy(a = Unimportant)
|
||||
case t if t.size == 0 => finalImportance.copy(a = Unimportant, b = Unimportant)
|
||||
case _ => finalImportance
|
||||
}
|
||||
if (!f.inAssembly) {
|
||||
tmp = tmp.copy(x = Unimportant)
|
||||
if (!optimizationContext.options.flag(CompilationFlag.UseUForStack)) {
|
||||
tmp = tmp.copy(u = Unimportant)
|
||||
}
|
||||
if (!optimizationContext.options.flag(CompilationFlag.UseYForStack)) {
|
||||
tmp = tmp.copy(y = Unimportant)
|
||||
}
|
||||
}
|
||||
tmp
|
||||
}
|
||||
while (changed) {
|
||||
changed = false
|
||||
var currentImportance = finalImportance
|
||||
var currentImportance = actualFinalImportance
|
||||
for (i <- codeArray.indices.reverse) {
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.node.M6809NiceFunctionProperty._
|
||||
|
@ -126,11 +147,22 @@ object ReverseFlowAnalyzer {
|
|||
case MLine0(LABEL, _, MemoryAddressConstant(Label(L))) => true
|
||||
case _ => false
|
||||
}
|
||||
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex) ~ currentImportance
|
||||
currentImportance = if (labelIndex < 0) actualFinalImportance else importanceArray(labelIndex) ~ currentImportance
|
||||
case MLine0(JMP | BRA, _, MemoryAddressConstant(Label(l))) =>
|
||||
val L = l
|
||||
val labelIndex = codeArray.indexWhere {
|
||||
case MLine0(LABEL, _, MemoryAddressConstant(Label(L))) => true
|
||||
case _ => false
|
||||
}
|
||||
currentImportance = if (labelIndex < 0) actualFinalImportance else importanceArray(labelIndex)
|
||||
case _ =>
|
||||
}
|
||||
currentLine match {
|
||||
|
||||
case MLine0(RTS, _, _) =>
|
||||
currentImportance = actualFinalImportance
|
||||
case MLine0(LABEL, _, _) =>
|
||||
// do nothing
|
||||
case MLine0(JSR | JMP, Absolute(false), MemoryAddressConstant(fun: FunctionInMemory)) =>
|
||||
// this case has to be handled first, because the generic JSR importance handler is too conservative
|
||||
var result = importanceBeforeJsr
|
||||
|
@ -161,6 +193,7 @@ object ReverseFlowAnalyzer {
|
|||
if (readsB(fun.name)) result = result.copy(b = Important)
|
||||
if (readsX(fun.name)) result = result.copy(x = Important)
|
||||
if (readsY(fun.name)) result = result.copy(y = Important)
|
||||
if (preservesX(fun.name)) result = result.copy(x = currentImportance.x)
|
||||
currentImportance = result.copy(
|
||||
a = if (niceFunctionProperties(DoesntChangeA -> fun.name)) currentImportance.a ~ result.a else result.a,
|
||||
b = if (niceFunctionProperties(DoesntChangeB -> fun.name)) currentImportance.b ~ result.b else result.b,
|
||||
|
@ -173,7 +206,7 @@ object ReverseFlowAnalyzer {
|
|||
case MLine0(opcode, addrMode, _) =>
|
||||
if (MOpcode.ChangesC(opcode)) currentImportance = currentImportance.copy(cf = Unimportant)
|
||||
if (MOpcode.ChangesN(opcode)) currentImportance = currentImportance.copy(nf = Unimportant)
|
||||
if (MOpcode.ChangesZ(opcode)) currentImportance = currentImportance.copy(zf = Unimportant)
|
||||
if (MOpcode.ChangesH(opcode)) currentImportance = currentImportance.copy(hf = Unimportant)
|
||||
if (MOpcode.ChangesZ(opcode)) currentImportance = currentImportance.copy(zf = Unimportant)
|
||||
if (MOpcode.ReadsC(opcode)) currentImportance = currentImportance.copy(cf = Important)
|
||||
if (MOpcode.ReadsH(opcode)) currentImportance = currentImportance.copy(hf = Important)
|
||||
|
|
|
@ -30,42 +30,52 @@ object FlowInfoRequirement extends Enumeration {
|
|||
}
|
||||
|
||||
def assertLabels(x: FlowInfoRequirement.Value): Unit = x match {
|
||||
case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required")
|
||||
case NoRequirement => FatalErrorReporting.reportFlyingPig("Label info required")
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
trait AssemblyRuleSet{
|
||||
def flatten: Seq[AssemblyRule]
|
||||
|
||||
def minimumRequiredLines: Int
|
||||
}
|
||||
|
||||
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRuleSet*) extends AssemblyOptimization[MLine] {
|
||||
|
||||
private val actualRules = rules.flatMap(_.flatten)
|
||||
actualRules.foreach(_.pattern.validate(needsFlowInfo))
|
||||
private val actualRulesWithIndex = actualRules.zipWithIndex
|
||||
|
||||
override val minimumRequiredLines: Int = rules.map(_.minimumRequiredLines).min
|
||||
|
||||
override def toString: String = name
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[MLine], optimizationContext: OptimizationContext): List[MLine] = {
|
||||
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
|
||||
val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext)
|
||||
if (changed) optimized else code
|
||||
optimizeImpl(f, code, taggedCode, optimizationContext)
|
||||
}
|
||||
|
||||
def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, MLine)], optimizationContext: OptimizationContext): (Boolean, List[MLine]) = {
|
||||
final def optimizeImpl(f: NormalFunction, code: List[MLine], taggedCode: List[(FlowInfo, MLine)], optimizationContext: OptimizationContext): List[MLine] = {
|
||||
val log = optimizationContext.log
|
||||
code match {
|
||||
case Nil => false -> Nil
|
||||
taggedCode match {
|
||||
case Nil => code
|
||||
case head :: tail =>
|
||||
for ((rule, index) <- actualRules.zipWithIndex) {
|
||||
val codeLength = code.length
|
||||
for {
|
||||
(rule, index) <- actualRulesWithIndex
|
||||
if codeLength >= rule.minimumRequiredLines
|
||||
} {
|
||||
val ctx = new AssemblyMatchingContext(
|
||||
optimizationContext.options,
|
||||
optimizationContext.labelMap,
|
||||
optimizationContext.niceFunctionProperties,
|
||||
head._1.labelUseCount(_)
|
||||
)
|
||||
rule.pattern.matchTo(ctx, code) match {
|
||||
rule.pattern.matchTo(ctx, taggedCode) match {
|
||||
case Some(rest: List[(FlowInfo, MLine)]) =>
|
||||
val matchedChunkToOptimize: List[MLine] = code.take(code.length - rest.length).map(_._2)
|
||||
val optimizedChunkLengthBefore = taggedCode.length - rest.length
|
||||
val (matchedChunkToOptimize, restOfCode) = code.splitAt(optimizedChunkLengthBefore)
|
||||
val optimizedChunk: List[MLine] = rule.result(matchedChunkToOptimize, ctx)
|
||||
val optimizedChunkWithSource =
|
||||
if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk
|
||||
|
@ -74,34 +84,40 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
|
|||
else if (optimizedChunk.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
|
||||
else if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
|
||||
else optimizedChunk
|
||||
log.debug(s"Applied $name ($index)")
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
val before = code.head._1.statusBefore
|
||||
val after = code(matchedChunkToOptimize.length - 1)._1.importanceAfter
|
||||
log.trace(s"Before: $before")
|
||||
log.trace(s"After: $after")
|
||||
if (log.debugEnabled) {
|
||||
log.debug(s"Applied $name ($index)")
|
||||
}
|
||||
if (log.traceEnabled) {
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
val before = head._1.statusBefore
|
||||
val after = taggedCode(matchedChunkToOptimize.length - 1)._1.importanceAfter
|
||||
log.trace(s"Before: $before")
|
||||
log.trace(s"After: $after")
|
||||
}
|
||||
matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString))
|
||||
log.trace(" ↓")
|
||||
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
|
||||
}
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2)
|
||||
return optimizedChunkWithSource ++ optimizeImpl(f, restOfCode, rest, optimizationContext)
|
||||
} else {
|
||||
return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext)
|
||||
return optimize(f, optimizedChunkWithSource ++ restOfCode, optimizationContext)
|
||||
}
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext)
|
||||
(changedTail, head._2 :: optimizedTail)
|
||||
val optimizedTail = optimizeImpl(f, code.tail, tail, optimizationContext)
|
||||
if (optimizedTail eq code.tail) {
|
||||
code
|
||||
} else {
|
||||
code.head :: optimizedTail
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
||||
val labelMap: Map[String, (Int, Int)],
|
||||
val labelMap: Map[String, (String, Int)],
|
||||
val niceFunctionProperties: Set[(NiceFunctionProperty, String)],
|
||||
val labelUseCount: String => Int) {
|
||||
@inline
|
||||
|
@ -211,10 +227,14 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
|||
|
||||
case class AssemblyRule(pattern: AssemblyPattern, result: (List[MLine], AssemblyMatchingContext) => List[MLine]) extends AssemblyRuleSet {
|
||||
override def flatten: Seq[AssemblyRule] = List(this)
|
||||
|
||||
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
|
||||
}
|
||||
|
||||
case class MultipleAssemblyRules(list: Seq[AssemblyRuleSet]) extends AssemblyRuleSet {
|
||||
override def flatten: Seq[AssemblyRule] = list.flatMap(_.flatten)
|
||||
|
||||
override val minimumRequiredLines: Int = flatten.map(_.minimumRequiredLines).sum
|
||||
}
|
||||
|
||||
trait AssemblyPattern {
|
||||
|
@ -235,6 +255,8 @@ trait AssemblyPattern {
|
|||
|
||||
def captureLength(i: Int) = CaptureLength(i, this)
|
||||
|
||||
def minimumRequiredLines: Int
|
||||
|
||||
}
|
||||
object HelperCheckers {
|
||||
private def badAddrModes(am: MAddrMode): Boolean = am match {
|
||||
|
@ -337,6 +359,8 @@ case class Capture(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
|
|||
}
|
||||
|
||||
override def toString: String = s"(?<$i>$pattern)"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
|
||||
|
@ -349,6 +373,8 @@ case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPatte
|
|||
}
|
||||
|
||||
override def toString: String = s"(?<$i>$pattern)"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
|
||||
|
@ -358,6 +384,8 @@ case class Where(predicate: AssemblyMatchingContext => Boolean) extends Assembly
|
|||
}
|
||||
|
||||
override def toString: String = "Where(...)"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends AssemblyPattern {
|
||||
|
@ -380,6 +408,8 @@ case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends Assembl
|
|||
case (_: Both, _) => s"($l) · $r"
|
||||
case _ => s"$l · $r"
|
||||
}
|
||||
|
||||
override val minimumRequiredLines: Int = l.minimumRequiredLines + r.minimumRequiredLines
|
||||
}
|
||||
|
||||
case class Many(rule: MLinePattern) extends AssemblyPattern {
|
||||
|
@ -405,6 +435,8 @@ case class Many(rule: MLinePattern) extends AssemblyPattern {
|
|||
}
|
||||
|
||||
override def toString: String = s"[$rule]*"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class ManyWhereAtLeastOne(rule: MLinePattern, atLeastOneIsThis: MLinePattern) extends AssemblyPattern {
|
||||
|
@ -439,6 +471,8 @@ case class ManyWhereAtLeastOne(rule: MLinePattern, atLeastOneIsThis: MLinePatter
|
|||
}
|
||||
|
||||
override def toString: String = s"[∃$atLeastOneIsThis:$rule]*"
|
||||
|
||||
override def minimumRequiredLines: Int = 1
|
||||
}
|
||||
|
||||
case class Opt(rule: MLinePattern) extends AssemblyPattern {
|
||||
|
@ -461,6 +495,8 @@ case class Opt(rule: MLinePattern) extends AssemblyPattern {
|
|||
}
|
||||
|
||||
override def toString: String = s"[$rule]?"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
trait MLinePattern extends AssemblyPattern {
|
||||
|
@ -488,6 +524,8 @@ trait MLinePattern extends AssemblyPattern {
|
|||
else Both(x, this)
|
||||
|
||||
def hitRate: Double
|
||||
|
||||
override def minimumRequiredLines: Int = 1
|
||||
}
|
||||
|
||||
//noinspection ScalaUnnecessaryParentheses
|
||||
|
@ -509,6 +547,8 @@ case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) ext
|
|||
val s2s = ctx.get[List[MLine]](ix2)
|
||||
if (s1s.forall(s1 => s2s.forall(s2 => HelperCheckers.memoryAccessDoesntOverlap(s1, s2)))) Some(code) else None
|
||||
}
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class MatchA(i: Int) extends MLinePattern {
|
||||
|
@ -754,6 +794,8 @@ case object DebugMatching extends AssemblyPattern {
|
|||
code.foreach(println)
|
||||
Some(code)
|
||||
}
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case object Linear extends MLinePattern {
|
||||
|
@ -1274,6 +1316,8 @@ case class MatchElidableCopyOf(i: Int, firstLinePattern: MLinePattern, lastLineP
|
|||
}
|
||||
Some(after)
|
||||
}
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case object IsNotALabelUsedManyTimes extends MLinePattern {
|
||||
|
@ -1290,3 +1334,14 @@ case object IsNotALabelUsedManyTimes extends MLinePattern {
|
|||
|
||||
override def hitRate: Double = 0.92 // ?
|
||||
}
|
||||
|
||||
|
||||
object ParameterIsLocalLabel extends MLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
|
||||
line match {
|
||||
case MLine0(MOpcode.LABEL, _, MemoryAddressConstant(Label(l))) => l.startsWith(".")
|
||||
case _ => false
|
||||
}
|
||||
|
||||
override def hitRate: Double = 0.056
|
||||
}
|
||||
|
|
|
@ -693,6 +693,8 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
|
|||
parameter match {
|
||||
case StructureConstant(_, List(a,b)) => s" $opcode $a,$b"
|
||||
}
|
||||
} else if (addrMode == LongRelative && opcode != BSR) { // BSR on Hudson is always 8-bit short, and on 65CE02 it's always 16-bit
|
||||
s" L$opcode ${AddrMode.addrModeToString(addrMode, parameter.toString)}"
|
||||
} else if (addrMode == ImmediateWithAbsolute || addrMode == ImmediateWithZeroPage) {
|
||||
parameter match {
|
||||
case StructureConstant(_, List(a,b)) => s" $opcode #$a,$b"
|
||||
|
|
|
@ -180,20 +180,31 @@ object Opcode extends Enumeration {
|
|||
case "AXA" => AHX
|
||||
case "AXS" => SBX // could mean SAX
|
||||
case "BCC" => BCC
|
||||
case "LBCC" => BCC
|
||||
case "BCS" => BCS
|
||||
case "LBCS" => BCS
|
||||
case "BEQ" => BEQ
|
||||
case "LBEQ" => BEQ
|
||||
case "BIT" => BIT
|
||||
case "BMI" => BMI
|
||||
case "LBMI" => BMI
|
||||
case "BNE" => BNE
|
||||
case "LBNE" => BNE
|
||||
case "BPL" => BPL
|
||||
case "LBPL" => BPL
|
||||
case "BRA" => BRA
|
||||
case "LBRA" => BRA
|
||||
case "BRK" => BRK
|
||||
case "BRL" => BRL
|
||||
case "BSR" => BSR
|
||||
case "LBSR" => BSR
|
||||
case "BVC" => BVC
|
||||
case "LBVC" => BVC
|
||||
case "BVS" => BVS
|
||||
case "LBVS" => BVS
|
||||
case "CLC" => CLC
|
||||
case "CLD" => CLD
|
||||
case "CLE" => CLE
|
||||
case "CLI" => CLI
|
||||
case "CLV" => CLV
|
||||
case "CLX" => CLX
|
||||
|
@ -232,6 +243,7 @@ object Opcode extends Enumeration {
|
|||
case "LSE" => SRE
|
||||
case "LSR" => LSR
|
||||
case "LXA" => LXA
|
||||
case "MAP" => MAP
|
||||
case "NEG" => NEG
|
||||
case "NOP" => NOP
|
||||
case "OAL" => LXA
|
||||
|
@ -247,17 +259,19 @@ object Opcode extends Enumeration {
|
|||
case "PHW" => PHW
|
||||
case "PHX" => PHX
|
||||
case "PHY" => PHY
|
||||
case "PHZ" => PHZ
|
||||
case "PLA" => PLA
|
||||
case "PLB" => PLB
|
||||
case "PLD" => PLD
|
||||
case "PLP" => PLP
|
||||
case "PLX" => PLX
|
||||
case "PLY" => PLY
|
||||
case "PLZ" => PLZ
|
||||
case "REP" => REP
|
||||
case "RLA" => RLA
|
||||
case "ROL" => ROL
|
||||
case "ROR" => ROR
|
||||
case "ROW" => ROR_W // TODO: is this correct?
|
||||
case "ROW" => ROL_W
|
||||
case "RRA" => RRA
|
||||
case "RTI" => RTI
|
||||
case "RTL" => RTL
|
||||
|
@ -268,6 +282,7 @@ object Opcode extends Enumeration {
|
|||
case "SBX" => SBX
|
||||
case "SEC" => SEC
|
||||
case "SED" => SED
|
||||
case "SEE" => SEE
|
||||
case "SEI" => SEI
|
||||
case "SEP" => SEP
|
||||
case "SET" => SET
|
||||
|
|
|
@ -995,16 +995,10 @@ object AlwaysGoodOptimizations {
|
|||
|
||||
val ConstantFlowAnalysis = new RuleBasedAssemblyOptimization("Constant flow analysis",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
(MatchX(0) & HasAddrMode(AbsoluteX) & SupportsAbsolute & Elidable & HasParameterWhere({
|
||||
case MemoryAddressConstant(th) => th.name == "identity$"
|
||||
case _ => false
|
||||
})) ~~> { (code, ctx) =>
|
||||
(MatchX(0) & HasAddrMode(AbsoluteX) & SupportsAbsolute & Not(HasOpcode(BIT)) & Elidable & HasIdentityPageParameter) ~~> { (code, ctx) =>
|
||||
code.map(l => l.copy(addrMode = Immediate, parameter = NumericConstant(ctx.get[Int](0), 1)))
|
||||
},
|
||||
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable & HasParameterWhere({
|
||||
case MemoryAddressConstant(th) => th.name == "identity$"
|
||||
case _ => false
|
||||
})) ~~> { (code, ctx) =>
|
||||
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable & HasIdentityPageParameter) ~~> { (code, ctx) =>
|
||||
code.map(l => l.copy(addrMode = Immediate, parameter = NumericConstant(ctx.get[Int](0), 1)))
|
||||
},
|
||||
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable) ~~> { (code, ctx) =>
|
||||
|
@ -1605,6 +1599,15 @@ object AlwaysGoodOptimizations {
|
|||
Where(ctx => ctx.get[Int](1).&(1) == 0)~~> { (lines, ctx) =>
|
||||
lines.head.copy(opcode = ASL) :: lines.tail
|
||||
},
|
||||
(Elidable & HasOpcode(LDA)) ~
|
||||
(Elidable & HasOpcode(AND) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(BNE) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(LDA)) ~
|
||||
(Elidable & HasOpcode(AND) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(BEQ)) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(1)) ~~> { (code, ctx) =>
|
||||
List(code.head, code(3).copy(opcode = ORA)) ++ code.drop(4)
|
||||
},
|
||||
)
|
||||
|
||||
val SimplifiableIndexChanging = new RuleBasedAssemblyOptimization("Simplifiable index changing",
|
||||
|
@ -2065,7 +2068,7 @@ object AlwaysGoodOptimizations {
|
|||
|
||||
val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal",
|
||||
needsFlowInfo = FlowInfoRequirement.JustLabels,
|
||||
(Elidable & HasOpcode(LABEL) & HasCallerCount(0)) ~~> (_ => Nil)
|
||||
(Elidable & HasOpcode(LABEL) & HasCallerCount(0) & ParameterIsLocalLabel) ~~> (_ => Nil)
|
||||
)
|
||||
|
||||
val OperationsAroundShifting = new RuleBasedAssemblyOptimization("Operations around shifting",
|
||||
|
@ -2982,7 +2985,9 @@ object AlwaysGoodOptimizations {
|
|||
|
||||
(Elidable & HasOpcode(EOR) & MatchImmediate(0)) ~
|
||||
(Elidable & HasOpcode(CMP) & MatchImmediate(1) & DoesntMatterWhatItDoesWith(State.A, State.N, State.C)) ~~> { (code, ctx) =>
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, ctx.get[Constant](0), ctx.get[Constant](1))))
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
|
||||
MultipleAssemblyRules(for {
|
||||
|
@ -3106,6 +3111,42 @@ object AlwaysGoodOptimizations {
|
|||
code(1).copy(opcode = branch, parameter = code(3).parameter),
|
||||
code(4))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(INC) & HasAddrMode(Implied)) ~
|
||||
(Elidable & HasOpcode(CMP) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.A, State.C, State.V)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(INX) & HasAddrMode(Implied)) ~
|
||||
(Elidable & HasOpcode(CPX) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.X, State.C, State.V)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(INY) & HasAddrMode(Implied)) ~
|
||||
(Elidable & HasOpcode(CPY) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Y, State.C, State.V)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(DEC) & HasAddrMode(Implied)) ~
|
||||
(Elidable & HasOpcode(CMP) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.A, State.C, State.V)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(DEX) & HasAddrMode(Implied)) ~
|
||||
(Elidable & HasOpcode(CPX) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.X, State.C, State.V)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(DEY) & HasAddrMode(Implied)) ~
|
||||
(Elidable & HasOpcode(CPY) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Y, State.C, State.V)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
)
|
||||
|
||||
private val powersOf2: List[(Int, Int)] = List(
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user