mirror of
https://github.com/KarolS/millfork.git
synced 2024-06-26 11:29:28 +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
|
*.seq
|
||||||
*.asm
|
*.asm
|
||||||
*.lbl
|
*.lbl
|
||||||
|
*.labels
|
||||||
*.nl
|
*.nl
|
||||||
*.fns
|
*.fns
|
||||||
*.sym
|
*.sym
|
||||||
|
*.mlb
|
||||||
|
*.dbg
|
||||||
*.deb
|
*.deb
|
||||||
*.xex
|
*.xex
|
||||||
*.nes
|
*.nes
|
||||||
|
|
64
CHANGELOG.md
64
CHANGELOG.md
|
@ -1,5 +1,69 @@
|
||||||
# Change log
|
# 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)
|
## 0.3.26 (2021-03-01)
|
||||||
|
|
||||||
* Array fields in structs.
|
* Array fields in structs.
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
A middle-level programming language targeting 6502-based, 8080-based, Z80-based and 6809-based microcomputers.
|
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)
|
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).
|
For build instructions, see [Build instructions](./COMPILING.md).
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name := "millfork"
|
name := "millfork"
|
||||||
|
|
||||||
version := "0.3.26"
|
version := "0.3.31-SNAPSHOT"
|
||||||
|
|
||||||
// keep it at 2.12.11 for GraalVM native image compatibility!
|
// keep it at 2.12.11 for GraalVM native image compatibility!
|
||||||
scalaVersion := "2.12.11"
|
scalaVersion := "2.12.11"
|
||||||
|
|
|
@ -14,6 +14,8 @@ It implies the following:
|
||||||
|
|
||||||
* cannot contain variable or array declarations
|
* 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
|
* can be `asm` - in this case, they should **not** end with a return instruction
|
||||||
|
|
||||||
* do not have an address
|
* do not have an address
|
||||||
|
@ -35,7 +37,13 @@ It implies the following:
|
||||||
* `call` parameters exceptionally can have their type declared as `void`;
|
* `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
|
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
|
* 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 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 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` –
|
* `-fbreakpoints`, `-fno-breakpoints` –
|
||||||
Whether the compiler should use the `breakpoint` macro.
|
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.
|
* `-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.
|
* `-finline`, `-fno-inline` – Whether should inline functions automatically.
|
||||||
See the [documentation about inlining](../abi/inlining.md). Computationally easy, can give decent gains.
|
See the [documentation about inlining](../abi/inlining.md). Computationally easy, can give decent gains.
|
||||||
`.ini` equivalent: `inline`.
|
`.ini` equivalent: `inline`.
|
||||||
|
@ -289,6 +299,10 @@ You can also enable or disable warnings individually:
|
||||||
Whether should warn about deprecated aliases.
|
Whether should warn about deprecated aliases.
|
||||||
Default: enabled.
|
Default: enabled.
|
||||||
|
|
||||||
|
* `-Wcomparisons`, `-Wno-comparisons` –
|
||||||
|
Whether should warn about comparisons between bytes and pointers.
|
||||||
|
Default: enabled.
|
||||||
|
|
||||||
* `-Wextra-comparisons`, `-Wno-extra-comparisons` –
|
* `-Wextra-comparisons`, `-Wno-extra-comparisons` –
|
||||||
Whether should warn about simplifiable unsigned integer comparisons.
|
Whether should warn about simplifiable unsigned integer comparisons.
|
||||||
Default: disabled.
|
Default: disabled.
|
||||||
|
@ -312,3 +326,7 @@ You can also enable or disable warnings individually:
|
||||||
* `-Wuseless`, `-Wno-useless` –
|
* `-Wuseless`, `-Wno-useless` –
|
||||||
Whether should warn about code that does nothing.
|
Whether should warn about code that does nothing.
|
||||||
Default: enabled.
|
Default: enabled.
|
||||||
|
|
||||||
|
* `-Whints`, `-Wno-hints` –
|
||||||
|
Whether should warn about unsupported optimization hints.
|
||||||
|
Default: enabled.
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
### A note about Commodore 64
|
### 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
|
#### Multifile programs
|
||||||
|
|
||||||
A multifile program is a program stored on a disk that consists of the main program file that is executed first
|
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)
|
* `z80` (Zilog Z80)
|
||||||
|
|
||||||
* `strictz80` (Z80 without illegal instructions)
|
* `strictz80` (Z80 without illegal instructions)
|
||||||
|
|
||||||
|
* `r800` (R800)
|
||||||
|
|
||||||
* `z80next` (Z80 core from ZX Spectrum Next)
|
* `z80next` (Z80 core from ZX Spectrum Next)
|
||||||
Note: Millfork version 0.3.18 and earlier uses the name `zx80next` for this architecture.
|
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_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_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,
|
* `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
|
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`.
|
* `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`.
|
* `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)
|
* [Important guidelines regarding reentrancy](lang/reentrancy.md)
|
||||||
|
|
||||||
|
* [Optimization hints](lang/hints.md)
|
||||||
|
|
||||||
* [List of keywords](lang/keywords.md)
|
* [List of keywords](lang/keywords.md)
|
||||||
|
|
||||||
## Library reference
|
## Library reference
|
||||||
|
|
|
@ -16,7 +16,7 @@ No other lines are allowed in the file.
|
||||||
* `NAME=<name>` defines the name for this encoding. Required.
|
* `NAME=<name>` defines the name for this encoding. Required.
|
||||||
|
|
||||||
* `BUILTIN=<internal name>` defines this encoding to be a UTF-based encoding.
|
* `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.
|
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.
|
* `EOT=<xx>` where `<xx>` are two hex digits, defines the string terminator byte.
|
||||||
|
|
|
@ -4,17 +4,17 @@
|
||||||
|
|
||||||
Syntax:
|
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:
|
Examples:
|
||||||
|
|
||||||
void do_nothing() { }
|
void do_nothing() { }
|
||||||
inline byte two() = 2
|
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
|
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 6502, it means that the function will not cross a page boundary if possible
|
||||||
* on Z80, it is ignored
|
* 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.
|
* `<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,
|
* `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`
|
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.
|
It is not allowed in any other places.
|
||||||
|
|
||||||
The type of a literal is the smallest type of undefined signedness
|
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`
|
You can override the values for `nullchar` and `nullchar_scr`
|
||||||
by defining preprocessor features `NULLCHAR` and `NULLCHAR_SCR` respectively.
|
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.
|
* `nullchar` and `nullchar_scr` will still be bytes, equal to zero.
|
||||||
* the `string` module in the Millfork standard library will not work correctly
|
* 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.
|
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.
|
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.
|
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,
|
In case of UTF-16, the length prefix contains the number of code units,
|
||||||
so the number of bytes divided by two,
|
so the number of bytes divided by two,
|
||||||
which allows for strings of practically unlimited length.
|
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.
|
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 a Pascal string" pascii
|
||||||
"this is also a Pascal string"p
|
"this is also a Pascal string"p
|
||||||
"this is a zero-terminated Pascal string"pz
|
"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!
|
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
|
pointer p
|
||||||
p = "test"pz
|
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.
|
A module name is a sequence of slash-separated valid Millfork identifiers.
|
||||||
The name also defines where the module is located:
|
The name also defines where the module is located:
|
||||||
a module named `a/b` is presumed to exist in `a/b.mfk`
|
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.
|
and then in the include directories.
|
||||||
|
|
||||||
A module can import other modules, using the `import` statement.
|
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 `[]`
|
* `->` 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.
|
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.
|
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 `-'`.
|
The only exceptions are `+` and `-`, and `$+` and `$-`.
|
||||||
They are interpreted as expected: `5 - 3 + 2 == 4` and `5 -' 3 +' 2 == 4`.
|
They are interpreted as expected: `5 - 3 + 2 == 4` and `5 $- 3 $+ 2 == 4`.
|
||||||
Note that you cannot mix `+'` and `-'` with `+` and `-`.
|
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.
|
i.e. `x / y / z` will not compile.
|
||||||
|
|
||||||
The decimal operators have two different forms:
|
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
|
* 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
|
On Ricoh-based targets (e.g. Famicom) they require the zeropage register to have size at least 4
|
||||||
|
|
||||||
* `+'`, `-'`: decimal addition/subtraction
|
* `$+`, `$-`: decimal addition/subtraction
|
||||||
`$+`, `$-`: (since Millfork 0.3.22)
|
`+'`, `-'`: (deprecated form)
|
||||||
`byte +' byte`
|
`byte $+ byte`
|
||||||
`constant word +' constant word`
|
`constant word $+ constant word`
|
||||||
`constant long +' constant long`
|
`constant long $+ constant long`
|
||||||
`word +' word` (zpreg)
|
`word $+ word` (zpreg)
|
||||||
|
|
||||||
* `*'`: decimal multiplication
|
* `$*`: decimal multiplication
|
||||||
`$*`: (since Millfork 0.3.22)
|
`*'`: (deprecated form)
|
||||||
`constant *' constant`
|
`constant $* constant`
|
||||||
|
|
||||||
* `<<'`, `>>'`: decimal multiplication/division by power of two
|
* `$<<`, `$>>`: decimal multiplication/division by power of two
|
||||||
`$<<`, `$>>`: (since Millfork 0.3.22)
|
`<<'`, `>>'`: (deprecated form)
|
||||||
`byte <<' constant byte`
|
`byte $<< constant byte`
|
||||||
|
|
||||||
## Comparison operators
|
## 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 word = word`
|
||||||
`mutable long = long`
|
`mutable long = long`
|
||||||
|
|
||||||
* `+=`, `+'=`, `|=`, `^=`, `&=`: modification in place
|
* `+=`, `$+=`, `|=`, `^=`, `&=`: modification in place
|
||||||
`$+=` (since Millfork 0.3.22)
|
`+'=` (deprecated form)
|
||||||
`mutable byte += byte`
|
`mutable byte += byte`
|
||||||
`mutable word += word`
|
`mutable word += word`
|
||||||
`mutable trivial long += long`
|
`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 word <<= byte`
|
||||||
`mutable trivial long <<= byte`
|
`mutable trivial long <<= byte`
|
||||||
|
|
||||||
* `<<'=`, `>>'=`: decimal shift in place
|
* `$<<=`, `$>>=`: decimal shift in place
|
||||||
`$<<=`, `$>>=` (since Millfork 0.3.22)
|
`<<'=`, `>>'=` (deprecated form)
|
||||||
`mutable byte <<'= constant byte`
|
`mutable byte $<<= constant byte`
|
||||||
`mutable word <<'= constant byte`
|
`mutable word $<<= constant byte`
|
||||||
`mutable trivial long <<'= constant byte`
|
`mutable trivial long $<<= constant byte`
|
||||||
|
|
||||||
* `-=`, `-'=`: subtraction in place
|
* `-=`, `$-=`: subtraction in place
|
||||||
`$-=` (since Millfork 0.3.22)
|
`-'=` (deprecated form)
|
||||||
`mutable byte -= byte`
|
`mutable byte -= byte`
|
||||||
`mutable word -= simple word`
|
`mutable word -= simple word`
|
||||||
`mutable trivial long -= simple long`
|
`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 *= unsigned byte` (zpreg)
|
||||||
`mutable word *= word` (zpreg)
|
`mutable word *= word` (zpreg)
|
||||||
|
|
||||||
* `*'=`: decimal multiplication in place
|
* `$*=`: decimal multiplication in place
|
||||||
`$*=` (since Millfork 0.3.22)
|
`*'=` (deprecated form)
|
||||||
`mutable byte *'= constant byte`
|
`mutable byte $*= constant byte`
|
||||||
|
|
||||||
* `/=`, `%%=`: unsigned division and modulo in place
|
* `/=`, `%%=`: unsigned division and modulo in place
|
||||||
`mutable unsigned byte /= unsigned byte` (zpreg)
|
`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`: expansion of an 8-bit operation to a 9-bit operation
|
||||||
`nonet(byte + byte)`
|
`nonet(byte + byte)`
|
||||||
`nonet(byte +' byte)`
|
`nonet(byte $+ byte)`
|
||||||
`nonet(byte << constant 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.
|
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
|
* `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.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
|
* `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
|
* `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.
|
* `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
|
* `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:
|
// prints 500:
|
||||||
#infoeval if(0, 400, 500)
|
#infoeval if(0, 400, 500)
|
||||||
|
|
||||||
TODO
|
The `min` and `max` functions return the smallest or largest parameter respectively. They support any number of arguments:
|
||||||
`not`, `lo`, `hi`, `min`, `max` `+`, `-`, `*`, `|`, `&`, `^`, `||`, `&&`, `<<`, `>>`,`==`, `!=`, `>`, `>=`, `<`, `<=`
|
|
||||||
|
// 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:
|
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`
|
### `#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)
|
* `.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
|
* `.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:
|
Syntax:
|
||||||
|
|
||||||
`[segment(<segment>)] [volatile] [<storage>] <type> <name> [@<address>] [= <initial_value>]`
|
`[segment(<segment>)] [volatile] [<storage>] <type> <name> [<optimization hints>] [@<address>] [= <initial_value>]`
|
||||||
|
|
||||||
Examples:
|
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.
|
* `<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.
|
`register` is only a hint for the optimizer.
|
||||||
See [the description of variable storage](../abi/variable-storage.md).
|
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.
|
* `<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.
|
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)
|
* `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
|
* `cbmscr` or `petscr` – Commodore screencodes
|
||||||
|
|
||||||
* `cbmscrjp` or `petscrjp` – Commodore screencodes as used on Japanese versions of Commodore 64
|
* `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
|
* `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
|
* `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
|
* `utf16be`, `utf16le` – UTF-16BE and UTF-16LE
|
||||||
|
|
||||||
|
* `utf32be`, `utf32le` – UTF-32BE and UTF-32LE
|
||||||
|
|
||||||
When programming for Commodore,
|
When programming for Commodore,
|
||||||
use `petscii` for strings you're printing using standard I/O routines
|
use `petscii` for strings you're printing using standard I/O routines
|
||||||
and `petsciiscr` for strings you're copying to screen memory directly.
|
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 `zx80` encoding it's `{x01}`,
|
||||||
* in the `zx81` encoding it's `{x0b}`,
|
* in the `zx81` encoding it's `{x0b}`,
|
||||||
* in the `petscr` and `petscrjp` encodings it's `{xe0}`,
|
* 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 `atasciiscr` encoding it's `{xdb}`,
|
||||||
* in the `pokemon1*` encodings it's `{x50}`,
|
* in the `pokemon1*` encodings it's `{x50}`,
|
||||||
* in the `cocoscr` encoding it's exceptionally two bytes: `{xd0}`
|
* 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 `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).
|
* in other encodings it's `{x00}` (this may be a subject to change in future versions).
|
||||||
|
|
||||||
##### Available only in some encodings
|
##### Available only in some encodings
|
||||||
|
@ -211,6 +217,7 @@ Encoding | lowercase letters | backslash | currencies | intl | card suits
|
||||||
`petscr` | yes¹ | no | £ | none | yes¹
|
`petscr` | yes¹ | no | £ | none | yes¹
|
||||||
`petjp` | no | no | ¥ | katakana³ | yes³
|
`petjp` | no | no | ¥ | katakana³ | yes³
|
||||||
`petscrjp` | no | no | ¥ | katakana³ | yes³
|
`petscrjp` | no | no | ¥ | katakana³ | yes³
|
||||||
|
`geos_de` | yes | no | | | no
|
||||||
`sinclair`, `bbc` | yes | yes | £ | none | no
|
`sinclair`, `bbc` | yes | yes | £ | none | no
|
||||||
`zx80`, `zx81` | no | no | £ | none | no
|
`zx80`, `zx81` | no | no | £ | none | no
|
||||||
`apple2` | no | yes | | 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
|
`origpet` | yes | no | no | yes | no | yes | no
|
||||||
`oldpet` | yes | no | no | yes | no | yes | no
|
`oldpet` | yes | no | no | yes | no | yes | no
|
||||||
`petscr`, `petscrjp`| no | no | no | no | no | no | 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
|
`sinclair` | yes | yes | no | yes | yes | yes | yes
|
||||||
`zx80`,`zx81` | yes | no | yes | yes | no | no | no
|
`zx80`,`zx81` | yes | no | yes | yes | no | no | no
|
||||||
`ascii`, `iso_*` | yes | yes | yes | no | 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.
|
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:
|
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.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)
|
* [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.)
|
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||||
// Input: A = Byte to write.
|
// 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) {
|
asm void putchar(byte register(a) char) {
|
||||||
JSR chrout
|
JSR chrout
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||||
// Input: A = Byte to write.
|
// 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) {
|
asm void putchar(byte register(a) char) {
|
||||||
JSR chrout
|
JSR chrout
|
||||||
|
|
|
@ -2,7 +2,7 @@ import err
|
||||||
|
|
||||||
inline asm void switch_hirom(byte register(a) bank) {
|
inline asm void switch_hirom(byte register(a) bank) {
|
||||||
? and #$3F
|
? and #$3F
|
||||||
! sta $DE01
|
! sta $DE00
|
||||||
? rts
|
? rts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||||
// Input: A = Byte to write.
|
// 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.)
|
// 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.
|
// Output: A = Byte read.
|
||||||
|
|
|
@ -10,7 +10,7 @@ word sid_v1_freq @$D400
|
||||||
word sid_v1_pulse @$D402
|
word sid_v1_pulse @$D402
|
||||||
byte sid_v1_cr @$D404
|
byte sid_v1_cr @$D404
|
||||||
byte sid_v1_ad @$D405
|
byte sid_v1_ad @$D405
|
||||||
byte sid_v1_sr @$D409
|
byte sid_v1_sr @$D406
|
||||||
|
|
||||||
word sid_v2_freq @$D407
|
word sid_v2_freq @$D407
|
||||||
word sid_v2_pulse @$D409
|
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
|
a-z=C1
|
||||||
|
|
||||||
{q}=02
|
{q}=A2
|
||||||
{apos}=07
|
{apos}=A7
|
||||||
{nbsp}=40
|
{nbsp}=40
|
||||||
{n}=8D
|
{n}=8D
|
||||||
|
|
|
@ -13,3 +13,4 @@ EOT=00
|
||||||
{apos}=27
|
{apos}=27
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
|
{pound}=5c
|
||||||
|
|
|
@ -7,8 +7,8 @@ EOT=7F
|
||||||
20-3f=@abcdefghijklmnopqrstuvwxyz[\]^_
|
20-3f=@abcdefghijklmnopqrstuvwxyz[\]^_
|
||||||
60-7e=πABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~
|
60-7e=πABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~
|
||||||
|
|
||||||
{q}=22
|
{q}=02
|
||||||
{apos}=27
|
{apos}=07
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
{pi}=60
|
{pi}=60
|
||||||
|
|
|
@ -9,4 +9,4 @@ E0-FE=`abcdefghijklmnopqrstuvwxyz{\}~
|
||||||
{q}=A2
|
{q}=A2
|
||||||
{apos}=A7
|
{apos}=A7
|
||||||
{lbrace}=FB
|
{lbrace}=FB
|
||||||
{rbrace}=FC
|
{rbrace}=FD
|
||||||
|
|
|
@ -26,6 +26,7 @@ f0-ff=đńňóôőö÷řůúűüýţ˙
|
||||||
{apos}=27
|
{apos}=27
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
|
{euro}=80
|
||||||
{copy}=a9
|
{copy}=a9
|
||||||
{ss}=df
|
{ss}=df
|
||||||
{nbsp}=A0
|
{nbsp}=A0
|
||||||
|
|
|
@ -23,6 +23,7 @@ f0-ff=рстуфхцчшщъыьэюя
|
||||||
{apos}=27
|
{apos}=27
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
|
{euro}=88
|
||||||
{copy}=a9
|
{copy}=a9
|
||||||
{nbsp}=A0
|
{nbsp}=A0
|
||||||
{shy}=AD
|
{shy}=AD
|
||||||
|
|
|
@ -25,6 +25,7 @@ f0-ff=ðñòóôõö÷øùúûüýþÿ
|
||||||
{apos}=27
|
{apos}=27
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
|
{euro}=80
|
||||||
{cent}=a2
|
{cent}=a2
|
||||||
{pound}=a3
|
{pound}=a3
|
||||||
{yen}=a5
|
{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
|
{pound}=9c
|
||||||
{yen}=9d
|
{yen}=9d
|
||||||
{ss}=e1
|
{ss}=e1
|
||||||
|
{pi}=e3
|
||||||
{nbsp}=FF
|
{nbsp}=FF
|
||||||
|
|
|
@ -23,6 +23,7 @@ F1-FE=±‗¾¶§÷¸°¨·¹³²■
|
||||||
{apos}=27
|
{apos}=27
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
|
{copy}=b8
|
||||||
{cent}=BD
|
{cent}=BD
|
||||||
{pound}=9c
|
{pound}=9c
|
||||||
{yen}=BE
|
{yen}=BE
|
||||||
|
|
|
@ -24,6 +24,7 @@ F1-FE=±υφχ§ψ¸°¨ωϋΰώ■
|
||||||
{apos}=27
|
{apos}=27
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
|
{pound}=9c
|
||||||
{ss}=D7
|
{ss}=D7
|
||||||
{nbsp}=FF
|
{nbsp}=FF
|
||||||
{shy}=F0
|
{shy}=F0
|
||||||
|
|
|
@ -25,6 +25,7 @@ F1-FE=±‗¾¶§÷¸°¨·¹³²■
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
{cent}=BD
|
{cent}=BD
|
||||||
{pound}=9c
|
{pound}=9c
|
||||||
|
{copy}=b8
|
||||||
{yen}=BE
|
{yen}=BE
|
||||||
{euro}=D5
|
{euro}=D5
|
||||||
{ss}=e1
|
{ss}=e1
|
||||||
|
|
|
@ -19,6 +19,9 @@ fe-ff=↕↔
|
||||||
{apos}=27
|
{apos}=27
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
|
{pound}=a3
|
||||||
|
{copy}=a4
|
||||||
|
{pi}=b8
|
||||||
{AE}=5b
|
{AE}=5b
|
||||||
{OE}=5c
|
{OE}=5c
|
||||||
{AA}=5d
|
{AA}=5d
|
||||||
|
|
|
@ -19,3 +19,6 @@ fe-ff=↕↔
|
||||||
{apos}=27
|
{apos}=27
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
|
{pound}=a3
|
||||||
|
{copy}=a4
|
||||||
|
{pi}=b8
|
||||||
|
|
|
@ -19,3 +19,5 @@ fe-ff=↕↔
|
||||||
{apos}=27
|
{apos}=27
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
|
{copy}=a4
|
||||||
|
{pi}=b8
|
||||||
|
|
|
@ -20,3 +20,6 @@ fe-ff=↕↔
|
||||||
{apos}=27
|
{apos}=27
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
|
{pound}=a3
|
||||||
|
{copy}=a4
|
||||||
|
{pi}=b8
|
||||||
|
|
|
@ -24,3 +24,4 @@ F0-F9=0123456789
|
||||||
{q}=7F
|
{q}=7F
|
||||||
{lbrace}=C0
|
{lbrace}=C0
|
||||||
{rbrace}=D0
|
{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
|
{ss}=df
|
||||||
{nbsp}=A0
|
{nbsp}=A0
|
||||||
{shy}=AD
|
{shy}=AD
|
||||||
|
{pound}=A3
|
||||||
|
|
|
@ -24,4 +24,5 @@ F0-FE=≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
{ss}=e1
|
{ss}=e1
|
||||||
|
{pi}=e3
|
||||||
{nbsp}=FF
|
{nbsp}=FF
|
||||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
{nbsp}=a0
|
{nbsp}=a0
|
||||||
|
{copy}=98
|
||||||
•=95
|
•=95
|
||||||
|
|
||||||
b0=№
|
b0=№
|
||||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
{nbsp}=9a
|
{nbsp}=9a
|
||||||
|
{copy}=bf
|
||||||
|
|
||||||
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
|
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
|
||||||
90-99=░▒▓⌠■∙√≈≤≥
|
90-99=░▒▓⌠■∙√≈≤≥
|
||||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
{nbsp}=9a
|
{nbsp}=9a
|
||||||
|
{copy}=bf
|
||||||
|
|
||||||
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
|
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
|
||||||
90-99=░▒▓“■∙”—№™
|
90-99=░▒▓“■∙”—№™
|
||||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
{shy}=8d
|
{shy}=8d
|
||||||
|
{copy}=bf
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
{nbsp}=9a
|
{nbsp}=9a
|
||||||
|
{copy}=bf
|
||||||
|
|
||||||
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
|
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
|
||||||
90-99=░▒▓⌠■∙√≈≤≥
|
90-99=░▒▓⌠■∙√≈≤≥
|
||||||
|
|
|
@ -29,5 +29,6 @@ f1-ff=ÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ
|
||||||
{ss}=a7
|
{ss}=a7
|
||||||
{nbsp}=CA
|
{nbsp}=CA
|
||||||
{euro}=DB
|
{euro}=DB
|
||||||
|
{pi}=b9
|
||||||
€=DB
|
€=DB
|
||||||
U+F8FF=F0
|
U+F8FF=F0
|
||||||
|
|
|
@ -26,4 +26,5 @@ F0-FE=≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
|
||||||
{cent}=9b
|
{cent}=9b
|
||||||
{yen}=9d
|
{yen}=9d
|
||||||
{ss}=e1
|
{ss}=e1
|
||||||
|
{pi}=e3
|
||||||
{nbsp}=FF
|
{nbsp}=FF
|
||||||
|
|
|
@ -55,3 +55,8 @@ f0-ff=äëïöüçæåøñãõ⇒⇐⇔≡
|
||||||
{lbrace}=7b
|
{lbrace}=7b
|
||||||
{rbrace}=7d
|
{rbrace}=7d
|
||||||
{pi}=1b18
|
{pi}=1b18
|
||||||
|
{pound}=a3
|
||||||
|
{copy}=a4
|
||||||
|
{yen}=bd
|
||||||
|
{cent}=b1
|
||||||
|
{ss}=ba
|
||||||
|
|
|
@ -23,6 +23,7 @@ F0-FF=₽×./,♀0123456789
|
||||||
{ot}=BE
|
{ot}=BE
|
||||||
{'v}=BF
|
{'v}=BF
|
||||||
{hav}=BF
|
{hav}=BF
|
||||||
|
{apos}=E0
|
||||||
{'r}=E4
|
{'r}=E4
|
||||||
{ar}=E4
|
{ar}=E4
|
||||||
{'m}=E5
|
{'m}=E5
|
||||||
|
|
|
@ -22,6 +22,7 @@ F0-FF=₽×./,♀0123456789
|
||||||
{'t}=DD
|
{'t}=DD
|
||||||
{'v}=DE
|
{'v}=DE
|
||||||
|
|
||||||
|
{apos}=E0
|
||||||
{PK}=E1
|
{PK}=E1
|
||||||
{pk}=E1
|
{pk}=E1
|
||||||
{MN}=E2
|
{MN}=E2
|
||||||
|
|
|
@ -13,6 +13,7 @@ E6-E8=?!.
|
||||||
EC-EF=▷▶▼♂
|
EC-EF=▷▶▼♂
|
||||||
F0-FF=₽×./,♀0123456789
|
F0-FF=₽×./,♀0123456789
|
||||||
|
|
||||||
|
{ss}=be
|
||||||
{c'}=d4
|
{c'}=d4
|
||||||
{ce}=d4
|
{ce}=d4
|
||||||
{d'}=d5
|
{d'}=d5
|
||||||
|
@ -37,6 +38,7 @@ F0-FF=₽×./,♀0123456789
|
||||||
{u'}=de
|
{u'}=de
|
||||||
{ue}=de
|
{ue}=de
|
||||||
{y'}=df
|
{y'}=df
|
||||||
|
{apos}=e0
|
||||||
|
|
||||||
{PK}=E1
|
{PK}=E1
|
||||||
{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
|
#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, $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,
|
$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,
|
$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)
|
||||||
ld a,(reg_joypad)
|
ld a,(reg_joypad)
|
||||||
}
|
}
|
||||||
byte tmp
|
|
||||||
tmp = reg_joypad ^ $ff
|
tmp = reg_joypad ^ $ff
|
||||||
input_a = (tmp & 1) >> 0
|
input_a = (tmp & 1) // >> 0
|
||||||
input_b = (tmp & 2) >> 1
|
input_b = (tmp & 2) >> 1
|
||||||
input_select = (tmp & 4) >> 2
|
input_select = (tmp & 4) >> 2
|
||||||
input_start = (tmp & 8) >> 3
|
input_start = (tmp & 8) >> 3
|
||||||
|
|
|
@ -11,6 +11,12 @@ inline asm byte __mul_u8u8u8() {
|
||||||
? LD A, E
|
? LD A, E
|
||||||
? RET
|
? RET
|
||||||
}
|
}
|
||||||
|
#elseif CPUFEATURE_R800
|
||||||
|
inline asm byte __mul_u8u8u8() {
|
||||||
|
? MULUB A,D
|
||||||
|
? LD A,L
|
||||||
|
? RET
|
||||||
|
}
|
||||||
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||||
//A = A * D
|
//A = A * D
|
||||||
noinline asm byte __mul_u8u8u8() {
|
noinline asm byte __mul_u8u8u8() {
|
||||||
|
@ -89,6 +95,16 @@ __divmod_u16u8u16u8_skip:
|
||||||
? RET
|
? 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() {
|
noinline asm word __mul_u16u8u16() {
|
||||||
? LD HL,0
|
? LD HL,0
|
||||||
? LD B,8
|
? LD B,8
|
||||||
|
@ -113,8 +129,17 @@ __mul_u16u8u16_skip:
|
||||||
#endif
|
#endif
|
||||||
? RET
|
? 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() {
|
noinline asm word __mul_u16u16u16() {
|
||||||
LD HL,0
|
LD HL,0
|
||||||
LD A,16
|
LD A,16
|
||||||
|
|
|
@ -1,19 +1 @@
|
||||||
#if not(CBM)
|
import cbm/basic_loader<$401>
|
||||||
#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
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1 @@
|
||||||
#if not(CBM)
|
import cbm/basic_loader<$801>
|
||||||
#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
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
|
@ -22,4 +22,5 @@ asm void __init_16bit() @$80D {
|
||||||
clc
|
clc
|
||||||
xce
|
xce
|
||||||
sep #$30
|
sep #$30
|
||||||
}
|
jmp main
|
||||||
|
}
|
||||||
|
|
|
@ -1,19 +1 @@
|
||||||
#if not(CBM)
|
import cbm/basic_loader<$1001>
|
||||||
#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
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1 @@
|
||||||
#if not(CBM)
|
import cbm/basic_loader<$1201>
|
||||||
#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
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1 @@
|
||||||
#if not(CBM)
|
import cbm/basic_loader<$1c01>
|
||||||
#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
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#warn m6809_math module should be used only on 6809-like targets
|
#warn m6809_math module should be used only on 6809-like targets
|
||||||
#endif
|
#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
|
pshs d,x
|
||||||
exg d,x
|
exg d,x
|
||||||
lda ,s
|
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
|
// 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
|
pshs b,cc
|
||||||
ldb #8
|
ldb #8
|
||||||
stb ,-s
|
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
|
// 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
|
pshs x,d,cc
|
||||||
ldb #16
|
ldb #16
|
||||||
pshs b
|
pshs b
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||||
// Input: A = Byte to write.
|
// 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() {
|
inline void new_line() {
|
||||||
putchar(13)
|
putchar(13)
|
||||||
|
|
|
@ -11,14 +11,21 @@ modules=loader_0801,x16_kernal,x16_hardware,c64_panic,stdlib
|
||||||
|
|
||||||
[allocation]
|
[allocation]
|
||||||
; Let's not use the BASIC:
|
; Let's not use the BASIC:
|
||||||
zp_pointers=0-$7F
|
; $00-$01 are used for bank switching
|
||||||
segments=default,himem_00,himem_ff
|
zp_pointers=$02-$7F
|
||||||
|
segments=default,user,himem_00,himem_ff
|
||||||
default_code_segment=default
|
default_code_segment=default
|
||||||
segment_default_start=$80D
|
segment_default_start=$80D
|
||||||
segment_default_codeend=$9eff
|
segment_default_codeend=$9eff
|
||||||
segment_default_datastart=after_code
|
segment_default_datastart=after_code
|
||||||
segment_default_end=$9eff
|
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_start=$a000
|
||||||
segment_himem_00_codeend=$bfff
|
segment_himem_00_codeend=$bfff
|
||||||
segment_himem_00_datastart=after_code
|
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.)
|
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||||
// Input: A = Byte to write.
|
// 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) {
|
asm void putchar(byte register(a) char) {
|
||||||
JSR chrout
|
JSR chrout
|
||||||
|
|
|
@ -5,16 +5,16 @@
|
||||||
|
|
||||||
#pragma zilog_syntax
|
#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
|
rst $10
|
||||||
? ret
|
? ret
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void new_line() {
|
inline void new_line() !preserves_bc !preserves_de !preserves_hl {
|
||||||
putchar(13)
|
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
|
out (254),a
|
||||||
? ret
|
? ret
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,15 @@
|
||||||
+= -= *= >>= <<=
|
+= -= *= >>= <<=
|
||||||
+' -' *' <<' >>'
|
+' -' *' <<' >>'
|
||||||
+'= -'= *'= <<'= >>'=
|
+'= -'= *'= <<'= >>'=
|
||||||
|
$+ $- $* $<< $>>
|
||||||
|
$+= $-= $*= $<<= $>>=
|
||||||
= ?
|
= ?
|
||||||
/ %% /= %%=
|
/ %% /= %%=
|
||||||
> >= < <= != ==
|
> >= < <= != ==
|
||||||
| || |= ^ ^= & && &=
|
| || |= ^ ^= & && &=
|
||||||
( ) { } @ [ ] : # </Keywords>
|
( ) { } @ [ ] : #
|
||||||
|
; .
|
||||||
|
</Keywords>
|
||||||
<Keywords name="Operators2"></Keywords>
|
<Keywords name="Operators2"></Keywords>
|
||||||
<Keywords name="Folders in code1, open">{</Keywords>
|
<Keywords name="Folders in code1, open">{</Keywords>
|
||||||
<Keywords name="Folders in code1, middle"></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
|
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
|
int8 int16 int24 int32 int40 int48 int56 int64
|
||||||
int72 int80 int88 int96 int104 int112 int120 int128
|
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
|
unsigned8 unsigned16 unsigned24 unsigned32 unsigned40 unsigned48 unsigned56 unsigned64
|
||||||
unsigned72 unsigned80 unsigned88 unsigned96 unsigned104 unsigned112 unsigned120 unsigned128
|
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">
|
<Keywords name="Keywords2">
|
||||||
import segment
|
import segment
|
||||||
if else for return while do asm extern break continue default goto label
|
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
|
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
|
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
|
until to downto parallelto paralleluntil
|
||||||
|
function
|
||||||
static stack ref const volatile inline noinline macro register kernal_interrupt interrupt align reentrant
|
static stack ref const volatile inline noinline macro register kernal_interrupt interrupt align reentrant
|
||||||
hi lo sin cos tan call nonet
|
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="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="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>
|
<Keywords name="Keywords6"></Keywords>
|
||||||
|
|
|
@ -35,6 +35,7 @@ nav:
|
||||||
- Inline 8080/LR35902/Z80 assembly: lang/assemblyz80.md
|
- Inline 8080/LR35902/Z80 assembly: lang/assemblyz80.md
|
||||||
- Inline 6809 assembly: lang/assembly6809.md
|
- Inline 6809 assembly: lang/assembly6809.md
|
||||||
- Reentrancy guidelines: lang/reentrancy.md
|
- Reentrancy guidelines: lang/reentrancy.md
|
||||||
|
- Optimization hints: lang/hints.md
|
||||||
- List of keywords: lang/keywords.md
|
- List of keywords: lang/keywords.md
|
||||||
- Library reference:
|
- Library reference:
|
||||||
- stdlib module: stdlib/stdlib.md
|
- stdlib module: stdlib/stdlib.md
|
||||||
|
|
|
@ -17,6 +17,11 @@
|
||||||
"allDeclaredFields":true,
|
"allDeclaredFields":true,
|
||||||
"allPublicMethods":true
|
"allPublicMethods":true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name":"millfork.assembly.m6809.MOpcode$",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"allPublicMethods":true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name":"org.apache.commons.logging.LogFactory"
|
"name":"org.apache.commons.logging.LogFactory"
|
||||||
},
|
},
|
||||||
|
|
|
@ -45,7 +45,7 @@ case class CompilationOptions(platform: Platform,
|
||||||
EmitIntel8085Opcodes, EmitIntel8080Opcodes, UseIxForStack, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
EmitIntel8085Opcodes, EmitIntel8080Opcodes, UseIxForStack, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
||||||
|
|
||||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(
|
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(
|
||||||
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes,
|
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes, EmitR800Opcodes,
|
||||||
UseIyForStack, UseIxForScratch, UseIyForScratch, UseShadowRegistersForInterrupts)
|
UseIyForStack, UseIxForScratch, UseIyForScratch, UseShadowRegistersForInterrupts)
|
||||||
|
|
||||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6809) invalids ++= Set(
|
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")
|
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 (flags(EmitIntel8080Opcodes)) {
|
||||||
if (!Intel8080Compatible(platform.cpu)) {
|
if (!Intel8080Compatible(platform.cpu)) {
|
||||||
log.error("Intel 8080 opcodes enabled for architecture that doesn't support them")
|
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)),
|
"OPTIMIZE_IPO" -> toLong(flag(CompilationFlag.InterproceduralOptimization)),
|
||||||
"CPUFEATURE_DECIMAL_MODE" -> toLong(flag(CompilationFlag.DecimalMode)),
|
"CPUFEATURE_DECIMAL_MODE" -> toLong(flag(CompilationFlag.DecimalMode)),
|
||||||
"CPUFEATURE_Z80" -> toLong(flag(CompilationFlag.EmitZ80Opcodes)),
|
"CPUFEATURE_Z80" -> toLong(flag(CompilationFlag.EmitZ80Opcodes)),
|
||||||
|
"CPUFEATURE_R800" -> toLong(flag(CompilationFlag.EmitR800Opcodes)),
|
||||||
"CPUFEATURE_EZ80" -> toLong(flag(CompilationFlag.EmitEZ80Opcodes)),
|
"CPUFEATURE_EZ80" -> toLong(flag(CompilationFlag.EmitEZ80Opcodes)),
|
||||||
"CPUFEATURE_8080" -> toLong(flag(CompilationFlag.EmitIntel8080Opcodes)),
|
"CPUFEATURE_8080" -> toLong(flag(CompilationFlag.EmitIntel8080Opcodes)),
|
||||||
"CPUFEATURE_8085" -> toLong(flag(CompilationFlag.EmitIntel8085Opcodes)),
|
"CPUFEATURE_8085" -> toLong(flag(CompilationFlag.EmitIntel8085Opcodes)),
|
||||||
|
@ -290,7 +296,7 @@ object CpuFamily extends Enumeration {
|
||||||
import Cpu._
|
import Cpu._
|
||||||
cpu match {
|
cpu match {
|
||||||
case Mos | StrictMos | Ricoh | StrictRicoh | Cmos | SC02 | Rockwell | Wdc | HuC6280 | CE02 | Sixteen => M6502
|
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 Intel8086 | Intel80186 => I86
|
||||||
case Cpu.Motorola6809 => M6809
|
case Cpu.Motorola6809 => M6809
|
||||||
}
|
}
|
||||||
|
@ -368,6 +374,10 @@ object Cpu extends Enumeration {
|
||||||
* The Zilog Z80 processor, without illegal instructions
|
* The Zilog Z80 processor, without illegal instructions
|
||||||
*/
|
*/
|
||||||
val StrictZ80: Cpu.Value = Value
|
val StrictZ80: Cpu.Value = Value
|
||||||
|
/**
|
||||||
|
* The R800 CPU (used in MSX Turbo-R)
|
||||||
|
*/
|
||||||
|
val R800: Cpu.Value = Value
|
||||||
/**
|
/**
|
||||||
* The Zilog eZ80 processor
|
* The Zilog eZ80 processor
|
||||||
*/
|
*/
|
||||||
|
@ -400,11 +410,11 @@ object Cpu extends Enumeration {
|
||||||
/**
|
/**
|
||||||
* Processors that can run code for Zilog Z80
|
* 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
|
* 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
|
* Processors that can run code for Intel 8085
|
||||||
*/
|
*/
|
||||||
|
@ -419,14 +429,18 @@ object Cpu extends Enumeration {
|
||||||
RegisterVariables,
|
RegisterVariables,
|
||||||
FunctionDeduplication,
|
FunctionDeduplication,
|
||||||
EnableBreakpoints,
|
EnableBreakpoints,
|
||||||
|
UseOptimizationHints,
|
||||||
GenericWarnings,
|
GenericWarnings,
|
||||||
|
ByteOverflowWarning,
|
||||||
UselessCodeWarning,
|
UselessCodeWarning,
|
||||||
BuggyCodeWarning,
|
BuggyCodeWarning,
|
||||||
FallbackValueUseWarning,
|
FallbackValueUseWarning,
|
||||||
|
BytePointerComparisonWarning,
|
||||||
DeprecationWarning,
|
DeprecationWarning,
|
||||||
NonZeroTerminatedLiteralWarning,
|
NonZeroTerminatedLiteralWarning,
|
||||||
CallToOverlappingBankWarning,
|
CallToOverlappingBankWarning,
|
||||||
DataMissingInOutputWarning,
|
DataMissingInOutputWarning,
|
||||||
|
UnsupportedOptimizationHintWarning,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val mosAlwaysDefaultFlags = alwaysDefaultFlags
|
private val mosAlwaysDefaultFlags = alwaysDefaultFlags
|
||||||
|
@ -464,6 +478,8 @@ object Cpu extends Enumeration {
|
||||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitIntel8085Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitIntel8085Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
||||||
case StrictZ80 | Z80 =>
|
case StrictZ80 | Z80 =>
|
||||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts)
|
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts)
|
||||||
|
case R800 =>
|
||||||
|
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitR800Opcodes)
|
||||||
case Z80Next =>
|
case Z80Next =>
|
||||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitIllegals, EmitZ80NextOpcodes)
|
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitIllegals, EmitZ80NextOpcodes)
|
||||||
case EZ80 =>
|
case EZ80 =>
|
||||||
|
@ -510,6 +526,7 @@ object Cpu extends Enumeration {
|
||||||
case "strict2a07" => StrictRicoh
|
case "strict2a07" => StrictRicoh
|
||||||
case "z80" => Z80
|
case "z80" => Z80
|
||||||
case "strictz80" => Z80
|
case "strictz80" => Z80
|
||||||
|
case "r800" => R800
|
||||||
case "zx80next" => Z80Next
|
case "zx80next" => Z80Next
|
||||||
case "z80next" => Z80Next
|
case "z80next" => Z80Next
|
||||||
// disabled for now:
|
// disabled for now:
|
||||||
|
@ -558,9 +575,9 @@ object CompilationFlag extends Enumeration {
|
||||||
EmitIllegals, DecimalMode, LenientTextEncoding, LineNumbersInAssembly, SourceInAssembly, EnableBreakpoints,
|
EmitIllegals, DecimalMode, LenientTextEncoding, LineNumbersInAssembly, SourceInAssembly, EnableBreakpoints,
|
||||||
// compilation options for MOS:
|
// compilation options for MOS:
|
||||||
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitWdcOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitWdcOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack,
|
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack, IdentityPage,
|
||||||
// compilation options for I80
|
// compilation options for I80
|
||||||
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
|
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitR800Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
|
||||||
UseShadowRegistersForInterrupts,
|
UseShadowRegistersForInterrupts,
|
||||||
UseIxForStack, UseIyForStack,
|
UseIxForStack, UseIyForStack,
|
||||||
UseIxForScratch, UseIyForScratch,
|
UseIxForScratch, UseIyForScratch,
|
||||||
|
@ -582,15 +599,18 @@ object CompilationFlag extends Enumeration {
|
||||||
SingleThreaded,
|
SingleThreaded,
|
||||||
// warning options
|
// warning options
|
||||||
GenericWarnings,
|
GenericWarnings,
|
||||||
|
ByteOverflowWarning,
|
||||||
UselessCodeWarning,
|
UselessCodeWarning,
|
||||||
BuggyCodeWarning,
|
BuggyCodeWarning,
|
||||||
DeprecationWarning,
|
DeprecationWarning,
|
||||||
FallbackValueUseWarning,
|
FallbackValueUseWarning,
|
||||||
|
BytePointerComparisonWarning,
|
||||||
ExtraComparisonWarnings,
|
ExtraComparisonWarnings,
|
||||||
RorWarning,
|
RorWarning,
|
||||||
NonZeroTerminatedLiteralWarning,
|
NonZeroTerminatedLiteralWarning,
|
||||||
CallToOverlappingBankWarning,
|
CallToOverlappingBankWarning,
|
||||||
DataMissingInOutputWarning,
|
DataMissingInOutputWarning,
|
||||||
|
UnsupportedOptimizationHintWarning,
|
||||||
FatalWarnings,
|
FatalWarnings,
|
||||||
// special options for internal compiler use
|
// special options for internal compiler use
|
||||||
EnableInternalTestSyntax,
|
EnableInternalTestSyntax,
|
||||||
|
@ -598,14 +618,17 @@ object CompilationFlag extends Enumeration {
|
||||||
|
|
||||||
val allWarnings: Set[CompilationFlag.Value] = Set(
|
val allWarnings: Set[CompilationFlag.Value] = Set(
|
||||||
GenericWarnings,
|
GenericWarnings,
|
||||||
|
ByteOverflowWarning,
|
||||||
UselessCodeWarning,
|
UselessCodeWarning,
|
||||||
BuggyCodeWarning,
|
BuggyCodeWarning,
|
||||||
DeprecationWarning,
|
DeprecationWarning,
|
||||||
FallbackValueUseWarning,
|
FallbackValueUseWarning,
|
||||||
|
BytePointerComparisonWarning,
|
||||||
ExtraComparisonWarnings,
|
ExtraComparisonWarnings,
|
||||||
NonZeroTerminatedLiteralWarning,
|
NonZeroTerminatedLiteralWarning,
|
||||||
CallToOverlappingBankWarning,
|
CallToOverlappingBankWarning,
|
||||||
DataMissingInOutputWarning,
|
DataMissingInOutputWarning,
|
||||||
|
UnsupportedOptimizationHintWarning,
|
||||||
)
|
)
|
||||||
|
|
||||||
val fromString: Map[String, CompilationFlag.Value] = Map(
|
val fromString: Map[String, CompilationFlag.Value] = Map(
|
||||||
|
@ -618,6 +641,7 @@ object CompilationFlag extends Enumeration {
|
||||||
"emit_65ce02" -> Emit65CE02Opcodes,
|
"emit_65ce02" -> Emit65CE02Opcodes,
|
||||||
"emit_huc6280" -> EmitHudsonOpcodes,
|
"emit_huc6280" -> EmitHudsonOpcodes,
|
||||||
"emit_z80" -> EmitZ80Opcodes,
|
"emit_z80" -> EmitZ80Opcodes,
|
||||||
|
"emit_r800" -> EmitR800Opcodes,
|
||||||
"emit_ez80" -> EmitEZ80Opcodes,
|
"emit_ez80" -> EmitEZ80Opcodes,
|
||||||
"emit_x80" -> EmitExtended80Opcodes,
|
"emit_x80" -> EmitExtended80Opcodes,
|
||||||
"emit_8080" -> EmitIntel8080Opcodes,
|
"emit_8080" -> EmitIntel8080Opcodes,
|
||||||
|
@ -630,6 +654,7 @@ object CompilationFlag extends Enumeration {
|
||||||
"u_stack" -> UseUForStack,
|
"u_stack" -> UseUForStack,
|
||||||
"y_stack" -> UseYForStack,
|
"y_stack" -> UseYForStack,
|
||||||
"software_stack" -> SoftwareStack,
|
"software_stack" -> SoftwareStack,
|
||||||
|
"identity_page" -> IdentityPage,
|
||||||
"use_shadow_registers_for_irq" -> UseShadowRegistersForInterrupts,
|
"use_shadow_registers_for_irq" -> UseShadowRegistersForInterrupts,
|
||||||
"output_intel_syntax" -> UseIntelSyntaxForOutput,
|
"output_intel_syntax" -> UseIntelSyntaxForOutput,
|
||||||
"input_intel_syntax" -> UseIntelSyntaxForInput,
|
"input_intel_syntax" -> UseIntelSyntaxForInput,
|
||||||
|
|
|
@ -46,10 +46,24 @@ case class Context(errorReporting: Logger,
|
||||||
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)) {
|
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)) {
|
||||||
addons += CompilationFlag.EmitZ80Opcodes -> true
|
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
|
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
|
addons += CompilationFlag.EmitIntel8080Opcodes -> true
|
||||||
}
|
}
|
||||||
if (isFlagSet(CompilationFlag.OptimizeForSpeed)) {
|
if (isFlagSet(CompilationFlag.OptimizeForSpeed)) {
|
||||||
|
|
|
@ -1,23 +1,37 @@
|
||||||
package millfork
|
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
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
|
|
||||||
object DebugOutputFormat {
|
object DebugOutputFormat {
|
||||||
val map: Map[String, DebugOutputFormat] = Map(
|
val map: Map[String, DebugOutputFormat] = Map(
|
||||||
|
"raw" -> RawDebugOutputFormat,
|
||||||
"vice" -> ViceDebugOutputFormat,
|
"vice" -> ViceDebugOutputFormat,
|
||||||
"nesasm" -> NesasmDebugOutputFormat,
|
"nesasm" -> NesasmDebugOutputFormat,
|
||||||
"fns" -> NesasmDebugOutputFormat,
|
"fns" -> NesasmDebugOutputFormat,
|
||||||
"fceux" -> FceuxDebugOutputFormat,
|
"fceux" -> FceuxDebugOutputFormat,
|
||||||
"nl" -> FceuxDebugOutputFormat,
|
"nl" -> FceuxDebugOutputFormat,
|
||||||
|
"mlb" -> MesenOutputFormat,
|
||||||
|
"mesen" -> MesenOutputFormat,
|
||||||
|
"asm6f" -> MesenOutputFormat,
|
||||||
|
"ld65" -> Ld65OutputFormat,
|
||||||
|
"ca65" -> Ld65OutputFormat,
|
||||||
|
"cc65" -> Ld65OutputFormat,
|
||||||
|
"dbg" -> Ld65OutputFormat,
|
||||||
"sym" -> SymDebugOutputFormat)
|
"sym" -> SymDebugOutputFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait DebugOutputFormat {
|
sealed trait DebugOutputFormat {
|
||||||
|
|
||||||
def formatAll(labels: Seq[(String, (Int, Int))], breakpoints: Seq[(Int, Int)]): String = {
|
def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
|
||||||
val labelPart = labelsHeader + labels.map(formatLineTupled).mkString("\n") + "\n"
|
val labelPart = labelsHeader + labels.map(formatLine).mkString("\n") + "\n"
|
||||||
if (breakpoints.isEmpty) {
|
if (breakpoints.isEmpty) {
|
||||||
labelPart
|
labelPart
|
||||||
} else {
|
} 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: FormattableLabel): String
|
||||||
|
|
||||||
def formatLine(label: String, bank: Int, value: Int): String
|
|
||||||
|
|
||||||
final def formatBreakpointTupled(value: (Int, Int)): Seq[String] = formatBreakpoint(value._1, value._2).toSeq
|
final def formatBreakpointTupled(value: (Int, Int)): Seq[String] = formatBreakpoint(value._1, value._2).toSeq
|
||||||
|
|
||||||
|
@ -45,10 +57,25 @@ sealed trait DebugOutputFormat {
|
||||||
def breakpointsHeader: String = ""
|
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 {
|
object ViceDebugOutputFormat extends DebugOutputFormat {
|
||||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
override def formatLine(label: FormattableLabel): String = {
|
||||||
val normalized = label.replace('$', '_').replace('.', '_')
|
val normalized = label.labelName.replace('$', '_').replace('.', '_')
|
||||||
s"al ${value.toHexString} .$normalized"
|
s"al ${label.startValue.toHexString} .$normalized"
|
||||||
}
|
}
|
||||||
|
|
||||||
override def fileExtension(bank: Int): String = ".lbl"
|
override def fileExtension(bank: Int): String = ".lbl"
|
||||||
|
@ -61,8 +88,8 @@ object ViceDebugOutputFormat extends DebugOutputFormat {
|
||||||
}
|
}
|
||||||
|
|
||||||
object NesasmDebugOutputFormat extends DebugOutputFormat {
|
object NesasmDebugOutputFormat extends DebugOutputFormat {
|
||||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
override def formatLine(label: FormattableLabel): String = {
|
||||||
label + " = $" + value.toHexString
|
label.labelName + " = $" + label.startValue.toHexString
|
||||||
}
|
}
|
||||||
|
|
||||||
override def fileExtension(bank: Int): String = ".fns"
|
override def fileExtension(bank: Int): String = ".fns"
|
||||||
|
@ -75,8 +102,8 @@ object NesasmDebugOutputFormat extends DebugOutputFormat {
|
||||||
}
|
}
|
||||||
|
|
||||||
object SymDebugOutputFormat extends DebugOutputFormat {
|
object SymDebugOutputFormat extends DebugOutputFormat {
|
||||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
override def formatLine(label: FormattableLabel): String = {
|
||||||
f"$bank%02x:$value%04x $label%s"
|
f"${label.bankNumber}%02x:${label.startValue}%04x ${label.labelName}%s"
|
||||||
}
|
}
|
||||||
|
|
||||||
override def fileExtension(bank: Int): String = ".sym"
|
override def fileExtension(bank: Int): String = ".sym"
|
||||||
|
@ -93,8 +120,8 @@ object SymDebugOutputFormat extends DebugOutputFormat {
|
||||||
}
|
}
|
||||||
|
|
||||||
object FceuxDebugOutputFormat extends DebugOutputFormat {
|
object FceuxDebugOutputFormat extends DebugOutputFormat {
|
||||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
override def formatLine(label: FormattableLabel): String = {
|
||||||
f"$$$value%04x#$label%s#"
|
f"$$${label.startValue}%04x#${label.labelName}%s#"
|
||||||
}
|
}
|
||||||
|
|
||||||
override def fileExtension(bank: Int): String = if (bank == 0xff) ".ram.nl" else s".$bank.nl"
|
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
|
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 if (l.startsWith("__")) 7
|
||||||
else 0
|
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 sortedBreakpoints = result.breakpoints
|
||||||
val format = c.outputLabelsFormatOverride.getOrElse(platform.outputLabelsFormat)
|
val format = c.outputLabelsFormatOverride.getOrElse(platform.outputLabelsFormat)
|
||||||
val basename = if (format.addOutputExtension) output + platform.fileExtension else output
|
val basename = if (format.addOutputExtension) output + platform.fileExtension else output
|
||||||
if (format.filePerBank) {
|
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 =>
|
banks.foreach{ bank =>
|
||||||
val labels = sortedLabels.filter(_._2._1.==(bank))
|
val labels = sortedLabels.filter(_.bankNumber.==(bank))
|
||||||
val breakpoints = sortedBreakpoints.filter(_._1.==(bank))
|
val breakpoints = sortedBreakpoints.filter(_._1.==(bank))
|
||||||
val labelOutput = basename + format.fileExtension(bank)
|
val labelOutput = basename + format.fileExtension(bank)
|
||||||
val path = Paths.get(labelOutput)
|
val path = Paths.get(labelOutput)
|
||||||
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
|
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 {
|
} else {
|
||||||
val labelOutput = basename + format.fileExtension(0)
|
val labelOutput = basename + format.fileExtension(0)
|
||||||
val path = Paths.get(labelOutput)
|
val path = Paths.get(labelOutput)
|
||||||
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
|
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
|
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))
|
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 =>
|
c.runFileName.foreach{ program =>
|
||||||
if (File.separatorChar == '\\') {
|
if (File.separatorChar == '\\') {
|
||||||
if (!new File(program).exists() && !new File(program + ".exe").exists()) {
|
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")
|
log.debug(s"Failed to find the default include path: $err")
|
||||||
case Right(path) =>
|
case Right(path) =>
|
||||||
log.debug(s"Automatically detected include path: $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 = {
|
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.EmitHudsonOpcodes)) HudsonOptimizations.All else Nil,
|
||||||
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForEmulation else Nil,
|
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForEmulation else Nil,
|
||||||
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) SixteenOptimizations.AllForNative 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
|
if (options.flag(CompilationFlag.DangerousOptimizations)) DangerousOptimizations.All else Nil
|
||||||
).flatten
|
).flatten
|
||||||
val goodCycle = List.fill(optLevel - 2)(OptimizationPresets.Good ++ goodExtras).flatten
|
val goodCycle = List.fill(optLevel - 2)(OptimizationPresets.Good ++ goodExtras).flatten
|
||||||
|
@ -321,7 +332,9 @@ object Main {
|
||||||
val assemblyOptimizations = optLevel match {
|
val assemblyOptimizations = optLevel match {
|
||||||
case 0 => Nil
|
case 0 => Nil
|
||||||
case _ =>
|
case _ =>
|
||||||
if (options.flag(CompilationFlag.EmitZ80Opcodes))
|
if (options.flag(CompilationFlag.EmitR800Opcodes))
|
||||||
|
Z80OptimizationPresets.GoodForR800
|
||||||
|
else if (options.flag(CompilationFlag.EmitZ80Opcodes))
|
||||||
Z80OptimizationPresets.GoodForZ80
|
Z80OptimizationPresets.GoodForZ80
|
||||||
else if (options.flag(CompilationFlag.EmitIntel8080Opcodes))
|
else if (options.flag(CompilationFlag.EmitIntel8080Opcodes))
|
||||||
Z80OptimizationPresets.GoodForIntel8080
|
Z80OptimizationPresets.GoodForIntel8080
|
||||||
|
@ -427,7 +440,7 @@ object Main {
|
||||||
p.toLowerCase(Locale.ROOT),
|
p.toLowerCase(Locale.ROOT),
|
||||||
errorReporting.fatal("Invalid label file format: " + p))
|
errorReporting.fatal("Invalid label file format: " + p))
|
||||||
c.copy(outputLabels = true, outputLabelsFormatOverride = Some(f))
|
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) =>
|
boolean("-fbreakpoints", "-fno-breakpoints").action((c,v) =>
|
||||||
c.changeFlag(CompilationFlag.EnableBreakpoints, v)
|
c.changeFlag(CompilationFlag.EnableBreakpoints, v)
|
||||||
|
@ -487,7 +500,7 @@ object Main {
|
||||||
} else {
|
} else {
|
||||||
errorReporting.fatal("Invalid syntax for -D option")
|
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) =>
|
boolean("-finput_intel_syntax", "-finput_zilog_syntax").repeatable().action((c,v) =>
|
||||||
c.changeFlag(CompilationFlag.UseIntelSyntaxForInput, v)
|
c.changeFlag(CompilationFlag.UseIntelSyntaxForInput, v)
|
||||||
|
@ -650,7 +663,7 @@ object Main {
|
||||||
c.changeFlag(CompilationFlag.CompactReturnDispatchParams, v)
|
c.changeFlag(CompilationFlag.CompactReturnDispatchParams, v)
|
||||||
}.description("Whether parameter values in return dispatch statements may overlap other objects. Enabled by default.")
|
}.description("Whether parameter values in return dispatch statements may overlap other objects. Enabled by default.")
|
||||||
boolean("-fbounds-checking", "-fno-bounds-checking").action { (c, v) =>
|
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.")
|
}.description("Whether should insert bounds checking on array access.")
|
||||||
boolean("-flenient-encoding", "-fno-lenient-encoding").action { (c, v) =>
|
boolean("-flenient-encoding", "-fno-lenient-encoding").action { (c, v) =>
|
||||||
c.changeFlag(CompilationFlag.LenientTextEncoding, v)
|
c.changeFlag(CompilationFlag.LenientTextEncoding, v)
|
||||||
|
@ -712,6 +725,9 @@ object Main {
|
||||||
}.description("Optimize code even more.")
|
}.description("Optimize code even more.")
|
||||||
if (i == 1 || i > 4) f.hidden()
|
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 =>
|
flag("--inline").repeatable().action { c =>
|
||||||
c.changeFlag(CompilationFlag.InlineFunctions, true)
|
c.changeFlag(CompilationFlag.InlineFunctions, true)
|
||||||
}.description("Inline functions automatically.").hidden()
|
}.description("Inline functions automatically.").hidden()
|
||||||
|
@ -742,6 +758,9 @@ object Main {
|
||||||
boolean("-fregister-variables", "-fno-register-variables").action { (c, v) =>
|
boolean("-fregister-variables", "-fno-register-variables").action { (c, v) =>
|
||||||
c.changeFlag(CompilationFlag.RegisterVariables, v)
|
c.changeFlag(CompilationFlag.RegisterVariables, v)
|
||||||
}.description("Allow moving local variables into CPU registers. Enabled by default.")
|
}.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 =>
|
flag("-Os", "--size").repeatable().action { c =>
|
||||||
c.changeFlag(CompilationFlag.OptimizeForSize, true).
|
c.changeFlag(CompilationFlag.OptimizeForSize, true).
|
||||||
changeFlag(CompilationFlag.OptimizeForSpeed, false).
|
changeFlag(CompilationFlag.OptimizeForSpeed, false).
|
||||||
|
@ -754,6 +773,7 @@ object Main {
|
||||||
}.description("Prefer faster code even if it is slightly bigger (experimental). Implies -finline.")
|
}.description("Prefer faster code even if it is slightly bigger (experimental). Implies -finline.")
|
||||||
flag("-Ob", "--blast-processing").repeatable().action { c =>
|
flag("-Ob", "--blast-processing").repeatable().action { c =>
|
||||||
c.changeFlag(CompilationFlag.OptimizeForSize, false).
|
c.changeFlag(CompilationFlag.OptimizeForSize, false).
|
||||||
|
changeFlag(CompilationFlag.IdentityPage, true).
|
||||||
changeFlag(CompilationFlag.OptimizeForSpeed, true).
|
changeFlag(CompilationFlag.OptimizeForSpeed, true).
|
||||||
changeFlag(CompilationFlag.OptimizeForSonicSpeed, true)
|
changeFlag(CompilationFlag.OptimizeForSonicSpeed, true)
|
||||||
}.description("Prefer faster code even if it is much bigger (experimental). Implies -finline.")
|
}.description("Prefer faster code even if it is much bigger (experimental). Implies -finline.")
|
||||||
|
@ -791,6 +811,10 @@ object Main {
|
||||||
c.changeFlag(CompilationFlag.DeprecationWarning, v)
|
c.changeFlag(CompilationFlag.DeprecationWarning, v)
|
||||||
}.description("Whether should warn about deprecated aliases. Default: enabled.")
|
}.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) =>
|
boolean("-Wextra-comparisons", "-Wno-extra-comparisons").repeatable().action { (c, v) =>
|
||||||
c.changeFlag(CompilationFlag.ExtraComparisonWarnings, v)
|
c.changeFlag(CompilationFlag.ExtraComparisonWarnings, v)
|
||||||
}.description("Whether should warn about simplifiable unsigned integer comparisons. Default: disabled.")
|
}.description("Whether should warn about simplifiable unsigned integer comparisons. Default: disabled.")
|
||||||
|
@ -811,10 +835,18 @@ object Main {
|
||||||
c.changeFlag(CompilationFlag.RorWarning, v)
|
c.changeFlag(CompilationFlag.RorWarning, v)
|
||||||
}.description("Whether should warn about the ROR instruction (6502 only). Default: disabled.")
|
}.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) =>
|
boolean("-Wuseless", "-Wno-useless").repeatable().action { (c, v) =>
|
||||||
c.changeFlag(CompilationFlag.UselessCodeWarning, v)
|
c.changeFlag(CompilationFlag.UselessCodeWarning, v)
|
||||||
}.description("Whether should warn about code that does nothing. Default: enabled.")
|
}.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:", "")
|
fluff("", "Other options:", "")
|
||||||
|
|
||||||
expansion("-Xd")("-O1", "-s", "-fsource-in-asm", "-g").description("Do a debug build. Equivalent to -O1 -s -fsource-in-asm -g")
|
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 outputLabelsFormat: DebugOutputFormat,
|
||||||
val outputStyle: OutputStyle.Value
|
val outputStyle: OutputStyle.Value
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def hasZeroPage: Boolean = cpuFamily == CpuFamily.M6502
|
def hasZeroPage: Boolean = cpuFamily == CpuFamily.M6502
|
||||||
|
|
||||||
def cpuFamily: CpuFamily.Value = CpuFamily.forType(this.cpu)
|
def cpuFamily: CpuFamily.Value = CpuFamily.forType(this.cpu)
|
||||||
|
@ -60,6 +61,18 @@ class Platform(
|
||||||
val e2 = bankEndBefore(bank2)
|
val e2 = bankEndBefore(bank2)
|
||||||
e2 > s1 && s2 < e1
|
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 {
|
object Platform {
|
||||||
|
|
|
@ -2,7 +2,7 @@ package millfork.assembly
|
||||||
|
|
||||||
import millfork.{CompilationFlag, CompilationOptions}
|
import millfork.{CompilationFlag, CompilationOptions}
|
||||||
import millfork.compiler.LabelGenerator
|
import millfork.compiler.LabelGenerator
|
||||||
import millfork.env.{NormalFunction, ThingInMemory}
|
import millfork.env.{Constant, NormalFunction, ThingInMemory}
|
||||||
import millfork.error.Logger
|
import millfork.error.Logger
|
||||||
import millfork.node.NiceFunctionProperty
|
import millfork.node.NiceFunctionProperty
|
||||||
|
|
||||||
|
@ -10,8 +10,9 @@ import millfork.node.NiceFunctionProperty
|
||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
case class OptimizationContext(options: CompilationOptions,
|
case class OptimizationContext(options: CompilationOptions,
|
||||||
labelMap: Map[String, (Int, Int)],
|
labelMap: Map[String, (String, Int)],
|
||||||
zreg: Option[ThingInMemory],
|
zreg: Option[ThingInMemory],
|
||||||
|
identityPage: Constant,
|
||||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)]) {
|
niceFunctionProperties: Set[(NiceFunctionProperty, String)]) {
|
||||||
@inline
|
@inline
|
||||||
def log: Logger = options.log
|
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 optimize(f: NormalFunction, code: List[T], context: OptimizationContext): List[T]
|
||||||
|
|
||||||
def requiredFlags: Set[CompilationFlag.Value] = Set.empty
|
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 = {
|
def changesRegister(reg: M6809Register.Value): Boolean = {
|
||||||
import M6809Register._
|
import M6809Register._
|
||||||
|
if (MOpcode.NotActualOpcodes(opcode)) return false
|
||||||
def overlaps(other: M6809Register.Value): Boolean = {
|
def overlaps(other: M6809Register.Value): Boolean = {
|
||||||
if (reg == D && (other == A || other == B)) true
|
if (reg == D && (other == A || other == B)) true
|
||||||
else if (other == D && (reg == A || reg == 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 = {
|
def readsRegister(reg: M6809Register.Value): Boolean = {
|
||||||
import M6809Register._
|
import M6809Register._
|
||||||
|
if (MOpcode.NotActualOpcodes(opcode)) return false
|
||||||
def overlaps(other: M6809Register.Value): Boolean = {
|
def overlaps(other: M6809Register.Value): Boolean = {
|
||||||
if (reg == D && (other == A || other == B)) true
|
if (reg == D && (other == A || other == B)) true
|
||||||
else if (other == D && (reg == A || reg == 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 MUL => overlaps(D)
|
||||||
case ABX => reg == X || overlaps(B)
|
case ABX => reg == X || overlaps(B)
|
||||||
case NOP | SWI | SWI2 | SWI3 | SYNC => false
|
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 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 op if Branching(op) => false
|
||||||
case JMP => false
|
case JMP | RTS => false
|
||||||
case _ => true // TODO
|
case _ => true // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def readsMemory(): Boolean = {
|
def readsMemory(): Boolean = {
|
||||||
import MOpcode._
|
import MOpcode._
|
||||||
|
if (MOpcode.NotActualOpcodes(opcode)) return false
|
||||||
val opcodeIsForReading = opcode match {
|
val opcodeIsForReading = opcode match {
|
||||||
case LDA | LDB | LDD | LDX | LDY | LDU | LDS | PULU | PULS => true
|
case LDA | LDB | LDD | LDX | LDY | LDU | LDS | PULU | PULS => true
|
||||||
case ADDA | SUBA | ADCA | SBCA | ORA | EORA | ANDA | CMPA | BITA => 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 = {
|
def changesMemory(): Boolean = {
|
||||||
import MOpcode._
|
import MOpcode._
|
||||||
|
if (NotActualOpcodes(opcode)) return false
|
||||||
val opcodeIsForWriting = opcode match {
|
val opcodeIsForWriting = opcode match {
|
||||||
case LDA | LDB | LDD | LDX | LDY | LDU | LDS | PULU | PULS => false
|
case LDA | LDB | LDD | LDX | LDY | LDU | LDS | PULU | PULS => false
|
||||||
case ADDA | SUBA | ADCA | SBCA | ORA | EORA | ANDA | CMPA | BITA => false
|
case ADDA | SUBA | ADCA | SBCA | ORA | EORA | ANDA | CMPA | BITA => false
|
||||||
|
|
|
@ -32,9 +32,10 @@ object MOpcode extends Enumeration {
|
||||||
TFR, TST,
|
TFR, TST,
|
||||||
DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL = Value
|
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] = {
|
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 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
|
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.AssemblyOptimization
|
||||||
import millfork.assembly.m6809.MOpcode._
|
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
|
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(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 & HasOpcodeIn(LDY, LEAY) & DoesntMatterWhatItDoesWith(MState.Y, MState.NF, MState.ZF, MState.VF)) ~~> (_ => Nil),
|
||||||
(Elidable & HasOpcode(STB) & MatchAddrMode(0) & MatchParameter(1)) ~
|
(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)
|
(Elidable & HasOpcode(LDB) & MatchAddrMode(0) & MatchParameter(1)
|
||||||
& DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> (_.init),
|
& DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> (_.init),
|
||||||
(Elidable & HasOpcode(STD) & MatchAddrMode(0) & MatchParameter(1)) ~
|
(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)
|
(Elidable & HasOpcode(LDD) & MatchAddrMode(0) & MatchParameter(1)
|
||||||
& DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> (_.init),
|
& 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",
|
val SimplifiableZeroStore = new RuleBasedAssemblyOptimization("Simplifiable zero store",
|
||||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||||
(Elidable & HasOpcode(LDA) & HasImmediate(0) & DoesntMatterWhatItDoesWith(MState.CF)) ~~> {
|
(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",
|
val PointlessRegisterTransfers = new RuleBasedAssemblyOptimization("Pointless register transfers",
|
||||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||||
(Elidable & IsTfr(M6809Register.D, M6809Register.X)) ~
|
(Elidable & IsTfr(M6809Register.D, M6809Register.X)) ~
|
||||||
|
@ -144,14 +237,67 @@ object AlwaysGoodMOptimizations {
|
||||||
(_.map(_.copy(opcode = LDD))),
|
(_.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)) ~~>
|
(Elidable & HasImmediate(0) & HasOpcodeIn(ORB, EORB, ADDB, ORA, EORA, ADDA, ADDD) & DoesntMatterWhatItDoesWith(MState.VF, MState.ZF, MState.NF, MState.CF, MState.HF)) ~~>
|
||||||
(_ => Nil),
|
(_ => 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(
|
val All: Seq[AssemblyOptimization[MLine]] = Seq(
|
||||||
PointlessLoad,
|
PointlessLoad,
|
||||||
|
PointlessCompare,
|
||||||
PointlessRegisterTransfers,
|
PointlessRegisterTransfers,
|
||||||
|
SimplifiableAddressing,
|
||||||
SimplifiableArithmetics,
|
SimplifiableArithmetics,
|
||||||
|
SimplifiableComparison,
|
||||||
SimplifiableJumps,
|
SimplifiableJumps,
|
||||||
SimplifiableZeroStore
|
SimplifiableZeroStore,
|
||||||
|
UnusedLabelRemoval
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,11 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||||
def nzB(i: Long): CpuStatus =
|
def nzB(i: Long): CpuStatus =
|
||||||
this.copy(n = SingleStatus((i & 0x80) != 0), z = SingleStatus((i & 0xff) == 0))
|
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 =
|
def nzW(i: Long): CpuStatus =
|
||||||
this.copy(n = SingleStatus((i & 0x8000) != 0), z = SingleStatus((i & 0xffff) == 0))
|
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
|
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(
|
def ~(that: CpuStatus) = new CpuStatus(
|
||||||
a = this.a ~ that.a,
|
a = this.a ~ that.a,
|
||||||
b = this.b ~ that.b,
|
b = this.b ~ that.b,
|
||||||
|
|
|
@ -52,7 +52,7 @@ object FlowAnalyzer {
|
||||||
}
|
}
|
||||||
val labelMap: () => Option[Map[String, Int]] = () => req match {
|
val labelMap: () => Option[Map[String, Int]] = () => req match {
|
||||||
case FlowInfoRequirement.NoRequirement => None
|
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)
|
val holder = new FlowHolder(forwardFlow, reverseFlow)
|
||||||
code.zipWithIndex.map{ case (line, i) => FlowInfo(holder, i, labelMap) -> line}
|
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.CompilationFlag
|
||||||
import millfork.assembly.OptimizationContext
|
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.Status.SingleFalse
|
||||||
import millfork.assembly.opt.{AnyStatus, FlowCache, SingleStatus, Status}
|
import millfork.assembly.opt.{AnyStatus, FlowCache, SingleStatus, Status}
|
||||||
import millfork.env._
|
import millfork.env._
|
||||||
|
import millfork.node.M6809NiceFunctionProperty.{DoesntChangeA, DoesntChangeB, DoesntChangeU, DoesntChangeX, DoesntChangeY}
|
||||||
import millfork.node.{M6809Register, NiceFunctionProperty}
|
import millfork.node.{M6809Register, NiceFunctionProperty}
|
||||||
|
|
||||||
import scala.util.control.Breaks._
|
import scala.util.control.Breaks._
|
||||||
|
@ -62,33 +63,108 @@ object ForwardFlowAnalysis {
|
||||||
case _ => None
|
case _ => None
|
||||||
}).fold(currentStatus)(_ ~ _)
|
}).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(
|
currentStatus = initialStatus.copy(
|
||||||
memStack = currentStatus.memStack,
|
memStack = currentStatus.memStack,
|
||||||
u = if (bpInU) currentStatus.u else AnyStatus,
|
u = if (prU) currentStatus.u else AnyStatus,
|
||||||
y = if (bpInY) currentStatus.y 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, _, _) =>
|
case MLine0(JSR | BYTE, _, _) =>
|
||||||
currentStatus = initialStatus
|
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, _)) =>
|
case MLine0(LDA, _, _) =>
|
||||||
currentStatus = currentStatus.copy(a = SingleStatus(n.toInt & 0xff), v = SingleFalse).nzB(n)
|
currentStatus = currentStatus.copy(a = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||||
case MLine0(LDB, Immediate, NumericConstant(n, _)) =>
|
case MLine0(LDB, _, _) =>
|
||||||
currentStatus = currentStatus.copy(b = SingleStatus(n.toInt & 0xff), v = SingleFalse).nzB(n)
|
currentStatus = currentStatus.copy(b = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||||
case MLine0(LDD, Immediate, NumericConstant(n, _)) =>
|
case MLine0(LDD, _, _) =>
|
||||||
currentStatus = currentStatus.copy(
|
currentStatus = currentStatus.copy(a = AnyStatus, b = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||||
a = SingleStatus(n.toInt.>>(8) & 0xff),
|
case MLine0(LDX, _, _) =>
|
||||||
b = SingleStatus(n.toInt & 0xff),
|
currentStatus = currentStatus.copy(x = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||||
v = SingleFalse
|
case MLine0(LDY, _, _) =>
|
||||||
).nzW(n)
|
currentStatus = currentStatus.copy(y = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||||
case MLine0(LDX, Immediate, c) =>
|
case MLine0(STA | STB | STD | STX | STU | STY | STS, _, _) =>
|
||||||
currentStatus = currentStatus.copy(x = SingleStatus(c), v = SingleFalse).nzW(c)
|
// don't change
|
||||||
case MLine0(LDY, Immediate, c) =>
|
case MLine0(TFR, TwoRegisters(source, target), _) =>
|
||||||
currentStatus = currentStatus.copy(y = SingleStatus(c), v = SingleFalse).nzW(c)
|
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, _) =>
|
case MLine0(opcode, addrMode, _) =>
|
||||||
// TODO
|
// 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
|
package millfork.assembly.m6809.opt
|
||||||
|
|
||||||
|
import jdk.jfr.BooleanFlag
|
||||||
|
import millfork.CompilationFlag
|
||||||
import millfork.assembly._
|
import millfork.assembly._
|
||||||
import millfork.assembly.m6809.{Absolute, MLine, MLine0, MOpcode, MState}
|
import millfork.assembly.m6809.{Absolute, MLine, MLine0, MOpcode, MState}
|
||||||
import millfork.assembly.opt.FlowCache
|
import millfork.assembly.opt.FlowCache
|
||||||
|
@ -81,6 +83,7 @@ object ReverseFlowAnalyzer {
|
||||||
val readsB: Set[String] = Set("call")
|
val readsB: Set[String] = Set("call")
|
||||||
val readsX: Set[String] = Set("call")
|
val readsX: Set[String] = Set("call")
|
||||||
val readsY: Set[String] = Set("")
|
val readsY: Set[String] = Set("")
|
||||||
|
val preservesX: Set[String] = Set("__divmod_u8u8u8u8")
|
||||||
|
|
||||||
val cache = new FlowCache[MLine, CpuImportance]("m6809 reverse")
|
val cache = new FlowCache[MLine, CpuImportance]("m6809 reverse")
|
||||||
private val importanceBeforeJsr: CpuImportance = CpuImportance(
|
private val importanceBeforeJsr: CpuImportance = CpuImportance(
|
||||||
|
@ -96,8 +99,8 @@ object ReverseFlowAnalyzer {
|
||||||
hf = Unimportant)
|
hf = Unimportant)
|
||||||
private val finalImportance: CpuImportance = CpuImportance(
|
private val finalImportance: CpuImportance = CpuImportance(
|
||||||
a = Important, b = Important,
|
a = Important, b = Important,
|
||||||
x = Important, y = Important, u = Important,
|
x = Important, y = Important, u = Important, // TODO: correctly check which registers are important given the function and the compilation options
|
||||||
cf = Important, vf = Important, hf = Important, zf = Important, nf = Important)
|
cf = Unimportant, vf = Unimportant, hf = Unimportant, zf = Unimportant, nf = Unimportant)
|
||||||
|
|
||||||
//noinspection RedundantNewCaseClass
|
//noinspection RedundantNewCaseClass
|
||||||
def analyze(f: NormalFunction, code: List[MLine], optimizationContext: OptimizationContext): List[CpuImportance] = {
|
def analyze(f: NormalFunction, code: List[MLine], optimizationContext: OptimizationContext): List[CpuImportance] = {
|
||||||
|
@ -108,9 +111,27 @@ object ReverseFlowAnalyzer {
|
||||||
|
|
||||||
var changed = true
|
var changed = true
|
||||||
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) {
|
while (changed) {
|
||||||
changed = false
|
changed = false
|
||||||
var currentImportance = finalImportance
|
var currentImportance = actualFinalImportance
|
||||||
for (i <- codeArray.indices.reverse) {
|
for (i <- codeArray.indices.reverse) {
|
||||||
import millfork.assembly.m6809.MOpcode._
|
import millfork.assembly.m6809.MOpcode._
|
||||||
import millfork.node.M6809NiceFunctionProperty._
|
import millfork.node.M6809NiceFunctionProperty._
|
||||||
|
@ -126,11 +147,22 @@ object ReverseFlowAnalyzer {
|
||||||
case MLine0(LABEL, _, MemoryAddressConstant(Label(L))) => true
|
case MLine0(LABEL, _, MemoryAddressConstant(Label(L))) => true
|
||||||
case _ => false
|
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 _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
currentLine match {
|
currentLine match {
|
||||||
|
|
||||||
|
case MLine0(RTS, _, _) =>
|
||||||
|
currentImportance = actualFinalImportance
|
||||||
|
case MLine0(LABEL, _, _) =>
|
||||||
|
// do nothing
|
||||||
case MLine0(JSR | JMP, Absolute(false), MemoryAddressConstant(fun: FunctionInMemory)) =>
|
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
|
// this case has to be handled first, because the generic JSR importance handler is too conservative
|
||||||
var result = importanceBeforeJsr
|
var result = importanceBeforeJsr
|
||||||
|
@ -161,6 +193,7 @@ object ReverseFlowAnalyzer {
|
||||||
if (readsB(fun.name)) result = result.copy(b = Important)
|
if (readsB(fun.name)) result = result.copy(b = Important)
|
||||||
if (readsX(fun.name)) result = result.copy(x = Important)
|
if (readsX(fun.name)) result = result.copy(x = Important)
|
||||||
if (readsY(fun.name)) result = result.copy(y = Important)
|
if (readsY(fun.name)) result = result.copy(y = Important)
|
||||||
|
if (preservesX(fun.name)) result = result.copy(x = currentImportance.x)
|
||||||
currentImportance = result.copy(
|
currentImportance = result.copy(
|
||||||
a = if (niceFunctionProperties(DoesntChangeA -> fun.name)) currentImportance.a ~ result.a else result.a,
|
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,
|
b = if (niceFunctionProperties(DoesntChangeB -> fun.name)) currentImportance.b ~ result.b else result.b,
|
||||||
|
@ -173,7 +206,7 @@ object ReverseFlowAnalyzer {
|
||||||
case MLine0(opcode, addrMode, _) =>
|
case MLine0(opcode, addrMode, _) =>
|
||||||
if (MOpcode.ChangesC(opcode)) currentImportance = currentImportance.copy(cf = Unimportant)
|
if (MOpcode.ChangesC(opcode)) currentImportance = currentImportance.copy(cf = Unimportant)
|
||||||
if (MOpcode.ChangesN(opcode)) currentImportance = currentImportance.copy(nf = 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.ChangesZ(opcode)) currentImportance = currentImportance.copy(zf = Unimportant)
|
||||||
if (MOpcode.ReadsC(opcode)) currentImportance = currentImportance.copy(cf = Important)
|
if (MOpcode.ReadsC(opcode)) currentImportance = currentImportance.copy(cf = Important)
|
||||||
if (MOpcode.ReadsH(opcode)) currentImportance = currentImportance.copy(hf = 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 {
|
def assertLabels(x: FlowInfoRequirement.Value): Unit = x match {
|
||||||
case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required")
|
case NoRequirement => FatalErrorReporting.reportFlyingPig("Label info required")
|
||||||
case _ => ()
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait AssemblyRuleSet{
|
trait AssemblyRuleSet{
|
||||||
def flatten: Seq[AssemblyRule]
|
def flatten: Seq[AssemblyRule]
|
||||||
|
|
||||||
|
def minimumRequiredLines: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRuleSet*) extends AssemblyOptimization[MLine] {
|
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRuleSet*) extends AssemblyOptimization[MLine] {
|
||||||
|
|
||||||
private val actualRules = rules.flatMap(_.flatten)
|
private val actualRules = rules.flatMap(_.flatten)
|
||||||
actualRules.foreach(_.pattern.validate(needsFlowInfo))
|
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] = {
|
override def optimize(f: NormalFunction, code: List[MLine], optimizationContext: OptimizationContext): List[MLine] = {
|
||||||
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
|
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
|
||||||
val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext)
|
optimizeImpl(f, code, taggedCode, optimizationContext)
|
||||||
if (changed) optimized else code
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
val log = optimizationContext.log
|
||||||
code match {
|
taggedCode match {
|
||||||
case Nil => false -> Nil
|
case Nil => code
|
||||||
case head :: tail =>
|
case head :: tail =>
|
||||||
for ((rule, index) <- actualRules.zipWithIndex) {
|
val codeLength = code.length
|
||||||
|
for {
|
||||||
|
(rule, index) <- actualRulesWithIndex
|
||||||
|
if codeLength >= rule.minimumRequiredLines
|
||||||
|
} {
|
||||||
val ctx = new AssemblyMatchingContext(
|
val ctx = new AssemblyMatchingContext(
|
||||||
optimizationContext.options,
|
optimizationContext.options,
|
||||||
optimizationContext.labelMap,
|
optimizationContext.labelMap,
|
||||||
optimizationContext.niceFunctionProperties,
|
optimizationContext.niceFunctionProperties,
|
||||||
head._1.labelUseCount(_)
|
head._1.labelUseCount(_)
|
||||||
)
|
)
|
||||||
rule.pattern.matchTo(ctx, code) match {
|
rule.pattern.matchTo(ctx, taggedCode) match {
|
||||||
case Some(rest: List[(FlowInfo, MLine)]) =>
|
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 optimizedChunk: List[MLine] = rule.result(matchedChunkToOptimize, ctx)
|
||||||
val optimizedChunkWithSource =
|
val optimizedChunkWithSource =
|
||||||
if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk
|
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 (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 if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
|
||||||
else optimizedChunk
|
else optimizedChunk
|
||||||
log.debug(s"Applied $name ($index)")
|
if (log.debugEnabled) {
|
||||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
log.debug(s"Applied $name ($index)")
|
||||||
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.traceEnabled) {
|
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))
|
matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString))
|
||||||
log.trace(" ↓")
|
log.trace(" ↓")
|
||||||
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
|
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
|
||||||
}
|
}
|
||||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||||
return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2)
|
return optimizedChunkWithSource ++ optimizeImpl(f, restOfCode, rest, optimizationContext)
|
||||||
} else {
|
} else {
|
||||||
return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext)
|
return optimize(f, optimizedChunkWithSource ++ restOfCode, optimizationContext)
|
||||||
}
|
}
|
||||||
case None => ()
|
case None => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext)
|
val optimizedTail = optimizeImpl(f, code.tail, tail, optimizationContext)
|
||||||
(changedTail, head._2 :: optimizedTail)
|
if (optimizedTail eq code.tail) {
|
||||||
|
code
|
||||||
|
} else {
|
||||||
|
code.head :: optimizedTail
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
||||||
val labelMap: Map[String, (Int, Int)],
|
val labelMap: Map[String, (String, Int)],
|
||||||
val niceFunctionProperties: Set[(NiceFunctionProperty, String)],
|
val niceFunctionProperties: Set[(NiceFunctionProperty, String)],
|
||||||
val labelUseCount: String => Int) {
|
val labelUseCount: String => Int) {
|
||||||
@inline
|
@inline
|
||||||
|
@ -211,10 +227,14 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
||||||
|
|
||||||
case class AssemblyRule(pattern: AssemblyPattern, result: (List[MLine], AssemblyMatchingContext) => List[MLine]) extends AssemblyRuleSet {
|
case class AssemblyRule(pattern: AssemblyPattern, result: (List[MLine], AssemblyMatchingContext) => List[MLine]) extends AssemblyRuleSet {
|
||||||
override def flatten: Seq[AssemblyRule] = List(this)
|
override def flatten: Seq[AssemblyRule] = List(this)
|
||||||
|
|
||||||
|
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
|
||||||
}
|
}
|
||||||
|
|
||||||
case class MultipleAssemblyRules(list: Seq[AssemblyRuleSet]) extends AssemblyRuleSet {
|
case class MultipleAssemblyRules(list: Seq[AssemblyRuleSet]) extends AssemblyRuleSet {
|
||||||
override def flatten: Seq[AssemblyRule] = list.flatMap(_.flatten)
|
override def flatten: Seq[AssemblyRule] = list.flatMap(_.flatten)
|
||||||
|
|
||||||
|
override val minimumRequiredLines: Int = flatten.map(_.minimumRequiredLines).sum
|
||||||
}
|
}
|
||||||
|
|
||||||
trait AssemblyPattern {
|
trait AssemblyPattern {
|
||||||
|
@ -235,6 +255,8 @@ trait AssemblyPattern {
|
||||||
|
|
||||||
def captureLength(i: Int) = CaptureLength(i, this)
|
def captureLength(i: Int) = CaptureLength(i, this)
|
||||||
|
|
||||||
|
def minimumRequiredLines: Int
|
||||||
|
|
||||||
}
|
}
|
||||||
object HelperCheckers {
|
object HelperCheckers {
|
||||||
private def badAddrModes(am: MAddrMode): Boolean = am match {
|
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 toString: String = s"(?<$i>$pattern)"
|
||||||
|
|
||||||
|
override def minimumRequiredLines: Int = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
|
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 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 toString: String = "Where(...)"
|
||||||
|
|
||||||
|
override def minimumRequiredLines: Int = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends AssemblyPattern {
|
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 (_: Both, _) => s"($l) · $r"
|
||||||
case _ => s"$l · $r"
|
case _ => s"$l · $r"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val minimumRequiredLines: Int = l.minimumRequiredLines + r.minimumRequiredLines
|
||||||
}
|
}
|
||||||
|
|
||||||
case class Many(rule: MLinePattern) extends AssemblyPattern {
|
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 toString: String = s"[$rule]*"
|
||||||
|
|
||||||
|
override def minimumRequiredLines: Int = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
case class ManyWhereAtLeastOne(rule: MLinePattern, atLeastOneIsThis: MLinePattern) extends AssemblyPattern {
|
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 toString: String = s"[∃$atLeastOneIsThis:$rule]*"
|
||||||
|
|
||||||
|
override def minimumRequiredLines: Int = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
case class Opt(rule: MLinePattern) extends AssemblyPattern {
|
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 toString: String = s"[$rule]?"
|
||||||
|
|
||||||
|
override def minimumRequiredLines: Int = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
trait MLinePattern extends AssemblyPattern {
|
trait MLinePattern extends AssemblyPattern {
|
||||||
|
@ -488,6 +524,8 @@ trait MLinePattern extends AssemblyPattern {
|
||||||
else Both(x, this)
|
else Both(x, this)
|
||||||
|
|
||||||
def hitRate: Double
|
def hitRate: Double
|
||||||
|
|
||||||
|
override def minimumRequiredLines: Int = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
//noinspection ScalaUnnecessaryParentheses
|
//noinspection ScalaUnnecessaryParentheses
|
||||||
|
@ -509,6 +547,8 @@ case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) ext
|
||||||
val s2s = ctx.get[List[MLine]](ix2)
|
val s2s = ctx.get[List[MLine]](ix2)
|
||||||
if (s1s.forall(s1 => s2s.forall(s2 => HelperCheckers.memoryAccessDoesntOverlap(s1, s2)))) Some(code) else None
|
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 {
|
case class MatchA(i: Int) extends MLinePattern {
|
||||||
|
@ -754,6 +794,8 @@ case object DebugMatching extends AssemblyPattern {
|
||||||
code.foreach(println)
|
code.foreach(println)
|
||||||
Some(code)
|
Some(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def minimumRequiredLines: Int = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
case object Linear extends MLinePattern {
|
case object Linear extends MLinePattern {
|
||||||
|
@ -1274,6 +1316,8 @@ case class MatchElidableCopyOf(i: Int, firstLinePattern: MLinePattern, lastLineP
|
||||||
}
|
}
|
||||||
Some(after)
|
Some(after)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def minimumRequiredLines: Int = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
case object IsNotALabelUsedManyTimes extends MLinePattern {
|
case object IsNotALabelUsedManyTimes extends MLinePattern {
|
||||||
|
@ -1290,3 +1334,14 @@ case object IsNotALabelUsedManyTimes extends MLinePattern {
|
||||||
|
|
||||||
override def hitRate: Double = 0.92 // ?
|
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 {
|
parameter match {
|
||||||
case StructureConstant(_, List(a,b)) => s" $opcode $a,$b"
|
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) {
|
} else if (addrMode == ImmediateWithAbsolute || addrMode == ImmediateWithZeroPage) {
|
||||||
parameter match {
|
parameter match {
|
||||||
case StructureConstant(_, List(a,b)) => s" $opcode #$a,$b"
|
case StructureConstant(_, List(a,b)) => s" $opcode #$a,$b"
|
||||||
|
|
|
@ -180,20 +180,31 @@ object Opcode extends Enumeration {
|
||||||
case "AXA" => AHX
|
case "AXA" => AHX
|
||||||
case "AXS" => SBX // could mean SAX
|
case "AXS" => SBX // could mean SAX
|
||||||
case "BCC" => BCC
|
case "BCC" => BCC
|
||||||
|
case "LBCC" => BCC
|
||||||
case "BCS" => BCS
|
case "BCS" => BCS
|
||||||
|
case "LBCS" => BCS
|
||||||
case "BEQ" => BEQ
|
case "BEQ" => BEQ
|
||||||
|
case "LBEQ" => BEQ
|
||||||
case "BIT" => BIT
|
case "BIT" => BIT
|
||||||
case "BMI" => BMI
|
case "BMI" => BMI
|
||||||
|
case "LBMI" => BMI
|
||||||
case "BNE" => BNE
|
case "BNE" => BNE
|
||||||
|
case "LBNE" => BNE
|
||||||
case "BPL" => BPL
|
case "BPL" => BPL
|
||||||
|
case "LBPL" => BPL
|
||||||
case "BRA" => BRA
|
case "BRA" => BRA
|
||||||
|
case "LBRA" => BRA
|
||||||
case "BRK" => BRK
|
case "BRK" => BRK
|
||||||
case "BRL" => BRL
|
case "BRL" => BRL
|
||||||
case "BSR" => BSR
|
case "BSR" => BSR
|
||||||
|
case "LBSR" => BSR
|
||||||
case "BVC" => BVC
|
case "BVC" => BVC
|
||||||
|
case "LBVC" => BVC
|
||||||
case "BVS" => BVS
|
case "BVS" => BVS
|
||||||
|
case "LBVS" => BVS
|
||||||
case "CLC" => CLC
|
case "CLC" => CLC
|
||||||
case "CLD" => CLD
|
case "CLD" => CLD
|
||||||
|
case "CLE" => CLE
|
||||||
case "CLI" => CLI
|
case "CLI" => CLI
|
||||||
case "CLV" => CLV
|
case "CLV" => CLV
|
||||||
case "CLX" => CLX
|
case "CLX" => CLX
|
||||||
|
@ -232,6 +243,7 @@ object Opcode extends Enumeration {
|
||||||
case "LSE" => SRE
|
case "LSE" => SRE
|
||||||
case "LSR" => LSR
|
case "LSR" => LSR
|
||||||
case "LXA" => LXA
|
case "LXA" => LXA
|
||||||
|
case "MAP" => MAP
|
||||||
case "NEG" => NEG
|
case "NEG" => NEG
|
||||||
case "NOP" => NOP
|
case "NOP" => NOP
|
||||||
case "OAL" => LXA
|
case "OAL" => LXA
|
||||||
|
@ -247,17 +259,19 @@ object Opcode extends Enumeration {
|
||||||
case "PHW" => PHW
|
case "PHW" => PHW
|
||||||
case "PHX" => PHX
|
case "PHX" => PHX
|
||||||
case "PHY" => PHY
|
case "PHY" => PHY
|
||||||
|
case "PHZ" => PHZ
|
||||||
case "PLA" => PLA
|
case "PLA" => PLA
|
||||||
case "PLB" => PLB
|
case "PLB" => PLB
|
||||||
case "PLD" => PLD
|
case "PLD" => PLD
|
||||||
case "PLP" => PLP
|
case "PLP" => PLP
|
||||||
case "PLX" => PLX
|
case "PLX" => PLX
|
||||||
case "PLY" => PLY
|
case "PLY" => PLY
|
||||||
|
case "PLZ" => PLZ
|
||||||
case "REP" => REP
|
case "REP" => REP
|
||||||
case "RLA" => RLA
|
case "RLA" => RLA
|
||||||
case "ROL" => ROL
|
case "ROL" => ROL
|
||||||
case "ROR" => ROR
|
case "ROR" => ROR
|
||||||
case "ROW" => ROR_W // TODO: is this correct?
|
case "ROW" => ROL_W
|
||||||
case "RRA" => RRA
|
case "RRA" => RRA
|
||||||
case "RTI" => RTI
|
case "RTI" => RTI
|
||||||
case "RTL" => RTL
|
case "RTL" => RTL
|
||||||
|
@ -268,6 +282,7 @@ object Opcode extends Enumeration {
|
||||||
case "SBX" => SBX
|
case "SBX" => SBX
|
||||||
case "SEC" => SEC
|
case "SEC" => SEC
|
||||||
case "SED" => SED
|
case "SED" => SED
|
||||||
|
case "SEE" => SEE
|
||||||
case "SEI" => SEI
|
case "SEI" => SEI
|
||||||
case "SEP" => SEP
|
case "SEP" => SEP
|
||||||
case "SET" => SET
|
case "SET" => SET
|
||||||
|
|
|
@ -995,16 +995,10 @@ object AlwaysGoodOptimizations {
|
||||||
|
|
||||||
val ConstantFlowAnalysis = new RuleBasedAssemblyOptimization("Constant flow analysis",
|
val ConstantFlowAnalysis = new RuleBasedAssemblyOptimization("Constant flow analysis",
|
||||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||||
(MatchX(0) & HasAddrMode(AbsoluteX) & SupportsAbsolute & Elidable & HasParameterWhere({
|
(MatchX(0) & HasAddrMode(AbsoluteX) & SupportsAbsolute & Not(HasOpcode(BIT)) & Elidable & HasIdentityPageParameter) ~~> { (code, ctx) =>
|
||||||
case MemoryAddressConstant(th) => th.name == "identity$"
|
|
||||||
case _ => false
|
|
||||||
})) ~~> { (code, ctx) =>
|
|
||||||
code.map(l => l.copy(addrMode = Immediate, parameter = NumericConstant(ctx.get[Int](0), 1)))
|
code.map(l => l.copy(addrMode = Immediate, parameter = NumericConstant(ctx.get[Int](0), 1)))
|
||||||
},
|
},
|
||||||
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable & HasParameterWhere({
|
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable & HasIdentityPageParameter) ~~> { (code, ctx) =>
|
||||||
case MemoryAddressConstant(th) => th.name == "identity$"
|
|
||||||
case _ => false
|
|
||||||
})) ~~> { (code, ctx) =>
|
|
||||||
code.map(l => l.copy(addrMode = Immediate, parameter = NumericConstant(ctx.get[Int](0), 1)))
|
code.map(l => l.copy(addrMode = Immediate, parameter = NumericConstant(ctx.get[Int](0), 1)))
|
||||||
},
|
},
|
||||||
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable) ~~> { (code, ctx) =>
|
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable) ~~> { (code, ctx) =>
|
||||||
|
@ -1605,6 +1599,15 @@ object AlwaysGoodOptimizations {
|
||||||
Where(ctx => ctx.get[Int](1).&(1) == 0)~~> { (lines, ctx) =>
|
Where(ctx => ctx.get[Int](1).&(1) == 0)~~> { (lines, ctx) =>
|
||||||
lines.head.copy(opcode = ASL) :: lines.tail
|
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",
|
val SimplifiableIndexChanging = new RuleBasedAssemblyOptimization("Simplifiable index changing",
|
||||||
|
@ -2065,7 +2068,7 @@ object AlwaysGoodOptimizations {
|
||||||
|
|
||||||
val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal",
|
val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal",
|
||||||
needsFlowInfo = FlowInfoRequirement.JustLabels,
|
needsFlowInfo = FlowInfoRequirement.JustLabels,
|
||||||
(Elidable & HasOpcode(LABEL) & HasCallerCount(0)) ~~> (_ => Nil)
|
(Elidable & HasOpcode(LABEL) & HasCallerCount(0) & ParameterIsLocalLabel) ~~> (_ => Nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
val OperationsAroundShifting = new RuleBasedAssemblyOptimization("Operations around shifting",
|
val OperationsAroundShifting = new RuleBasedAssemblyOptimization("Operations around shifting",
|
||||||
|
@ -2982,7 +2985,9 @@ object AlwaysGoodOptimizations {
|
||||||
|
|
||||||
(Elidable & HasOpcode(EOR) & MatchImmediate(0)) ~
|
(Elidable & HasOpcode(EOR) & MatchImmediate(0)) ~
|
||||||
(Elidable & HasOpcode(CMP) & MatchImmediate(1) & DoesntMatterWhatItDoesWith(State.A, State.N, State.C)) ~~> { (code, ctx) =>
|
(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 {
|
MultipleAssemblyRules(for {
|
||||||
|
@ -3106,6 +3111,42 @@ object AlwaysGoodOptimizations {
|
||||||
code(1).copy(opcode = branch, parameter = code(3).parameter),
|
code(1).copy(opcode = branch, parameter = code(3).parameter),
|
||||||
code(4))
|
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(
|
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