mirror of
https://github.com/KarolS/millfork.git
synced 2024-05-31 18:41:30 +00:00
Compare commits
135 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d8c11a9c50 | ||
|
ee47ccef5a | ||
|
24de2f7530 | ||
|
1beb695151 | ||
|
9229092309 | ||
|
ca7166d1ae | ||
|
1594d63a9a | ||
|
75cc34663c | ||
|
f4d2fdd370 | ||
|
29c1e3f2a6 | ||
|
e9cfec54b5 | ||
|
1bc1ab3539 | ||
|
6f294d6dec | ||
|
9866c974ad | ||
|
ef2f5b5918 | ||
|
a70a1c0e6b | ||
|
790c836771 | ||
|
6af84d1628 | ||
|
7b205a2754 | ||
|
d23fe1756e | ||
|
3b3ee1bc55 | ||
|
fb30075ea5 | ||
|
b5084cd180 | ||
|
b1a2be5574 | ||
|
a260d0a806 | ||
|
38ad919ed9 | ||
|
34ef8b8de9 | ||
|
f676e74e38 | ||
|
c9313e5dbe | ||
|
73a223b9b6 | ||
|
b168818bab | ||
|
effa723863 | ||
|
b2ab3dbeab | ||
|
c9ef5e636b | ||
|
fc2f0782c5 | ||
|
f0e22f02a6 | ||
|
b2291d1cb2 | ||
|
166acf2b18 | ||
|
7530b382a8 | ||
|
b66435d06b | ||
|
0c8951d015 | ||
|
84dde8589c | ||
|
3b0aa9a425 | ||
|
1b8de990a5 | ||
|
b060fc599b | ||
|
90e5360bfd | ||
|
bbf430a1c7 | ||
|
7f6a0c6b0d | ||
|
7f0def54bc | ||
|
faf97cee1f | ||
|
da862069a7 | ||
|
431a25d325 | ||
|
73beafd65e | ||
|
307ad90ecf | ||
|
2e592a2331 | ||
|
91c9b42f3d | ||
|
144da70594 | ||
|
4f6eefab79 | ||
|
ca35367974 | ||
|
2065c3b4ac | ||
|
9028d55a7e | ||
|
21d4d3252f | ||
|
45ad049a51 | ||
|
0172e29bb2 | ||
|
ffb46c4250 | ||
|
c51c08ad56 | ||
|
fcdad413b0 | ||
|
63edce28c4 | ||
|
1f318a2a0e | ||
|
1bcb6d5010 | ||
|
510f85960c | ||
|
8412075175 | ||
|
e25df3d1b3 | ||
|
b71d058c6a | ||
|
bf273456a3 | ||
|
062483971a | ||
|
1e4a193741 | ||
|
2468d8cca5 | ||
|
58b5b6ff28 | ||
|
8aac3bc329 | ||
|
24eac6708b | ||
|
66fc1d3984 | ||
|
0bbdc348e7 | ||
|
15a32a4caf | ||
|
e8f5667faa | ||
|
03bc5894aa | ||
|
bd9ff2f66d | ||
|
a111af384e | ||
|
6f2c8cd991 | ||
|
a0c2eaabcf | ||
|
83393d49f1 | ||
|
84d707b932 | ||
|
f01879e4a3 | ||
|
efb3d3c7c9 | ||
|
22bd6ac443 | ||
|
ffa0ffb899 | ||
|
3155d7a571 | ||
|
ba7c5b507c | ||
|
196ad6542f | ||
|
ff6106a838 | ||
|
9ebbbdc022 | ||
|
ab9cdf7ad5 | ||
|
4ff6120702 | ||
|
521b73d0d3 | ||
|
478b2eefbd | ||
|
3d57421959 | ||
|
78c29c784d | ||
|
25adb05229 | ||
|
bb46b1e7e9 | ||
|
7c60a89776 | ||
|
02031da61a | ||
|
d20cc677bb | ||
|
2beabb7bed | ||
|
fb71f88343 | ||
|
7689afb5d6 | ||
|
958c1c09e7 | ||
|
919f11af2e | ||
|
9c7e946f4c | ||
|
32b98750a9 | ||
|
ef34f534f9 | ||
|
3a2e29888d | ||
|
d6deb81166 | ||
|
394ac63805 | ||
|
42f6efc9bd | ||
|
8b0c026bf7 | ||
|
c1959b356f | ||
|
499e650752 | ||
|
fe4f0dcfd9 | ||
|
c6932da6b3 | ||
|
600bfce0c1 | ||
|
9feda54d92 | ||
|
91699b64c6 | ||
|
2a5933e115 | ||
|
bd8078cc47 | ||
|
30216d0be6 |
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -31,9 +31,12 @@ issue*.mfk
|
|||
*.seq
|
||||
*.asm
|
||||
*.lbl
|
||||
*.labels
|
||||
*.nl
|
||||
*.fns
|
||||
*.sym
|
||||
*.mlb
|
||||
*.dbg
|
||||
*.deb
|
||||
*.xex
|
||||
*.nes
|
||||
|
|
108
CHANGELOG.md
108
CHANGELOG.md
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
A middle-level programming language targeting 6502-based, 8080-based, Z80-based and 6809-based microcomputers.
|
||||
|
||||
For binary releases, see: [https://github.com/KarolS/millfork/releases](https://github.com/KarolS/millfork/releases)
|
||||
(latest: 0.3.24).
|
||||
(latest: 0.3.30).
|
||||
For build instructions, see [Build instructions](./COMPILING.md).
|
||||
|
||||
## Features
|
||||
|
|
|
@ -1,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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
### A note about Commodore 64
|
||||
|
||||
#### Unusual main function location
|
||||
|
||||
If you're creating a prg file and your `main` function may be located at address $2710 hex (10000 decimal) or higher,
|
||||
you might need to define the `DISPLACED_MAIN=1` preprocessor feature.
|
||||
|
||||
#### Multifile programs
|
||||
|
||||
A multifile program is a program stored on a disk that consists of the main program file that is executed first
|
||||
|
|
|
@ -37,6 +37,8 @@ if a line ends with a backslash character, the value continues to the next line.
|
|||
* `z80` (Zilog Z80)
|
||||
|
||||
* `strictz80` (Z80 without illegal instructions)
|
||||
|
||||
* `r800` (R800)
|
||||
|
||||
* `z80next` (Z80 core from ZX Spectrum Next)
|
||||
Note: Millfork version 0.3.18 and earlier uses the name `zx80next` for this architecture.
|
||||
|
@ -80,6 +82,8 @@ This list cannot contain module template instantiations.
|
|||
* `emit_x80` – whether the compiler should emit instructions present on Sharp LR35902 and Z80, but absent on Intel 8080, default is `true` on compatible processors and `false` elsewhere
|
||||
|
||||
* `emit_z80` – whether the compiler should emit Zilog Z80 instructions not covered by `emit_x80`, default is `true` on compatible processors and `false` elsewhere
|
||||
|
||||
* `emit_r800` – whether the compiler should emit R800 instructions, default is `true` on compatible processors and `false` elsewhere
|
||||
|
||||
* `prevent_jmp_indirect_bug` – whether the compiler should try to avoid the indirect JMP bug,
|
||||
default is `false` on 65C02-compatible or non-6502 processors and `true` elsewhere
|
||||
|
@ -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`.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ No other lines are allowed in the file.
|
|||
* `NAME=<name>` defines the name for this encoding. Required.
|
||||
|
||||
* `BUILTIN=<internal name>` defines this encoding to be a UTF-based encoding.
|
||||
`<internal name>` may be one of `UTF-8`, `UTF-16LE`, `UTF-16BE`.
|
||||
`<internal name>` may be one of `UTF-8`, `UTF-16LE`, `UTF-16BE`, `UTF-32LE`, `UTF-32BE`.
|
||||
If this directive is present, the only other allowed directive in the file is the `NAME` directive.
|
||||
|
||||
* `EOT=<xx>` where `<xx>` are two hex digits, defines the string terminator byte.
|
||||
|
|
|
@ -4,17 +4,17 @@
|
|||
|
||||
Syntax:
|
||||
|
||||
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [@ <address>] { <body> }`
|
||||
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [<optimization hints>] [@ <address>] { <body> }`
|
||||
|
||||
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [@ <address>] = <expression>`
|
||||
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [<optimization hints>] [@ <address>] = <expression>`
|
||||
|
||||
`[segment (<segment>)] asm <return_type> <name> ( <params> ) @ <address> extern`
|
||||
`[segment (<segment>)] asm <return_type> <name> ( <params> ) [<optimization hints>] @ <address> extern`
|
||||
|
||||
Examples:
|
||||
|
||||
void do_nothing() { }
|
||||
inline byte two() = 2
|
||||
asm void chkout(byte register(a) char) @ $FFD2 extern
|
||||
asm void chkout(byte register(a) char) !preserves_x !preserves_y @ $FFD2 extern
|
||||
segment(prgrom0) void main_loop(word w, byte x) align(fast) { // body omitted
|
||||
|
||||
|
||||
|
@ -68,6 +68,8 @@ For assembly functions, certain parameter names are interpreted as CPU registers
|
|||
* on 6502, it means that the function will not cross a page boundary if possible
|
||||
* on Z80, it is ignored
|
||||
|
||||
* `<optimization hints>` is a list of [optimization hints](./hints.md), separated by spaces
|
||||
|
||||
* `<address>` is a constant expression that defines where in the memory the function is or will be located.
|
||||
|
||||
* `extern` is a keyword than marks functions that are not defined in the current program,
|
||||
|
|
84
docs/lang/hints.md
Normal file
84
docs/lang/hints.md
Normal file
|
@ -0,0 +1,84 @@
|
|||
[< back to index](../doc_index.md)
|
||||
|
||||
# Optimization hints
|
||||
|
||||
Optimization hints are optional annotations for functions and variables
|
||||
that allow the compiler to make extra assumptions that help with code optimization.
|
||||
|
||||
The general idea is that removing or disabling optimization hints will not break the code,
|
||||
but adding invalid optimization hints may break the code.
|
||||
|
||||
Every optimization hint's name starts with an exclamation mark.
|
||||
|
||||
Optimization hints marked with **(X)** currently do nothing and are planned to be implemented in the future.
|
||||
|
||||
## Hints for functions
|
||||
|
||||
* `!preserves_memory` – the function does not write to memory
|
||||
|
||||
* `!idempotent` – calling the function multiple times in succession has no effect
|
||||
|
||||
* `!hot` – the function is hot and should be optimized for speed at the cost of increased size
|
||||
|
||||
* `!cold` **(X)** – the function is cold and should be optimized for size at the cost of increased run time
|
||||
|
||||
* `!odd` – the function returns an odd value
|
||||
|
||||
* `!even` – the function returns an even value
|
||||
|
||||
## Hints for 6502 assembly functions
|
||||
|
||||
These hints have only effect when used on an assembly function on a 6502 target:
|
||||
|
||||
* `!preserves_a` – the function preserves the contents of the A register
|
||||
|
||||
* `!preserves_x` – the function preserves the contents of the X register
|
||||
|
||||
* `!preserves_y` – the function preserves the contents of the Y register
|
||||
|
||||
* `!preserves_c` – the function preserves the contents of the carry flag
|
||||
|
||||
## Hints for 8080/Z80/LR35902 assembly functions
|
||||
|
||||
These hints have only effect when used on an assembly function on a 8080-like target:
|
||||
|
||||
* `!preserves_a` – the function preserves the contents of the A register
|
||||
|
||||
* `!preserves_bc` – the function preserves the contents of the B and C registers
|
||||
|
||||
* `!preserves_de` – the function preserves the contents of the D and E registers
|
||||
|
||||
* `!preserves_hl` – the function preserves the contents of the H and L registers
|
||||
|
||||
* `!preserves_cf` – the function preserves the contents of the carry flag
|
||||
|
||||
## Hints for 6809 assembly functions
|
||||
|
||||
These hints have only effect when used on an assembly function on a 6809 target:
|
||||
|
||||
* `!preserves_a` – the function preserves the contents of the A register
|
||||
|
||||
* `!preserves_b` – the function preserves the contents of the B register
|
||||
|
||||
* `!preserves_d` – the function preserves the contents of the A and B register
|
||||
|
||||
* `!preserves_dp` **(X)** – the function preserves the contents of the DP register; exceptionally this can also be used on non-assembly functions
|
||||
|
||||
* `!preserves_x` – the function preserves the contents of the X register
|
||||
|
||||
* `!preserves_y` – the function preserves the contents of the Y register
|
||||
|
||||
* `!preserves_u` – the function preserves the contents of the U register
|
||||
|
||||
* `!preserves_c` – the function preserves the contents of the carry flag
|
||||
|
||||
## Hints for variables
|
||||
|
||||
* `!odd` **(X)** – the variable can only contain odd values
|
||||
|
||||
* `!even` **(X)** – the variable can only contain even values
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -14,7 +14,14 @@ Octal: `0o172`
|
|||
|
||||
Hexadecimal: `$D323`, `0x2a2`
|
||||
|
||||
When using Intel syntax for inline assembly, another hexadecimal syntax is available: `0D323H`, `2a2h`.
|
||||
Digits can be separated by underscores for readability. Underscores are also allowed between the radix prefix and the digits:
|
||||
|
||||
123_456
|
||||
0x01_ff
|
||||
0b_0101_1111__1100_0001
|
||||
$___________FF
|
||||
|
||||
When using Intel syntax for inline assembly, another hexadecimal syntax is available: `0D323H`, `2a2h`, `3e_h`.
|
||||
It is not allowed in any other places.
|
||||
|
||||
The type of a literal is the smallest type of undefined signedness
|
||||
|
@ -65,7 +72,7 @@ and the byte constant `nullchar_scr` is defined to be equal to the string termin
|
|||
You can override the values for `nullchar` and `nullchar_scr`
|
||||
by defining preprocessor features `NULLCHAR` and `NULLCHAR_SCR` respectively.
|
||||
|
||||
Warning: If you define UTF-16 to be you default or screen encoding, you will encounter several problems:
|
||||
Warning: If you define UTF-16 or UTF-32 to be you default or screen encoding, you will encounter several problems:
|
||||
|
||||
* `nullchar` and `nullchar_scr` will still be bytes, equal to zero.
|
||||
* the `string` module in the Millfork standard library will not work correctly
|
||||
|
@ -75,21 +82,26 @@ Warning: If you define UTF-16 to be you default or screen encoding, you will enc
|
|||
You can also prepend `p` to the name of the encoding to make the string length-prefixed.
|
||||
|
||||
The length is measured in bytes and doesn't include the zero terminator, if present.
|
||||
In all encodings except for UTF-16 the prefix takes one byte,
|
||||
In all encodings except for UTF-16 and UTF-32 the prefix takes one byte,
|
||||
which means that length-prefixed strings cannot be longer than 255 bytes.
|
||||
|
||||
In case of UTF-16, the length prefix contains the number of code units,
|
||||
so the number of bytes divided by two,
|
||||
which allows for strings of practically unlimited length.
|
||||
The length is stores as two bytes and is always little endian,
|
||||
The length is stored as two bytes and is always little endian,
|
||||
even in case of the `utf16be` encoding or a big-endian processor.
|
||||
|
||||
In case of UTF-32, the length prefix contains the number of Unicode codepoints,
|
||||
so the number of bytes divided by four.
|
||||
The length is stored as four bytes and is always little endian,
|
||||
even in case of the `utf32be` encoding or a big-endian processor.
|
||||
|
||||
"this is a Pascal string" pascii
|
||||
"this is also a Pascal string"p
|
||||
"this is a zero-terminated Pascal string"pz
|
||||
|
||||
Note: A string that's both length-prefixed and zero-terminated does not count as a normal zero-terminated string!
|
||||
To pass it to a function that expects a zero-terminated string, add 1 (or, in case of UTF-16, 2):
|
||||
To pass it to a function that expects a zero-terminated string, add 1 (or, in case of UTF-16, 2, or UTF-32, 4):
|
||||
|
||||
pointer p
|
||||
p = "test"pz
|
||||
|
|
|
@ -11,7 +11,8 @@ Each module has a name, which is its unique identifier.
|
|||
A module name is a sequence of slash-separated valid Millfork identifiers.
|
||||
The name also defines where the module is located:
|
||||
a module named `a/b` is presumed to exist in `a/b.mfk`
|
||||
and it's looked up first in the current working directory,
|
||||
and it's looked up first in the directory that contains the current source file,
|
||||
then in the current working directory,
|
||||
and then in the include directories.
|
||||
|
||||
A module can import other modules, using the `import` statement.
|
||||
|
|
|
@ -18,9 +18,9 @@ Millfork has different operator precedence compared to most other languages. Fro
|
|||
|
||||
* `->` and `[]`
|
||||
|
||||
* `*`, `*'`, `/`, `%%`
|
||||
* `*`, `$*`, `/`, `%%`
|
||||
|
||||
* `+`, `+'`, `-`, `-'`, `|`, `&`, `^`, `>>`, `>>'`, `<<`, `<<'`, `>>>>`
|
||||
* `+`, `$+`, `-`, `$-`, `|`, `&`, `^`, `>>`, `$>>`, `<<`, `$<<`, `>>>>`
|
||||
|
||||
* `:`
|
||||
|
||||
|
@ -34,16 +34,16 @@ Millfork has different operator precedence compared to most other languages. Fro
|
|||
|
||||
You cannot use two different operators at the same precedence levels without using parentheses to disambiguate.
|
||||
It is to prevent confusion about whether `a + b & c << d` means `(a + b) & (c << d)` `((a + b) & c) << d` or something else.
|
||||
The only exceptions are `+` and `-`, and `+'` and `-'`.
|
||||
They are interpreted as expected: `5 - 3 + 2 == 4` and `5 -' 3 +' 2 == 4`.
|
||||
Note that you cannot mix `+'` and `-'` with `+` and `-`.
|
||||
The only exceptions are `+` and `-`, and `$+` and `$-`.
|
||||
They are interpreted as expected: `5 - 3 + 2 == 4` and `5 $- 3 $+ 2 == 4`.
|
||||
Note that you cannot mix `$+` and `$-` with `+` and `-`.
|
||||
|
||||
Certain operators (`/`, `%%`, `<<`, `>>`, `<<'`, `>>'`, `>>>>`, `:`, `!=`) cannot have more than 2 parameters,
|
||||
Certain operators (`/`, `%%`, `<<`, `>>`, `$<<`, `$>>`, `>>>>`, `:`, `!=`) cannot have more than 2 parameters,
|
||||
i.e. `x / y / z` will not compile.
|
||||
|
||||
The decimal operators have two different forms:
|
||||
|
||||
* apostrophe form (e.g. `+'`) – the original one, to be deprecated in the future, may be removed in Millfork 0.4
|
||||
* apostrophe form (e.g. `+'`) – the original one, deprecated, will be removed in Millfork 0.4
|
||||
|
||||
* dollar form (e.g. `$+`) – available since Millfork 0.3.22
|
||||
|
||||
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
52
docs/lang/suffixes.md
Normal 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).
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
91
docs/stdlib/apple2.md
Normal 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`.
|
|
@ -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)
|
||||
|
|
37
examples/apple2/diskrom_dump.mfk
Normal file
37
examples/apple2/diskrom_dump.mfk
Normal 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 { }
|
||||
}
|
|
@ -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
273
include/apple2_prodos.mfk
Normal 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)
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// Input: A = Byte to write.
|
||||
asm void chrout(byte register(a) char) @$FFD2 extern
|
||||
asm void chrout(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
|
||||
|
||||
asm void putchar(byte register(a) char) {
|
||||
JSR chrout
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// Input: A = Byte to write.
|
||||
asm void chrout(byte register(a) char) @$FFD2 extern
|
||||
asm void chrout(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
|
||||
|
||||
asm void putchar(byte register(a) char) {
|
||||
JSR chrout
|
||||
|
|
|
@ -2,7 +2,7 @@ import err
|
|||
|
||||
inline asm void switch_hirom(byte register(a) bank) {
|
||||
? and #$3F
|
||||
! sta $DE01
|
||||
! sta $DE00
|
||||
? rts
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// Input: A = Byte to write.
|
||||
asm void chrout(byte register(a) char) @$FFD2 extern
|
||||
asm void chrout(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
|
||||
|
||||
// CHRIN. Read byte from default input (for keyboard, read a line from the screen). (If not keyboard, must call OPEN and CHKIN beforehands.)
|
||||
// Output: A = Byte read.
|
||||
|
|
|
@ -10,7 +10,7 @@ word sid_v1_freq @$D400
|
|||
word sid_v1_pulse @$D402
|
||||
byte sid_v1_cr @$D404
|
||||
byte sid_v1_ad @$D405
|
||||
byte sid_v1_sr @$D409
|
||||
byte sid_v1_sr @$D406
|
||||
|
||||
word sid_v2_freq @$D407
|
||||
word sid_v2_pulse @$D409
|
||||
|
|
31
include/cbm/basic_loader.mfk
Normal file
31
include/cbm/basic_loader.mfk
Normal file
|
@ -0,0 +1,31 @@
|
|||
#template $ADDR$
|
||||
|
||||
#if not(CBM)
|
||||
#warn cbm/basic_loader module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @ $ADDR$ = [
|
||||
#if DISPLACED_MAIN
|
||||
@word_le [
|
||||
$ADDR$ + 0xB
|
||||
],
|
||||
#else
|
||||
@word_le [
|
||||
$ADDR$ + if(main.addr >= 10000, 1/0, 0xA) // use -D DISPLACED_MAIN=1 if you get an error here
|
||||
],
|
||||
#endif
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
#if DISPLACED_MAIN
|
||||
$30 + (main.addr/10000)%%10,
|
||||
#endif
|
||||
$30 + (main.addr/1000)%%10,
|
||||
$30 + (main.addr/100)%%10,
|
||||
$30 + (main.addr/10)%%10,
|
||||
$30 + (main.addr/1)%%10,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
|
@ -7,7 +7,7 @@ C0-DF=@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
|||
|
||||
a-z=C1
|
||||
|
||||
{q}=02
|
||||
{apos}=07
|
||||
{q}=A2
|
||||
{apos}=A7
|
||||
{nbsp}=40
|
||||
|
||||
{n}=8D
|
||||
|
|
|
@ -13,3 +13,4 @@ EOT=00
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pound}=5c
|
||||
|
|
|
@ -7,8 +7,8 @@ EOT=7F
|
|||
20-3f=@abcdefghijklmnopqrstuvwxyz[\]^_
|
||||
60-7e=πABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~
|
||||
|
||||
{q}=22
|
||||
{apos}=27
|
||||
{q}=02
|
||||
{apos}=07
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pi}=60
|
||||
|
|
|
@ -9,4 +9,4 @@ E0-FE=`abcdefghijklmnopqrstuvwxyz{\}~
|
|||
{q}=A2
|
||||
{apos}=A7
|
||||
{lbrace}=FB
|
||||
{rbrace}=FC
|
||||
{rbrace}=FD
|
||||
|
|
|
@ -26,6 +26,7 @@ f0-ff=đńňóôőö÷řůúűüýţ˙
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{euro}=80
|
||||
{copy}=a9
|
||||
{ss}=df
|
||||
{nbsp}=A0
|
||||
|
|
|
@ -23,6 +23,7 @@ f0-ff=рстуфхцчшщъыьэюя
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{euro}=88
|
||||
{copy}=a9
|
||||
{nbsp}=A0
|
||||
{shy}=AD
|
||||
|
|
|
@ -25,6 +25,7 @@ f0-ff=ðñòóôõö÷øùúûüýþÿ
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{euro}=80
|
||||
{cent}=a2
|
||||
{pound}=a3
|
||||
{yen}=a5
|
||||
|
|
39
include/encoding/cp1253.tbl
Normal file
39
include/encoding/cp1253.tbl
Normal file
|
@ -0,0 +1,39 @@
|
|||
NAME=CP1253
|
||||
EOT=00
|
||||
|
||||
20=U+0020
|
||||
21-3f=!"#$%&'()*+,-./0123456789:;<=>?
|
||||
40-5f=@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
||||
60-7e=`abcdefghijklmnopqrstuvwxyz{|}~
|
||||
80=€
|
||||
82-87=‚ƒ„…†‡
|
||||
89=‰
|
||||
8b=‹
|
||||
91-97=‘’“”•–—
|
||||
99=™
|
||||
9b=›
|
||||
a1-ac=΅Ά£¤¥¦§¨©ͺ«¬
|
||||
ae-af=®―
|
||||
b0-bf=°±²³΄µ¶·ΈΉΊ»Ό½ΎΏ
|
||||
c0-cf=ΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟ
|
||||
d0-d1=ΠΡ
|
||||
d3-df=ΣΤΥΦΧΨΩΪΫάέήί
|
||||
e0-ef=ΰαβγδεζηθικλμνξο
|
||||
f0-fe=πρςστυφχψωϊϋόύώ
|
||||
|
||||
|
||||
|
||||
{b}=08
|
||||
{t}=09
|
||||
{n}=0d0a
|
||||
{q}=22
|
||||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{euro}=80
|
||||
{pound}=a3
|
||||
{yen}=a5
|
||||
{copy}=a9
|
||||
{pi}=f0
|
||||
{nbsp}=A0
|
||||
{shy}=AD
|
34
include/encoding/cp1254.tbl
Normal file
34
include/encoding/cp1254.tbl
Normal file
|
@ -0,0 +1,34 @@
|
|||
NAME=CP1254
|
||||
EOT=00
|
||||
|
||||
20=U+0020
|
||||
21-3f=!"#$%&'()*+,-./0123456789:;<=>?
|
||||
40-5f=@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
||||
60-7e=`abcdefghijklmnopqrstuvwxyz{|}~
|
||||
80=€
|
||||
82-8c=‚ƒ„…†‡ˆ‰Š‹Œ
|
||||
91-9c=‘’“”•–—˜™š›œ
|
||||
9f=Ÿ
|
||||
a1-ac=¡¢£¤¥¦§¨©ª«¬
|
||||
ae-af=®¯
|
||||
b0-bf=°±²³´µ¶·¸¹º»¼½¾¿
|
||||
c0-cf=ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ
|
||||
d0-df=ĞÑÒÓÔÕÖ×ØÙÚÛÜİŞß
|
||||
e0-ef=àáâãäåæçèéêëìíîï
|
||||
f0-ff=ğñòóôõö÷øùúûüışÿ
|
||||
|
||||
{b}=08
|
||||
{t}=09
|
||||
{n}=0d0a
|
||||
{q}=22
|
||||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{euro}=80
|
||||
{cent}=a2
|
||||
{pound}=a3
|
||||
{yen}=a5
|
||||
{copy}=a9
|
||||
{ss}=df
|
||||
{nbsp}=A0
|
||||
{shy}=AD
|
36
include/encoding/cp1257.tbl
Normal file
36
include/encoding/cp1257.tbl
Normal file
|
@ -0,0 +1,36 @@
|
|||
NAME=CP1257
|
||||
EOT=00
|
||||
|
||||
20=U+0020
|
||||
21-3f=!"#$%&'()*+,-./0123456789:;<=>?
|
||||
40-5f=@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
||||
60-7e=`abcdefghijklmnopqrstuvwxyz{|}~
|
||||
80=€
|
||||
82-8c=‚ƒ„…†‡ˆ‰Š‹Œ
|
||||
8d-8f=¨ˇ¸
|
||||
91-9c=‘’“”•–—˜™š›œ
|
||||
9e-9f=¯˛
|
||||
a1-ac=¡¢£¤¥¦§Ø©Ŗ«¬
|
||||
ae-af=®Æ
|
||||
b0-bf=°±²³´µ¶·ø¹ŗ»¼½¾æ
|
||||
c0-cf=ĄĮĀĆÄÅĘĒČÉŹĖĢĶĪĻ
|
||||
d0-df=ŠŃŅÓŌÕÖ×ŲŁŚŪÜŻŽß
|
||||
e0-ef=ąįāćäåęēčéźėģķīļ
|
||||
f0-ff=šńņóōõö÷ųłśūüżž˙
|
||||
|
||||
|
||||
{b}=08
|
||||
{t}=09
|
||||
{n}=0d0a
|
||||
{q}=22
|
||||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{euro}=80
|
||||
{cent}=a2
|
||||
{pound}=a3
|
||||
{yen}=a5
|
||||
{copy}=a9
|
||||
{ss}=df
|
||||
{nbsp}=A0
|
||||
{shy}=AD
|
|
@ -27,4 +27,5 @@ F0-FE=≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
|
|||
{pound}=9c
|
||||
{yen}=9d
|
||||
{ss}=e1
|
||||
{pi}=e3
|
||||
{nbsp}=FF
|
||||
|
|
|
@ -23,6 +23,7 @@ F1-FE=±‗¾¶§÷¸°¨·¹³²■
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{copy}=b8
|
||||
{cent}=BD
|
||||
{pound}=9c
|
||||
{yen}=BE
|
||||
|
|
|
@ -24,6 +24,7 @@ F1-FE=±υφχ§ψ¸°¨ωϋΰώ■
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pound}=9c
|
||||
{ss}=D7
|
||||
{nbsp}=FF
|
||||
{shy}=F0
|
||||
|
|
|
@ -25,6 +25,7 @@ F1-FE=±‗¾¶§÷¸°¨·¹³²■
|
|||
{rbrace}=7d
|
||||
{cent}=BD
|
||||
{pound}=9c
|
||||
{copy}=b8
|
||||
{yen}=BE
|
||||
{euro}=D5
|
||||
{ss}=e1
|
||||
|
|
|
@ -19,6 +19,9 @@ fe-ff=↕↔
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pound}=a3
|
||||
{copy}=a4
|
||||
{pi}=b8
|
||||
{AE}=5b
|
||||
{OE}=5c
|
||||
{AA}=5d
|
||||
|
|
|
@ -19,3 +19,6 @@ fe-ff=↕↔
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pound}=a3
|
||||
{copy}=a4
|
||||
{pi}=b8
|
||||
|
|
|
@ -19,3 +19,5 @@ fe-ff=↕↔
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{copy}=a4
|
||||
{pi}=b8
|
||||
|
|
|
@ -20,3 +20,6 @@ fe-ff=↕↔
|
|||
{apos}=27
|
||||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pound}=a3
|
||||
{copy}=a4
|
||||
{pi}=b8
|
||||
|
|
|
@ -24,3 +24,4 @@ F0-F9=0123456789
|
|||
{q}=7F
|
||||
{lbrace}=C0
|
||||
{rbrace}=D0
|
||||
{cent}=4A
|
||||
|
|
20
include/encoding/geos_de.tbl
Normal file
20
include/encoding/geos_de.tbl
Normal file
|
@ -0,0 +1,20 @@
|
|||
NAME=GEOS-DE
|
||||
EOT=00
|
||||
|
||||
20=U+0020
|
||||
21-3f=!"#$%&'()*+,-./0123456789:;<=>?
|
||||
40-5f=§ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ^_
|
||||
60-7e=`abcdefghijklmnopqrstuvwxyzäöüß
|
||||
|
||||
{b}=08
|
||||
{t}=09
|
||||
{n}=0d0a
|
||||
{q}=22
|
||||
{apos}=27
|
||||
{AE}=5b
|
||||
{OE}=5c
|
||||
{UE}=5d
|
||||
{ae}=7b
|
||||
{oe}=7c
|
||||
{ue}=7d
|
||||
{ss}=7e
|
|
@ -27,3 +27,4 @@ f1-ff=ñòóôġö÷ĝùúûüŭŝ˙
|
|||
{ss}=df
|
||||
{nbsp}=A0
|
||||
{shy}=AD
|
||||
{pound}=A3
|
||||
|
|
|
@ -24,4 +24,5 @@ F0-FE=≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{ss}=e1
|
||||
{pi}=e3
|
||||
{nbsp}=FF
|
||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{nbsp}=a0
|
||||
{copy}=98
|
||||
•=95
|
||||
|
||||
b0=№
|
||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{nbsp}=9a
|
||||
{copy}=bf
|
||||
|
||||
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
|
||||
90-99=░▒▓⌠■∙√≈≤≥
|
||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{nbsp}=9a
|
||||
{copy}=bf
|
||||
|
||||
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
|
||||
90-99=░▒▓“■∙”—№™
|
||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{shy}=8d
|
||||
{copy}=bf
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ EOT=00
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{nbsp}=9a
|
||||
{copy}=bf
|
||||
|
||||
80-8f=─│┌┐└┘├┤┬┴┼▀▄█▌▐
|
||||
90-99=░▒▓⌠■∙√≈≤≥
|
||||
|
|
|
@ -29,5 +29,6 @@ f1-ff=ÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ
|
|||
{ss}=a7
|
||||
{nbsp}=CA
|
||||
{euro}=DB
|
||||
{pi}=b9
|
||||
€=DB
|
||||
U+F8FF=F0
|
||||
|
|
|
@ -26,4 +26,5 @@ F0-FE=≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
|
|||
{cent}=9b
|
||||
{yen}=9d
|
||||
{ss}=e1
|
||||
{pi}=e3
|
||||
{nbsp}=FF
|
||||
|
|
|
@ -55,3 +55,8 @@ f0-ff=äëïöüçæåøñãõ⇒⇐⇔≡
|
|||
{lbrace}=7b
|
||||
{rbrace}=7d
|
||||
{pi}=1b18
|
||||
{pound}=a3
|
||||
{copy}=a4
|
||||
{yen}=bd
|
||||
{cent}=b1
|
||||
{ss}=ba
|
||||
|
|
|
@ -23,6 +23,7 @@ F0-FF=₽×./,♀0123456789
|
|||
{ot}=BE
|
||||
{'v}=BF
|
||||
{hav}=BF
|
||||
{apos}=E0
|
||||
{'r}=E4
|
||||
{ar}=E4
|
||||
{'m}=E5
|
||||
|
|
|
@ -22,6 +22,7 @@ F0-FF=₽×./,♀0123456789
|
|||
{'t}=DD
|
||||
{'v}=DE
|
||||
|
||||
{apos}=E0
|
||||
{PK}=E1
|
||||
{pk}=E1
|
||||
{MN}=E2
|
||||
|
|
|
@ -13,6 +13,7 @@ E6-E8=?!.
|
|||
EC-EF=▷▶▼♂
|
||||
F0-FF=₽×./,♀0123456789
|
||||
|
||||
{ss}=be
|
||||
{c'}=d4
|
||||
{ce}=d4
|
||||
{d'}=d5
|
||||
|
@ -37,6 +38,7 @@ F0-FF=₽×./,♀0123456789
|
|||
{u'}=de
|
||||
{ue}=de
|
||||
{y'}=df
|
||||
{apos}=e0
|
||||
|
||||
{PK}=E1
|
||||
{pk}=E1
|
||||
|
|
2
include/encoding/utf32be.tbl
Normal file
2
include/encoding/utf32be.tbl
Normal file
|
@ -0,0 +1,2 @@
|
|||
NAME=UTF-32BE
|
||||
BUILTIN=UTF-32BE
|
2
include/encoding/utf32le.tbl
Normal file
2
include/encoding/utf32le.tbl
Normal file
|
@ -0,0 +1,2 @@
|
|||
NAME=UTF-32LE
|
||||
BUILTIN=UTF-32LE
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#pragma zilog_syntax
|
||||
|
||||
array __header @ $100 = [
|
||||
const array __header @ $100 = [
|
||||
$00, $C3, $50, $01, $CE, $ED, $66, $66, $CC, $0D, $00, $0B, $03, $73, $00, $83,
|
||||
$00, $0C, $00, $0D, $00, $08, $11, $1F, $88, $89, $00, $0E, $DC, $CC, $6E, $E6,
|
||||
$DD, $DD, $D9, $99, $BB, $BB, $67, $63, $6E, $0E, $EC, $CC, $DD, $DC, $99, $9F,
|
||||
|
|
|
@ -38,9 +38,9 @@ void read_joy() {
|
|||
ld a,(reg_joypad)
|
||||
ld a,(reg_joypad)
|
||||
}
|
||||
byte tmp
|
||||
|
||||
tmp = reg_joypad ^ $ff
|
||||
input_a = (tmp & 1) >> 0
|
||||
input_a = (tmp & 1) // >> 0
|
||||
input_b = (tmp & 2) >> 1
|
||||
input_select = (tmp & 4) >> 2
|
||||
input_start = (tmp & 8) >> 3
|
||||
|
|
|
@ -11,6 +11,12 @@ inline asm byte __mul_u8u8u8() {
|
|||
? LD A, E
|
||||
? RET
|
||||
}
|
||||
#elseif CPUFEATURE_R800
|
||||
inline asm byte __mul_u8u8u8() {
|
||||
? MULUB A,D
|
||||
? LD A,L
|
||||
? RET
|
||||
}
|
||||
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||
//A = A * D
|
||||
noinline asm byte __mul_u8u8u8() {
|
||||
|
@ -89,6 +95,16 @@ __divmod_u16u8u16u8_skip:
|
|||
? RET
|
||||
}
|
||||
|
||||
|
||||
#if CPUFEATURE_R800
|
||||
inline asm word __mul_u16u8u16() {
|
||||
? LD L,A
|
||||
? LD H,0
|
||||
? MULUW HL,DE
|
||||
? RET
|
||||
}
|
||||
#else
|
||||
// HL=A*DE
|
||||
noinline asm word __mul_u16u8u16() {
|
||||
? LD HL,0
|
||||
? LD B,8
|
||||
|
@ -113,8 +129,17 @@ __mul_u16u8u16_skip:
|
|||
#endif
|
||||
? RET
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||
|
||||
#if CPUFEATURE_R800
|
||||
inline asm word __mul_u16u16u16() {
|
||||
? EX DE,HL
|
||||
? MULUW HL,BC
|
||||
? RET
|
||||
}
|
||||
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||
// HL=BC*DE
|
||||
noinline asm word __mul_u16u16u16() {
|
||||
LD HL,0
|
||||
LD A,16
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_0401 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$401 = [
|
||||
$0b,
|
||||
4,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$31,
|
||||
$30,
|
||||
$33,
|
||||
$37,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$401>
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_0801 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$801 = [
|
||||
$0b,
|
||||
$08,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$32,
|
||||
$30,
|
||||
$36,
|
||||
$31,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$801>
|
||||
|
|
|
@ -22,4 +22,5 @@ asm void __init_16bit() @$80D {
|
|||
clc
|
||||
xce
|
||||
sep #$30
|
||||
}
|
||||
jmp main
|
||||
}
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_1001 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$1001 = [
|
||||
$0b,
|
||||
$10,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$34,
|
||||
$31,
|
||||
$30,
|
||||
$39,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$1001>
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_1201 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$1201 = [
|
||||
$0b,
|
||||
$12,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$34,
|
||||
$36,
|
||||
$32,
|
||||
$31,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$1201>
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_1c01 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$1C01 = [
|
||||
$0b,
|
||||
$1C,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$37,
|
||||
$31,
|
||||
$38,
|
||||
$31,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$1c01>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#warn m6809_math module should be used only on 6809-like targets
|
||||
#endif
|
||||
|
||||
noinline asm word __mul_u16u16u16(word register(x) x, word register(d) d) {
|
||||
noinline asm word __mul_u16u16u16(word register(x) x, word register(d) d) !preserves_y !preserves_u {
|
||||
pshs d,x
|
||||
exg d,x
|
||||
lda ,s
|
||||
|
@ -26,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
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// Input: A = Byte to write.
|
||||
asm void putchar(byte register(a) char) @$FFD2 extern
|
||||
asm void putchar(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
|
||||
|
||||
inline void new_line() {
|
||||
putchar(13)
|
||||
|
|
|
@ -11,14 +11,21 @@ modules=loader_0801,x16_kernal,x16_hardware,c64_panic,stdlib
|
|||
|
||||
[allocation]
|
||||
; Let's not use the BASIC:
|
||||
zp_pointers=0-$7F
|
||||
segments=default,himem_00,himem_ff
|
||||
; $00-$01 are used for bank switching
|
||||
zp_pointers=$02-$7F
|
||||
segments=default,user,himem_00,himem_ff
|
||||
default_code_segment=default
|
||||
segment_default_start=$80D
|
||||
segment_default_codeend=$9eff
|
||||
segment_default_datastart=after_code
|
||||
segment_default_end=$9eff
|
||||
|
||||
;1KB user space
|
||||
segment_user_start=$0400
|
||||
segment_user_codeend=$07ff
|
||||
segment_user_datastart=after_code
|
||||
segment_user_end=$07ff
|
||||
|
||||
segment_himem_00_start=$a000
|
||||
segment_himem_00_codeend=$bfff
|
||||
segment_himem_00_datastart=after_code
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// Input: A = Byte to write.
|
||||
asm void chrout(byte register(a) char) @$FFD2 extern
|
||||
asm void chrout(byte register(a) char) !preserves_a !preserves_x !preserves_y @$FFD2 extern
|
||||
|
||||
asm void putchar(byte register(a) char) {
|
||||
JSR chrout
|
||||
|
|
|
@ -5,16 +5,16 @@
|
|||
|
||||
#pragma zilog_syntax
|
||||
|
||||
inline asm void putchar(byte register(a) char) {
|
||||
inline asm void putchar(byte register(a) char) !preserves_bc !preserves_de !preserves_hl {
|
||||
rst $10
|
||||
? ret
|
||||
}
|
||||
|
||||
inline void new_line() {
|
||||
inline void new_line() !preserves_bc !preserves_de !preserves_hl {
|
||||
putchar(13)
|
||||
}
|
||||
|
||||
inline asm void set_border(byte register(a) colour) {
|
||||
inline asm void set_border(byte register(a) colour) !preserves_bc !preserves_de !preserves_hl {
|
||||
out (254),a
|
||||
? ret
|
||||
}
|
||||
|
|
|
@ -19,11 +19,15 @@
|
|||
+= -= *= >>= <<=
|
||||
+' -' *' <<' >>'
|
||||
+'= -'= *'= <<'= >>'=
|
||||
$+ $- $* $<< $>>
|
||||
$+= $-= $*= $<<= $>>=
|
||||
= ?
|
||||
/ %% /= %%=
|
||||
> >= < <= != ==
|
||||
| || |= ^ ^= & && &=
|
||||
( ) { } @ [ ] : # </Keywords>
|
||||
( ) { } @ [ ] : #
|
||||
; .
|
||||
</Keywords>
|
||||
<Keywords name="Operators2"></Keywords>
|
||||
<Keywords name="Folders in code1, open">{</Keywords>
|
||||
<Keywords name="Folders in code1, middle"></Keywords>
|
||||
|
@ -38,10 +42,14 @@
|
|||
void bool byte sbyte ubyte word farword pointer farpointer long word_be word_le long_be long_le file
|
||||
int8 int16 int24 int32 int40 int48 int56 int64
|
||||
int72 int80 int88 int96 int104 int112 int120 int128
|
||||
signed8
|
||||
signed8 signed16 signed24 signed32 signed40 signed48 signed56 signed64
|
||||
signed72 signed80 signed88 signed96 signed104 signed112 signed120 signed128
|
||||
unsigned8 unsigned16 unsigned24 unsigned32 unsigned40 unsigned48 unsigned56 unsigned64
|
||||
unsigned72 unsigned80 unsigned88 unsigned96 unsigned104 unsigned112 unsigned120 unsigned128
|
||||
array addr fast</Keywords>
|
||||
array addr fast
|
||||
clear_carry clear_zero clear_overflow clear_negative
|
||||
set_carry set_zero set_overflow set_negative
|
||||
</Keywords>
|
||||
<Keywords name="Keywords2">
|
||||
import segment
|
||||
if else for return while do asm extern break continue default goto label
|
||||
|
@ -54,9 +62,11 @@
|
|||
utf8 utf16le utf16be latin0 latin9 iso8859_15 zx80 zx81 vectrex koi7n2 short_koi msx_intl msx_us msx_uk msx_de msx_fr msx_es msx_ru msx_jp msx_br
|
||||
utf8z utf16lez utf16bez latin0z latin9z iso8859_15z zx80z zx81z vectrexz koi7n2z short_koiz msx_intlz msx_usz msx_ukz msx_dez msx_frz msx_esz msx_ruz msx_jpz msx_brz
|
||||
until to downto parallelto paralleluntil
|
||||
function
|
||||
static stack ref const volatile inline noinline macro register kernal_interrupt interrupt align reentrant
|
||||
hi lo sin cos tan call nonet
|
||||
false true nullptr</Keywords>
|
||||
false true nullptr nullchar nullchar_scr
|
||||
</Keywords>
|
||||
<Keywords name="Keywords4">"sta " "lda " "jmp " "bit " "eor " "adc " "sbc " "ora " "and " "ldx " "ldy " "stx " "sty " "tax" "tay" "tya" "txa" "txs" "tsx" "sei" "cli" "clv" "clc" "cld" "sed" "sec" "bra " "beq " "bne " "bmi " "bpl " "bcc " "bcs " "bvs " bvc " "jsr " rts" "rti" "brk" "rol" "ror" "asl" "lsr" "inc " "dec " "cmp " "cpx " "cpy " inx iny dex dey pla pha plp hp phx plx phy ply "stz " "ldz " tza taz "tsb " "trb " ra txy tyx pld plb phb phd phk xce

"STA " "LDA " "JMP " "BIT " "EOR " "ADC " "SBC " "ORA " "AND " "LDX " "LDY " "STX " "STY " "TAX" "TAY" "TYA" "TXA" "TXS" "TSX" "SEI" "CLI" "CLV" "CLC" "CLD" "SED" "SEC" "BEQ " "BRA " "BNE " "BMI " "BPL " "BCC " "BCS " "BVS " BVC " "JSR " RTS" "RTI" "BRK" "ROL" "ROR" "ASL" "LSR" "INC " "DEC " "CMP " "CPX " "CPY " INX INY DEX DEY PLA PHA PLP HP PHX PLX PHY PLY "STZ " "LDZ " TZA TAZ "TSB " "TRB " RA TXY TYX PLD PLB PHB PHD PHK XCE</Keywords>
|
||||
<Keywords name="Keywords5">"sbx " "isc " "dcp " "lax " "sax " "anc " "alr " "arr " "rra " "rla " "lxa " "ane " "xaa "
"SBX " "ISC " "DCP " "LAX " "SAX " "ANC " "ALR " "ARR " "RRA " "RLA " "LXA " "ANE " "XAA "</Keywords>
|
||||
<Keywords name="Keywords6"></Keywords>
|
||||
|
|
|
@ -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 Boy–only modules: stdlib/gb.md
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
"allDeclaredFields":true,
|
||||
"allPublicMethods":true
|
||||
},
|
||||
{
|
||||
"name":"millfork.assembly.m6809.MOpcode$",
|
||||
"allDeclaredFields":true,
|
||||
"allPublicMethods":true
|
||||
},
|
||||
{
|
||||
"name":"org.apache.commons.logging.LogFactory"
|
||||
},
|
||||
|
|
|
@ -45,7 +45,7 @@ case class CompilationOptions(platform: Platform,
|
|||
EmitIntel8085Opcodes, EmitIntel8080Opcodes, UseIxForStack, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
||||
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(
|
||||
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes,
|
||||
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes, EmitR800Opcodes,
|
||||
UseIyForStack, UseIxForScratch, UseIyForScratch, UseShadowRegistersForInterrupts)
|
||||
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6809) invalids ++= Set(
|
||||
|
@ -172,6 +172,11 @@ case class CompilationOptions(platform: Platform,
|
|||
log.error("Extended 8080-like opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
}
|
||||
if (flags(EmitR800Opcodes)) {
|
||||
if (platform.cpu != R800) {
|
||||
log.error("R800 opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
}
|
||||
if (flags(EmitIntel8080Opcodes)) {
|
||||
if (!Intel8080Compatible(platform.cpu)) {
|
||||
log.error("Intel 8080 opcodes enabled for architecture that doesn't support them")
|
||||
|
@ -220,6 +225,7 @@ case class CompilationOptions(platform: Platform,
|
|||
"OPTIMIZE_IPO" -> toLong(flag(CompilationFlag.InterproceduralOptimization)),
|
||||
"CPUFEATURE_DECIMAL_MODE" -> toLong(flag(CompilationFlag.DecimalMode)),
|
||||
"CPUFEATURE_Z80" -> toLong(flag(CompilationFlag.EmitZ80Opcodes)),
|
||||
"CPUFEATURE_R800" -> toLong(flag(CompilationFlag.EmitR800Opcodes)),
|
||||
"CPUFEATURE_EZ80" -> toLong(flag(CompilationFlag.EmitEZ80Opcodes)),
|
||||
"CPUFEATURE_8080" -> toLong(flag(CompilationFlag.EmitIntel8080Opcodes)),
|
||||
"CPUFEATURE_8085" -> toLong(flag(CompilationFlag.EmitIntel8085Opcodes)),
|
||||
|
@ -290,7 +296,7 @@ object CpuFamily extends Enumeration {
|
|||
import Cpu._
|
||||
cpu match {
|
||||
case Mos | StrictMos | Ricoh | StrictRicoh | Cmos | SC02 | Rockwell | Wdc | HuC6280 | CE02 | Sixteen => M6502
|
||||
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | EZ80 | Z80Next => I80
|
||||
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | R800 | EZ80 | Z80Next => I80
|
||||
case Intel8086 | Intel80186 => I86
|
||||
case Cpu.Motorola6809 => M6809
|
||||
}
|
||||
|
@ -368,6 +374,10 @@ object Cpu extends Enumeration {
|
|||
* The Zilog Z80 processor, without illegal instructions
|
||||
*/
|
||||
val StrictZ80: Cpu.Value = Value
|
||||
/**
|
||||
* The R800 CPU (used in MSX Turbo-R)
|
||||
*/
|
||||
val R800: Cpu.Value = Value
|
||||
/**
|
||||
* The Zilog eZ80 processor
|
||||
*/
|
||||
|
@ -400,11 +410,11 @@ object Cpu extends Enumeration {
|
|||
/**
|
||||
* Processors that can run code for Zilog Z80
|
||||
*/
|
||||
val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, EZ80, Z80Next)
|
||||
val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, R800, EZ80, Z80Next)
|
||||
/**
|
||||
* Processors that can run code for Intel 8080
|
||||
*/
|
||||
val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, EZ80, Z80Next)
|
||||
val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, R800, EZ80, Z80Next)
|
||||
/**
|
||||
* Processors that can run code for Intel 8085
|
||||
*/
|
||||
|
@ -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,
|
||||
|
|
|
@ -46,10 +46,24 @@ case class Context(errorReporting: Logger,
|
|||
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)) {
|
||||
addons += CompilationFlag.EmitZ80Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.EmitZ80Opcodes) || isFlagSet(CompilationFlag.EmitSharpOpcodes)) {
|
||||
if (isFlagSet(CompilationFlag.EmitZ80NextOpcodes)) {
|
||||
addons += CompilationFlag.EmitZ80Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.EmitR800Opcodes)) {
|
||||
addons += CompilationFlag.EmitZ80Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitZ80NextOpcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitR800Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitZ80Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitSharpOpcodes)) {
|
||||
addons += CompilationFlag.EmitExtended80Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.EmitZ80Opcodes) || isFlagSet(CompilationFlag.EmitIntel8085Opcodes)) {
|
||||
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitZ80NextOpcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitR800Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitZ80Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitIntel8085Opcodes)) {
|
||||
addons += CompilationFlag.EmitIntel8080Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.OptimizeForSpeed)) {
|
||||
|
|
|
@ -1,23 +1,37 @@
|
|||
package millfork
|
||||
|
||||
import millfork.output.{BankLayoutInFile, FormattableLabel}
|
||||
|
||||
import java.util.regex.Pattern
|
||||
import scala.collection.mutable
|
||||
import scala.util.control.Breaks.{break, breakable}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
object DebugOutputFormat {
|
||||
val map: Map[String, DebugOutputFormat] = Map(
|
||||
"raw" -> RawDebugOutputFormat,
|
||||
"vice" -> ViceDebugOutputFormat,
|
||||
"nesasm" -> NesasmDebugOutputFormat,
|
||||
"fns" -> NesasmDebugOutputFormat,
|
||||
"fceux" -> FceuxDebugOutputFormat,
|
||||
"nl" -> FceuxDebugOutputFormat,
|
||||
"mlb" -> MesenOutputFormat,
|
||||
"mesen" -> MesenOutputFormat,
|
||||
"asm6f" -> MesenOutputFormat,
|
||||
"ld65" -> Ld65OutputFormat,
|
||||
"ca65" -> Ld65OutputFormat,
|
||||
"cc65" -> Ld65OutputFormat,
|
||||
"dbg" -> Ld65OutputFormat,
|
||||
"sym" -> SymDebugOutputFormat)
|
||||
}
|
||||
|
||||
sealed trait DebugOutputFormat {
|
||||
|
||||
def formatAll(labels: Seq[(String, (Int, Int))], breakpoints: Seq[(Int, Int)]): String = {
|
||||
val labelPart = labelsHeader + labels.map(formatLineTupled).mkString("\n") + "\n"
|
||||
def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
|
||||
val labelPart = labelsHeader + labels.map(formatLine).mkString("\n") + "\n"
|
||||
if (breakpoints.isEmpty) {
|
||||
labelPart
|
||||
} else {
|
||||
|
@ -25,9 +39,7 @@ sealed trait DebugOutputFormat {
|
|||
}
|
||||
}
|
||||
|
||||
final def formatLineTupled(labelAndValue: (String, (Int, Int))): String = formatLine(labelAndValue._1, labelAndValue._2._1, labelAndValue._2._2)
|
||||
|
||||
def formatLine(label: String, bank: Int, value: Int): String
|
||||
def formatLine(label: FormattableLabel): String
|
||||
|
||||
final def formatBreakpointTupled(value: (Int, Int)): Seq[String] = formatBreakpoint(value._1, value._2).toSeq
|
||||
|
||||
|
@ -45,10 +57,25 @@ sealed trait DebugOutputFormat {
|
|||
def breakpointsHeader: String = ""
|
||||
}
|
||||
|
||||
object RawDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
f"${label.bankNumber}%02X:${label.startValue}%04X:${label.endValue.fold("")(_.formatted("%04X"))}%s:${label.category}%s:$label%s"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".labels"
|
||||
|
||||
override def filePerBank: Boolean = false
|
||||
|
||||
override def addOutputExtension: Boolean = false
|
||||
|
||||
override def formatBreakpoint(bank: Int, value: Int): Option[String] =
|
||||
Some(f"$bank%02X:$value%04X::b:<breakpoint@$value%04X>")
|
||||
}
|
||||
|
||||
object ViceDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
val normalized = label.replace('$', '_').replace('.', '_')
|
||||
s"al ${value.toHexString} .$normalized"
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
val normalized = label.labelName.replace('$', '_').replace('.', '_')
|
||||
s"al ${label.startValue.toHexString} .$normalized"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".lbl"
|
||||
|
@ -61,8 +88,8 @@ object ViceDebugOutputFormat extends DebugOutputFormat {
|
|||
}
|
||||
|
||||
object NesasmDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
label + " = $" + value.toHexString
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
label.labelName + " = $" + label.startValue.toHexString
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".fns"
|
||||
|
@ -75,8 +102,8 @@ object NesasmDebugOutputFormat extends DebugOutputFormat {
|
|||
}
|
||||
|
||||
object SymDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
f"$bank%02x:$value%04x $label%s"
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
f"${label.bankNumber}%02x:${label.startValue}%04x ${label.labelName}%s"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".sym"
|
||||
|
@ -93,8 +120,8 @@ object SymDebugOutputFormat extends DebugOutputFormat {
|
|||
}
|
||||
|
||||
object FceuxDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
f"$$$value%04x#$label%s#"
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
f"$$${label.startValue}%04x#${label.labelName}%s#"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = if (bank == 0xff) ".ram.nl" else s".$bank.nl"
|
||||
|
@ -105,3 +132,97 @@ object FceuxDebugOutputFormat extends DebugOutputFormat {
|
|||
|
||||
override def formatBreakpoint(bank: Int, value: Int): Option[String] = None
|
||||
}
|
||||
|
||||
object MesenOutputFormat extends DebugOutputFormat {
|
||||
|
||||
override def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
|
||||
val allStarts = labels.groupBy(_.bankName).mapValues(_.map(_.startValue).toSet)
|
||||
labels.flatMap{ l =>
|
||||
val mesenShift = b.getMesenShift(l.bankName)
|
||||
val shiftedStart = l.startValue + mesenShift
|
||||
var shiftedEnd = l.endValue.map(_ + mesenShift).filter(_ != shiftedStart)
|
||||
l.endValue match {
|
||||
case None =>
|
||||
case Some(e) =>
|
||||
// Mesen does not like labels of form XXX-XXX, where both ends are equal
|
||||
breakable {
|
||||
for (i <- l.startValue.+(1) to e) {
|
||||
if (allStarts.getOrElse(l.bankName, Set.empty).contains(i)) {
|
||||
shiftedEnd = None
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shiftedStart >= 0 && shiftedEnd.forall(_ >= 0)) {
|
||||
val normalized = l.labelName.replace('$', '_').replace('.', '_')
|
||||
val comment = (l.category match {
|
||||
case 'F' => "function "
|
||||
case 'A' => "initialized array "
|
||||
case 'V' => "initialized variable "
|
||||
case 'a' => "array "
|
||||
case 'v' => "variable "
|
||||
case _ => ""
|
||||
}) + l.labelName
|
||||
Some(f"${l.mesenSymbol}%s:${shiftedStart}%04X${shiftedEnd.fold("")(e => f"-$e%04X")}%s:$normalized%s:$comment%s")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}.mkString("\n")
|
||||
}
|
||||
|
||||
override def formatLine(label: FormattableLabel): String = throw new UnsupportedOperationException()
|
||||
|
||||
override def formatBreakpoint(bank: Int, value: Int): Option[String] = None
|
||||
|
||||
override def fileExtension(bank: Int): String = ".mlb"
|
||||
|
||||
override def filePerBank: Boolean = false
|
||||
|
||||
override def addOutputExtension: Boolean = false
|
||||
}
|
||||
|
||||
object Ld65OutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: FormattableLabel): String = throw new UnsupportedOperationException()
|
||||
|
||||
override def formatBreakpoint(bank: Int, value: Int): Option[String] = None
|
||||
|
||||
override def fileExtension(bank: Int): String = ".dbg"
|
||||
|
||||
override def filePerBank: Boolean = false
|
||||
|
||||
override def addOutputExtension: Boolean = true
|
||||
|
||||
override def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
|
||||
val q = '"'
|
||||
val allBanksInFile = b.allBanks()
|
||||
val allBanks = (labels.map(_.bankName).distinct ++ allBanksInFile).distinct
|
||||
val result = mutable.ListBuffer[String]()
|
||||
result += "version\tmajor=2,minor=0"
|
||||
result += s"info\tcsym=0,file=0,lib=0,line=0,mod=0,scope=1,seg=${allBanks.size},span=0,sym=${labels.size},type=4"
|
||||
for ((bank, ix) <- allBanks.zipWithIndex) {
|
||||
val common = s"seg\tid=${ix},name=$q${bank}$q,start=0x${b.getStart(bank).toHexString},size=0x0,addrsize=absolute"
|
||||
if (allBanksInFile.contains(bank)) {
|
||||
result += common + s",ooffs=${b.ld65Offset(bank)}"
|
||||
} else {
|
||||
result += common
|
||||
}
|
||||
}
|
||||
result += "scope\tid=0,name=\"\""
|
||||
for ((label, ix) <- labels.sortBy(l => (l.bankName, l.startValue, l.labelName)).zipWithIndex) {
|
||||
val name = label.labelName.replace('$', '@').replace('.', '@')
|
||||
val sb = new StringBuilder
|
||||
sb ++= s"sym\tid=${ix},name=$q${name}$q,addrsize=absolute,"
|
||||
label.endValue match {
|
||||
case Some(e) =>
|
||||
if (!labels.exists(l => l.ne(label) && l.startValue >= label.startValue && l.startValue <= e)) {
|
||||
sb ++= s"size=${ e - label.startValue + 1 },"
|
||||
}
|
||||
case None =>
|
||||
}
|
||||
sb ++= s"scope=0,val=0x${label.startValue.toHexString},seg=${allBanks.indexOf(label.bankName)},type=lab"
|
||||
result += sb.toString
|
||||
}
|
||||
result.mkString("\n")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,25 +119,35 @@ object Main {
|
|||
else if (l.startsWith("__")) 7
|
||||
else 0
|
||||
}
|
||||
val sortedLabels = result.labels.groupBy(_._2).values.map(_.minBy(a => labelUnimportance(a._1) -> a._1)).toSeq.sortBy(_._2)
|
||||
val sortedLabels: Seq[FormattableLabel] =
|
||||
result.labels.groupBy(_._2).values
|
||||
.map(_.minBy(a => labelUnimportance(a._1) -> a._1)).toSeq.sortBy(_._2)
|
||||
.map { case (l, (b, s)) =>
|
||||
val bankNumber = options.platform.bankNumbers.getOrElse(b, 0)
|
||||
val mesenCategory = options.platform.getMesenLabelCategory(b, s)
|
||||
result.endLabels.get(l) match {
|
||||
case Some((c, e)) => FormattableLabel(l, b, bankNumber, s, Some(e), c, mesenCategory)
|
||||
case _ => FormattableLabel(l, b, bankNumber, s, None, 'x', mesenCategory)
|
||||
}
|
||||
}
|
||||
val sortedBreakpoints = result.breakpoints
|
||||
val format = c.outputLabelsFormatOverride.getOrElse(platform.outputLabelsFormat)
|
||||
val basename = if (format.addOutputExtension) output + platform.fileExtension else output
|
||||
if (format.filePerBank) {
|
||||
val banks = sortedLabels.map(_._2._1).toSet ++ sortedBreakpoints.map(_._2).toSet
|
||||
val banks: Set[Int] = sortedLabels.map(_.bankNumber).toSet ++ sortedBreakpoints.map(_._1).toSet
|
||||
banks.foreach{ bank =>
|
||||
val labels = sortedLabels.filter(_._2._1.==(bank))
|
||||
val labels = sortedLabels.filter(_.bankNumber.==(bank))
|
||||
val breakpoints = sortedBreakpoints.filter(_._1.==(bank))
|
||||
val labelOutput = basename + format.fileExtension(bank)
|
||||
val path = Paths.get(labelOutput)
|
||||
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
|
||||
Files.write(path, format.formatAll(labels, breakpoints).getBytes(StandardCharsets.UTF_8))
|
||||
Files.write(path, format.formatAll(result.bankLayoutInFile, labels, breakpoints).getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
} else {
|
||||
val labelOutput = basename + format.fileExtension(0)
|
||||
val path = Paths.get(labelOutput)
|
||||
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
|
||||
Files.write(path, format.formatAll(sortedLabels, sortedBreakpoints).getBytes(StandardCharsets.UTF_8))
|
||||
Files.write(path, format.formatAll(result.bankLayoutInFile, sortedLabels, sortedBreakpoints).getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
}
|
||||
val defaultPrgOutput = if (output.endsWith(platform.fileExtension)) output else output + platform.fileExtension
|
||||
|
@ -165,7 +175,7 @@ object Main {
|
|||
f"${path.getFileName}%s ${start}%04X ${start}%04X ${codeLength}%04X".getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
}
|
||||
errorReporting.debug(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
|
||||
errorReporting.info(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
|
||||
c.runFileName.foreach{ program =>
|
||||
if (File.separatorChar == '\\') {
|
||||
if (!new File(program).exists() && !new File(program + ".exe").exists()) {
|
||||
|
@ -241,10 +251,10 @@ object Main {
|
|||
log.debug(s"Failed to find the default include path: $err")
|
||||
case Right(path) =>
|
||||
log.debug(s"Automatically detected include path: $path")
|
||||
return c.copy(includePath = List(path) ++ c.extraIncludePath)
|
||||
return c.copy(includePath = List(System.getProperty("user.dir"), path) ++ c.extraIncludePath)
|
||||
}
|
||||
}
|
||||
c.copy(includePath = c.includePath ++ c.extraIncludePath)
|
||||
c.copy(includePath = System.getProperty("user.dir") :: (c.includePath ++ c.extraIncludePath))
|
||||
}
|
||||
|
||||
private def assembleForMos(c: Context, platform: Platform, options: CompilationOptions): AssemblerOutput = {
|
||||
|
@ -283,6 +293,7 @@ object Main {
|
|||
if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.All else Nil,
|
||||
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForEmulation else Nil,
|
||||
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) SixteenOptimizations.AllForNative else Nil,
|
||||
if (options.flag(CompilationFlag.IdentityPage)) IdentityPageOptimizations.All else Nil,
|
||||
if (options.flag(CompilationFlag.DangerousOptimizations)) DangerousOptimizations.All else Nil
|
||||
).flatten
|
||||
val goodCycle = List.fill(optLevel - 2)(OptimizationPresets.Good ++ goodExtras).flatten
|
||||
|
@ -321,7 +332,9 @@ object Main {
|
|||
val assemblyOptimizations = optLevel match {
|
||||
case 0 => Nil
|
||||
case _ =>
|
||||
if (options.flag(CompilationFlag.EmitZ80Opcodes))
|
||||
if (options.flag(CompilationFlag.EmitR800Opcodes))
|
||||
Z80OptimizationPresets.GoodForR800
|
||||
else if (options.flag(CompilationFlag.EmitZ80Opcodes))
|
||||
Z80OptimizationPresets.GoodForZ80
|
||||
else if (options.flag(CompilationFlag.EmitIntel8080Opcodes))
|
||||
Z80OptimizationPresets.GoodForIntel8080
|
||||
|
@ -427,7 +440,7 @@ object Main {
|
|||
p.toLowerCase(Locale.ROOT),
|
||||
errorReporting.fatal("Invalid label file format: " + p))
|
||||
c.copy(outputLabels = true, outputLabelsFormatOverride = Some(f))
|
||||
}.description("Generate also the label file in the given format. Available options: vice, nesasm, sym.")
|
||||
}.description("Generate also the label file in the given format. Available options: vice, nesasm, sym, raw.")
|
||||
|
||||
boolean("-fbreakpoints", "-fno-breakpoints").action((c,v) =>
|
||||
c.changeFlag(CompilationFlag.EnableBreakpoints, v)
|
||||
|
@ -487,7 +500,7 @@ object Main {
|
|||
} else {
|
||||
errorReporting.fatal("Invalid syntax for -D option")
|
||||
}
|
||||
}.description("Define a feature value for the preprocessor.")
|
||||
}.description("Define a feature value for the preprocessor.").maxCount(Integer.MAX_VALUE)
|
||||
|
||||
boolean("-finput_intel_syntax", "-finput_zilog_syntax").repeatable().action((c,v) =>
|
||||
c.changeFlag(CompilationFlag.UseIntelSyntaxForInput, v)
|
||||
|
@ -650,7 +663,7 @@ object Main {
|
|||
c.changeFlag(CompilationFlag.CompactReturnDispatchParams, v)
|
||||
}.description("Whether parameter values in return dispatch statements may overlap other objects. Enabled by default.")
|
||||
boolean("-fbounds-checking", "-fno-bounds-checking").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.VariableOverlap, v)
|
||||
c.changeFlag(CompilationFlag.CheckIndexOutOfBounds, v)
|
||||
}.description("Whether should insert bounds checking on array access.")
|
||||
boolean("-flenient-encoding", "-fno-lenient-encoding").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.LenientTextEncoding, v)
|
||||
|
@ -712,6 +725,9 @@ object Main {
|
|||
}.description("Optimize code even more.")
|
||||
if (i == 1 || i > 4) f.hidden()
|
||||
}
|
||||
boolean("-fhints", "-fnohints").action{ (c,v) =>
|
||||
c.changeFlag(CompilationFlag.UseOptimizationHints, v)
|
||||
}.description("Whether optimization hints should be used.")
|
||||
flag("--inline").repeatable().action { c =>
|
||||
c.changeFlag(CompilationFlag.InlineFunctions, true)
|
||||
}.description("Inline functions automatically.").hidden()
|
||||
|
@ -742,6 +758,9 @@ object Main {
|
|||
boolean("-fregister-variables", "-fno-register-variables").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.RegisterVariables, v)
|
||||
}.description("Allow moving local variables into CPU registers. Enabled by default.")
|
||||
boolean("-fidentity-page", "-fno-identity-page").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.IdentityPage, v)
|
||||
}.description("Whether should use an identity page to optimize certain operations.")
|
||||
flag("-Os", "--size").repeatable().action { c =>
|
||||
c.changeFlag(CompilationFlag.OptimizeForSize, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSpeed, false).
|
||||
|
@ -754,6 +773,7 @@ object Main {
|
|||
}.description("Prefer faster code even if it is slightly bigger (experimental). Implies -finline.")
|
||||
flag("-Ob", "--blast-processing").repeatable().action { c =>
|
||||
c.changeFlag(CompilationFlag.OptimizeForSize, false).
|
||||
changeFlag(CompilationFlag.IdentityPage, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSpeed, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSonicSpeed, true)
|
||||
}.description("Prefer faster code even if it is much bigger (experimental). Implies -finline.")
|
||||
|
@ -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")
|
||||
|
|
|
@ -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(",")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ class Platform(
|
|||
val outputLabelsFormat: DebugOutputFormat,
|
||||
val outputStyle: OutputStyle.Value
|
||||
) {
|
||||
|
||||
def hasZeroPage: Boolean = cpuFamily == CpuFamily.M6502
|
||||
|
||||
def cpuFamily: CpuFamily.Value = CpuFamily.forType(this.cpu)
|
||||
|
@ -60,6 +61,18 @@ class Platform(
|
|||
val e2 = bankEndBefore(bank2)
|
||||
e2 > s1 && s2 < e1
|
||||
}
|
||||
|
||||
lazy val banksExplicitlyWrittenToOutput: Set[String] = bankNumbers.keySet.filter(b => defaultOutputPackager.writes(b)).toSet
|
||||
|
||||
def getMesenLabelCategory(bank: String, address: Int): Char = {
|
||||
// see: https://www.mesen.ca/docs/debugging/debuggerintegration.html#mesen-label-files-mlb
|
||||
val start = bankStart(bank)
|
||||
val end = bankEndBefore(bank)
|
||||
if (start > address || address >= end) 'G'
|
||||
else if (banksExplicitlyWrittenToOutput(bank)) 'P'
|
||||
else if (bank == "default") 'R'
|
||||
else 'W' // TODO: distinguish between W and S
|
||||
}
|
||||
}
|
||||
|
||||
object Platform {
|
||||
|
|
|
@ -2,7 +2,7 @@ package millfork.assembly
|
|||
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.compiler.LabelGenerator
|
||||
import millfork.env.{NormalFunction, ThingInMemory}
|
||||
import millfork.env.{Constant, NormalFunction, ThingInMemory}
|
||||
import millfork.error.Logger
|
||||
import millfork.node.NiceFunctionProperty
|
||||
|
||||
|
@ -10,8 +10,9 @@ import millfork.node.NiceFunctionProperty
|
|||
* @author Karol Stasiak
|
||||
*/
|
||||
case class OptimizationContext(options: CompilationOptions,
|
||||
labelMap: Map[String, (Int, Int)],
|
||||
labelMap: Map[String, (String, Int)],
|
||||
zreg: Option[ThingInMemory],
|
||||
identityPage: Constant,
|
||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)]) {
|
||||
@inline
|
||||
def log: Logger = options.log
|
||||
|
@ -25,4 +26,6 @@ trait AssemblyOptimization[T <: AbstractCode] {
|
|||
def optimize(f: NormalFunction, code: List[T], context: OptimizationContext): List[T]
|
||||
|
||||
def requiredFlags: Set[CompilationFlag.Value] = Set.empty
|
||||
|
||||
def minimumRequiredLines: Int
|
||||
}
|
||||
|
|
|
@ -168,6 +168,7 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
|||
|
||||
def changesRegister(reg: M6809Register.Value): Boolean = {
|
||||
import M6809Register._
|
||||
if (MOpcode.NotActualOpcodes(opcode)) return false
|
||||
def overlaps(other: M6809Register.Value): Boolean = {
|
||||
if (reg == D && (other == A || other == B)) true
|
||||
else if (other == D && (reg == A || reg == B)) true
|
||||
|
@ -202,12 +203,13 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
|||
}
|
||||
}
|
||||
|
||||
def changesCarryFlag: Boolean = !MOpcode.PreservesC(opcode)
|
||||
def changesCarryFlag: Boolean = !MOpcode.NotActualOpcodes(opcode) && !MOpcode.PreservesC(opcode)
|
||||
|
||||
|
||||
|
||||
def readsRegister(reg: M6809Register.Value): Boolean = {
|
||||
import M6809Register._
|
||||
if (MOpcode.NotActualOpcodes(opcode)) return false
|
||||
def overlaps(other: M6809Register.Value): Boolean = {
|
||||
if (reg == D && (other == A || other == B)) true
|
||||
else if (other == D && (reg == A || reg == B)) true
|
||||
|
@ -252,15 +254,17 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
|||
case MUL => overlaps(D)
|
||||
case ABX => reg == X || overlaps(B)
|
||||
case NOP | SWI | SWI2 | SWI3 | SYNC => false
|
||||
case LDB | LDA | LDX | LDY | LDU => false
|
||||
case INC | DEC | ROL | ROR | ASL | ASR | LSR | CLR | COM | NEG | TST => false // variants for A and B handled before
|
||||
case op if Branching(op) => false
|
||||
case JMP => false
|
||||
case JMP | RTS => false
|
||||
case _ => true // TODO
|
||||
}
|
||||
}
|
||||
|
||||
def readsMemory(): Boolean = {
|
||||
import MOpcode._
|
||||
if (MOpcode.NotActualOpcodes(opcode)) return false
|
||||
val opcodeIsForReading = opcode match {
|
||||
case LDA | LDB | LDD | LDX | LDY | LDU | LDS | PULU | PULS => true
|
||||
case ADDA | SUBA | ADCA | SBCA | ORA | EORA | ANDA | CMPA | BITA => true
|
||||
|
@ -289,6 +293,7 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
|||
|
||||
def changesMemory(): Boolean = {
|
||||
import MOpcode._
|
||||
if (NotActualOpcodes(opcode)) return false
|
||||
val opcodeIsForWriting = opcode match {
|
||||
case LDA | LDB | LDD | LDX | LDY | LDU | LDS | PULU | PULS => false
|
||||
case ADDA | SUBA | ADCA | SBCA | ORA | EORA | ANDA | CMPA | BITA => false
|
||||
|
|
|
@ -32,9 +32,10 @@ object MOpcode extends Enumeration {
|
|||
TFR, TST,
|
||||
DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL = Value
|
||||
|
||||
val NotActualOpcodes: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL)
|
||||
|
||||
private val toMap: Map[String, MOpcode.Value] = {
|
||||
val notActualOpcodes: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL)
|
||||
values.filterNot(notActualOpcodes).map(o => o.toString -> o).toMap ++ Map("BHS" -> BCC, "BLO" -> BCS, "LSL" -> ASL)
|
||||
values.filterNot(NotActualOpcodes).map(o => o.toString -> o).toMap ++ Map("BHS" -> BCC, "BLO" -> BCS, "LSL" -> ASL)
|
||||
}
|
||||
val NoopDiscard: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC)
|
||||
val PrefixedBy10: Set[MOpcode.Value] = Set(CMPD, CMPY, LDS, LDY, SWI2, STS, STY) // TODO: branches
|
||||
|
|
|
@ -3,7 +3,8 @@ package millfork.assembly.m6809.opt
|
|||
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, LongRelative, MAddrMode, MLine, MState, PostIncremented, RegisterSet}
|
||||
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, Indexed, InherentA, InherentB, LongRelative, MAddrMode, MLine, MState, PostIncremented, RegisterSet}
|
||||
import millfork.env.{CompoundConstant, Constant, MathOperator}
|
||||
import millfork.node.M6809Register
|
||||
|
||||
/**
|
||||
|
@ -19,15 +20,38 @@ object AlwaysGoodMOptimizations {
|
|||
(Elidable & HasOpcodeIn(LDX, LEAX) & DoesntMatterWhatItDoesWith(MState.X, MState.NF, MState.ZF, MState.VF)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(LDY, LEAY) & DoesntMatterWhatItDoesWith(MState.Y, MState.NF, MState.ZF, MState.VF)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcode(STB) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesB) & DoesntChangeIndexingInAddrMode(0)).* ~
|
||||
(Linear & Not(ChangesB) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDB) & MatchAddrMode(0) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> (_.init),
|
||||
(Elidable & HasOpcode(STD) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesB) & DoesntChangeIndexingInAddrMode(0)).* ~
|
||||
(Linear & Not(ChangesB) & Not(ChangesA) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDD) & MatchAddrMode(0) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.NF, MState.ZF, MState.VF)) ~~> (_.init),
|
||||
)
|
||||
|
||||
val PointlessCompare = new RuleBasedAssemblyOptimization("Pointless compare",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(HasOpcodeIn(LDA, ANDA, ORA, EORA, ADDA, ADCA, SUBA, SBCA)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasImmediate(0) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> {code => code.init},
|
||||
(HasOpcodeIn(LDB, ANDB, ORB, EORB, ADDB, ADCB, SUBB, SBCB)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasImmediate(0) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> {code => code.init},
|
||||
|
||||
(Elidable & HasOpcode(EORA) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.A, MState.NF, MState.VF, MState.CF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(EORB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.B, MState.NF, MState.VF, MState.CF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
)
|
||||
|
||||
val SimplifiableZeroStore = new RuleBasedAssemblyOptimization("Simplifiable zero store",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
(Elidable & HasOpcode(LDA) & HasImmediate(0) & DoesntMatterWhatItDoesWith(MState.CF)) ~~> {
|
||||
|
@ -50,6 +74,75 @@ object AlwaysGoodMOptimizations {
|
|||
},
|
||||
)
|
||||
|
||||
val SimplifiableComparison = new RuleBasedAssemblyOptimization("Simplifiable comparison",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
|
||||
(Elidable & HasOpcode(EORB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(ADDB) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(SUBB) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(INC) & HasAddrMode(InherentB)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(DEC) & HasAddrMode(InherentB)) ~
|
||||
(Elidable & HasOpcode(CMPB) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.B, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(EORA) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c0 = ctx.get[Constant](0)
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = CompoundConstant(MathOperator.Exor, c0, c1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(ADDA) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(SUBA) & HasImmediate(1)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(INC) & HasAddrMode(InherentA)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 - 1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(DEC) & HasAddrMode(InherentA)) ~
|
||||
(Elidable & HasOpcode(CMPA) & HasAddrMode(Immediate) & MatchParameter(1)
|
||||
& DoesntMatterWhatItDoesWith(MState.CF, MState.A, MState.VF, MState.HF)) ~~> { (code, ctx) =>
|
||||
val c1 = ctx.get[Constant](1)
|
||||
List(code.last.copy(parameter = (c1 + 1).quickSimplify))
|
||||
},
|
||||
|
||||
)
|
||||
|
||||
val PointlessRegisterTransfers = new RuleBasedAssemblyOptimization("Pointless register transfers",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & IsTfr(M6809Register.D, M6809Register.X)) ~
|
||||
|
@ -144,14 +237,67 @@ object AlwaysGoodMOptimizations {
|
|||
(_.map(_.copy(opcode = LDD))),
|
||||
(Elidable & HasImmediate(0) & HasOpcodeIn(ORB, EORB, ADDB, ORA, EORA, ADDA, ADDD) & DoesntMatterWhatItDoesWith(MState.VF, MState.ZF, MState.NF, MState.CF, MState.HF)) ~~>
|
||||
(_ => Nil),
|
||||
(HasOpcode(LDB)) ~
|
||||
(Elidable & HasOpcode(ANDB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(BNE) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(LDB)) ~
|
||||
(HasOpcode(ANDB) & HasAddrMode(Immediate) & MatchParameter(0)) ~
|
||||
(HasOpcode(BEQ)) ~
|
||||
(HasOpcode(LABEL) & MatchParameter(1)) ~~> { code =>
|
||||
List(code.head, code(3).copy(opcode = ORB)) ++ code.drop(4)
|
||||
},
|
||||
(Elidable & HasOpcode(ADDB) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentB(INC)) },
|
||||
(Elidable & HasOpcode(ADDA) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentA(INC)) },
|
||||
(Elidable & HasOpcode(SUBB) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentB(DEC)) },
|
||||
(Elidable & HasOpcode(SUBA) & HasImmediate(1) & DoesntMatterWhatItDoesWith(MState.VF, MState.CF, MState.HF)) ~~> { code => List(MLine.inherentA(DEC)) },
|
||||
|
||||
)
|
||||
|
||||
val SimplifiableAddressing = new RuleBasedAssemblyOptimization("Simplifiable addressing",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
(NotFixed & MatchX(0) & HasAddrMode(Indexed(M6809Register.X, indirect = false)) & Not(HasOpcodeIn(LEAX, LEAY, LEAS, LEAU))) ~~> { (code, ctx) =>
|
||||
val x = ctx.get[Constant](0)
|
||||
code.map(l => l.copy(addrMode = Absolute(indirect = false), parameter = (x + l.parameter).quickSimplify))
|
||||
},
|
||||
(NotFixed & MatchY(0) & HasAddrMode(Indexed(M6809Register.Y, indirect = false)) & Not(HasOpcodeIn(LEAX, LEAY, LEAS, LEAU))) ~~> { (code, ctx) =>
|
||||
val y = ctx.get[Constant](0)
|
||||
code.map(l => l.copy(addrMode = Absolute(indirect = false), parameter = (y + l.parameter).quickSimplify))
|
||||
},
|
||||
(NotFixed & MatchX(0) & HasAddrMode(Indexed(M6809Register.X, indirect = false)) & HasOpcodeIn(LEAX, LEAY, LEAS, LEAU)) ~~> { (code, ctx) =>
|
||||
val x = ctx.get[Constant](0)
|
||||
code.map(l => l.copy(opcode = l.opcode match {
|
||||
case LEAX => LDX
|
||||
case LEAY => LDY
|
||||
case LEAU => LDU
|
||||
case LEAS => LDS
|
||||
}, addrMode = Immediate, parameter = (x + l.parameter).quickSimplify))
|
||||
},
|
||||
(NotFixed & MatchY(0) & HasAddrMode(Indexed(M6809Register.X, indirect = false)) & HasOpcodeIn(LEAX, LEAY, LEAS, LEAU)) ~~> { (code, ctx) =>
|
||||
val y = ctx.get[Constant](0)
|
||||
code.map(l => l.copy(opcode = l.opcode match {
|
||||
case LEAX => LDX
|
||||
case LEAY => LDY
|
||||
case LEAU => LDU
|
||||
case LEAS => LDS
|
||||
}, addrMode = Immediate, parameter = (y + l.parameter).quickSimplify))
|
||||
},
|
||||
)
|
||||
|
||||
val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal",
|
||||
needsFlowInfo = FlowInfoRequirement.JustLabels,
|
||||
(Elidable & HasOpcode(LABEL) & HasCallerCount(0) & ParameterIsLocalLabel) ~~> (_ => Nil)
|
||||
)
|
||||
|
||||
|
||||
val All: Seq[AssemblyOptimization[MLine]] = Seq(
|
||||
PointlessLoad,
|
||||
PointlessCompare,
|
||||
PointlessRegisterTransfers,
|
||||
SimplifiableAddressing,
|
||||
SimplifiableArithmetics,
|
||||
SimplifiableComparison,
|
||||
SimplifiableJumps,
|
||||
SimplifiableZeroStore
|
||||
SimplifiableZeroStore,
|
||||
UnusedLabelRemoval
|
||||
)
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user