1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-26 11:29:28 +00:00

Compare commits

...

83 Commits

Author SHA1 Message Date
Karol Stasiak
d8c11a9c50 Minor optimizations 2023-02-03 14:46:01 +01:00
Karol Stasiak
ee47ccef5a fix z80 indexing in hardwritten assembly (#137) 2023-01-27 18:27:53 +01:00
Karol Stasiak
24de2f7530 enable tests of identity page 2023-01-27 18:19:19 +01:00
Karol Stasiak
1beb695151 more tests 2023-01-27 18:17:41 +01:00
Karol Stasiak
9229092309 random minor stuff 2023-01-27 18:16:25 +01:00
Karol Stasiak
ca7166d1ae oops 2023-01-27 18:16:02 +01:00
Karol Stasiak
1594d63a9a Add a command line flag for identity page 2023-01-27 18:15:40 +01:00
Karol Stasiak
75cc34663c Z80: fix an optimization 2023-01-27 18:15:10 +01:00
Karol Stasiak
f4d2fdd370 6502: use identity page for maths 2023-01-27 18:14:50 +01:00
Karol Stasiak
29c1e3f2a6 6502 and Z80: Optimize optimizations 2023-01-27 18:13:21 +01:00
Karol Stasiak
e9cfec54b5 Various 6502 optimization fixes 2023-01-27 17:34:09 +01:00
Karol Stasiak
1bc1ab3539 Fix some codegen bugs on 6502 2023-01-27 17:00:29 +01:00
Karol Stasiak
6f294d6dec Fix -fbounds-checking 2022-02-12 02:13:30 +01:00
Karol Stasiak
9866c974ad Don't apply Constant index offset propagation in decimal mode 2022-02-12 02:12:55 +01:00
Karol Stasiak
ef2f5b5918 Add test 2022-02-11 21:48:24 +01:00
Karol Stasiak
a70a1c0e6b Consider kernal_interrupt functions as entry points 2022-02-11 21:48:13 +01:00
Karol Stasiak
790c836771 Fix handling of non-top-level return dispatch statements 2022-02-11 21:47:39 +01:00
Karol Stasiak
6af84d1628 Optimize the optimizer 2022-02-08 14:42:30 +01:00
Karol Stasiak
7b205a2754 Allow multiple -D options 2022-02-08 13:21:26 +01:00
Karol Stasiak
d23fe1756e Switch to snapshot versioning 2022-02-08 13:20:15 +01:00
Karol Stasiak
3b3ee1bc55 Version 0.3.30 2021-12-15 16:01:46 +01:00
Karol Stasiak
fb30075ea5 Update CHANGELOG 2021-12-15 15:54:00 +01:00
Karol Stasiak
b5084cd180 More tests 2021-12-15 15:53:49 +01:00
Karol Stasiak
b1a2be5574 6809: Fix constant condition compilation 2021-11-22 01:58:09 +01:00
Karol Stasiak
a260d0a806 CBM BASIC loader should use the actual address of main (fixes #111) 2021-11-12 02:45:53 +01:00
Karol Stasiak
38ad919ed9 Show the positions of unevaluated constants 2021-11-12 02:44:58 +01:00
Karol Stasiak
34ef8b8de9 Better evaluation of the if function in constants 2021-11-12 02:44:20 +01:00
Karol Stasiak
f676e74e38 Macro improvements:
– allow local constants in macros
– allow untyped macro parameters with void
– treat the name of a function as a pointer to it
– add this.function local alias (#118)
2021-11-12 02:10:07 +01:00
Karol Stasiak
c9313e5dbe Add support for ld65 label file format (#128) 2021-11-12 00:47:12 +01:00
Karol Stasiak
73a223b9b6 Add support for Mesen label file format by tracking file-relative positions of segments (#128) 2021-11-03 21:48:45 +01:00
Karol Stasiak
b168818bab Report every hole in the output 4 bytes or larger; count total free bytes in the entire bank 2021-09-21 00:43:29 +02:00
Karol Stasiak
effa723863 Never commit .labels files again 2021-09-21 00:21:21 +02:00
Karol Stasiak
b2ab3dbeab Clean accidentally commited labels file 2021-09-21 00:20:54 +02:00
Karol Stasiak
c9ef5e636b Add raw label file format 2021-09-21 00:09:59 +02:00
Karol Stasiak
fc2f0782c5 Update Notepad++ syntax 2021-09-20 00:56:04 +02:00
Karol Stasiak
f0e22f02a6
Merge pull request #126 from mookiexl/master
Update x16_experimental.ini
2021-09-18 14:23:02 +02:00
Karol Stasiak
b2291d1cb2
Merge pull request #122 from retrac0/master
gb library code updates to current syntax
2021-09-18 13:21:15 +02:00
Karol Stasiak
166acf2b18 R800 support 2021-09-18 00:36:16 +02:00
Karol Stasiak
7530b382a8 Fix array fields in certain contexts 2021-09-17 22:19:39 +02:00
Karol Stasiak
b66435d06b Fix EasyFlash switch_hirom (fixes #121) 2021-09-13 15:10:11 +02:00
Karol Stasiak
0c8951d015 Fix unused variable elimination in for-array statements (fixes #125) 2021-09-13 09:27:34 +02:00
Karol Stasiak
84dde8589c Allow spaces after the array keyword (fixes #120) 2021-09-13 09:26:54 +02:00
Karol Stasiak
3b0aa9a425 Fixes for flag boolean types (fixes #123) 2021-09-13 09:26:27 +02:00
mookiexl
1b8de990a5 Update x16_experimental.ini 2021-08-19 14:37:20 +02:00
mookiexl
b060fc599b Update x16_experimental.ini
$00-$01 are used for bank switching.
Unintentional write to those will crash the machine.
2021-08-16 16:06:28 +02:00
Karol Stasiak
90e5360bfd Related to #119:
– Detection of simple byte overflow cases.
– Optimization of 8×8→16 multiplication on 6809.
– Multiplication optimizations on Z80.
2021-08-06 21:01:03 +02:00
Joel Heikkila
bbf430a1c7 update gb lib code to current syntax for example/ 2021-07-11 18:15:39 -04:00
Karol Stasiak
7f6a0c6b0d Fix documentation of .loword and .hiword 2021-06-29 02:39:23 +02:00
Karol Stasiak
7f0def54bc Improvements for 65CE02 assembly (fixes #116) 2021-06-29 02:29:30 +02:00
Karol Stasiak
faf97cee1f Don't call valid control characters invalid 2021-06-29 02:29:12 +02:00
Karol Stasiak
da862069a7 Fix for volatile fields 2021-06-29 02:28:32 +02:00
Karol Stasiak
431a25d325 Internal support for pointers to volatile objects; add volatile fields (closes #112) 2021-06-21 14:20:24 +02:00
Karol Stasiak
73beafd65e Support for expressions in file() (fixes #114) 2021-06-21 14:18:17 +02:00
Karol Stasiak
307ad90ecf 6809: Improve flow analysis and add few more optimizations 2021-06-21 14:15:40 +02:00
Karol Stasiak
2e592a2331 Correct address for sid_v1_sr (fixing #115) 2021-06-21 14:12:33 +02:00
Karol Stasiak
91c9b42f3d Switch to snapshot versioning 2021-06-21 14:11:56 +02:00
Karol Stasiak
144da70594 Version 0.3.28 2021-05-24 00:17:51 +02:00
Karol Stasiak
4f6eefab79 Officially deprecate old decimal operators 2021-05-23 22:31:11 +02:00
Karol Stasiak
ca35367974 Tease floating-point numbers 2021-05-16 23:32:33 +02:00
Karol Stasiak
2065c3b4ac Fix 6809 under native-image 2021-05-16 23:32:18 +02:00
Karol Stasiak
9028d55a7e Various optimization improvements and fixes, mostly for 6809 2021-05-16 23:31:52 +02:00
Karol Stasiak
21d4d3252f Remove debugging statements 2021-05-16 00:40:12 +02:00
Karol Stasiak
45ad049a51 Update CHANGELOG 2021-05-08 00:46:37 +02:00
Karol Stasiak
0172e29bb2 Underscores in numeric literals. Fix parsing of Intel hex constants starting with 0B. 2021-05-08 00:42:06 +02:00
Karol Stasiak
ffb46c4250 Optimize some bitmask operations 2021-05-05 02:58:57 +02:00
Karol Stasiak
c51c08ad56 6809: Fix and improve optimizations 2021-05-05 02:58:28 +02:00
Karol Stasiak
fcdad413b0 #110 Add a warning for comparisons between bytes and pointers. 2021-05-05 00:59:54 +02:00
Karol Stasiak
63edce28c4 Update CHANGELOG 2021-04-24 01:21:03 +02:00
Karol Stasiak
1f318a2a0e 6502: Optimize sign extension 2021-04-24 01:18:34 +02:00
Karol Stasiak
1bcb6d5010 Fix sign extension when using pointers 2021-04-24 01:18:21 +02:00
Karol Stasiak
510f85960c Fix optimizations of unused labels 2021-04-24 01:18:01 +02:00
Karol Stasiak
8412075175 Fix pointer type compatibility checks 2021-04-24 01:13:59 +02:00
Karol Stasiak
e25df3d1b3 Parser optimizations again 2021-04-19 01:06:51 +02:00
Karol Stasiak
b71d058c6a Parser optimizations 2021-04-18 23:48:23 +02:00
Karol Stasiak
bf273456a3 Fix #107 2021-03-20 01:25:27 +01:00
Karol Stasiak
062483971a Fix #107 (syntax errors in stdlib, overpanicky statement preprocessor) 2021-03-20 01:23:51 +01:00
Karol Stasiak
1e4a193741 Optimization hints 2021-03-15 00:44:14 +01:00
Karol Stasiak
2468d8cca5 Update changelog 2021-03-13 21:42:52 +01:00
Karol Stasiak
58b5b6ff28 Fix #106: the current working directory should be always included in the include path 2021-03-13 21:42:11 +01:00
Karol Stasiak
8aac3bc329 Allow character literals in preprocessor 2021-03-13 21:40:38 +01:00
Karol Stasiak
24eac6708b Fix escape sequences in many encodings 2021-03-13 21:40:18 +01:00
Karol Stasiak
66fc1d3984 Add several more encodings 2021-03-13 21:39:48 +01:00
Karol Stasiak
0bbdc348e7 Switch to snapshot versioning 2021-03-13 02:19:29 +01:00
212 changed files with 5096 additions and 1324 deletions

3
.gitignore vendored
View File

@ -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

View File

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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

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

View File

@ -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

View File

@ -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`.

View File

@ -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

View File

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

View File

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

View File

@ -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

View File

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

View File

@ -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

View File

@ -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)

View File

@ -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`

View File

@ -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

View File

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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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
} }

View File

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

View File

@ -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

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

View File

@ -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

View File

@ -13,3 +13,4 @@ EOT=00
{apos}=27 {apos}=27
{lbrace}=7b {lbrace}=7b
{rbrace}=7d {rbrace}=7d
{pound}=5c

View File

@ -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

View File

@ -9,4 +9,4 @@ E0-FE=`abcdefghijklmnopqrstuvwxyz{\}~
{q}=A2 {q}=A2
{apos}=A7 {apos}=A7
{lbrace}=FB {lbrace}=FB
{rbrace}=FC {rbrace}=FD

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View 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

View 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

View File

@ -27,4 +27,5 @@ F0-FE=≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
{pound}=9c {pound}=9c
{yen}=9d {yen}=9d
{ss}=e1 {ss}=e1
{pi}=e3
{nbsp}=FF {nbsp}=FF

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -19,3 +19,6 @@ fe-ff=↕↔
{apos}=27 {apos}=27
{lbrace}=7b {lbrace}=7b
{rbrace}=7d {rbrace}=7d
{pound}=a3
{copy}=a4
{pi}=b8

View File

@ -19,3 +19,5 @@ fe-ff=↕↔
{apos}=27 {apos}=27
{lbrace}=7b {lbrace}=7b
{rbrace}=7d {rbrace}=7d
{copy}=a4
{pi}=b8

View File

@ -20,3 +20,6 @@ fe-ff=↕↔
{apos}=27 {apos}=27
{lbrace}=7b {lbrace}=7b
{rbrace}=7d {rbrace}=7d
{pound}=a3
{copy}=a4
{pi}=b8

View File

@ -24,3 +24,4 @@ F0-F9=0123456789
{q}=7F {q}=7F
{lbrace}=C0 {lbrace}=C0
{rbrace}=D0 {rbrace}=D0
{cent}=4A

View 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

View File

@ -27,3 +27,4 @@ f1-ff=ñòóôġö÷ĝùúûüŭŝ˙
{ss}=df {ss}=df
{nbsp}=A0 {nbsp}=A0
{shy}=AD {shy}=AD
{pound}=A3

View File

@ -24,4 +24,5 @@ F0-FE=≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
{lbrace}=7b {lbrace}=7b
{rbrace}=7d {rbrace}=7d
{ss}=e1 {ss}=e1
{pi}=e3
{nbsp}=FF {nbsp}=FF

View File

@ -14,6 +14,7 @@ EOT=00
{lbrace}=7b {lbrace}=7b
{rbrace}=7d {rbrace}=7d
{nbsp}=a0 {nbsp}=a0
{copy}=98
•=95 •=95
b0=№ b0=№

View File

@ -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=░▒▓⌠■∙√≈≤≥

View File

@ -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=░▒▓“■∙”—№™

View File

@ -14,6 +14,7 @@ EOT=00
{lbrace}=7b {lbrace}=7b
{rbrace}=7d {rbrace}=7d
{shy}=8d {shy}=8d
{copy}=bf

View File

@ -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=░▒▓⌠■∙√≈≤≥

View File

@ -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

View File

@ -26,4 +26,5 @@ F0-FE=≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
{cent}=9b {cent}=9b
{yen}=9d {yen}=9d
{ss}=e1 {ss}=e1
{pi}=e3
{nbsp}=FF {nbsp}=FF

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,2 @@
NAME=UTF-32BE
BUILTIN=UTF-32BE

View File

@ -0,0 +1,2 @@
NAME=UTF-32LE
BUILTIN=UTF-32LE

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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
]

View File

@ -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
]

View File

@ -22,4 +22,5 @@ asm void __init_16bit() @$80D {
clc clc
xce xce
sep #$30 sep #$30
} jmp main
}

View File

@ -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
]

View File

@ -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
]

View File

@ -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
]

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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
} }

View File

@ -19,11 +19,15 @@
+= -= *= &gt;&gt;= &lt;&lt;= += -= *= &gt;&gt;= &lt;&lt;=
+&apos; -&apos; *&apos; &lt;&lt;&apos; &gt;&gt;&apos; +&apos; -&apos; *&apos; &lt;&lt;&apos; &gt;&gt;&apos;
+&apos;= -&apos;= *&apos;= &lt;&lt;&apos;= &gt;&gt;&apos;= +&apos;= -&apos;= *&apos;= &lt;&lt;&apos;= &gt;&gt;&apos;=
$+ $- $* $&lt;&lt; $&gt;&gt;
$+= $-= $*= $&lt;&lt;= $&gt;&gt;=
= ? = ?
/ %% /= %%= / %% /= %%=
&gt; &gt;= &lt; &lt;= != == &gt; &gt;= &lt; &lt;= != ==
| || |= ^ ^= &amp; &amp;&amp; &amp;= | || |= ^ ^= &amp; &amp;&amp; &amp;=
( ) { } @ [ ] : # </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">&quot;sta &quot; &quot;lda &quot; &quot;jmp &quot; &quot;bit &quot; &quot;eor &quot; &quot;adc &quot; &quot;sbc &quot; &quot;ora &quot; &quot;and &quot; &quot;ldx &quot; &quot;ldy &quot; &quot;stx &quot; &quot;sty &quot; &quot;tax&quot; &quot;tay&quot; &quot;tya&quot; &quot;txa&quot; &quot;txs&quot; &quot;tsx&quot; &quot;sei&quot; &quot;cli&quot; &quot;clv&quot; &quot;clc&quot; &quot;cld&quot; &quot;sed&quot; &quot;sec&quot; &quot;bra &quot; &quot;beq &quot; &quot;bne &quot; &quot;bmi &quot; &quot;bpl &quot; &quot;bcc &quot; &quot;bcs &quot; &quot;bvs &quot; bvc &quot; &quot;jsr &quot; rts&quot; &quot;rti&quot; &quot;brk&quot; &quot;rol&quot; &quot;ror&quot; &quot;asl&quot; &quot;lsr&quot; &quot;inc &quot; &quot;dec &quot; &quot;cmp &quot; &quot;cpx &quot; &quot;cpy &quot; inx iny dex dey pla pha plp hp phx plx phy ply &quot;stz &quot; &quot;ldz &quot; tza taz &quot;tsb &quot; &quot;trb &quot; ra txy tyx pld plb phb phd phk xce&#x000D;&#x000A;&#x000D;&#x000A;&quot;STA &quot; &quot;LDA &quot; &quot;JMP &quot; &quot;BIT &quot; &quot;EOR &quot; &quot;ADC &quot; &quot;SBC &quot; &quot;ORA &quot; &quot;AND &quot; &quot;LDX &quot; &quot;LDY &quot; &quot;STX &quot; &quot;STY &quot; &quot;TAX&quot; &quot;TAY&quot; &quot;TYA&quot; &quot;TXA&quot; &quot;TXS&quot; &quot;TSX&quot; &quot;SEI&quot; &quot;CLI&quot; &quot;CLV&quot; &quot;CLC&quot; &quot;CLD&quot; &quot;SED&quot; &quot;SEC&quot; &quot;BEQ &quot; &quot;BRA &quot; &quot;BNE &quot; &quot;BMI &quot; &quot;BPL &quot; &quot;BCC &quot; &quot;BCS &quot; &quot;BVS &quot; BVC &quot; &quot;JSR &quot; RTS&quot; &quot;RTI&quot; &quot;BRK&quot; &quot;ROL&quot; &quot;ROR&quot; &quot;ASL&quot; &quot;LSR&quot; &quot;INC &quot; &quot;DEC &quot; &quot;CMP &quot; &quot;CPX &quot; &quot;CPY &quot; INX INY DEX DEY PLA PHA PLP HP PHX PLX PHY PLY &quot;STZ &quot; &quot;LDZ &quot; TZA TAZ &quot;TSB &quot; &quot;TRB &quot; RA TXY TYX PLD PLB PHB PHD PHK XCE</Keywords> <Keywords name="Keywords4">&quot;sta &quot; &quot;lda &quot; &quot;jmp &quot; &quot;bit &quot; &quot;eor &quot; &quot;adc &quot; &quot;sbc &quot; &quot;ora &quot; &quot;and &quot; &quot;ldx &quot; &quot;ldy &quot; &quot;stx &quot; &quot;sty &quot; &quot;tax&quot; &quot;tay&quot; &quot;tya&quot; &quot;txa&quot; &quot;txs&quot; &quot;tsx&quot; &quot;sei&quot; &quot;cli&quot; &quot;clv&quot; &quot;clc&quot; &quot;cld&quot; &quot;sed&quot; &quot;sec&quot; &quot;bra &quot; &quot;beq &quot; &quot;bne &quot; &quot;bmi &quot; &quot;bpl &quot; &quot;bcc &quot; &quot;bcs &quot; &quot;bvs &quot; bvc &quot; &quot;jsr &quot; rts&quot; &quot;rti&quot; &quot;brk&quot; &quot;rol&quot; &quot;ror&quot; &quot;asl&quot; &quot;lsr&quot; &quot;inc &quot; &quot;dec &quot; &quot;cmp &quot; &quot;cpx &quot; &quot;cpy &quot; inx iny dex dey pla pha plp hp phx plx phy ply &quot;stz &quot; &quot;ldz &quot; tza taz &quot;tsb &quot; &quot;trb &quot; ra txy tyx pld plb phb phd phk xce&#x000D;&#x000A;&#x000D;&#x000A;&quot;STA &quot; &quot;LDA &quot; &quot;JMP &quot; &quot;BIT &quot; &quot;EOR &quot; &quot;ADC &quot; &quot;SBC &quot; &quot;ORA &quot; &quot;AND &quot; &quot;LDX &quot; &quot;LDY &quot; &quot;STX &quot; &quot;STY &quot; &quot;TAX&quot; &quot;TAY&quot; &quot;TYA&quot; &quot;TXA&quot; &quot;TXS&quot; &quot;TSX&quot; &quot;SEI&quot; &quot;CLI&quot; &quot;CLV&quot; &quot;CLC&quot; &quot;CLD&quot; &quot;SED&quot; &quot;SEC&quot; &quot;BEQ &quot; &quot;BRA &quot; &quot;BNE &quot; &quot;BMI &quot; &quot;BPL &quot; &quot;BCC &quot; &quot;BCS &quot; &quot;BVS &quot; BVC &quot; &quot;JSR &quot; RTS&quot; &quot;RTI&quot; &quot;BRK&quot; &quot;ROL&quot; &quot;ROR&quot; &quot;ASL&quot; &quot;LSR&quot; &quot;INC &quot; &quot;DEC &quot; &quot;CMP &quot; &quot;CPX &quot; &quot;CPY &quot; INX INY DEX DEY PLA PHA PLP HP PHX PLX PHY PLY &quot;STZ &quot; &quot;LDZ &quot; TZA TAZ &quot;TSB &quot; &quot;TRB &quot; RA TXY TYX PLD PLB PHB PHD PHK XCE</Keywords>
<Keywords name="Keywords5">&quot;sbx &quot; &quot;isc &quot; &quot;dcp &quot; &quot;lax &quot; &quot;sax &quot; &quot;anc &quot; &quot;alr &quot; &quot;arr &quot; &quot;rra &quot; &quot;rla &quot; &quot;lxa &quot; &quot;ane &quot; &quot;xaa &quot;&#x000D;&#x000A;&quot;SBX &quot; &quot;ISC &quot; &quot;DCP &quot; &quot;LAX &quot; &quot;SAX &quot; &quot;ANC &quot; &quot;ALR &quot; &quot;ARR &quot; &quot;RRA &quot; &quot;RLA &quot; &quot;LXA &quot; &quot;ANE &quot; &quot;XAA &quot;</Keywords> <Keywords name="Keywords5">&quot;sbx &quot; &quot;isc &quot; &quot;dcp &quot; &quot;lax &quot; &quot;sax &quot; &quot;anc &quot; &quot;alr &quot; &quot;arr &quot; &quot;rra &quot; &quot;rla &quot; &quot;lxa &quot; &quot;ane &quot; &quot;xaa &quot;&#x000D;&#x000A;&quot;SBX &quot; &quot;ISC &quot; &quot;DCP &quot; &quot;LAX &quot; &quot;SAX &quot; &quot;ANC &quot; &quot;ALR &quot; &quot;ARR &quot; &quot;RRA &quot; &quot;RLA &quot; &quot;LXA &quot; &quot;ANE &quot; &quot;XAA &quot;</Keywords>
<Keywords name="Keywords6"></Keywords> <Keywords name="Keywords6"></Keywords>

View File

@ -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

View File

@ -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"
}, },

View File

@ -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,

View File

@ -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)) {

View File

@ -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")
}
}

View File

@ -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")

View File

@ -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 {

View File

@ -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
} }

View File

@ -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

View File

@ -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

View File

@ -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
) )
} }

View File

@ -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,

View File

@ -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}

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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
}

View File

@ -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"

View File

@ -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

View File

@ -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