1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-31 18:41:30 +00:00

Compare commits

...

135 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
Karol Stasiak
15a32a4caf Version 0.3.26 2021-03-01 10:25:38 +01:00
Karol Stasiak
e8f5667faa Move apple2_prodos module documentation 2021-02-28 20:53:22 +01:00
Karol Stasiak
03bc5894aa
Merge pull request #103 from retrac0/master
outlined ProDOS API in docs
2021-02-28 20:43:01 +01:00
Karol Stasiak
bd9ff2f66d Unfinished test 2021-02-28 19:38:25 +01:00
Karol Stasiak
a111af384e Unify assembly parameter evaluation 2021-02-27 00:08:31 +01:00
Karol Stasiak
6f2c8cd991 Show unused space in the middle of segments 2021-02-26 23:20:32 +01:00
Karol Stasiak
a0c2eaabcf Struct array fields performance improvements 2021-02-26 23:13:16 +01:00
Karol Stasiak
83393d49f1 Z80: optimization improvements 2021-02-26 23:12:43 +01:00
Karol Stasiak
84d707b932 Cosmetic improvements 2021-02-26 23:10:08 +01:00
Karol Stasiak
f01879e4a3 Use local labels in m6809_math 2021-02-26 23:05:29 +01:00
Joel Heikkila
efb3d3c7c9 outlined ProDOS API in docs 2021-02-25 13:19:37 -05:00
Karol Stasiak
22bd6ac443 Update changelog 2021-02-24 03:05:09 +01:00
Karol Stasiak
ffa0ffb899 Allow enabling/disabling warnings individually 2021-02-24 03:04:53 +01:00
Karol Stasiak
3155d7a571 Improve error message 2021-02-24 02:32:24 +01:00
Karol Stasiak
ba7c5b507c Optimize constant resolution 2021-02-24 02:32:13 +01:00
Karol Stasiak
196ad6542f Allow refering to labels from other functions in assembly (fixes #101) 2021-02-24 02:32:00 +01:00
Karol Stasiak
ff6106a838 Fix module name resolution (#89) 2021-02-24 02:31:20 +01:00
Karol Stasiak
9ebbbdc022 Summarize the compilation 2021-02-24 02:30:28 +01:00
Karol Stasiak
ab9cdf7ad5 Fix deduplication problems with trampolined functions 2021-02-24 02:29:47 +01:00
Karol Stasiak
4ff6120702 Warn about data not being included in the output 2021-02-24 02:28:29 +01:00
Karol Stasiak
521b73d0d3 Array fields for structs and unions (see #87) 2021-02-22 23:23:00 +01:00
Karol Stasiak
478b2eefbd Update changelog 2021-02-18 03:26:24 +01:00
Karol Stasiak
3d57421959
Merge pull request #100 from retrac0/apple2
ProDOS library + some small Apple II terminal fixes
2021-02-18 01:33:47 +01:00
Karol Stasiak
78c29c784d
Merge pull request #102 from agg23/agg23/OverflowErrors
Added position logging to overflow errors
2021-02-18 01:30:09 +01:00
Karol Stasiak
25adb05229 Fix typeof 2021-02-18 01:29:27 +01:00
Karol Stasiak
bb46b1e7e9 Update documentation 2021-02-18 00:51:07 +01:00
Karol Stasiak
7c60a89776 Signed multiplication 2021-02-18 00:39:14 +01:00
Karol Stasiak
02031da61a Correctly optimize variables around macros 2021-02-18 00:38:51 +01:00
Karol Stasiak
d20cc677bb The typeof builtin function 2021-02-18 00:38:30 +01:00
Karol Stasiak
2beabb7bed Improvements to constant folding 2021-02-18 00:36:13 +01:00
Karol Stasiak
fb71f88343 8080: Correctly compile sbytes to BC 2021-02-18 00:35:02 +01:00
Adam Gastineau
7689afb5d6 Added position logging to overflow errors 2021-02-03 11:51:41 -08:00
Karol Stasiak
958c1c09e7 #98: Warning about a missing call to init_rw_memory 2021-02-03 09:58:39 +01:00
Karol Stasiak
919f11af2e #101: Allow using local labels in all assembly instructions and all assembly expressions 2021-02-03 09:50:44 +01:00
Karol Stasiak
9c7e946f4c Allow labels in assembly on the same line (see #101) 2021-02-03 09:49:17 +01:00
Karol Stasiak
32b98750a9 Warn about non-volatile spinlocks (see #95) 2021-02-03 09:46:09 +01:00
Joel Heikkila
ef34f534f9 first attempt at a ProDOS library for the Apple II 2021-02-02 23:33:45 -05:00
Joel Heikkila
3a2e29888d some documentation of what I've tested 2021-01-25 20:29:54 -05:00
Joel Heikkila
d6deb81166 change monitor address to not print prompt 2021-01-25 20:15:18 -05:00
Joel Heikkila
394ac63805 0x80 + 0x0D is a2 newline 2021-01-25 16:13:59 -05:00
Karol Stasiak
42f6efc9bd Switch to snapshot versioning 2021-01-13 19:57:52 +01:00
Karol Stasiak
8b0c026bf7 Update changelog 2021-01-13 19:57:17 +01:00
Karol Stasiak
c1959b356f Make all constants referring to segments match their equivalents from the platform definition (see #87) 2021-01-13 19:55:11 +01:00
Karol Stasiak
499e650752 Define and document more magic suffixes and constants (see #87) 2021-01-13 19:35:11 +01:00
Karol Stasiak
fe4f0dcfd9 6502: Optimize more jumps in place 2021-01-13 19:26:17 +01:00
Karol Stasiak
c6932da6b3 Update changelog 2021-01-13 14:39:32 +01:00
Karol Stasiak
600bfce0c1 Allow new lines after = 2021-01-13 14:38:59 +01:00
Karol Stasiak
9feda54d92 Optimize word shifts by 7–12 bits 2021-01-13 14:31:20 +01:00
Karol Stasiak
91699b64c6 Less useless logging 2021-01-13 14:18:41 +01:00
Karol Stasiak
2a5933e115 6502: Fix sbyte to word promotion in some contexts 2021-01-13 14:18:28 +01:00
Karol Stasiak
bd8078cc47
Merge pull request #90 from agg23/agg23/RemovedOptimizationHintPrint
Removed optimization hint debug println statements
2021-01-02 01:06:31 +01:00
Adam Gastineau
30216d0be6 Removed optimization hint debug println statements 2021-01-01 14:52:37 -08:00
235 changed files with 7217 additions and 1702 deletions

3
.gitignore vendored
View File

@ -31,9 +31,12 @@ issue*.mfk
*.seq
*.asm
*.lbl
*.labels
*.nl
*.fns
*.sym
*.mlb
*.dbg
*.deb
*.xex
*.nes

View File

@ -1,5 +1,113 @@
# Change log
## 0.3.30 (2021-12-15)
* Added volatile structure fields (#112).
* Added `this.function` as the alias for the current function (#118).
* Added support for constant evaluation in `file` expressions (#114).
* Allowed declaring local constants and passing untyped parameters for macros.
* Allowed treating bare function name as a pointer to it.
* Added Mesen, ld65 and "raw" label file formats (#128).
* Commodore: the address used by SYS is now determined automatically instead of hardcoded (#111).
* C64: Fixed address for `sid_v1_sr` (#115).
* EasyFlash: Fixed address for `switch_hirom` (#121).
* GB: Fixed standard library (thanks to @retrac0).
* Commander X16: Updated platform definition file (thanks to @mookiexl).
* 65CE02: Full assembly support.
* R800: Full assembly support.
* Various miscompilation fixes (#123, #125) and parser fixes (e.g. #120).
* 6809: Various optimizations.
* Improvements related to constant evaluation.
## 0.3.28 (2021-05-24)
* Officially deprecated decimal operators with apostrophes.
* Added optimization hints.
* Added `utf32be`, `utf32le`, `cp1253`, `cp1254`, `cp1257`, `geos_de` encodings.
* Allowed for underscores in numeric literals for readability purposes, similar to several other programming languages.
* Added a warning for comparisons between bytes and pointers (#110).
* Fixed escape sequences in many encodings.
* Fixed and documented absolute module imports (#106)
* Fixed and optimized sign extension.
* Fixed optimizations involving unused labels.
* Fixed pointer types to type aliases.
* Fixed parsing of Intel hex literals of the form `0BH`, `0B0H` etc.
* 6809: Fixed flow analysis in optimization.
* Optimization of certain bitmask operations.
* Parsing optimizations.
## 0.3.26 (2021-03-01)
* Array fields in structs.
* Various Apple II-related improvements, including ProDOS support (thanks to @retrac0).
* Segment-related constants now match their equivalents from the platform definition. Missing constants have been defined.
* Constants with heap start and segment start are now generated properly.
* Signed multiplication support for `sbyte` and `signed16`.
* Heavily experimental `typeof` builtin.
* Self-modifying assembly code is now supported (#101).
* Successful compilation now prints result program size.
* Warning about data not being included in the output file.
* Warnings can now be enabled and disabled individually.
* Imported modules are now identified by their full relative path, not just the token used in the `import` statement (#89).
* 6502: Fixed sbyte to word promotions in certain contexts.
* 8080: Fixed compilation of sign extension of `sbyte` values into the BC register pair.
* Fixed negative constant folding.
* Fixed optimizations around macro invocations.
* 6502: Fixed code deduplication in presence of trampolined functions.
* Optimized word shifts for between 7 and 12 bits.
* Allowed new lines after `=`.
* Various optimization improvements.
* Improved some error messages (thanks to @agg23).
* Other fixes and improvements.
## 0.3.24 (2020-12-02)
* Preliminary support for TRS-80 Model 1 and 3 running TRS-DOS.

View File

@ -57,7 +57,7 @@ Test suite is useful if you plan on modifying the compiler. Some test dependenci
* run `sbt compile` to compile the project
* run `sbt assemble` to build the executable jar file, it should appear in `target/scala-2.12`
* run `sbt assembly` to build the executable jar file, it should appear in `target/scala-2.12`
### Building a native executable

View File

@ -5,7 +5,7 @@
A middle-level programming language targeting 6502-based, 8080-based, Z80-based and 6809-based microcomputers.
For binary releases, see: [https://github.com/KarolS/millfork/releases](https://github.com/KarolS/millfork/releases)
(latest: 0.3.24).
(latest: 0.3.30).
For build instructions, see [Build instructions](./COMPILING.md).
## Features

View File

@ -1,7 +1,8 @@
name := "millfork"
version := "0.3.24"
version := "0.3.31-SNAPSHOT"
// keep it at 2.12.11 for GraalVM native image compatibility!
scalaVersion := "2.12.11"
resolvers += Resolver.mavenLocal

View File

@ -14,6 +14,8 @@ It implies the following:
* cannot contain variable or array declarations
* but can contain scalar constant declarations; the constants are scoped to the particular macro invocation
* can be `asm` - in this case, they should **not** end with a return instruction
* do not have an address
@ -35,7 +37,13 @@ It implies the following:
* `call` parameters exceptionally can have their type declared as `void`;
such parameters accept expressions of any type, including `void`, however, you cannot assign from those expressions
* macros do not have their own scope (they reuse the scope from their invocations) exceptions: the parameters and the local labels defined in assembly
* macros do not have their own scope (they reuse the scope from their invocations) exceptions:
* the parameters
* the local labels defined in assembly
* the local constants
* control-flow statements (`break`, `continue`, `return`, `goto`, `label`) are run as if places in the caller function

View File

@ -46,6 +46,8 @@ but the main disadvantages are:
* cannot use them in inline assembly code blocks
* structs and unions containing array fields are not supported
The implementation depends on the target architecture:
* on 6502, the stack pointer is transferred into the X register and used as a base

View File

@ -1,10 +1,16 @@
[< back to index](../doc_index.md)
### A note about Apple II
## Apple II
Apple II variants other than II+/IIe/Enhanced IIe are untested;
this includes the original II, IIc and IIc+, but also later compatible computers (Apple III and IIgs).
They may or may not work.
### Model support
The current platform configuration for the Apple II targets the original Apple II with an NMOS processor.
Simple programs have been tested on the Apple II, II+, IIe and enhanced IIe. The IIc, IIc+ are untested.
The IIgs may work in compatibility mode, but this is untested. The Apple III is untested.
ProDOS support is handled by the [`apple2_prodos` module](../stdlib/apple2.md).
### Running your program
The compiler output is a raw machine code file, which then has to be put on a disk.
You can do it using [CiderPress](http://a2ciderpress.com/),
@ -13,6 +19,12 @@ or some other tool.
The file has to be loaded from $0C00. An example how to put such a file onto a disk using AppleCommander:
java -jar AppleCommander-1.3.5.jar -p disk_image.dsk FILENAME B 0xc00 < compiler_output.a2
java -jar AppleCommander.jar -p disk_image.dsk FILENAME B 0xc00 < compiler_output.a2
When you have placed your file on disk, boot the disk and enter this at the BASIC prompt:
] BRUN FILENAME
This has been successfully tested under DOS 3.3 and [ProDOS 2.4](https://prodos8.com/), on an Apple II+ and Apple IIe.
Creating a bootable disk is beyond the scope of this document.

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 fceux` multi-file format used by the FCEUX emulator. The extension is `.nl`.
* `-G mesen` format used by the Mesen emulator. The extension is `.mlb`.
* `-G ld65` a simplified version of the format used by the `ld65` linker (used by CC65 and CA65). The extension is `.dbg`.
* `-G raw` Millfork-specific format. The extension is '.labels'. Each row contains bank number, start address, end address (if known), object type, and Millfork-specific object identifier.
* `-fbreakpoints`, `-fno-breakpoints`
Whether the compiler should use the `breakpoint` macro.
@ -206,6 +212,10 @@ The compiler doesn't support accessing the stack variables via the S stack point
* `-O9` Optimize code using superoptimizer (experimental). Computationally very expensive, decent results.
* `-fhints`, `-fno-hints`
Whether optimization hints should be used.
Default: yes.
* `-finline`, `-fno-inline` Whether should inline functions automatically.
See the [documentation about inlining](../abi/inlining.md). Computationally easy, can give decent gains.
`.ini` equivalent: `inline`.
@ -278,3 +288,45 @@ By default, the compiler emits only some of the most important warnings.
* `-Wnone` Disable all warnings.
* `-Wfatal` Treat warnings as errors.
You can also enable or disable warnings individually:
* `-Wbuggy`, `-Wno-buggy`
Whether should warn about code that may cause surprising behaviours or even miscompilation.
Default: enabled.
* `-Wdeprecation`, `-Wno-deprecation`
Whether should warn about deprecated aliases.
Default: enabled.
* `-Wcomparisons`, `-Wno-comparisons`
Whether should warn about comparisons between bytes and pointers.
Default: enabled.
* `-Wextra-comparisons`, `-Wno-extra-comparisons`
Whether should warn about simplifiable unsigned integer comparisons.
Default: disabled.
* `-Wfallback`, `-Wno-fallback`
Whether should warn about the use of default values by text codecs, the preprocessor, and array literals.
Default: enabled.
* `-Wmissing-output`, `-Wno-missing-output`
Whether should warn about data that is missing in output files.
Default: enabled.
* `-Woverlapping-call`, `-Wno-overlapping-call`
Whether should warn about calls to functions in a different, yet overlapping segment.
Default: enabled.
* `-Wror`, `-Wno-ror`
Whether should warn about the ROR instruction (6502 only).
Default: disabled.
* `-Wuseless`, `-Wno-useless`
Whether should warn about code that does nothing.
Default: enabled.
* `-Whints`, `-Wno-hints`
Whether should warn about unsupported optimization hints.
Default: enabled.

View File

@ -2,6 +2,11 @@
### A note about Commodore 64
#### Unusual main function location
If you're creating a prg file and your `main` function may be located at address $2710 hex (10000 decimal) or higher,
you might need to define the `DISPLACED_MAIN=1` preprocessor feature.
#### Multifile programs
A multifile program is a program stored on a disk that consists of the main program file that is executed first

View File

@ -37,6 +37,8 @@ if a line ends with a backslash character, the value continues to the next line.
* `z80` (Zilog Z80)
* `strictz80` (Z80 without illegal instructions)
* `r800` (R800)
* `z80next` (Z80 core from ZX Spectrum Next)
Note: Millfork version 0.3.18 and earlier uses the name `zx80next` for this architecture.
@ -80,6 +82,8 @@ This list cannot contain module template instantiations.
* `emit_x80` whether the compiler should emit instructions present on Sharp LR35902 and Z80, but absent on Intel 8080, default is `true` on compatible processors and `false` elsewhere
* `emit_z80` whether the compiler should emit Zilog Z80 instructions not covered by `emit_x80`, default is `true` on compatible processors and `false` elsewhere
* `emit_r800` whether the compiler should emit R800 instructions, default is `true` on compatible processors and `false` elsewhere
* `prevent_jmp_indirect_bug` whether the compiler should try to avoid the indirect JMP bug,
default is `false` on 65C02-compatible or non-6502 processors and `true` elsewhere
@ -173,7 +177,7 @@ Default: `after_code`.
* `segment_NAME_bank` the bank number the segment belongs to. Default: `0`.
For better debugging on NES, RAM segments should use bank number `$ff`.
* `segment_NAME_fill` the byte value used to fill gaps and other unused space in the bank. Default: `0`.
* `segment_NAME_fill` the byte value used to fill gaps and other unused space in the segment. Default: `0`.
* `segment_NAME_layout` a comma-separated list of object names that defines in what order the objects are laid out in the segment.
One item has to be `*`, it means "all the other objects".
@ -263,3 +267,7 @@ Default: `main,*`
* `sym` format used by the WLA/DX assembler. The extension is `.sym`.
* `fceux` multi-file format used by the FCEUX emulator. The extension is `.nl`.
* `mesen` format used by the Mesen emulator. The extension is `.mlb`.
* `ld65` format used by the `ld65` linker. The extension is `.dbg`.

View File

@ -26,6 +26,8 @@
* [Predefined constants](lang/predefined_constants.md)
* [List of magic suffixes](lang/suffixes.md)
* [List of text encodings and escape sequences](lang/text.md)
* [Defining custom encodings](lang/custom-encoding.md)
@ -42,6 +44,8 @@
* [Important guidelines regarding reentrancy](lang/reentrancy.md)
* [Optimization hints](lang/hints.md)
* [List of keywords](lang/keywords.md)
## Library reference
@ -75,6 +79,8 @@
* [PET-only modules](stdlib/cbm_pet.md)
* [`cbm_file` module](stdlib/cbm_file.md)
* [Apple II-only modules](stdlib/apple2.md)
* [NES-only modules](stdlib/nes.md)

View File

@ -52,6 +52,17 @@ Global label names have to start with a letter and can contain digits, underscor
Local label names (available since Millfork 0.3.22) start with a period and are visible only in the given function.
Anonymous labels designated with `+` or `-` are also not supported.
Referring to a global label with an offset requires wrapping it in `label(…)`:
STA .local_opcode // ok
STA label(.local_opcode) // ok
STA .local_opcode + 1 // ok
STA label(.local_opcode) + 1 // ok
STA global_opcode // ok
STA label(global_opcode) // ok
STA global_opcode + 1 // NOT OK
sta label(global_opcode) + 1 // ok
Assembly can refer to variables and constants defined in Millfork,
but you need to be careful with using absolute vs immediate addressing:

View File

@ -27,6 +27,17 @@ Global label names have to start with a letter and can contain digits, underscor
Local label names (available since Millfork 0.3.22) start with a period and are visible only in the given function.
Anonymous labels designated with `+` or `-` are also not supported.
Referring to a global label with an offset requires wrapping it in `label(…)`:
STA .local_opcode // ok
STA label(.local_opcode) // ok
STA .local_opcode + 1 // ok
STA label(.local_opcode) + 1 // ok
STA global_opcode // ok
STA label(global_opcode) // ok
STA global_opcode + 1 // NOT OK
STA label(global_opcode) + 1 // ok
Assembly can refer to variables and constants defined in Millfork,
but you need to be careful with using absolute vs immediate addressing:

View File

@ -52,6 +52,17 @@ Global label names have to start with a letter and can contain digits, underscor
Local label names (available since Millfork 0.3.22) start with a period and are visible only in the given function.
Anonymous labels designated with `+` or `-` are also not supported.
Referring to a global label with an offset requires wrapping it in `label(…)`:
LD (.local_opcode),A // ok
LD (label(.local_opcode)),A // ok
LD (.local_opcode + 1),A // ok
LD (label(.local_opcode) + 1),A // ok
LD (global_opcode),A // ok
LD (label(global_opcode)),A // ok
LD (global_opcode + 1),A // NOT OK
LD (label(global_opcode) + 1),A // ok
Assembly can refer to variables and constants defined in Millfork,
but you need to be careful with using absolute vs immediate addressing:

View File

@ -16,7 +16,7 @@ No other lines are allowed in the file.
* `NAME=<name>` defines the name for this encoding. Required.
* `BUILTIN=<internal name>` defines this encoding to be a UTF-based encoding.
`<internal name>` may be one of `UTF-8`, `UTF-16LE`, `UTF-16BE`.
`<internal name>` may be one of `UTF-8`, `UTF-16LE`, `UTF-16BE`, `UTF-32LE`, `UTF-32BE`.
If this directive is present, the only other allowed directive in the file is the `NAME` directive.
* `EOT=<xx>` where `<xx>` are two hex digits, defines the string terminator byte.

View File

@ -4,17 +4,17 @@
Syntax:
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [@ <address>] { <body> }`
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [<optimization hints>] [@ <address>] { <body> }`
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [@ <address>] = <expression>`
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [<optimization hints>] [@ <address>] = <expression>`
`[segment (<segment>)] asm <return_type> <name> ( <params> ) @ <address> extern`
`[segment (<segment>)] asm <return_type> <name> ( <params> ) [<optimization hints>] @ <address> extern`
Examples:
void do_nothing() { }
inline byte two() = 2
asm void chkout(byte register(a) char) @ $FFD2 extern
asm void chkout(byte register(a) char) !preserves_x !preserves_y @ $FFD2 extern
segment(prgrom0) void main_loop(word w, byte x) align(fast) { // body omitted
@ -68,6 +68,8 @@ For assembly functions, certain parameter names are interpreted as CPU registers
* on 6502, it means that the function will not cross a page boundary if possible
* on Z80, it is ignored
* `<optimization hints>` is a list of [optimization hints](./hints.md), separated by spaces
* `<address>` is a constant expression that defines where in the memory the function is or will be located.
* `extern` is a keyword than marks functions that are not defined in the current program,

84
docs/lang/hints.md Normal file
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`
When using Intel syntax for inline assembly, another hexadecimal syntax is available: `0D323H`, `2a2h`.
Digits can be separated by underscores for readability. Underscores are also allowed between the radix prefix and the digits:
123_456
0x01_ff
0b_0101_1111__1100_0001
$___________FF
When using Intel syntax for inline assembly, another hexadecimal syntax is available: `0D323H`, `2a2h`, `3e_h`.
It is not allowed in any other places.
The type of a literal is the smallest type of undefined signedness
@ -65,7 +72,7 @@ and the byte constant `nullchar_scr` is defined to be equal to the string termin
You can override the values for `nullchar` and `nullchar_scr`
by defining preprocessor features `NULLCHAR` and `NULLCHAR_SCR` respectively.
Warning: If you define UTF-16 to be you default or screen encoding, you will encounter several problems:
Warning: If you define UTF-16 or UTF-32 to be you default or screen encoding, you will encounter several problems:
* `nullchar` and `nullchar_scr` will still be bytes, equal to zero.
* the `string` module in the Millfork standard library will not work correctly
@ -75,21 +82,26 @@ Warning: If you define UTF-16 to be you default or screen encoding, you will enc
You can also prepend `p` to the name of the encoding to make the string length-prefixed.
The length is measured in bytes and doesn't include the zero terminator, if present.
In all encodings except for UTF-16 the prefix takes one byte,
In all encodings except for UTF-16 and UTF-32 the prefix takes one byte,
which means that length-prefixed strings cannot be longer than 255 bytes.
In case of UTF-16, the length prefix contains the number of code units,
so the number of bytes divided by two,
which allows for strings of practically unlimited length.
The length is stores as two bytes and is always little endian,
The length is stored as two bytes and is always little endian,
even in case of the `utf16be` encoding or a big-endian processor.
In case of UTF-32, the length prefix contains the number of Unicode codepoints,
so the number of bytes divided by four.
The length is stored as four bytes and is always little endian,
even in case of the `utf32be` encoding or a big-endian processor.
"this is a Pascal string" pascii
"this is also a Pascal string"p
"this is a zero-terminated Pascal string"pz
Note: A string that's both length-prefixed and zero-terminated does not count as a normal zero-terminated string!
To pass it to a function that expects a zero-terminated string, add 1 (or, in case of UTF-16, 2):
To pass it to a function that expects a zero-terminated string, add 1 (or, in case of UTF-16, 2, or UTF-32, 4):
pointer p
p = "test"pz

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.
The name also defines where the module is located:
a module named `a/b` is presumed to exist in `a/b.mfk`
and it's looked up first in the current working directory,
and it's looked up first in the directory that contains the current source file,
then in the current working directory,
and then in the include directories.
A module can import other modules, using the `import` statement.

View File

@ -18,9 +18,9 @@ Millfork has different operator precedence compared to most other languages. Fro
* `->` and `[]`
* `*`, `*'`, `/`, `%%`
* `*`, `$*`, `/`, `%%`
* `+`, `+'`, `-`, `-'`, `|`, `&`, `^`, `>>`, `>>'`, `<<`, `<<'`, `>>>>`
* `+`, `$+`, `-`, `$-`, `|`, `&`, `^`, `>>`, `$>>`, `<<`, `$<<`, `>>>>`
* `:`
@ -34,16 +34,16 @@ Millfork has different operator precedence compared to most other languages. Fro
You cannot use two different operators at the same precedence levels without using parentheses to disambiguate.
It is to prevent confusion about whether `a + b & c << d` means `(a + b) & (c << d)` `((a + b) & c) << d` or something else.
The only exceptions are `+` and `-`, and `+'` and `-'`.
They are interpreted as expected: `5 - 3 + 2 == 4` and `5 -' 3 +' 2 == 4`.
Note that you cannot mix `+'` and `-'` with `+` and `-`.
The only exceptions are `+` and `-`, and `$+` and `$-`.
They are interpreted as expected: `5 - 3 + 2 == 4` and `5 $- 3 $+ 2 == 4`.
Note that you cannot mix `$+` and `$-` with `+` and `-`.
Certain operators (`/`, `%%`, `<<`, `>>`, `<<'`, `>>'`, `>>>>`, `:`, `!=`) cannot have more than 2 parameters,
Certain operators (`/`, `%%`, `<<`, `>>`, `$<<`, `$>>`, `>>>>`, `:`, `!=`) cannot have more than 2 parameters,
i.e. `x / y / z` will not compile.
The decimal operators have two different forms:
* apostrophe form (e.g. `+'`) the original one, to be deprecated in the future, may be removed in Millfork 0.4
* apostrophe form (e.g. `+'`) the original one, deprecated, will be removed in Millfork 0.4
* dollar form (e.g. `$+`) available since Millfork 0.3.22
@ -95,7 +95,7 @@ TODO
`constant word + byte`
`word + word` (zpreg)
* `*`: multiplication; the size of the result is the same as the size of the arguments
* `*`: multiplication (signed or unsigned); the size of the result is the same as the size of the largest of the arguments
`byte * constant byte`
`constant byte * byte`
`constant word * constant word`
@ -135,20 +135,20 @@ These operators work using the decimal arithmetic (packed BCD).
On Ricoh-based targets (e.g. Famicom) they require the zeropage register to have size at least 4
* `+'`, `-'`: decimal addition/subtraction
`$+`, `$-`: (since Millfork 0.3.22)
`byte +' byte`
`constant word +' constant word`
`constant long +' constant long`
`word +' word` (zpreg)
* `$+`, `$-`: decimal addition/subtraction
`+'`, `-'`: (deprecated form)
`byte $+ byte`
`constant word $+ constant word`
`constant long $+ constant long`
`word $+ word` (zpreg)
* `*'`: decimal multiplication
`$*`: (since Millfork 0.3.22)
`constant *' constant`
* `$*`: decimal multiplication
`*'`: (deprecated form)
`constant $* constant`
* `<<'`, `>>'`: decimal multiplication/division by power of two
`$<<`, `$>>`: (since Millfork 0.3.22)
`byte <<' constant byte`
* `$<<`, `$>>`: decimal multiplication/division by power of two
`<<'`, `>>'`: (deprecated form)
`byte $<< constant byte`
## Comparison operators
@ -201,8 +201,8 @@ An expression of form `a[f()] += b` may call `f` an undefined number of times.
`mutable word = word`
`mutable long = long`
* `+=`, `+'=`, `|=`, `^=`, `&=`: modification in place
`$+=` (since Millfork 0.3.22)
* `+=`, `$+=`, `|=`, `^=`, `&=`: modification in place
`+'=` (deprecated form)
`mutable byte += byte`
`mutable word += word`
`mutable trivial long += long`
@ -212,14 +212,14 @@ An expression of form `a[f()] += b` may call `f` an undefined number of times.
`mutable word <<= byte`
`mutable trivial long <<= byte`
* `<<'=`, `>>'=`: decimal shift in place
`$<<=`, `$>>=` (since Millfork 0.3.22)
`mutable byte <<'= constant byte`
`mutable word <<'= constant byte`
`mutable trivial long <<'= constant byte`
* `$<<=`, `$>>=`: decimal shift in place
`<<'=`, `>>'=` (deprecated form)
`mutable byte $<<= constant byte`
`mutable word $<<= constant byte`
`mutable trivial long $<<= constant byte`
* `-=`, `-'=`: subtraction in place
`$-=` (since Millfork 0.3.22)
* `-=`, `$-=`: subtraction in place
`-'=` (deprecated form)
`mutable byte -= byte`
`mutable word -= simple word`
`mutable trivial long -= simple long`
@ -230,9 +230,9 @@ An expression of form `a[f()] += b` may call `f` an undefined number of times.
`mutable word *= unsigned byte` (zpreg)
`mutable word *= word` (zpreg)
* `*'=`: decimal multiplication in place
`$*=` (since Millfork 0.3.22)
`mutable byte *'= constant byte`
* `$*=`: decimal multiplication in place
`*'=` (deprecated form)
`mutable byte $*= constant byte`
* `/=`, `%%=`: unsigned division and modulo in place
`mutable unsigned byte /= unsigned byte` (zpreg)
@ -291,9 +291,9 @@ but you can access its fields or take its pointer:
* `nonet`: expansion of an 8-bit operation to a 9-bit operation
`nonet(byte + byte)`
`nonet(byte +' byte)`
`nonet(byte $+ byte)`
`nonet(byte << constant byte)`
`nonet(byte <<' constant byte)`
`nonet(byte $<< constant byte)`
Other kinds of expressions than the above (even `nonet(byte + byte + byte)`) will not work as expected.
* `hi`, `lo`: most/least significant byte of a word
@ -313,6 +313,14 @@ some enum → `word`
and the result is a constant of either `byte` or `word` type, depending on the actual value.
In case of aligned types, this returns the aligned size.
* `typeof`: a word constant that identifies the type of the argument; the argument can be an expression or a type.
The argument is never evaluated.
**Warnings:**
* **This is a highly experimental feature.**
* The exact values may change in any future version of the compiler. Only compare one `typeof` to another `typeof`.
* There is no guarantee that different types will have different values of `typeof`. Indeed, it's even easy to see that a Millfork program can have more than 65536 types and values of `typeof` can clash even before that.
* In certain circumstances, pointer types and function pointer types may have different `typeof` values even if they're essentially the same.
* `call`: calls a function via a pointer;
the first argument is the pointer to the function;
the second argument, if present, is the argument to the called function.

View File

@ -2,7 +2,9 @@
# Predefined constants
* `byte nullchar` the null terminator for strings in the default encoding, equivalent to `""z[0]`
* `byte nullchar` the null terminator for strings in the default encoding, equivalent to `""z[0]`, can be overriden by the `NULLCHAR` feature
* `byte nullchar_scr` the null terminator for strings in the screen encoding, equivalent to `""scrz[0]`, can be overriden by the `NULLCHAR_SCR` feature
* `null$ nullptr` the invalid pointer value; the value of the `NULLPTR` feature
@ -10,6 +12,10 @@
* `pointer segment.N.start` the value of `segment_N_start` from the platform definition
* `pointer segment.N.codeend` the value of `segment_N_codeend` from the platform definition
* `pointer segment.N.datastart` the value of `segment_N_datastart` from the platform definition
* `pointer segment.N.end` the value of `segment_N_end` from the platform definition
* `pointer segment.N.heapstart` the address of the first byte in the `N` segment that was not automatically allocated
@ -17,3 +23,7 @@
* `word segment.N.length` the number of byte locations between `segment_N_start` and `segment_N_end`, inclusive
* `byte segment.N.bank` the value of `segment_N_bank` from the platform definition
* `byte segment.N.fill` the value of `segment_N_fill` from the platform definition
* `this.function` the alias of the current function (in macros, it resolves to the actual non-macro function that called the macro)

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
* `DISPLACED_MAIN` set this to 1 if the `main` function is in a very unusual location for the target
* `USE_MOUSE_MBM` set this to 1 if you want to enable middle button support for the mouse.
* `JOYSTICKS` the maximum number of joysticks using standard hardware configurations, may be 0
@ -151,11 +153,40 @@ The `if` function returns its second parameter if the first parameter is defined
// prints 500:
#infoeval if(0, 400, 500)
TODO
`not`, `lo`, `hi`, `min`, `max` `+`, `-`, `*`, `|`, `&`, `^`, `||`, `&&`, `<<`, `>>`,`==`, `!=`, `>`, `>=`, `<`, `<=`
The `min` and `max` functions return the smallest or largest parameter respectively. They support any number of arguments:
// prints 400:
#infoeval min(400, 500, 600)
// prints 500:
#infoeval max(400, 500)
The following Millfork operators and functions are also available in the preprocessor:
`not`, `lo`, `hi`, `+`, `-`, `*`, `|`, `&`, `^`, `||`, `&&`, `<<`, `>>`,`==`, `!=`, `>`, `>=`, `<`, `<=`
The following Millfork operators and functions are not available in the preprocessor:
`+'`, `-'`, `*'`, `<<'`, `>>'`, `:`, `>>>>`, `nonet`, all the assignment operators
`$+`, `$-`, `$*`, `$<<`, `$>>`, `:`, `>>>>`, `nonet`, all the assignment operators
### Character literals
Preprocessor supports character literals. By default, they are interpreted in the default encoding,
but you can suffix them with other encodings.
// usually prints 97:
#infoeval 'a'
// prints 97:
#infoeval 'a'ascii
Exceptionally, you can suffix the character literal with `utf32`.
This gives the literal the value of the Unicode codepoint of the character:
// may print 94, 96, 112, 173, 176, 184, 185, 222, 227, 234, 240, something else, or even fail to compile:
#infoeval 'π'
// prints 960:
#infoeval 'π'utf32
Escape sequences are supported, as per encoding. `utf32` pseudoencoding supports the same escape sequences as `utf8`.
### `#template`

52
docs/lang/suffixes.md Normal file
View File

@ -0,0 +1,52 @@
[< back to index](../doc_index.md)
# Magic suffixes
## Byte-related suffixes
These suffixes can be only applied to arithmetic or pointer variables:
* `.lo` the least significant byte of a two-byte variable (word, pointer) (use `lo(_)` for arbitrary expressions)
* `.hi` the most significant byte of a two-byte variable (word, pointer) (use `hi(_)` for arbitrary expressions)
* `.loword` the least significant word 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
## Pointer-related suffixes:
These suffixes can be applied to variables, arrays, functions or pointable expressions (sometimes called _lvalues_):
* `.addr` returns address of the object (type `pointer`) (constant unless on Lunix)
* `.rawaddr` returns the raw address constant of the object (type `pointer`, the same as `.addr` unless on Lunix, guaranteed to be constant)
* `.pointer` returns the typed pointer to the object
This suffix is available only on expressions that have a type of a typed pointer:
* `.raw` a view of the pointer as a raw pointer
## Segment-related suffixes
These suffixes can be applied to variables, arrays, or functions:
* `.segment.bank` (or `.segment` for short) returns the bank number of the segment the object is in
* `.segment.start` returns the start address of the segment the object is in
* `.segment.codeend` returns the last address of code in the segment the object is in
* `.segment.datastart` returns the start address of data in the segment the object is in
* `.segment.heapstart` returns the start address of uninitialized data in the segment the object is in
* `.segment.end` returns the last address of the segment the object is in
* `.segment.fill` returns the byte value used to fill gaps and other unused space in the segment the object is in
See also [the list of predefined constants](./predefined_constants.md).

View File

@ -50,7 +50,7 @@ or a top level of a function (*local* variables).
Syntax:
`[segment(<segment>)] [volatile] [<storage>] <type> <name> [@<address>] [= <initial_value>]`
`[segment(<segment>)] [volatile] [<storage>] <type> <name> [<optimization hints>] [@<address>] [= <initial_value>]`
Examples:
@ -69,6 +69,8 @@ Volatile variables cannot be declared as `register` or `stack`.
* `<storage>` can be only specified for local variables. It can be either `stack`, `static`, `register` or nothing.
`register` is only a hint for the optimizer.
See [the description of variable storage](../abi/variable-storage.md).
* `<optimization hints>` is a list of [optimization hints](./hints.md), separated by spaces
* `<address>` is a constant expression that defines where in the memory the variable will be located.
If not specified, it will be located according to the usual allocation rules.
@ -107,6 +109,12 @@ For every variable `x` larger than a byte, extra subvariables are defined:
* constituent bytes, from low to high: `x.b0`, `x.b1`, `x.b2`, etc.
* the lowest word: `x.loword` (=`x.b1:x.b0`)
* if `x` is a typed pointer:
* a view of as a raw pointer: `x.raw`
See also [the list of magic suffixes](./suffixes.md).
### Constant declarations

View File

@ -26,6 +26,8 @@ TODO: document the file format.
* `oldpetscii` or `oldpet` old PETSCII (Commodore PET with newer ROMs)
* `geos_de` text encoding used by the German version of GEOS for C64
* `cbmscr` or `petscr` Commodore screencodes
* `cbmscrjp` or `petscrjp` Commodore screencodes as used on Japanese versions of Commodore 64
@ -89,7 +91,7 @@ DOS codepages 437, 850, 851, 852, 855, 858, 866
* `kamenicky` Kamenický encoding
* `cp1250`, `cp1251`, `cp1252` Windows codepages 1250, 1251, 1252
* `cp1250`, `cp1251`, `cp1252`, `cp1253`, `cp1254`, `cp1257` Windows codepages 1250, 1251, 1252, 1253, 1254, 1257
* `msx_intl`, `msx_jp`, `msx_ru`, `msx_br` MSX character encoding, International, Japanese, Russian and Brazilian respectively
@ -132,6 +134,8 @@ English, Japanese, Spanish/Italian and French/German respectively
* `utf16be`, `utf16le` UTF-16BE and UTF-16LE
* `utf32be`, `utf32le` UTF-32BE and UTF-32LE
When programming for Commodore,
use `petscii` for strings you're printing using standard I/O routines
and `petsciiscr` for strings you're copying to screen memory directly.
@ -163,10 +167,12 @@ The exact value of `{nullchar}` is encoding-dependent:
* in the `zx80` encoding it's `{x01}`,
* in the `zx81` encoding it's `{x0b}`,
* in the `petscr` and `petscrjp` encodings it's `{xe0}`,
* in the `apple2e` encoding it's `{x7f}`,
* in the `atasciiscr` encoding it's `{xdb}`,
* in the `pokemon1*` encodings it's `{x50}`,
* in the `cocoscr` encoding it's exceptionally two bytes: `{xd0}`
* in the `utf16be` and `utf16le` encodings it's exceptionally two bytes: `{x00}{x00}`
* in the `utf32be` and `utf32le` encodings it's exceptionally four bytes: `{x00}{x00}{x00}{x00}`
* in other encodings it's `{x00}` (this may be a subject to change in future versions).
##### Available only in some encodings
@ -211,6 +217,7 @@ Encoding | lowercase letters | backslash | currencies | intl | card suits
`petscr` | yes¹ | no | £ | none | yes¹
`petjp` | no | no | ¥ | katakana³ | yes³
`petscrjp` | no | no | ¥ | katakana³ | yes³
`geos_de` | yes | no | | | no
`sinclair`, `bbc` | yes | yes | £ | none | no
`zx80`, `zx81` | no | no | £ | none | no
`apple2` | no | yes | | none | no
@ -273,6 +280,7 @@ Encoding | new line | braces | backspace | cursor movement | text colour | rever
`origpet` | yes | no | no | yes | no | yes | no
`oldpet` | yes | no | no | yes | no | yes | no
`petscr`, `petscrjp`| no | no | no | no | no | no | no
`geos_de` | no | no | no | no | no | yes | no
`sinclair` | yes | yes | no | yes | yes | yes | yes
`zx80`,`zx81` | yes | no | yes | yes | no | no | no
`ascii`, `iso_*` | yes | yes | yes | no | no | no | no

View File

@ -158,7 +158,9 @@ Functions that are interrupt pointers have their own pointer types:
Boolean types can be used as conditions. They have two possible values, `true` and `false`.
* `bool` a 1-byte boolean value. An uninitialized variable of type `bool` may contain an invalid value.
* `bool` a 1-byte boolean value.
An uninitialized variable of type `bool` may contain an invalid value.
The value `false` is stored as 0, `true` as 1.
* several boolean types based on the CPU flags that may be used only as a return type for a function written in assembly:
@ -174,6 +176,10 @@ Boolean types can be used as conditions. They have two possible values, `true` a
2\. LR35902 does not support these types due to the lack of appropriate flags
You can convert from a boolean type to an arithmetic type by simply casting:
byte c = byte(x >= 0x80)
Examples:
bool f() = true
@ -250,11 +256,24 @@ as there are no checks on values when converting bytes to enumeration values and
## Structs
Struct is a compound type containing multiple fields of various types:
Struct is a compound type containing multiple fields of various types.
A struct is represented in memory as a contiguous area of variables or arrays laid out one after another.
struct <name> [align (alignment)] { <field definitions (type and name), separated by commas or newlines>}
Declaration syntax:
A struct is represented in memory as a contiguous area of variables laid out one after another.
struct <name> [align (alignment)] { <field definitions, separated by commas or newlines>}
where a field definition is either:
* `<type> <name>` and defines a scalar field,
* or `array (<type>) <name> [<size>]`, which defines an array field,
where the array contains items of type `<type>`,
and either contains `<size>` elements
if `<size>` is a constant expression between 0 and 127,
or, if `<size>` is a plain enumeration type, the array is indexed by that type,
and the number of elements is equal to the number of variants in that enumeration.
`(<type>)` can be omitted and defaults to `byte`.
Struct can have a maximum size of 255 bytes. Larger structs are not supported.
@ -284,8 +303,8 @@ All arguments to the constructor must be constant.
Structures declared with an alignment are allocated at appropriate memory addresses.
The alignment has to be a power of two.
If the structs are in an array, they are padded with unused bytes.
If the struct is smaller that its alignment, then arrays of it are faster
If the structs with declared alignment are in an array, they are padded with unused bytes.
If the struct is smaller that its alignment, then arrays of it are faster than if it were not aligned
struct a align(4) { byte x,byte y, byte z }
struct b { byte x,byte y, byte z }
@ -303,9 +322,15 @@ If the struct is smaller that its alignment, then arrays of it are faster
A struct that contains substructs or subunions with non-trivial alignments has its alignment equal
to the least common multiple of the alignments of the substructs and its own declared alignment.
**Warning:** Limitations of array fields:
* Structs containing arrays cannot be allocated on the stack.
* Struct constructors for structs with array fields are not supported.
## Unions
union <name> [align (alignment)] { <field definitions (type and name), separated by commas or newlines>}
union <name> [align (alignment)] { <field definitions, separated by commas or newlines>}
Unions are pretty similar to structs, with the difference that all fields of the union
start at the same point in memory and therefore overlap each other.
@ -321,3 +346,5 @@ start at the same point in memory and therefore overlap each other.
Offset constants are also available, but they're obviously all zero.
Unions currently do not have an equivalent of struct constructors. This may be improved on in the future.
Unions with array fields have the same limitations as structs with array fields.

91
docs/stdlib/apple2.md Normal file
View File

@ -0,0 +1,91 @@
[< back to index](../doc_index.md)
# Apple II-oriented modules
## apple2_prodos module
This module provides basic support for issuing ProDOS calls.
It assumes ProDOS has been loaded normally before your program has started.
The API closely follows the ProDOS machine language interface.
See the
[ProDOS 8 Technical Reference Manual](http://www.easy68k.com/paulrsm/6502/PDOS8TRM.HTM)
for more details, such as the error code values returned.
The following functions are currently implemented:
#### void prodos_read_block(byte unit, pointer data_buffer, word block_number)
Read the specified block from device `unit` into `data_buffer`.
`data_buffer` must be page-aligned.
#### void prodos_write_block(byte unit, pointer data_buffer, word block_number)
Write the specified block from device `unit` into `data_buffer`.
`data_buffer` must be page-aligned.
#### void prodos_close(byte rnum)
Close file referred to by reference `rnum`.
ProDOS will free the associated buffers and flush all changes to disk if necessary.
#### void prodos_flush(byte rnum)
Flush any changes to disk for file referred to by reference `rnum`.
#### void prodos_get_prefix(pointer filename)
Takes a pointer to a 64-byte buffer.
ProDOS will fill this with the current path prefix as a Pascal-style string.
#### void prodos_set_prefix(pointer filename)
Sets or modifies the ProDOS prefix.
Takes a pointer to a Pascal-style string.
#### void prodos_create(pointer filename, byte filetype)
Create a file.
`filename` is a pointer to a Pascal-style string.
`filetype` is one of the standard ProDOS file types.
See the ProDOS manual for more details.
This *must* be called before a file can be opened or written.
ProDOS does not create files implicitly.
#### void prodos_destroy(pointer filename)
Delete a file.
#### void prodos_rename(pointer filename, pointer new_filename)
Rename a file.
#### byte prodos_open (pointer filename, pointer buffer)
Open a file.
`filename` is a pointer to a Pascal-style string containing the path of file to be opened.
Buffer is a 1024 byte I/O buffer used internally by ProDOS.
The buffer must be page-aligned.
This call returns a byte which is the ProDOS file handle.
This handle is used by other calls.
A minimal example:
byte file_handle
array io_buffer [1024] align (256)
file_handle = prodos_open("myfile"p, io_buffer)
prodos_close(file_handle)
Files must exist to be opened.
Use the `prodos_create` call to create a file first if necessary.
#### void prodos_newline(byte rnum, byte mask, byte newline_char)
Set the ProDOS newline character and mask.
See ProDOS manual for details.
#### void prodos_read(byte rnum, pointer data_buffer, word read_count)
Read the number of bytes specified by `read_count` into `data_buffer` from file handle `rnum`.
#### void prodos_write(byte rnum, pointer data_buffer, word write_count)
Write the number of bytes specified by `write_count` from `data_buffer` to file handle `rnum`.

View File

@ -3,6 +3,12 @@
The examples showcased here are designed to compile with a compiler built from the newest sources.
If you are using a release version of the compiler, consider browsing the older versions of the examples:
* [for version 0.3.28](https://github.com/KarolS/millfork/tree/v0.3.28/examples)
* [for version 0.3.26](https://github.com/KarolS/millfork/tree/v0.3.26/examples)
* [for version 0.3.24](https://github.com/KarolS/millfork/tree/v0.3.24/examples)
* [for version 0.3.22](https://github.com/KarolS/millfork/tree/v0.3.22/examples)
* [for version 0.3.18](https://github.com/KarolS/millfork/tree/v0.3.18/examples)

View File

@ -0,0 +1,37 @@
// simple demonstration of ProDOS library routines that will write
// the Disk II ROM (assuming it is in slot 6) to the file DISKII.ROM
import stdio
import apple2_prodos
void main() {
// ProDOS requires a 1K aligned page for an open file as an IO buffer
array iobuf [1024] align(256)
// you have to explicitly create a file if it doesn't exist yet
// this can fail (so you should check prodos_error
// 06 is general binary type
prodos_create("DISKII.ROM"p, $06)
if prodos_error != 0 {
// prodos error call will be returned in prodos_error so you know
// what went wrong. 0 = no error
putstrz("{n}couldn't create file"z)
panic()
}
// ProDOS file handle
byte fp
fp = prodos_open("DISKII.ROM"p, iobuf)
// should check here again for error, and after all calls
// write the disk controller ROM to the file
prodos_write(fp, $c600, 256)
// closing frees the handle and io buffer
prodos_close(fp)
putstrz("{n}DONE"z)
while true { }
}

View File

@ -11,7 +11,7 @@ asm void bell() @$FBE4 extern
asm void putchar(byte register(a) char) @$FDED extern
asm void new_line() @$FC62 extern
asm pointer readline() {
jsr $FD6A
jsr $FD6F
ldx #$ff
__readline_loop:
inx

273
include/apple2_prodos.mfk Normal file
View File

@ -0,0 +1,273 @@
const byte PRODOS_ALLOC_INTERRUPT = $40
const byte PRODOS_DEALLOC_INTERRUPT = $41
const byte PRODOS_READ_BLOCK = $80
const byte PRODOS_WRITE_BLOCK = $81
const byte PRODOS_GET_TIME = $82
const byte PRODOS_CREATE = $c0
const byte PRODOS_DESTROY = $c1
const byte PRODOS_RENAME = $c2
const byte PRODOS_SET_FILE_INFO = $c3
const byte PRODOS_GET_FILE_INFO = $c4
const byte PRODOS_ON_LINE = $c5
const byte PRODOS_SET_PREFIX = $c6
const byte PRODOS_GET_PREFIX = $c7
const byte PRODOS_OPEN = $c8
const byte PRODOS_NEWLINE = $c9
const byte PRODOS_READ = $ca
const byte PRODOS_WRITE = $cb
const byte PRODOS_CLOSE = $cc
const byte PRODOS_FLUSH = $cd
const byte PRODOS_SET_MARK = $ce
const byte PRODOS_GET_MARK = $cf
const byte PRODOS_SET_EOF = $d0
const byte PRODOS_GET_EOF = $d1
const byte PRODOS_SET_BUF = $d2
const byte PRODOS_GET_BUF = $d3
// ProDOS MLI parameter lists
struct read_block_plist {
byte param_count
byte unit_num
pointer data_buffer
word block_num
}
struct write_block_plist {
byte param_count
byte unit_num
pointer data_buffer
word block_num
}
struct close_plist {
byte param_count
byte ref_num
}
struct flush_plist {
byte param_count
byte ref_num
}
struct create_plist {
byte param_count
pointer pathname
byte access
byte file_type
word aux_type
byte storage_type
word create_time
word create_date
}
struct destroy_plist {
byte param_count
pointer pathname
}
struct open_plist {
byte param_count
pointer pathname
pointer io_buffer
byte ref_num
}
struct newline_plist {
byte param_count
byte ref_num
byte enable_mask
byte newline_char
}
struct read_plist {
byte param_count
byte ref_num
pointer data_buffer
word request_count
word trans_count
}
struct rename_plist {
byte param_count
pointer pathname
pointer new_pathname
}
struct write_plist {
byte param_count
byte ref_num
pointer data_buffer
word request_count
word trans_count
}
struct get_prefix_plist {
byte param_count
pointer data_buffer
}
struct set_prefix_plist {
byte param_count
pointer data_buffer
}
// we'll just reuse the same area for all plists
union prodos_plist {
read_block_plist read_block
write_block_plist write_block
create_plist create
destroy_plist destroy
rename_plist rename
open_plist open
newline_plist newline
read_plist read
write_plist write
close_plist close
flush_plist flush
get_prefix_plist get_prefix
set_prefix_plist set_prefix
}
prodos_plist plist
byte prodos_error
// Millfork doesn't support self-modification in the assembler yet, so this
// code is placed in the following array:
//
// jsr $bf00
// $00
// plist.addr
// rts
//
// We modify mli_trampoline[3] ($00 on the second line) to set the specific
// ProDOS call before we jsr to $bf00
//
// TODO: can we just jmp to bf00 and save the extra rts ?
array mli_trampoline = [ $20, 0, $bf, 0, plist.addr.lo, plist.addr.hi, $60 ]
asm void prodos_mli_call(byte register(a) pdcall) {
sta mli_trampoline+3
jsr mli_trampoline
sta prodos_error
rts
}
void prodos_read_block(byte unum, pointer dbuf, word bnum) {
plist.read_block.param_count = 3
plist.read_block.unit_num = unum
plist.read_block.data_buffer = dbuf
plist.read_block.block_num = bnum
prodos_mli_call(PRODOS_READ_BLOCK)
}
void prodos_write_block(byte unum, pointer dbuf, word bnum) {
plist.write_block.param_count = 3
plist.write_block.unit_num = unum
plist.write_block.data_buffer = dbuf
plist.write_block.block_num = bnum
prodos_mli_call(PRODOS_WRITE_BLOCK)
}
void prodos_close(byte rnum) {
plist.close.param_count = 1
plist.close.ref_num = rnum
prodos_mli_call(PRODOS_CLOSE)
}
void prodos_flush(byte fp) {
plist.flush.param_count = 1
plist.flush.ref_num = fp
prodos_mli_call(PRODOS_FLUSH)
}
void prodos_get_prefix(pointer fnbuf) {
plist.get_prefix.param_count = 1
plist.get_prefix.data_buffer = fnbuf
prodos_mli_call(PRODOS_GET_PREFIX)
}
void prodos_set_prefix(pointer fnbuf) {
plist.set_prefix.param_count = 1
plist.set_prefix.data_buffer = fnbuf
prodos_mli_call(PRODOS_SET_PREFIX)
}
void prodos_create(pointer fn, byte ftype) {
plist.create.param_count = 7
plist.create.pathname = fn
plist.create.access = $c3
plist.create.file_type = ftype
plist.create.aux_type = $0
plist.create.storage_type = 1
plist.create.create_time = 0
plist.create.create_date = 0
prodos_mli_call(PRODOS_CREATE)
}
void prodos_destroy (pointer fn) {
plist.destroy.param_count = 0
plist.destroy.pathname = fn
prodos_mli_call(PRODOS_DESTROY)
}
void prodos_rename(pointer fn, pointer newfn) {
plist.rename.param_count = 2
plist.rename.pathname = fn
plist.rename.new_pathname = newfn
prodos_mli_call(PRODOS_RENAME)
}
// returns file handle if no error
byte prodos_open (pointer fn, pointer b) {
plist.open.param_count = 3
plist.open.pathname = fn
plist.open.io_buffer = b
prodos_mli_call(PRODOS_OPEN)
return plist.open.ref_num
}
void prodos_newline(byte fp, byte mask, byte nlchar) {
plist.newline.param_count = 3
plist.newline.ref_num = fp
plist.newline.enable_mask = mask
plist.newline.newline_char = nlchar
prodos_mli_call(PRODOS_NEWLINE)
}
void prodos_read(byte rnum, pointer dbuf, word rcnt) {
plist.read.param_count = 4
plist.read.ref_num = rnum
plist.read.data_buffer = dbuf
plist.read.request_count = rcnt
prodos_mli_call(PRODOS_READ)
}
void prodos_write(byte rnum, pointer dbuf, word rcnt) {
plist.write.param_count = 4
plist.write.ref_num = rnum
plist.write.data_buffer = dbuf
plist.write.request_count = rcnt
prodos_mli_call(PRODOS_WRITE)
}

View File

@ -6,7 +6,7 @@
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
// Input: A = Byte to write.
asm void chrout(byte register(a) char) @$FFD2 extern
asm void chrout(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
asm void putchar(byte register(a) char) {
JSR chrout

View File

@ -5,7 +5,7 @@
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
// Input: A = Byte to write.
asm void chrout(byte register(a) char) @$FFD2 extern
asm void chrout(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
asm void putchar(byte register(a) char) {
JSR chrout

View File

@ -2,7 +2,7 @@ import err
inline asm void switch_hirom(byte register(a) bank) {
? and #$3F
! sta $DE01
! sta $DE00
? rts
}

View File

@ -2,7 +2,7 @@
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
// Input: A = Byte to write.
asm void chrout(byte register(a) char) @$FFD2 extern
asm void chrout(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
// CHRIN. Read byte from default input (for keyboard, read a line from the screen). (If not keyboard, must call OPEN and CHKIN beforehands.)
// Output: A = Byte read.

View File

@ -10,7 +10,7 @@ word sid_v1_freq @$D400
word sid_v1_pulse @$D402
byte sid_v1_cr @$D404
byte sid_v1_ad @$D405
byte sid_v1_sr @$D409
byte sid_v1_sr @$D406
word sid_v2_freq @$D407
word sid_v2_pulse @$D409

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
{q}=02
{apos}=07
{q}=A2
{apos}=A7
{nbsp}=40
{n}=8D

View File

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

View File

@ -7,8 +7,8 @@ EOT=7F
20-3f=@abcdefghijklmnopqrstuvwxyz[\]^_
60-7e=πABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~
{q}=22
{apos}=27
{q}=02
{apos}=07
{lbrace}=7b
{rbrace}=7d
{pi}=60

View File

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

View File

@ -26,6 +26,7 @@ f0-ff=đńňóôőö÷řůúűüýţ˙
{apos}=27
{lbrace}=7b
{rbrace}=7d
{euro}=80
{copy}=a9
{ss}=df
{nbsp}=A0

View File

@ -23,6 +23,7 @@ f0-ff=рстуфхцчшщъыьэюя
{apos}=27
{lbrace}=7b
{rbrace}=7d
{euro}=88
{copy}=a9
{nbsp}=A0
{shy}=AD

View File

@ -25,6 +25,7 @@ f0-ff=ðñòóôõö÷øùúûüýþÿ
{apos}=27
{lbrace}=7b
{rbrace}=7d
{euro}=80
{cent}=a2
{pound}=a3
{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
{yen}=9d
{ss}=e1
{pi}=e3
{nbsp}=FF

View File

@ -23,6 +23,7 @@ F1-FE=±‗¾¶§÷¸°¨·¹³²■
{apos}=27
{lbrace}=7b
{rbrace}=7d
{copy}=b8
{cent}=BD
{pound}=9c
{yen}=BE

View File

@ -24,6 +24,7 @@ F1-FE=±υφχ§ψ¸°¨ωϋΰώ■
{apos}=27
{lbrace}=7b
{rbrace}=7d
{pound}=9c
{ss}=D7
{nbsp}=FF
{shy}=F0

View File

@ -25,6 +25,7 @@ F1-FE=±‗¾¶§÷¸°¨·¹³²■
{rbrace}=7d
{cent}=BD
{pound}=9c
{copy}=b8
{yen}=BE
{euro}=D5
{ss}=e1

View File

@ -19,6 +19,9 @@ fe-ff=↕↔
{apos}=27
{lbrace}=7b
{rbrace}=7d
{pound}=a3
{copy}=a4
{pi}=b8
{AE}=5b
{OE}=5c
{AA}=5d

View File

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

View File

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

View File

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

View File

@ -24,3 +24,4 @@ F0-F9=0123456789
{q}=7F
{lbrace}=C0
{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
{nbsp}=A0
{shy}=AD
{pound}=A3

View File

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

View File

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

View File

@ -14,6 +14,7 @@ EOT=00
{lbrace}=7b
{rbrace}=7d
{nbsp}=9a
{copy}=bf
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
90-99=░▒▓⌠■∙√≈≤≥

View File

@ -14,6 +14,7 @@ EOT=00
{lbrace}=7b
{rbrace}=7d
{nbsp}=9a
{copy}=bf
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
90-99=░▒▓“■∙”—№™

View File

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

View File

@ -14,6 +14,7 @@ EOT=00
{lbrace}=7b
{rbrace}=7d
{nbsp}=9a
{copy}=bf
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
90-99=░▒▓⌠■∙√≈≤≥

View File

@ -29,5 +29,6 @@ f1-ff=ÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ
{ss}=a7
{nbsp}=CA
{euro}=DB
{pi}=b9
€=DB
U+F8FF=F0

View File

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

View File

@ -55,3 +55,8 @@ f0-ff=äëïöüçæåøñãõ⇒⇐⇔≡
{lbrace}=7b
{rbrace}=7d
{pi}=1b18
{pound}=a3
{copy}=a4
{yen}=bd
{cent}=b1
{ss}=ba

View File

@ -23,6 +23,7 @@ F0-FF=₽×./,♀0123456789
{ot}=BE
{'v}=BF
{hav}=BF
{apos}=E0
{'r}=E4
{ar}=E4
{'m}=E5

View File

@ -22,6 +22,7 @@ F0-FF=₽×./,♀0123456789
{'t}=DD
{'v}=DE
{apos}=E0
{PK}=E1
{pk}=E1
{MN}=E2

View File

@ -13,6 +13,7 @@ E6-E8=?!.
EC-EF=▷▶▼♂
F0-FF=₽×./,♀0123456789
{ss}=be
{c'}=d4
{ce}=d4
{d'}=d5
@ -37,6 +38,7 @@ F0-FF=₽×./,♀0123456789
{u'}=de
{ue}=de
{y'}=df
{apos}=e0
{PK}=E1
{pk}=E1

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
array __header @ $100 = [
const array __header @ $100 = [
$00, $C3, $50, $01, $CE, $ED, $66, $66, $CC, $0D, $00, $0B, $03, $73, $00, $83,
$00, $0C, $00, $0D, $00, $08, $11, $1F, $88, $89, $00, $0E, $DC, $CC, $6E, $E6,
$DD, $DD, $D9, $99, $BB, $BB, $67, $63, $6E, $0E, $EC, $CC, $DD, $DC, $99, $9F,

View File

@ -38,9 +38,9 @@ void read_joy() {
ld a,(reg_joypad)
ld a,(reg_joypad)
}
byte tmp
tmp = reg_joypad ^ $ff
input_a = (tmp & 1) >> 0
input_a = (tmp & 1) // >> 0
input_b = (tmp & 2) >> 1
input_select = (tmp & 4) >> 2
input_start = (tmp & 8) >> 3

View File

@ -11,6 +11,12 @@ inline asm byte __mul_u8u8u8() {
? LD A, E
? RET
}
#elseif CPUFEATURE_R800
inline asm byte __mul_u8u8u8() {
? MULUB A,D
? LD A,L
? RET
}
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
//A = A * D
noinline asm byte __mul_u8u8u8() {
@ -89,6 +95,16 @@ __divmod_u16u8u16u8_skip:
? RET
}
#if CPUFEATURE_R800
inline asm word __mul_u16u8u16() {
? LD L,A
? LD H,0
? MULUW HL,DE
? RET
}
#else
// HL=A*DE
noinline asm word __mul_u16u8u16() {
? LD HL,0
? LD B,8
@ -113,8 +129,17 @@ __mul_u16u8u16_skip:
#endif
? RET
}
#endif
#if CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
#if CPUFEATURE_R800
inline asm word __mul_u16u16u16() {
? EX DE,HL
? MULUW HL,BC
? RET
}
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
// HL=BC*DE
noinline asm word __mul_u16u16u16() {
LD HL,0
LD A,16

View File

@ -1,19 +1 @@
#if not(CBM)
#warn loader_0401 module should be only used on Commodore targets
#endif
const array _basic_loader @$401 = [
$0b,
4,
10,
0,
$9e,
$31,
$30,
$33,
$37,
0,
0,
0
]
import cbm/basic_loader<$401>

View File

@ -1,19 +1 @@
#if not(CBM)
#warn loader_0801 module should be only used on Commodore targets
#endif
const array _basic_loader @$801 = [
$0b,
$08,
10,
0,
$9e,
$32,
$30,
$36,
$31,
0,
0,
0
]
import cbm/basic_loader<$801>

View File

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

View File

@ -1,19 +1 @@
#if not(CBM)
#warn loader_1001 module should be only used on Commodore targets
#endif
const array _basic_loader @$1001 = [
$0b,
$10,
10,
0,
$9e,
$34,
$31,
$30,
$39,
0,
0,
0
]
import cbm/basic_loader<$1001>

View File

@ -1,19 +1 @@
#if not(CBM)
#warn loader_1201 module should be only used on Commodore targets
#endif
const array _basic_loader @$1201 = [
$0b,
$12,
10,
0,
$9e,
$34,
$36,
$32,
$31,
0,
0,
0
]
import cbm/basic_loader<$1201>

View File

@ -1,19 +1 @@
#if not(CBM)
#warn loader_1c01 module should be only used on Commodore targets
#endif
const array _basic_loader @$1C01 = [
$0b,
$1C,
10,
0,
$9e,
$37,
$31,
$38,
$31,
0,
0,
0
]
import cbm/basic_loader<$1c01>

View File

@ -3,7 +3,7 @@
#warn m6809_math module should be used only on 6809-like targets
#endif
noinline asm word __mul_u16u16u16(word register(x) x, word register(d) d) {
noinline asm word __mul_u16u16u16(word register(x) x, word register(d) d) !preserves_y !preserves_u {
pshs d,x
exg d,x
lda ,s
@ -26,45 +26,45 @@ noinline asm word __mul_u16u16u16(word register(x) x, word register(d) d) {
}
// returns p/q: quotient in A, remainder in B
noinline asm word __divmod_u8u8u8u8(word register(a) p, word register(b) q) {
noinline asm word __divmod_u8u8u8u8(word register(a) p, word register(b) q) !preserves_x !preserves_y !preserves_u {
pshs b,cc
ldb #8
stb ,-s
clrb
__divmod_u8u8u8u8_loop:
.__divmod_u8u8u8u8_loop:
asla
rolb
cmpb 2,s
blo __divmod_u8u8u8u8_skip
blo .__divmod_u8u8u8u8_skip
subb 2,s
inca
__divmod_u8u8u8u8_skip:
.__divmod_u8u8u8u8_skip:
dec ,s
bne __divmod_u8u8u8u8_loop
bne .__divmod_u8u8u8u8_loop
stb 2,s
leas 1,s
puls pc,b,cc
}
// returns p/q: quotient in X, remainder in D
noinline asm word __divmod_u16u16u16u16(word register(x) p, word register(d) q) {
noinline asm word __divmod_u16u16u16u16(word register(x) p, word register(d) q) !preserves_y !preserves_u {
pshs x,d,cc
ldb #16
pshs b
clrb
clra
__divmod_u16u16u16u16_loop:
.__divmod_u16u16u16u16_loop:
asl 5,s
rol 4,s
rolb
rola
cmpd 2,s
blo __divmod_u16u16u16u16_skip
blo .__divmod_u16u16u16u16_skip
subd 2,s
inc 5,s
__divmod_u16u16u16u16_skip:
.__divmod_u16u16u16u16_skip:
dec ,s
bne __divmod_u16u16u16u16_loop
bne .__divmod_u16u16u16u16_loop
std 2,s
leas 1,s
puls pc,x,d,cc

View File

@ -6,7 +6,7 @@
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
// Input: A = Byte to write.
asm void putchar(byte register(a) char) @$FFD2 extern
asm void putchar(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
inline void new_line() {
putchar(13)

View File

@ -11,14 +11,21 @@ modules=loader_0801,x16_kernal,x16_hardware,c64_panic,stdlib
[allocation]
; Let's not use the BASIC:
zp_pointers=0-$7F
segments=default,himem_00,himem_ff
; $00-$01 are used for bank switching
zp_pointers=$02-$7F
segments=default,user,himem_00,himem_ff
default_code_segment=default
segment_default_start=$80D
segment_default_codeend=$9eff
segment_default_datastart=after_code
segment_default_end=$9eff
;1KB user space
segment_user_start=$0400
segment_user_codeend=$07ff
segment_user_datastart=after_code
segment_user_end=$07ff
segment_himem_00_start=$a000
segment_himem_00_codeend=$bfff
segment_himem_00_datastart=after_code

View File

@ -6,7 +6,7 @@
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
// Input: A = Byte to write.
asm void chrout(byte register(a) char) @$FFD2 extern
asm void chrout(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
asm void putchar(byte register(a) char) {
JSR chrout

View File

@ -5,16 +5,16 @@
#pragma zilog_syntax
inline asm void putchar(byte register(a) char) {
inline asm void putchar(byte register(a) char) !preserves_bc !preserves_de !preserves_hl {
rst $10
? ret
}
inline void new_line() {
inline void new_line() !preserves_bc !preserves_de !preserves_hl {
putchar(13)
}
inline asm void set_border(byte register(a) colour) {
inline asm void set_border(byte register(a) colour) !preserves_bc !preserves_de !preserves_hl {
out (254),a
? ret
}

View File

@ -19,11 +19,15 @@
+= -= *= &gt;&gt;= &lt;&lt;=
+&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;= != ==
| || |= ^ ^= &amp; &amp;&amp; &amp;=
( ) { } @ [ ] : # </Keywords>
( ) { } @ [ ] : #
; .
</Keywords>
<Keywords name="Operators2"></Keywords>
<Keywords name="Folders in code1, open">{</Keywords>
<Keywords name="Folders in code1, middle"></Keywords>
@ -38,10 +42,14 @@
void bool byte sbyte ubyte word farword pointer farpointer long word_be word_le long_be long_le file
int8 int16 int24 int32 int40 int48 int56 int64
int72 int80 int88 int96 int104 int112 int120 int128
signed8
signed8 signed16 signed24 signed32 signed40 signed48 signed56 signed64
signed72 signed80 signed88 signed96 signed104 signed112 signed120 signed128
unsigned8 unsigned16 unsigned24 unsigned32 unsigned40 unsigned48 unsigned56 unsigned64
unsigned72 unsigned80 unsigned88 unsigned96 unsigned104 unsigned112 unsigned120 unsigned128
array addr fast</Keywords>
array addr fast
clear_carry clear_zero clear_overflow clear_negative
set_carry set_zero set_overflow set_negative
</Keywords>
<Keywords name="Keywords2">
import segment
if else for return while do asm extern break continue default goto label
@ -54,9 +62,11 @@
utf8 utf16le utf16be latin0 latin9 iso8859_15 zx80 zx81 vectrex koi7n2 short_koi msx_intl msx_us msx_uk msx_de msx_fr msx_es msx_ru msx_jp msx_br
utf8z utf16lez utf16bez latin0z latin9z iso8859_15z zx80z zx81z vectrexz koi7n2z short_koiz msx_intlz msx_usz msx_ukz msx_dez msx_frz msx_esz msx_ruz msx_jpz msx_brz
until to downto parallelto paralleluntil
function
static stack ref const volatile inline noinline macro register kernal_interrupt interrupt align reentrant
hi lo sin cos tan call nonet
false true nullptr</Keywords>
false true nullptr nullchar nullchar_scr
</Keywords>
<Keywords name="Keywords4">&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="Keywords6"></Keywords>

View File

@ -26,6 +26,7 @@ nav:
- Types: lang/types.md
- Literals: lang/literals.md
- Predefined constants: lang/predefined_constants.md
- List of magic suffixes: lang/suffixes.md
- Text encodings: lang/text.md
- Custom text encodings: lang/custom-encoding.md
- Operators: lang/operators.md
@ -34,6 +35,7 @@ nav:
- Inline 8080/LR35902/Z80 assembly: lang/assemblyz80.md
- Inline 6809 assembly: lang/assembly6809.md
- Reentrancy guidelines: lang/reentrancy.md
- Optimization hints: lang/hints.md
- List of keywords: lang/keywords.md
- Library reference:
- stdlib module: stdlib/stdlib.md
@ -49,6 +51,7 @@ nav:
- C64-only modules: stdlib/c64.md
- VIC-20-only modules: stdlib/vic20.md
- PET-only modules: stdlib/cbm_pet.md
- Apple II-only modules: stdlib/apple2.md
- NES-only modules: stdlib/nes.md
- Lynx-only modules: stdlib/lynx.md
- Game Boyonly modules: stdlib/gb.md

View File

@ -17,6 +17,11 @@
"allDeclaredFields":true,
"allPublicMethods":true
},
{
"name":"millfork.assembly.m6809.MOpcode$",
"allDeclaredFields":true,
"allPublicMethods":true
},
{
"name":"org.apache.commons.logging.LogFactory"
},

View File

@ -45,7 +45,7 @@ case class CompilationOptions(platform: Platform,
EmitIntel8085Opcodes, EmitIntel8080Opcodes, UseIxForStack, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes,
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes, EmitR800Opcodes,
UseIyForStack, UseIxForScratch, UseIyForScratch, UseShadowRegistersForInterrupts)
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6809) invalids ++= Set(
@ -172,6 +172,11 @@ case class CompilationOptions(platform: Platform,
log.error("Extended 8080-like opcodes enabled for architecture that doesn't support them")
}
}
if (flags(EmitR800Opcodes)) {
if (platform.cpu != R800) {
log.error("R800 opcodes enabled for architecture that doesn't support them")
}
}
if (flags(EmitIntel8080Opcodes)) {
if (!Intel8080Compatible(platform.cpu)) {
log.error("Intel 8080 opcodes enabled for architecture that doesn't support them")
@ -220,6 +225,7 @@ case class CompilationOptions(platform: Platform,
"OPTIMIZE_IPO" -> toLong(flag(CompilationFlag.InterproceduralOptimization)),
"CPUFEATURE_DECIMAL_MODE" -> toLong(flag(CompilationFlag.DecimalMode)),
"CPUFEATURE_Z80" -> toLong(flag(CompilationFlag.EmitZ80Opcodes)),
"CPUFEATURE_R800" -> toLong(flag(CompilationFlag.EmitR800Opcodes)),
"CPUFEATURE_EZ80" -> toLong(flag(CompilationFlag.EmitEZ80Opcodes)),
"CPUFEATURE_8080" -> toLong(flag(CompilationFlag.EmitIntel8080Opcodes)),
"CPUFEATURE_8085" -> toLong(flag(CompilationFlag.EmitIntel8085Opcodes)),
@ -290,7 +296,7 @@ object CpuFamily extends Enumeration {
import Cpu._
cpu match {
case Mos | StrictMos | Ricoh | StrictRicoh | Cmos | SC02 | Rockwell | Wdc | HuC6280 | CE02 | Sixteen => M6502
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | EZ80 | Z80Next => I80
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | R800 | EZ80 | Z80Next => I80
case Intel8086 | Intel80186 => I86
case Cpu.Motorola6809 => M6809
}
@ -368,6 +374,10 @@ object Cpu extends Enumeration {
* The Zilog Z80 processor, without illegal instructions
*/
val StrictZ80: Cpu.Value = Value
/**
* The R800 CPU (used in MSX Turbo-R)
*/
val R800: Cpu.Value = Value
/**
* The Zilog eZ80 processor
*/
@ -400,11 +410,11 @@ object Cpu extends Enumeration {
/**
* Processors that can run code for Zilog Z80
*/
val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, EZ80, Z80Next)
val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, R800, EZ80, Z80Next)
/**
* Processors that can run code for Intel 8080
*/
val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, EZ80, Z80Next)
val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, R800, EZ80, Z80Next)
/**
* Processors that can run code for Intel 8085
*/
@ -413,8 +423,24 @@ object Cpu extends Enumeration {
import CompilationFlag._
private val alwaysDefaultFlags = Set(
VariableOverlap, CompactReturnDispatchParams, FunctionFallthrough, RegisterVariables, FunctionDeduplication, EnableBreakpoints,
GenericWarnings, UselessCodeWarning, BuggyCodeWarning, FallbackValueUseWarning, DeprecationWarning, NonZeroTerminatedLiteralWarning, CallToOverlappingBankWarning,
VariableOverlap,
CompactReturnDispatchParams,
FunctionFallthrough,
RegisterVariables,
FunctionDeduplication,
EnableBreakpoints,
UseOptimizationHints,
GenericWarnings,
ByteOverflowWarning,
UselessCodeWarning,
BuggyCodeWarning,
FallbackValueUseWarning,
BytePointerComparisonWarning,
DeprecationWarning,
NonZeroTerminatedLiteralWarning,
CallToOverlappingBankWarning,
DataMissingInOutputWarning,
UnsupportedOptimizationHintWarning,
)
private val mosAlwaysDefaultFlags = alwaysDefaultFlags
@ -452,6 +478,8 @@ object Cpu extends Enumeration {
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitIntel8085Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
case StrictZ80 | Z80 =>
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts)
case R800 =>
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitR800Opcodes)
case Z80Next =>
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitIllegals, EmitZ80NextOpcodes)
case EZ80 =>
@ -498,6 +526,7 @@ object Cpu extends Enumeration {
case "strict2a07" => StrictRicoh
case "z80" => Z80
case "strictz80" => Z80
case "r800" => R800
case "zx80next" => Z80Next
case "z80next" => Z80Next
// disabled for now:
@ -546,9 +575,9 @@ object CompilationFlag extends Enumeration {
EmitIllegals, DecimalMode, LenientTextEncoding, LineNumbersInAssembly, SourceInAssembly, EnableBreakpoints,
// compilation options for MOS:
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitWdcOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack,
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack, IdentityPage,
// compilation options for I80
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitR800Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
UseShadowRegistersForInterrupts,
UseIxForStack, UseIyForStack,
UseIxForScratch, UseIyForScratch,
@ -570,20 +599,37 @@ object CompilationFlag extends Enumeration {
SingleThreaded,
// warning options
GenericWarnings,
ByteOverflowWarning,
UselessCodeWarning,
BuggyCodeWarning,
DeprecationWarning,
FallbackValueUseWarning,
BytePointerComparisonWarning,
ExtraComparisonWarnings,
RorWarning,
NonZeroTerminatedLiteralWarning,
CallToOverlappingBankWarning,
DataMissingInOutputWarning,
UnsupportedOptimizationHintWarning,
FatalWarnings,
// special options for internal compiler use
EnableInternalTestSyntax,
InternalCurrentlyOptimizingForMeasurement = Value
val allWarnings: Set[CompilationFlag.Value] = Set(GenericWarnings, UselessCodeWarning, BuggyCodeWarning, DeprecationWarning, FallbackValueUseWarning, ExtraComparisonWarnings, NonZeroTerminatedLiteralWarning, CallToOverlappingBankWarning)
val allWarnings: Set[CompilationFlag.Value] = Set(
GenericWarnings,
ByteOverflowWarning,
UselessCodeWarning,
BuggyCodeWarning,
DeprecationWarning,
FallbackValueUseWarning,
BytePointerComparisonWarning,
ExtraComparisonWarnings,
NonZeroTerminatedLiteralWarning,
CallToOverlappingBankWarning,
DataMissingInOutputWarning,
UnsupportedOptimizationHintWarning,
)
val fromString: Map[String, CompilationFlag.Value] = Map(
"lunix" -> LUnixRelocatableCode,
@ -595,6 +641,7 @@ object CompilationFlag extends Enumeration {
"emit_65ce02" -> Emit65CE02Opcodes,
"emit_huc6280" -> EmitHudsonOpcodes,
"emit_z80" -> EmitZ80Opcodes,
"emit_r800" -> EmitR800Opcodes,
"emit_ez80" -> EmitEZ80Opcodes,
"emit_x80" -> EmitExtended80Opcodes,
"emit_8080" -> EmitIntel8080Opcodes,
@ -607,6 +654,7 @@ object CompilationFlag extends Enumeration {
"u_stack" -> UseUForStack,
"y_stack" -> UseYForStack,
"software_stack" -> SoftwareStack,
"identity_page" -> IdentityPage,
"use_shadow_registers_for_irq" -> UseShadowRegistersForInterrupts,
"output_intel_syntax" -> UseIntelSyntaxForOutput,
"input_intel_syntax" -> UseIntelSyntaxForInput,

View File

@ -46,10 +46,24 @@ case class Context(errorReporting: Logger,
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)) {
addons += CompilationFlag.EmitZ80Opcodes -> true
}
if (isFlagSet(CompilationFlag.EmitZ80Opcodes) || isFlagSet(CompilationFlag.EmitSharpOpcodes)) {
if (isFlagSet(CompilationFlag.EmitZ80NextOpcodes)) {
addons += CompilationFlag.EmitZ80Opcodes -> true
}
if (isFlagSet(CompilationFlag.EmitR800Opcodes)) {
addons += CompilationFlag.EmitZ80Opcodes -> true
}
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)
|| isFlagSet(CompilationFlag.EmitZ80NextOpcodes)
|| isFlagSet(CompilationFlag.EmitR800Opcodes)
|| isFlagSet(CompilationFlag.EmitZ80Opcodes)
|| isFlagSet(CompilationFlag.EmitSharpOpcodes)) {
addons += CompilationFlag.EmitExtended80Opcodes -> true
}
if (isFlagSet(CompilationFlag.EmitZ80Opcodes) || isFlagSet(CompilationFlag.EmitIntel8085Opcodes)) {
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)
|| isFlagSet(CompilationFlag.EmitZ80NextOpcodes)
|| isFlagSet(CompilationFlag.EmitR800Opcodes)
|| isFlagSet(CompilationFlag.EmitZ80Opcodes)
|| isFlagSet(CompilationFlag.EmitIntel8085Opcodes)) {
addons += CompilationFlag.EmitIntel8080Opcodes -> true
}
if (isFlagSet(CompilationFlag.OptimizeForSpeed)) {

View File

@ -1,23 +1,37 @@
package millfork
import millfork.output.{BankLayoutInFile, FormattableLabel}
import java.util.regex.Pattern
import scala.collection.mutable
import scala.util.control.Breaks.{break, breakable}
/**
* @author Karol Stasiak
*/
object DebugOutputFormat {
val map: Map[String, DebugOutputFormat] = Map(
"raw" -> RawDebugOutputFormat,
"vice" -> ViceDebugOutputFormat,
"nesasm" -> NesasmDebugOutputFormat,
"fns" -> NesasmDebugOutputFormat,
"fceux" -> FceuxDebugOutputFormat,
"nl" -> FceuxDebugOutputFormat,
"mlb" -> MesenOutputFormat,
"mesen" -> MesenOutputFormat,
"asm6f" -> MesenOutputFormat,
"ld65" -> Ld65OutputFormat,
"ca65" -> Ld65OutputFormat,
"cc65" -> Ld65OutputFormat,
"dbg" -> Ld65OutputFormat,
"sym" -> SymDebugOutputFormat)
}
sealed trait DebugOutputFormat {
def formatAll(labels: Seq[(String, (Int, Int))], breakpoints: Seq[(Int, Int)]): String = {
val labelPart = labelsHeader + labels.map(formatLineTupled).mkString("\n") + "\n"
def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
val labelPart = labelsHeader + labels.map(formatLine).mkString("\n") + "\n"
if (breakpoints.isEmpty) {
labelPart
} else {
@ -25,9 +39,7 @@ sealed trait DebugOutputFormat {
}
}
final def formatLineTupled(labelAndValue: (String, (Int, Int))): String = formatLine(labelAndValue._1, labelAndValue._2._1, labelAndValue._2._2)
def formatLine(label: String, bank: Int, value: Int): String
def formatLine(label: FormattableLabel): String
final def formatBreakpointTupled(value: (Int, Int)): Seq[String] = formatBreakpoint(value._1, value._2).toSeq
@ -45,10 +57,25 @@ sealed trait DebugOutputFormat {
def breakpointsHeader: String = ""
}
object RawDebugOutputFormat extends DebugOutputFormat {
override def formatLine(label: FormattableLabel): String = {
f"${label.bankNumber}%02X:${label.startValue}%04X:${label.endValue.fold("")(_.formatted("%04X"))}%s:${label.category}%s:$label%s"
}
override def fileExtension(bank: Int): String = ".labels"
override def filePerBank: Boolean = false
override def addOutputExtension: Boolean = false
override def formatBreakpoint(bank: Int, value: Int): Option[String] =
Some(f"$bank%02X:$value%04X::b:<breakpoint@$value%04X>")
}
object ViceDebugOutputFormat extends DebugOutputFormat {
override def formatLine(label: String, bank: Int, value: Int): String = {
val normalized = label.replace('$', '_').replace('.', '_')
s"al ${value.toHexString} .$normalized"
override def formatLine(label: FormattableLabel): String = {
val normalized = label.labelName.replace('$', '_').replace('.', '_')
s"al ${label.startValue.toHexString} .$normalized"
}
override def fileExtension(bank: Int): String = ".lbl"
@ -61,8 +88,8 @@ object ViceDebugOutputFormat extends DebugOutputFormat {
}
object NesasmDebugOutputFormat extends DebugOutputFormat {
override def formatLine(label: String, bank: Int, value: Int): String = {
label + " = $" + value.toHexString
override def formatLine(label: FormattableLabel): String = {
label.labelName + " = $" + label.startValue.toHexString
}
override def fileExtension(bank: Int): String = ".fns"
@ -75,8 +102,8 @@ object NesasmDebugOutputFormat extends DebugOutputFormat {
}
object SymDebugOutputFormat extends DebugOutputFormat {
override def formatLine(label: String, bank: Int, value: Int): String = {
f"$bank%02x:$value%04x $label%s"
override def formatLine(label: FormattableLabel): String = {
f"${label.bankNumber}%02x:${label.startValue}%04x ${label.labelName}%s"
}
override def fileExtension(bank: Int): String = ".sym"
@ -93,8 +120,8 @@ object SymDebugOutputFormat extends DebugOutputFormat {
}
object FceuxDebugOutputFormat extends DebugOutputFormat {
override def formatLine(label: String, bank: Int, value: Int): String = {
f"$$$value%04x#$label%s#"
override def formatLine(label: FormattableLabel): String = {
f"$$${label.startValue}%04x#${label.labelName}%s#"
}
override def fileExtension(bank: Int): String = if (bank == 0xff) ".ram.nl" else s".$bank.nl"
@ -105,3 +132,97 @@ object FceuxDebugOutputFormat extends DebugOutputFormat {
override def formatBreakpoint(bank: Int, value: Int): Option[String] = None
}
object MesenOutputFormat extends DebugOutputFormat {
override def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
val allStarts = labels.groupBy(_.bankName).mapValues(_.map(_.startValue).toSet)
labels.flatMap{ l =>
val mesenShift = b.getMesenShift(l.bankName)
val shiftedStart = l.startValue + mesenShift
var shiftedEnd = l.endValue.map(_ + mesenShift).filter(_ != shiftedStart)
l.endValue match {
case None =>
case Some(e) =>
// Mesen does not like labels of form XXX-XXX, where both ends are equal
breakable {
for (i <- l.startValue.+(1) to e) {
if (allStarts.getOrElse(l.bankName, Set.empty).contains(i)) {
shiftedEnd = None
break
}
}
}
}
if (shiftedStart >= 0 && shiftedEnd.forall(_ >= 0)) {
val normalized = l.labelName.replace('$', '_').replace('.', '_')
val comment = (l.category match {
case 'F' => "function "
case 'A' => "initialized array "
case 'V' => "initialized variable "
case 'a' => "array "
case 'v' => "variable "
case _ => ""
}) + l.labelName
Some(f"${l.mesenSymbol}%s:${shiftedStart}%04X${shiftedEnd.fold("")(e => f"-$e%04X")}%s:$normalized%s:$comment%s")
} else {
None
}
}.mkString("\n")
}
override def formatLine(label: FormattableLabel): String = throw new UnsupportedOperationException()
override def formatBreakpoint(bank: Int, value: Int): Option[String] = None
override def fileExtension(bank: Int): String = ".mlb"
override def filePerBank: Boolean = false
override def addOutputExtension: Boolean = false
}
object Ld65OutputFormat extends DebugOutputFormat {
override def formatLine(label: FormattableLabel): String = throw new UnsupportedOperationException()
override def formatBreakpoint(bank: Int, value: Int): Option[String] = None
override def fileExtension(bank: Int): String = ".dbg"
override def filePerBank: Boolean = false
override def addOutputExtension: Boolean = true
override def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
val q = '"'
val allBanksInFile = b.allBanks()
val allBanks = (labels.map(_.bankName).distinct ++ allBanksInFile).distinct
val result = mutable.ListBuffer[String]()
result += "version\tmajor=2,minor=0"
result += s"info\tcsym=0,file=0,lib=0,line=0,mod=0,scope=1,seg=${allBanks.size},span=0,sym=${labels.size},type=4"
for ((bank, ix) <- allBanks.zipWithIndex) {
val common = s"seg\tid=${ix},name=$q${bank}$q,start=0x${b.getStart(bank).toHexString},size=0x0,addrsize=absolute"
if (allBanksInFile.contains(bank)) {
result += common + s",ooffs=${b.ld65Offset(bank)}"
} else {
result += common
}
}
result += "scope\tid=0,name=\"\""
for ((label, ix) <- labels.sortBy(l => (l.bankName, l.startValue, l.labelName)).zipWithIndex) {
val name = label.labelName.replace('$', '@').replace('.', '@')
val sb = new StringBuilder
sb ++= s"sym\tid=${ix},name=$q${name}$q,addrsize=absolute,"
label.endValue match {
case Some(e) =>
if (!labels.exists(l => l.ne(label) && l.startValue >= label.startValue && l.startValue <= e)) {
sb ++= s"size=${ e - label.startValue + 1 },"
}
case None =>
}
sb ++= s"scope=0,val=0x${label.startValue.toHexString},seg=${allBanks.indexOf(label.bankName)},type=lab"
result += sb.toString
}
result.mkString("\n")
}
}

View File

@ -119,25 +119,35 @@ object Main {
else if (l.startsWith("__")) 7
else 0
}
val sortedLabels = result.labels.groupBy(_._2).values.map(_.minBy(a => labelUnimportance(a._1) -> a._1)).toSeq.sortBy(_._2)
val sortedLabels: Seq[FormattableLabel] =
result.labels.groupBy(_._2).values
.map(_.minBy(a => labelUnimportance(a._1) -> a._1)).toSeq.sortBy(_._2)
.map { case (l, (b, s)) =>
val bankNumber = options.platform.bankNumbers.getOrElse(b, 0)
val mesenCategory = options.platform.getMesenLabelCategory(b, s)
result.endLabels.get(l) match {
case Some((c, e)) => FormattableLabel(l, b, bankNumber, s, Some(e), c, mesenCategory)
case _ => FormattableLabel(l, b, bankNumber, s, None, 'x', mesenCategory)
}
}
val sortedBreakpoints = result.breakpoints
val format = c.outputLabelsFormatOverride.getOrElse(platform.outputLabelsFormat)
val basename = if (format.addOutputExtension) output + platform.fileExtension else output
if (format.filePerBank) {
val banks = sortedLabels.map(_._2._1).toSet ++ sortedBreakpoints.map(_._2).toSet
val banks: Set[Int] = sortedLabels.map(_.bankNumber).toSet ++ sortedBreakpoints.map(_._1).toSet
banks.foreach{ bank =>
val labels = sortedLabels.filter(_._2._1.==(bank))
val labels = sortedLabels.filter(_.bankNumber.==(bank))
val breakpoints = sortedBreakpoints.filter(_._1.==(bank))
val labelOutput = basename + format.fileExtension(bank)
val path = Paths.get(labelOutput)
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
Files.write(path, format.formatAll(labels, breakpoints).getBytes(StandardCharsets.UTF_8))
Files.write(path, format.formatAll(result.bankLayoutInFile, labels, breakpoints).getBytes(StandardCharsets.UTF_8))
}
} else {
val labelOutput = basename + format.fileExtension(0)
val path = Paths.get(labelOutput)
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
Files.write(path, format.formatAll(sortedLabels, sortedBreakpoints).getBytes(StandardCharsets.UTF_8))
Files.write(path, format.formatAll(result.bankLayoutInFile, sortedLabels, sortedBreakpoints).getBytes(StandardCharsets.UTF_8))
}
}
val defaultPrgOutput = if (output.endsWith(platform.fileExtension)) output else output + platform.fileExtension
@ -165,7 +175,7 @@ object Main {
f"${path.getFileName}%s ${start}%04X ${start}%04X ${codeLength}%04X".getBytes(StandardCharsets.UTF_8))
}
}
errorReporting.debug(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
errorReporting.info(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
c.runFileName.foreach{ program =>
if (File.separatorChar == '\\') {
if (!new File(program).exists() && !new File(program + ".exe").exists()) {
@ -241,10 +251,10 @@ object Main {
log.debug(s"Failed to find the default include path: $err")
case Right(path) =>
log.debug(s"Automatically detected include path: $path")
return c.copy(includePath = List(path) ++ c.extraIncludePath)
return c.copy(includePath = List(System.getProperty("user.dir"), path) ++ c.extraIncludePath)
}
}
c.copy(includePath = c.includePath ++ c.extraIncludePath)
c.copy(includePath = System.getProperty("user.dir") :: (c.includePath ++ c.extraIncludePath))
}
private def assembleForMos(c: Context, platform: Platform, options: CompilationOptions): AssemblerOutput = {
@ -283,6 +293,7 @@ object Main {
if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.All else Nil,
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForEmulation else Nil,
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) SixteenOptimizations.AllForNative else Nil,
if (options.flag(CompilationFlag.IdentityPage)) IdentityPageOptimizations.All else Nil,
if (options.flag(CompilationFlag.DangerousOptimizations)) DangerousOptimizations.All else Nil
).flatten
val goodCycle = List.fill(optLevel - 2)(OptimizationPresets.Good ++ goodExtras).flatten
@ -321,7 +332,9 @@ object Main {
val assemblyOptimizations = optLevel match {
case 0 => Nil
case _ =>
if (options.flag(CompilationFlag.EmitZ80Opcodes))
if (options.flag(CompilationFlag.EmitR800Opcodes))
Z80OptimizationPresets.GoodForR800
else if (options.flag(CompilationFlag.EmitZ80Opcodes))
Z80OptimizationPresets.GoodForZ80
else if (options.flag(CompilationFlag.EmitIntel8080Opcodes))
Z80OptimizationPresets.GoodForIntel8080
@ -427,7 +440,7 @@ object Main {
p.toLowerCase(Locale.ROOT),
errorReporting.fatal("Invalid label file format: " + p))
c.copy(outputLabels = true, outputLabelsFormatOverride = Some(f))
}.description("Generate also the label file in the given format. Available options: vice, nesasm, sym.")
}.description("Generate also the label file in the given format. Available options: vice, nesasm, sym, raw.")
boolean("-fbreakpoints", "-fno-breakpoints").action((c,v) =>
c.changeFlag(CompilationFlag.EnableBreakpoints, v)
@ -487,7 +500,7 @@ object Main {
} else {
errorReporting.fatal("Invalid syntax for -D option")
}
}.description("Define a feature value for the preprocessor.")
}.description("Define a feature value for the preprocessor.").maxCount(Integer.MAX_VALUE)
boolean("-finput_intel_syntax", "-finput_zilog_syntax").repeatable().action((c,v) =>
c.changeFlag(CompilationFlag.UseIntelSyntaxForInput, v)
@ -650,7 +663,7 @@ object Main {
c.changeFlag(CompilationFlag.CompactReturnDispatchParams, v)
}.description("Whether parameter values in return dispatch statements may overlap other objects. Enabled by default.")
boolean("-fbounds-checking", "-fno-bounds-checking").action { (c, v) =>
c.changeFlag(CompilationFlag.VariableOverlap, v)
c.changeFlag(CompilationFlag.CheckIndexOutOfBounds, v)
}.description("Whether should insert bounds checking on array access.")
boolean("-flenient-encoding", "-fno-lenient-encoding").action { (c, v) =>
c.changeFlag(CompilationFlag.LenientTextEncoding, v)
@ -712,6 +725,9 @@ object Main {
}.description("Optimize code even more.")
if (i == 1 || i > 4) f.hidden()
}
boolean("-fhints", "-fnohints").action{ (c,v) =>
c.changeFlag(CompilationFlag.UseOptimizationHints, v)
}.description("Whether optimization hints should be used.")
flag("--inline").repeatable().action { c =>
c.changeFlag(CompilationFlag.InlineFunctions, true)
}.description("Inline functions automatically.").hidden()
@ -742,6 +758,9 @@ object Main {
boolean("-fregister-variables", "-fno-register-variables").action { (c, v) =>
c.changeFlag(CompilationFlag.RegisterVariables, v)
}.description("Allow moving local variables into CPU registers. Enabled by default.")
boolean("-fidentity-page", "-fno-identity-page").action { (c, v) =>
c.changeFlag(CompilationFlag.IdentityPage, v)
}.description("Whether should use an identity page to optimize certain operations.")
flag("-Os", "--size").repeatable().action { c =>
c.changeFlag(CompilationFlag.OptimizeForSize, true).
changeFlag(CompilationFlag.OptimizeForSpeed, false).
@ -754,6 +773,7 @@ object Main {
}.description("Prefer faster code even if it is slightly bigger (experimental). Implies -finline.")
flag("-Ob", "--blast-processing").repeatable().action { c =>
c.changeFlag(CompilationFlag.OptimizeForSize, false).
changeFlag(CompilationFlag.IdentityPage, true).
changeFlag(CompilationFlag.OptimizeForSpeed, true).
changeFlag(CompilationFlag.OptimizeForSonicSpeed, true)
}.description("Prefer faster code even if it is much bigger (experimental). Implies -finline.")
@ -781,6 +801,52 @@ object Main {
c.changeFlag(CompilationFlag.FatalWarnings, true)
}.description("Treat warnings as errors.")
fluff("", "Specific warning options:", "")
boolean("-Wbuggy", "-Wno-buggy").repeatable().action { (c, v) =>
c.changeFlag(CompilationFlag.BuggyCodeWarning, v)
}.description("Whether should warn about code that may cause surprising behaviours or even miscompilation. Default: enabled.")
boolean("-Wdeprecation", "-Wno-deprecation").repeatable().action { (c, v) =>
c.changeFlag(CompilationFlag.DeprecationWarning, v)
}.description("Whether should warn about deprecated aliases. Default: enabled.")
boolean("-Wcomparisons", "-Wno-comparisons").repeatable().action { (c, v) =>
c.changeFlag(CompilationFlag.BytePointerComparisonWarning, v)
}.description("Whether should warn about comparisons between bytes and pointers. Default: enabled.")
boolean("-Wextra-comparisons", "-Wno-extra-comparisons").repeatable().action { (c, v) =>
c.changeFlag(CompilationFlag.ExtraComparisonWarnings, v)
}.description("Whether should warn about simplifiable unsigned integer comparisons. Default: disabled.")
boolean("-Wfallback", "-Wno-fallback").repeatable().action { (c, v) =>
c.changeFlag(CompilationFlag.FallbackValueUseWarning, v)
}.description("Whether should warn about the use of default values by text codecs, the preprocessor, and array literals. Default: enabled.")
boolean("-Wmissing-output", "-Wno-missing-output").repeatable().action { (c, v) =>
c.changeFlag(CompilationFlag.DataMissingInOutputWarning, v)
}.description("Whether should warn about data that is missing in output files. Default: enabled.")
boolean("-Woverlapping-call", "-Wno-overlapping-call").repeatable().action { (c, v) =>
c.changeFlag(CompilationFlag.CallToOverlappingBankWarning, v)
}.description("Whether should warn about calls to functions in a different, yet overlapping segment. Default: enabled.")
boolean("-Wror", "-Wno-ror").repeatable().action { (c, v) =>
c.changeFlag(CompilationFlag.RorWarning, v)
}.description("Whether should warn about the ROR instruction (6502 only). Default: disabled.")
boolean("-Woverflow", "-Wno-overflow").repeatable().action { (c, v) =>
c.changeFlag(CompilationFlag.ByteOverflowWarning, v)
}.description("Whether should warn about byte overflow. Default: enabled.")
boolean("-Wuseless", "-Wno-useless").repeatable().action { (c, v) =>
c.changeFlag(CompilationFlag.UselessCodeWarning, v)
}.description("Whether should warn about code that does nothing. Default: enabled.")
boolean("-Whints", "-Wno-hints").repeatable().action { (c, v) =>
c.changeFlag(CompilationFlag.UnsupportedOptimizationHintWarning, v)
}.description("Whether should warn about unsupported optimization hints. Default: enabled.")
fluff("", "Other options:", "")
expansion("-Xd")("-O1", "-s", "-fsource-in-asm", "-g").description("Do a debug build. Equivalent to -O1 -s -fsource-in-asm -g")

View File

@ -12,4 +12,28 @@ object MathUtils {
def lcm(a: Int, b: Int): Int =
(a.toLong * b.toLong / gcd(a, b)).toInt
def collapseConsecutiveInts(nums :Seq[Int], format: Int=>String): String = {
val result = Seq.newBuilder[String]
var lastIntervalStart = -2
var lastIntervalEnd = -2
def flushInterval(): Unit = {
if (lastIntervalEnd >= 0) {
if (lastIntervalStart == lastIntervalEnd) {
result += format(lastIntervalEnd)
} else {
result += format(lastIntervalStart) + "-" + format(lastIntervalEnd)
}
}
}
for(n <- nums) {
if (n != lastIntervalEnd + 1) {
flushInterval()
lastIntervalStart = n
}
lastIntervalEnd = n
}
flushInterval()
result.result().mkString(",")
}
}

View File

@ -42,6 +42,7 @@ class Platform(
val outputLabelsFormat: DebugOutputFormat,
val outputStyle: OutputStyle.Value
) {
def hasZeroPage: Boolean = cpuFamily == CpuFamily.M6502
def cpuFamily: CpuFamily.Value = CpuFamily.forType(this.cpu)
@ -60,6 +61,18 @@ class Platform(
val e2 = bankEndBefore(bank2)
e2 > s1 && s2 < e1
}
lazy val banksExplicitlyWrittenToOutput: Set[String] = bankNumbers.keySet.filter(b => defaultOutputPackager.writes(b)).toSet
def getMesenLabelCategory(bank: String, address: Int): Char = {
// see: https://www.mesen.ca/docs/debugging/debuggerintegration.html#mesen-label-files-mlb
val start = bankStart(bank)
val end = bankEndBefore(bank)
if (start > address || address >= end) 'G'
else if (banksExplicitlyWrittenToOutput(bank)) 'P'
else if (bank == "default") 'R'
else 'W' // TODO: distinguish between W and S
}
}
object Platform {

View File

@ -2,7 +2,7 @@ package millfork.assembly
import millfork.{CompilationFlag, CompilationOptions}
import millfork.compiler.LabelGenerator
import millfork.env.{NormalFunction, ThingInMemory}
import millfork.env.{Constant, NormalFunction, ThingInMemory}
import millfork.error.Logger
import millfork.node.NiceFunctionProperty
@ -10,8 +10,9 @@ import millfork.node.NiceFunctionProperty
* @author Karol Stasiak
*/
case class OptimizationContext(options: CompilationOptions,
labelMap: Map[String, (Int, Int)],
labelMap: Map[String, (String, Int)],
zreg: Option[ThingInMemory],
identityPage: Constant,
niceFunctionProperties: Set[(NiceFunctionProperty, String)]) {
@inline
def log: Logger = options.log
@ -25,4 +26,6 @@ trait AssemblyOptimization[T <: AbstractCode] {
def optimize(f: NormalFunction, code: List[T], context: OptimizationContext): List[T]
def requiredFlags: Set[CompilationFlag.Value] = Set.empty
def minimumRequiredLines: Int
}

View File

@ -168,6 +168,7 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
def changesRegister(reg: M6809Register.Value): Boolean = {
import M6809Register._
if (MOpcode.NotActualOpcodes(opcode)) return false
def overlaps(other: M6809Register.Value): Boolean = {
if (reg == D && (other == A || other == B)) true
else if (other == D && (reg == A || reg == B)) true
@ -202,12 +203,13 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
}
}
def changesCarryFlag: Boolean = !MOpcode.PreservesC(opcode)
def changesCarryFlag: Boolean = !MOpcode.NotActualOpcodes(opcode) && !MOpcode.PreservesC(opcode)
def readsRegister(reg: M6809Register.Value): Boolean = {
import M6809Register._
if (MOpcode.NotActualOpcodes(opcode)) return false
def overlaps(other: M6809Register.Value): Boolean = {
if (reg == D && (other == A || other == B)) true
else if (other == D && (reg == A || reg == B)) true
@ -252,15 +254,17 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
case MUL => overlaps(D)
case ABX => reg == X || overlaps(B)
case NOP | SWI | SWI2 | SWI3 | SYNC => false
case LDB | LDA | LDX | LDY | LDU => false
case INC | DEC | ROL | ROR | ASL | ASR | LSR | CLR | COM | NEG | TST => false // variants for A and B handled before
case op if Branching(op) => false
case JMP => false
case JMP | RTS => false
case _ => true // TODO
}
}
def readsMemory(): Boolean = {
import MOpcode._
if (MOpcode.NotActualOpcodes(opcode)) return false
val opcodeIsForReading = opcode match {
case LDA | LDB | LDD | LDX | LDY | LDU | LDS | PULU | PULS => true
case ADDA | SUBA | ADCA | SBCA | ORA | EORA | ANDA | CMPA | BITA => true
@ -289,6 +293,7 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
def changesMemory(): Boolean = {
import MOpcode._
if (NotActualOpcodes(opcode)) return false
val opcodeIsForWriting = opcode match {
case LDA | LDB | LDD | LDX | LDY | LDU | LDS | PULU | PULS => false
case ADDA | SUBA | ADCA | SBCA | ORA | EORA | ANDA | CMPA | BITA => false

View File

@ -32,9 +32,10 @@ object MOpcode extends Enumeration {
TFR, TST,
DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL = Value
val NotActualOpcodes: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL)
private val toMap: Map[String, MOpcode.Value] = {
val notActualOpcodes: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL)
values.filterNot(notActualOpcodes).map(o => o.toString -> o).toMap ++ Map("BHS" -> BCC, "BLO" -> BCS, "LSL" -> ASL)
values.filterNot(NotActualOpcodes).map(o => o.toString -> o).toMap ++ Map("BHS" -> BCC, "BLO" -> BCS, "LSL" -> ASL)
}
val NoopDiscard: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC)
val PrefixedBy10: Set[MOpcode.Value] = Set(CMPD, CMPY, LDS, LDY, SWI2, STS, STY) // TODO: branches

View File

@ -3,7 +3,8 @@ package millfork.assembly.m6809.opt
import millfork.assembly.AssemblyOptimization
import millfork.assembly.m6809.MOpcode._
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, LongRelative, MAddrMode, MLine, MState, PostIncremented, RegisterSet}
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, Indexed, InherentA, InherentB, LongRelative, MAddrMode, MLine, MState, PostIncremented, RegisterSet}
import millfork.env.{CompoundConstant, Constant, MathOperator}
import millfork.node.M6809Register
/**
@ -19,15 +20,38 @@ object AlwaysGoodMOptimizations {
(Elidable & HasOpcodeIn(LDX, LEAX) & DoesntMatterWhatItDoesWith(MState.X, MState.NF, MState.ZF, MState.VF)) ~~> (_ => Nil),
(Elidable & HasOpcodeIn(LDY, LEAY) & DoesntMatterWhatItDoesWith(MState.Y, MState.NF, MState.ZF, MState.VF)) ~~> (_ => Nil),
(Elidable & HasOpcode(STB) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & Not(ChangesB) & DoesntChangeIndexingInAddrMode(0)).* ~
(Linear & Not(ChangesB) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
(Elidable & HasOpcode(LDB) & MatchAddrMode(0) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> (_.init),
(Elidable & HasOpcode(STD) & MatchAddrMode(0) & MatchParameter(1)) ~
(Linear & Not(ChangesB) & DoesntChangeIndexingInAddrMode(0)).* ~
(Linear & Not(ChangesB) & Not(ChangesA) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
(Elidable & HasOpcode(LDD) & MatchAddrMode(0) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> (_.init),
)
val PointlessCompare = new RuleBasedAssemblyOptimization("Pointless compare",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(HasOpcodeIn(LDA, ANDA, ORA, EORA, ADDA, ADCA, SUBA, SBCA)) ~
(Elidable & HasOpcode(CMPA) & HasImmediate(0) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> {code => code.init},
(HasOpcodeIn(LDB, ANDB, ORB, EORB, ADDB, ADCB, SUBB, SBCB)) ~
(Elidable & HasOpcode(CMPB) & HasImmediate(0) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> {code => code.init},
(Elidable & HasOpcode(EORA) & HasAddrMode(Immediate) & MatchParameter(0)) ~
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.A, MState.NF, MState.VF, MState.CF, MState.HF)) ~~> { (code, ctx) =>
val c0 = ctx.get[Constant](0)
val c1 = ctx.get[Constant](1)
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
},
(Elidable & HasOpcode(EORB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.B, MState.NF, MState.VF, MState.CF, MState.HF)) ~~> { (code, ctx) =>
val c0 = ctx.get[Constant](0)
val c1 = ctx.get[Constant](1)
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
},
)
val SimplifiableZeroStore = new RuleBasedAssemblyOptimization("Simplifiable zero store",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcode(LDA) & HasImmediate(0) & DoesntMatterWhatItDoesWith(MState.CF)) ~~> {
@ -50,6 +74,75 @@ object AlwaysGoodMOptimizations {
},
)
val SimplifiableComparison = new RuleBasedAssemblyOptimization("Simplifiable comparison",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcode(EORB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
val c0 = ctx.get[Constant](0)
val c1 = ctx.get[Constant](1)
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
},
(Elidable & HasOpcode(ADDB) & HasImmediate(1)) ~
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
val c1 = ctx.get[Constant](1)
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
},
(Elidable & HasOpcode(SUBB) & HasImmediate(1)) ~
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
val c1 = ctx.get[Constant](1)
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
},
(Elidable & HasOpcode(INC) & HasAddrMode(InherentB)) ~
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
val c1 = ctx.get[Constant](1)
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
},
(Elidable & HasOpcode(DEC) & HasAddrMode(InherentB)) ~
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
val c1 = ctx.get[Constant](1)
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
},
(Elidable & HasOpcode(EORA) & HasAddrMode(Immediate) & MatchParameter(0)) ~
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
val c0 = ctx.get[Constant](0)
val c1 = ctx.get[Constant](1)
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
},
(Elidable & HasOpcode(ADDA) & HasImmediate(1)) ~
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
val c1 = ctx.get[Constant](1)
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
},
(Elidable & HasOpcode(SUBA) & HasImmediate(1)) ~
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
val c1 = ctx.get[Constant](1)
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
},
(Elidable & HasOpcode(INC) & HasAddrMode(InherentA)) ~
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
val c1 = ctx.get[Constant](1)
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
},
(Elidable & HasOpcode(DEC) & HasAddrMode(InherentA)) ~
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
val c1 = ctx.get[Constant](1)
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
},
)
val PointlessRegisterTransfers = new RuleBasedAssemblyOptimization("Pointless register transfers",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & IsTfr(M6809Register.D, M6809Register.X)) ~
@ -144,14 +237,67 @@ object AlwaysGoodMOptimizations {
(_.map(_.copy(opcode = LDD))),
(Elidable & HasImmediate(0) & HasOpcodeIn(ORB, EORB, ADDB, ORA, EORA, ADDA, ADDD) & DoesntMatterWhatItDoesWith(MState.VF, MState.ZF, MState.NF, MState.CF, MState.HF)) ~~>
(_ => Nil),
(HasOpcode(LDB)) ~
(Elidable & HasOpcode(ANDB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
(Elidable & HasOpcode(BNE) & MatchParameter(1)) ~
(Elidable & HasOpcode(LDB)) ~
(HasOpcode(ANDB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
(HasOpcode(BEQ)) ~
(HasOpcode(LABEL) & MatchParameter(1)) ~~> { code =>
List(code.head, code(3).copy(opcode = ORB)) ++ code.drop(4)
},
(Elidable & HasOpcode(ADDB) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentB(INC)) },
(Elidable & HasOpcode(ADDA) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentA(INC)) },
(Elidable & HasOpcode(SUBB) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentB(DEC)) },
(Elidable & HasOpcode(SUBA) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentA(DEC)) },
)
val SimplifiableAddressing = new RuleBasedAssemblyOptimization("Simplifiable addressing",
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
(NotFixed & MatchX(0) & HasAddrMode(Indexed(M6809Register.X, indirect = false)) & Not(HasOpcodeIn(LEAX, LEAY, LEAS, LEAU))) ~~> { (code, ctx) =>
val x = ctx.get[Constant](0)
code.map(l => l.copy(addrMode = Absolute(indirect = false), parameter = (x + l.parameter).quickSimplify))
},
(NotFixed & MatchY(0) & HasAddrMode(Indexed(M6809Register.Y, indirect = false)) & Not(HasOpcodeIn(LEAX, LEAY, LEAS, LEAU))) ~~> { (code, ctx) =>
val y = ctx.get[Constant](0)
code.map(l => l.copy(addrMode = Absolute(indirect = false), parameter = (y + l.parameter).quickSimplify))
},
(NotFixed & MatchX(0) & HasAddrMode(Indexed(M6809Register.X, indirect = false)) & HasOpcodeIn(LEAX, LEAY, LEAS, LEAU)) ~~> { (code, ctx) =>
val x = ctx.get[Constant](0)
code.map(l => l.copy(opcode = l.opcode match {
case LEAX => LDX
case LEAY => LDY
case LEAU => LDU
case LEAS => LDS
}, addrMode = Immediate, parameter = (x + l.parameter).quickSimplify))
},
(NotFixed & MatchY(0) & HasAddrMode(Indexed(M6809Register.X, indirect = false)) & HasOpcodeIn(LEAX, LEAY, LEAS, LEAU)) ~~> { (code, ctx) =>
val y = ctx.get[Constant](0)
code.map(l => l.copy(opcode = l.opcode match {
case LEAX => LDX
case LEAY => LDY
case LEAU => LDU
case LEAS => LDS
}, addrMode = Immediate, parameter = (y + l.parameter).quickSimplify))
},
)
val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal",
needsFlowInfo = FlowInfoRequirement.JustLabels,
(Elidable & HasOpcode(LABEL) & HasCallerCount(0) & ParameterIsLocalLabel) ~~> (_ => Nil)
)
val All: Seq[AssemblyOptimization[MLine]] = Seq(
PointlessLoad,
PointlessCompare,
PointlessRegisterTransfers,
SimplifiableAddressing,
SimplifiableArithmetics,
SimplifiableComparison,
SimplifiableJumps,
SimplifiableZeroStore
SimplifiableZeroStore,
UnusedLabelRemoval
)
}

Some files were not shown because too many files have changed in this diff Show More