mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-08 18:37:17 +00:00
Preliminary support for 65816, 65CE02 and HuC6280
This commit is contained in:
parent
b3e96a0c6e
commit
6ed5d51260
@ -4,6 +4,8 @@
|
||||
|
||||
* **Breaking change!** Renamed `inline` to `macro`.
|
||||
|
||||
* Added preliminary support for 65CE02, HuC6280 and 65816 processors.
|
||||
|
||||
* Added new `-O1` optimization preset; old `-O1` became `-O2`, old `-O2` became `-O3` and so on.
|
||||
|
||||
* Added support for parameters for macros written in Millfork.
|
||||
@ -18,6 +20,8 @@
|
||||
|
||||
* Added return dispatch statements.
|
||||
|
||||
* Added octal and quaternary literals.
|
||||
|
||||
* Fixed several optimization bugs.
|
||||
|
||||
* Fixed several C64 library bugs.
|
||||
|
@ -16,6 +16,8 @@ For binary releases, see: https://github.com/KarolS/millfork/releases (latest: 0
|
||||
|
||||
* Commodore 64 (the primary target)
|
||||
|
||||
* Commodore 64 with SuperCPU (experimental, incomplete and very buggy)
|
||||
|
||||
* Commodore 16 and Plus/4
|
||||
|
||||
* Commodore 128
|
||||
@ -47,3 +49,5 @@ For binary releases, see: https://github.com/KarolS/millfork/releases (latest: 0
|
||||
* multi-part programs
|
||||
|
||||
* more targets: Famicon/NES, BBC Micro/Electron, Oric computers, PC-Engine/Turbografx-16, Atari Lynx
|
||||
|
||||
* support for 65816, targetting SuperCPU, SuperFamicom/SNES and Apple IIgs
|
||||
|
@ -30,13 +30,13 @@ Millfork supports multiple mnemonics per opcode. The default one is given first:
|
||||
|
||||
* **RRA**
|
||||
|
||||
* **SAX**
|
||||
* **SAX**\*
|
||||
|
||||
* **SHX**, XAS
|
||||
|
||||
* **SHY**, SAY
|
||||
* **SHY**, SAY\*
|
||||
|
||||
* **SBX**, AXS\*
|
||||
* **SBX**, AXS\*\*
|
||||
|
||||
* **SRE**, LSE
|
||||
|
||||
@ -46,7 +46,10 @@ Millfork supports multiple mnemonics per opcode. The default one is given first:
|
||||
|
||||
* **XAA**, ANE
|
||||
|
||||
\* AXS is also used for SAX in some assemblers, but Millfork always interprets AXS as a synonym for SBX
|
||||
\* HuC2680 has different instructions also called SAX and SAY,
|
||||
but Millfork can distinguish between them and the NMOS illegal instructions based on the addressing mode.
|
||||
|
||||
\*\* AXS is also used for SAX in some assemblers. Millfork interprets AXS based on the addressing mode.
|
||||
|
||||
## Generation
|
||||
|
||||
|
@ -32,16 +32,34 @@
|
||||
## Code generation options
|
||||
|
||||
* `-fcmos-ops`, `-fno-cmos-ops` – Whether should emit CMOS opcodes.
|
||||
`.ini` equivalent: `emit_cmos`. Default: yes if targeting 65C02, no otherwise.
|
||||
`.ini` equivalent: `emit_cmos`.
|
||||
Default: yes if targeting a 65C02-compatible architecture, no otherwise.
|
||||
|
||||
* `-fillegals`, `-fno-illegals` – Whether should emit illegal (undocumented) NMOS opcodes.
|
||||
`.ini` equivalent: `emit_illegals`. Default: no.
|
||||
`.ini` equivalent: `emit_illegals`.
|
||||
Default: no.
|
||||
|
||||
* `-f65ce02-ops`, `-fno-65ce02-ops` – Whether should emit 65CE02 opcodes.
|
||||
`.ini` equivalent: `emit_65ce026`.
|
||||
Default: yes if targeting 65CE02, no otherwise.
|
||||
|
||||
* `-fhuc6280-ops`, `-fno-huc6280-ops` – Whether should emit HuC6280 opcodes.
|
||||
`.ini` equivalent: `emit_huc6280`.
|
||||
Default: yes if targeting HuC6280, no otherwise.
|
||||
|
||||
* `-fno-65816-ops`, `-femulation-65816-ops`, `-fnative-65816-ops` – Which subset of 65816 instructions to support.
|
||||
`-fnative-65816-ops` is required to use any 16-bit operations.
|
||||
Currently, there is not much support in the compiler for the native mode.
|
||||
`.ini` equivalent: `emit_65816`.
|
||||
Default: native if targeting 65816, no otherwise.
|
||||
|
||||
* `-fjmp-fix`, `-fno-jmp-fix` – Whether should prevent indirect JMP bug on page boundary.
|
||||
`.ini` equivalent: `prevent_jmp_indirect_bug`. Default: no if targeting 65C02, yes otherwise.
|
||||
`.ini` equivalent: `prevent_jmp_indirect_bug`.
|
||||
Default: no if targeting a 65C02-compatible architecture, yes otherwise.
|
||||
|
||||
* `-fdecimal-mode`, `-fno-decimal-mode` – Whether decimal mode should be available.
|
||||
`.ini` equivalent: `decimal_mode`. Default: no if targeting Ricoh, yes otherwise.
|
||||
`.ini` equivalent: `decimal_mode`.
|
||||
Default: no if targeting Ricoh, yes otherwise.
|
||||
|
||||
* `-fvariable-overlap`, `-fno-variable-overlap` – Whether variables should overlap if their scopes do not intersect.
|
||||
Default: yes.
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Target platforms
|
||||
|
||||
Currently, Millfork supports creating disk- or tape-based programs for Commodore and Atari 8-bit computers,
|
||||
Currently, Millfork supports creating disk- or tape-based programs for Commodore, Apple and Atari 8-bit computers,
|
||||
but it may be expanded to support other 6502-based platforms in the future.
|
||||
|
||||
## Supported platforms
|
||||
@ -9,6 +9,8 @@ The following platforms are currently supported:
|
||||
|
||||
* `c64` – Commodore 64
|
||||
|
||||
* `c64_scpu` – Commodore 64 with SuperCPU (very buggy)
|
||||
|
||||
* `c16` – Commodore 16
|
||||
|
||||
* `plus4` – Commodore Plus/4
|
||||
@ -57,15 +59,21 @@ Every platform is defined in an `.ini` file with an appropriate name.
|
||||
|
||||
* `arch` – CPU architecture. It defines which instructions are available. Available values:
|
||||
|
||||
* `nmos`
|
||||
* `nmos` (original 6502)
|
||||
|
||||
* `strict` (= NMOS without illegal instructions)
|
||||
* `strict` (NMOS without illegal instructions)
|
||||
|
||||
* `ricoh` (= NMOS without decimal mode)
|
||||
* `ricoh` (Ricoh 2A03/2A07, NMOS without decimal mode)
|
||||
|
||||
* `strictricoh`
|
||||
* `strictricoh` (Ricoh 2A03/2A07 without illegal instructions)
|
||||
|
||||
* `cmos` (= 65C02)
|
||||
* `cmos` (WDC 65C02 or 65SC02)
|
||||
|
||||
* `65ce02` (CSG 65CE02; experimental)
|
||||
|
||||
* `huc6280` (Hudson HuC6280; experimental)
|
||||
|
||||
* `65816` (WDC 65816/65802; experimental; currently only programs that use only 16-bit addressing are supported)
|
||||
|
||||
* `modules` – comma-separated list of modules that will be automatically imported
|
||||
|
||||
@ -73,13 +81,16 @@ Every platform is defined in an `.ini` file with an appropriate name.
|
||||
|
||||
* `emit_illegals` – whether the compiler should emit illegal instructions, default `false`
|
||||
|
||||
* `emit_cmos` – whether the compiler should emit CMOS instructions, default is `true` on `cmos` and `false` elsewhere
|
||||
* `emit_cmos` – whether the compiler should emit CMOS instructions, default is `true` on compatible processors and `false` elsewhere
|
||||
|
||||
* `emit_65816` – which 65816 instructions should the compiler emit, either `no`, `emulation` or `native`
|
||||
|
||||
* `decimal_mode` – whether the compiler should emit decimal instructions, default is `false` on `ricoh` and `strictricoh` and `true` elsewhere
|
||||
|
||||
* `ro_arrays` – whether the compiler should warn upon array writes, default is `false`
|
||||
|
||||
* `prevent_jmp_indirect_bug` – whether the compiler should try to avoid the indirect JMP bug, default is `false` on `cmos` and `true` elsewhere
|
||||
* `prevent_jmp_indirect_bug` – whether the compiler should try to avoid the indirect JMP bug,
|
||||
default is `false` on 65C02-compatible processors and `true` elsewhere
|
||||
|
||||
#### `[allocation]` section
|
||||
|
||||
|
@ -11,7 +11,8 @@ There are two ways to include raw assembly code in your Millfork programs:
|
||||
Millfork inline assembly uses the same three-letter opcodes as most other 6502 assemblers.
|
||||
Indexing syntax is also the same. Only instructions available on the current CPU architecture are available.
|
||||
|
||||
Currently, `RMBx`/`SMBx`/`BBRx`/`BBSx` are not supported yet.
|
||||
**Work in progress**:
|
||||
Currently, `RMBx`/`SMBx`/`BBRx`/`BBSx` and some extra 65CE02/HuC6280/65816 instructions are not supported yet.
|
||||
|
||||
Undocumented instructions are supported using various opcodes
|
||||
|
||||
@ -136,4 +137,32 @@ it should abide to the following rules:
|
||||
|
||||
* end non-inline assembly functions with `RTS`, `JMP` or `RTI` as appropriate
|
||||
|
||||
* on NMOS 6502:
|
||||
|
||||
* don't use `XAA`, `LXA`, `AHX`, `SHX`, `SHY`, `LAS` and `TAS` instructions
|
||||
|
||||
* on 65816:
|
||||
|
||||
* keep the direct page register set to $0000
|
||||
|
||||
* keep the M and X flags set to 1 (8-bit registers by default, native mode)
|
||||
|
||||
* if running in the native mode, be careful with the stack pointer (you should keep it between $000100 and $0001FF)
|
||||
|
||||
* do not change the data page register (keep an eye at the `PLD`, `MVN`, `MVP` instructions)
|
||||
|
||||
* explicitly use 16-bit immediate operands when appropriate; the assembler doesn't track flags and assumes 8-bit immediates by default
|
||||
|
||||
* use far jumps unless you're sure that the called function returns with an `RTS`
|
||||
|
||||
* on 65CE02:
|
||||
|
||||
* keep the `B` register set to $00
|
||||
|
||||
* don't change the `E` flag
|
||||
|
||||
* on HuC6280
|
||||
|
||||
* don't use the `SET` instruction
|
||||
|
||||
The above list is not exhaustive.
|
||||
|
@ -6,6 +6,10 @@ Decimal: `1`, `10`
|
||||
|
||||
Binary: `%0101`, `0b101001`
|
||||
|
||||
Quaternary: `0q2131`
|
||||
|
||||
Octal: `0o172`
|
||||
|
||||
Hexadecimal: `$D323`, `0x2a2`
|
||||
|
||||
## String literals
|
||||
|
21
include/c64_scpu.ini
Normal file
21
include/c64_scpu.ini
Normal file
@ -0,0 +1,21 @@
|
||||
; Commodore 64 with a SuperCPU
|
||||
; assuming a program loaded from disk or tape
|
||||
|
||||
[compilation]
|
||||
arch=65816
|
||||
modules=c64_hardware,loader_0801,c64_kernal,c64_panic,stdlib
|
||||
emit_65816=emulation
|
||||
|
||||
[allocation]
|
||||
main_org=$80D
|
||||
zp_pointers=$C1,$C3,$FB,$FD,$39,$3B,$3D,$43,$45,$47,$4B
|
||||
himem_style=per_bank
|
||||
himem_start=after_code
|
||||
himem_end=$9FFF
|
||||
|
||||
[output]
|
||||
style=per_bank
|
||||
format=startaddr,allocated
|
||||
extension=prg
|
||||
|
||||
|
@ -21,17 +21,46 @@ case class CompilationOptions(platform: Platform, commandLineFlags: Map[Compilat
|
||||
ErrorReporting.warn("Decimal mode enabled for Ricoh architecture", this)
|
||||
}
|
||||
}
|
||||
if (platform.cpu != Cmos) {
|
||||
if (platform.cpu == Sixteen) {
|
||||
if (flags(LargeCode)) {
|
||||
ErrorReporting.warn("Large code model doesn't work correctly yet", this)
|
||||
}
|
||||
}
|
||||
if (platform.cpu != Sixteen) {
|
||||
if (flags(LargeCode)) {
|
||||
ErrorReporting.error("Cannot use large code model on architectures other than 65816")
|
||||
}
|
||||
if (flags(ReturnWordsViaAccumulator)) {
|
||||
ErrorReporting.error("Cannot return words via accumulator on non-65816 architecture")
|
||||
}
|
||||
if (flags(EmitNative65816Opcodes) || flags(EmitEmulation65816Opcodes)) {
|
||||
ErrorReporting.error("65816 opcodes enabled for non-65816 architecture")
|
||||
}
|
||||
}
|
||||
if (platform.cpu != CE02) {
|
||||
if (flags(Emit65CE02Opcodes)) {
|
||||
ErrorReporting.error("65CE02 opcodes enabled for non-65CE02 architecture")
|
||||
}
|
||||
}
|
||||
if (flags(Emit65CE02Opcodes)) {
|
||||
ErrorReporting.warn("65CE02 opcodes are highly experimental", this)
|
||||
}
|
||||
if (platform.cpu != HuC6280) {
|
||||
if (flags(EmitHudsonOpcodes)) {
|
||||
ErrorReporting.error("HuC6280 opcodes enabled for non-HuC6280 architecture")
|
||||
}
|
||||
}
|
||||
if (!CmosCompatible(platform.cpu)) {
|
||||
if (!flags(PreventJmpIndirectBug)) {
|
||||
ErrorReporting.warn("JMP bug prevention should be enabled for non-CMOS architecture", this)
|
||||
}
|
||||
if (flags(EmitCmosOpcodes)) {
|
||||
ErrorReporting.warn("CMOS opcodes enabled for non-CMOS architecture", this)
|
||||
ErrorReporting.error("CMOS opcodes enabled for non-CMOS architecture")
|
||||
}
|
||||
}
|
||||
if (flags(EmitIllegals)) {
|
||||
if (platform.cpu == Cmos) {
|
||||
ErrorReporting.warn("Illegal opcodes enabled for CMOS architecture", this)
|
||||
if (CmosCompatible(platform.cpu)) {
|
||||
ErrorReporting.error("Illegal opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
if (platform.cpu == StrictRicoh || platform.cpu == StrictMos) {
|
||||
ErrorReporting.warn("Illegal opcodes enabled for strict architecture", this)
|
||||
@ -41,7 +70,9 @@ case class CompilationOptions(platform: Platform, commandLineFlags: Map[Compilat
|
||||
|
||||
object Cpu extends Enumeration {
|
||||
|
||||
val Mos, StrictMos, Ricoh, StrictRicoh, Cmos = Value
|
||||
val Mos, StrictMos, Ricoh, StrictRicoh, Cmos, HuC6280, CE02, Sixteen = Value
|
||||
|
||||
val CmosCompatible = Set(Cmos, HuC6280, CE02, Sixteen)
|
||||
|
||||
import CompilationFlag._
|
||||
|
||||
@ -50,7 +81,10 @@ object Cpu extends Enumeration {
|
||||
case Mos => Set(DecimalMode, PreventJmpIndirectBug, VariableOverlap, CompactReturnDispatchParams)
|
||||
case Ricoh => Set(PreventJmpIndirectBug, VariableOverlap, CompactReturnDispatchParams)
|
||||
case StrictRicoh => Set(PreventJmpIndirectBug, VariableOverlap, CompactReturnDispatchParams)
|
||||
case Cmos => Set(EmitCmosOpcodes, VariableOverlap, CompactReturnDispatchParams)
|
||||
case Cmos => Set(DecimalMode, EmitCmosOpcodes, VariableOverlap, CompactReturnDispatchParams)
|
||||
case HuC6280 => Set(DecimalMode, EmitCmosOpcodes, EmitHudsonOpcodes, VariableOverlap, CompactReturnDispatchParams)
|
||||
case CE02 => Set(DecimalMode, EmitCmosOpcodes, Emit65CE02Opcodes, VariableOverlap, CompactReturnDispatchParams)
|
||||
case Sixteen => Set(DecimalMode, EmitCmosOpcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes, ReturnWordsViaAccumulator, VariableOverlap, CompactReturnDispatchParams)
|
||||
}
|
||||
|
||||
def fromString(name: String): Cpu.Value = name match {
|
||||
@ -59,21 +93,33 @@ object Cpu extends Enumeration {
|
||||
case "6510" => Mos
|
||||
case "strict" => StrictMos
|
||||
case "cmos" => Cmos
|
||||
case "65sc02" => Cmos
|
||||
case "sc02" => Cmos
|
||||
case "65c02" => Cmos
|
||||
case "c02" => Cmos
|
||||
case "hudson" => HuC6280
|
||||
case "huc6280" => HuC6280
|
||||
case "c6280" => HuC6280
|
||||
case "6280" => HuC6280
|
||||
case "65ce02" => CE02
|
||||
case "ce02" => CE02
|
||||
case "65816" => Sixteen
|
||||
case "816" => Sixteen
|
||||
case "ricoh" => Ricoh
|
||||
case "2a03" => Ricoh
|
||||
case "2a07" => Ricoh
|
||||
case "strictricoh" => StrictRicoh
|
||||
case "strict2a03" => StrictRicoh
|
||||
case "strict2a07" => StrictRicoh
|
||||
case _ => ErrorReporting.fatal("Unknown CPU achitecture")
|
||||
case _ => ErrorReporting.fatal("Unknown CPU achitecture: " + name)
|
||||
}
|
||||
}
|
||||
|
||||
object CompilationFlag extends Enumeration {
|
||||
val
|
||||
// compilation options:
|
||||
EmitIllegals, EmitCmosOpcodes, DecimalMode, ReadOnlyArrays, PreventJmpIndirectBug,
|
||||
EmitIllegals, EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||
DecimalMode, ReadOnlyArrays, PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator,
|
||||
// optimization options:
|
||||
DetailedFlowAnalysis, DangerousOptimizations, InlineFunctions, OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed,
|
||||
// memory allocation options
|
||||
@ -90,6 +136,8 @@ object CompilationFlag extends Enumeration {
|
||||
val fromString = Map(
|
||||
"emit_illegals" -> EmitIllegals,
|
||||
"emit_cmos" -> EmitCmosOpcodes,
|
||||
"emit_65ce02" -> Emit65CE02Opcodes,
|
||||
"emit_huc6280" -> EmitHudsonOpcodes,
|
||||
"decimal_mode" -> DecimalMode,
|
||||
"ro_arrays" -> ReadOnlyArrays,
|
||||
"ror_warn" -> RorWarning,
|
||||
|
@ -65,7 +65,10 @@ object Main {
|
||||
"c64"
|
||||
})
|
||||
val options = CompilationOptions(platform, c.flags)
|
||||
ErrorReporting.debug("Effective flags: " + options.flags)
|
||||
ErrorReporting.debug("Effective flags: ")
|
||||
options.flags.toSeq.sortBy(_._1).foreach{
|
||||
case (f, b) => ErrorReporting.debug(f" $f%-30s : $b%s")
|
||||
}
|
||||
|
||||
val output = c.outputFileName.getOrElse("a")
|
||||
val assOutput = output + ".asm"
|
||||
@ -94,7 +97,11 @@ object Main {
|
||||
case _ =>
|
||||
val extras = List(
|
||||
if (options.flag(CompilationFlag.EmitIllegals)) UndocumentedOptimizations.All else Nil,
|
||||
if (options.flag(CompilationFlag.Emit65CE02Opcodes)) CE02Optimizations.All else Nil,
|
||||
if (options.flag(CompilationFlag.EmitCmosOpcodes)) CmosOptimizations.All else LaterOptimizations.Nmos,
|
||||
if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.All else Nil,
|
||||
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForEmulation else Nil,
|
||||
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForNative else Nil,
|
||||
if (options.flag(CompilationFlag.DangerousOptimizations)) DangerousOptimizations.All else Nil,
|
||||
).flatten
|
||||
val goodCycle = List.fill(optLevel - 2)(OptimizationPresets.Good).flatten
|
||||
@ -191,9 +198,32 @@ object Main {
|
||||
boolean("-fcmos-ops", "-fno-cmos-ops").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.EmitCmosOpcodes, v)
|
||||
}.description("Whether should emit CMOS opcodes.")
|
||||
boolean("-f65ce02-ops", "-fno-65ce02-ops").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.Emit65CE02Opcodes, v)
|
||||
}.description("Whether should emit 65CE02 opcodes.")
|
||||
boolean("-fhuc6280-ops", "-fno-huc6280-ops").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.EmitHudsonOpcodes, v)
|
||||
}.description("Whether should emit HuC6280huc6280 opcodes.")
|
||||
flag("-fno-65816-ops").action { c =>
|
||||
c.changeFlag(CompilationFlag.EmitEmulation65816Opcodes, b = false)
|
||||
c.changeFlag(CompilationFlag.EmitNative65816Opcodes, b = false)
|
||||
c.changeFlag(CompilationFlag.ReturnWordsViaAccumulator, b = false)
|
||||
}.description("Don't emit 65816 opcodes.")
|
||||
flag("-femulation-65816-ops").action { c =>
|
||||
c.changeFlag(CompilationFlag.EmitEmulation65816Opcodes, b = true)
|
||||
c.changeFlag(CompilationFlag.EmitNative65816Opcodes, b = false)
|
||||
c.changeFlag(CompilationFlag.ReturnWordsViaAccumulator, b = false)
|
||||
}.description("Emit 65816 opcodes (experimental).")
|
||||
flag("-fnative-65816-ops").action { c =>
|
||||
c.changeFlag(CompilationFlag.EmitEmulation65816Opcodes, b = true)
|
||||
c.changeFlag(CompilationFlag.EmitNative65816Opcodes, b = true)
|
||||
}.description("Emit 65816 opcodes (experimental).")
|
||||
boolean("-flarge-code", "-fsmall-code").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.LargeCode, v)
|
||||
}.description("Whether should use 24-bit or 16-bit jumps to subroutines (not yet implemented).").hidden()
|
||||
boolean("-fillegals", "-fno-illegals").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.EmitIllegals, v)
|
||||
}.description("Whether should emit illegal (undocumented) NMOS opcodes. Required -O2 or higher to have an effect.")
|
||||
}.description("Whether should emit illegal (undocumented) NMOS opcodes. Requires -O2 or higher to have an effect.")
|
||||
boolean("-fjmp-fix", "-fno-jmp-fix").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.PreventJmpIndirectBug, v)
|
||||
}.description("Whether should prevent indirect JMP bug on page boundary.")
|
||||
|
@ -173,6 +173,7 @@ object OptimizationPresets {
|
||||
AlwaysGoodOptimizations.ReverseFlowAnalysis,
|
||||
AlwaysGoodOptimizations.SimplifiableBitOpsSequence,
|
||||
AlwaysGoodOptimizations.SimplifiableCondition,
|
||||
AlwaysGoodOptimizations.SimplifiableStackOperation,
|
||||
AlwaysGoodOptimizations.SmarterShiftingOfWords,
|
||||
AlwaysGoodOptimizations.SmarterShiftingBytes,
|
||||
AlwaysGoodOptimizations.UnconditionalJumpRemoval,
|
||||
|
@ -20,6 +20,7 @@ class Platform(
|
||||
val allocator: VariableAllocator,
|
||||
val org: Int,
|
||||
val fileExtension: String,
|
||||
var defaultCodeBank: Int = 0,
|
||||
)
|
||||
|
||||
object Platform {
|
||||
@ -55,11 +56,35 @@ object Platform {
|
||||
|
||||
val cs = conf.getSection("compilation")
|
||||
val cpu = Cpu.fromString(cs.get(classOf[String], "arch", "strict"))
|
||||
val flagOverrides = CompilationFlag.fromString.flatMap { case (k, f) =>
|
||||
cs.get(classOf[String], k, "").toLowerCase match {
|
||||
val value65816 = cs.get(classOf[String], "emit_65816", "")
|
||||
val flagOverrides = (value65816.toLowerCase match {
|
||||
case "" => Nil
|
||||
case "false" | "none" | "no" | "off" | "0" =>
|
||||
List(
|
||||
CompilationFlag.EmitEmulation65816Opcodes -> false,
|
||||
CompilationFlag.EmitNative65816Opcodes -> false,
|
||||
CompilationFlag.ReturnWordsViaAccumulator -> false)
|
||||
case "emulation" =>
|
||||
List(
|
||||
CompilationFlag.EmitEmulation65816Opcodes -> true,
|
||||
CompilationFlag.EmitNative65816Opcodes -> false,
|
||||
CompilationFlag.ReturnWordsViaAccumulator -> false)
|
||||
case "native" =>
|
||||
List(
|
||||
CompilationFlag.EmitEmulation65816Opcodes -> true,
|
||||
CompilationFlag.EmitNative65816Opcodes -> true)
|
||||
case _ =>
|
||||
ErrorReporting.error(s"Unsupported `emit_65816` value: $value65816")
|
||||
Nil
|
||||
}).toMap ++ CompilationFlag.fromString.flatMap { case (k, f) =>
|
||||
val value = cs.get(classOf[String], k, "")
|
||||
value.toLowerCase match {
|
||||
case "" => None
|
||||
case "false" | "off" | "0" => Some(f -> false)
|
||||
case "true" | "on" | "1" => Some(f -> true)
|
||||
case "false" | "off" | "no" | "0" => Some(f -> false)
|
||||
case "true" | "on" | "yes" | "1" => Some(f -> true)
|
||||
case _ =>
|
||||
ErrorReporting.error(s"Unsupported `$k` value: $value")
|
||||
None
|
||||
}
|
||||
}
|
||||
val startingModules = cs.get(classOf[String], "modules", "").split("[, ]+").filter(_.nonEmpty).toList
|
||||
@ -104,10 +129,22 @@ object Platform {
|
||||
Integer.parseInt(s.substring(1), 16)
|
||||
} else if (s.startsWith("0x")) {
|
||||
Integer.parseInt(s.substring(2), 16)
|
||||
} else if (s.startsWith("0X")) {
|
||||
Integer.parseInt(s.substring(2), 16)
|
||||
} else if (s.startsWith("%")) {
|
||||
Integer.parseInt(s.substring(1), 2)
|
||||
} else if (s.startsWith("0b")) {
|
||||
Integer.parseInt(s.substring(2), 2)
|
||||
} else if (s.startsWith("0B")) {
|
||||
Integer.parseInt(s.substring(2), 2)
|
||||
} else if (s.startsWith("0o")) {
|
||||
Integer.parseInt(s.substring(2), 8)
|
||||
} else if (s.startsWith("0O")) {
|
||||
Integer.parseInt(s.substring(2), 8)
|
||||
} else if (s.startsWith("0q")) {
|
||||
Integer.parseInt(s.substring(2), 4)
|
||||
} else if (s.startsWith("0Q")) {
|
||||
Integer.parseInt(s.substring(2), 4)
|
||||
} else {
|
||||
s.toInt
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
package millfork.assembly
|
||||
|
||||
import java.lang.management.MemoryType
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly.Opcode._
|
||||
import millfork.assembly.opt.ReadsA
|
||||
import millfork.compiler.{CompilationContext, MfCompiler}
|
||||
import millfork.env._
|
||||
|
||||
@ -11,148 +9,317 @@ import millfork.env._
|
||||
object OpcodeClasses {
|
||||
|
||||
val ReadsAAlways = Set(
|
||||
ADC, AND, BIT, CMP, EOR, ORA, PHA, SBC, STA, TAX, TAY,
|
||||
SAX, SBX, ANC, DCP, ISC, RRA, RLA, SRE, SLO, LXA, XAA, AHX, TAS
|
||||
ADC, AND, BIT, CMP, EOR, ORA, PHA, SBC, STA,
|
||||
ADC_W, AND_W, BIT_W, CMP_W, EOR_W, ORA_W, PHA_W, SBC_W, STA_W,
|
||||
TAX, TAY,
|
||||
SAX, SBX, ANC, DCP, ISC, RRA, RLA, SRE, SLO, LXA, XAA, AHX, TAS,
|
||||
TAZ, TAB,
|
||||
HuSAX, SAY, TAM,
|
||||
TCD, TCS, XBA,
|
||||
)
|
||||
val ReadsAIfImplied = Set(
|
||||
ASL, LSR, ROL, ROR, INC, DEC,
|
||||
DEC_W, INC_W, ROL_W, ROR_W, ASL_W, LSR_W,
|
||||
)
|
||||
val ReadsAHAlways = Set(
|
||||
ADC_W, AND_W, BIT_W, CMP_W, EOR_W, ORA_W, PHA_W, SBC_W, STA_W,
|
||||
TCD, TCS, XBA,
|
||||
)
|
||||
val ReadsAHIfImplied = Set(
|
||||
DEC_W, INC_W, ROL_W, ROR_W, ASL_W, LSR_W,
|
||||
)
|
||||
val ReadsAIfImplied = Set(ASL, LSR, ROL, ROR, INC, DEC)
|
||||
val ReadsXAlways = Set(
|
||||
CPX, DEX, INX, STX, TXA, TXS, SBX,
|
||||
PLX,
|
||||
XAA, SAX, AHX, SHX, TAS
|
||||
CPX, DEX, INX, STX,
|
||||
CPX_W, DEX_W, INX_W, STX_W,
|
||||
TXA, TXS, SBX,
|
||||
PLX, PLX_W,
|
||||
XAA, SAX, AHX, SHX, TAS,
|
||||
HuSAX, SXY,
|
||||
TXY,
|
||||
)
|
||||
val ReadsYAlways = Set(CPY, DEY, INY, STY, TYA, PLY, SHY)
|
||||
val ReadsYAlways = Set(CPY, DEY, INY, STY, TYA, PLY, SHY, SAY, SXY, TYX)
|
||||
val ReadsIZAlways = Set(CPZ, DEZ, INZ, STZ, TZA, PLZ)
|
||||
val ReadsM = Set(
|
||||
ORA, AND, EOR, ADC, SBC, CMP, LDA, STA,
|
||||
ORA_W, AND_W, EOR_W, ADC_W, SBC_W, CMP_W, LDA_W, STA_W,
|
||||
STZ, BIT,
|
||||
STZ_W, BIT_W,
|
||||
PHA, PLA,
|
||||
PHA_W, PLA_W,
|
||||
DEC, INC, ROL, ROR, ASL, LSR,
|
||||
DEC_W, INC_W, ROL_W, ROR_W, ASL_W, LSR_W,
|
||||
TAX, TXA, TAY, TYA)
|
||||
val ReadsW = Set(
|
||||
LDX, LDY, CPX, CPY, STX, STY, INX, INY, DEX, DEY,
|
||||
LDX_W, LDY_W, CPX_W, CPY_W, STX_W, STY_W, INX_W, INY_W, DEX_W, DEY_W,
|
||||
PLX, PLY, PHX, PHY,
|
||||
PLX_W, PLY_W, PHX_W, PHY_W,
|
||||
TAX, TXA, TAY, TYA, TXY, TYX)
|
||||
val ReadsZ = Set(BNE, BEQ, PHP)
|
||||
val ReadsN = Set(BMI, BPL, PHP)
|
||||
val ReadsNOrZ = ReadsZ ++ ReadsN
|
||||
val ReadsV = Set(BVS, BVC, PHP)
|
||||
val ReadsD = Set(PHP, ADC, SBC, RRA, ARR, ISC, DCP) // TODO: ??
|
||||
val ReadsD = Set(PHP, ADC, SBC, RRA, ARR, ISC, ADC_W, SBC_W)
|
||||
val ReadsC = Set(
|
||||
PHP, ADC, SBC, BCC, BCS, ROL, ROR,
|
||||
ALR, ARR, ISC, RLA, RRA, SLO, SRE // TODO: ??
|
||||
PHP, BCC, BCS,
|
||||
ADC, SBC, ROL, ROR,
|
||||
ADC_W, SBC_W, ROL_W, ROR_W,
|
||||
ALR, ARR, ISC, RLA, RRA, SLO, SRE,
|
||||
XCE
|
||||
)
|
||||
|
||||
val ChangesAAlways = Set(
|
||||
TXA, TYA, PLA,
|
||||
TXA, TYA, PLA, PLA_W,
|
||||
ORA, AND, EOR, ADC, LDA, SBC,
|
||||
ORA_W, AND_W, EOR_W, ADC_W, LDA_W, SBC_W,
|
||||
SLO, RLA, SRE, RRA, LAX, ISC,
|
||||
XAA, ANC, ALR, ARR, LXA, LAS
|
||||
XAA, ANC, ALR, ARR, LXA, LAS,
|
||||
TZA, NEG,
|
||||
TMA,
|
||||
XBA, TDC,
|
||||
)
|
||||
val ChangesAIfImplied = Set(ASL, LSR, ROL, ROR, INC, DEC)
|
||||
|
||||
val ChangesAIfImplied = Set(
|
||||
ASL, LSR, ROL, ROR, INC, DEC,
|
||||
ASL_W, LSR_W, ROL_W, ROR_W, INC_W, DEC_W,
|
||||
)
|
||||
|
||||
val ChangesAHAlways = Set(
|
||||
PLA_W,
|
||||
ORA_W, AND_W, EOR_W, ADC_W, LDA_W, SBC_W,
|
||||
XBA, TDC,
|
||||
)
|
||||
|
||||
val ChangesAHIfImplied = Set(
|
||||
ASL, LSR, ROL, ROR, INC, DEC,
|
||||
ASL_W, LSR_W, ROL_W, ROR_W, INC_W, DEC_W,
|
||||
)
|
||||
|
||||
val ChangesX = Set(
|
||||
DEX, INX, TAX, LDX, TSX,
|
||||
DEX, INX, LDX,
|
||||
DEX_W, INX_W, LDX_W,
|
||||
TAX, TSX,
|
||||
SBX, LAX, LXA, LAS,
|
||||
PLX,
|
||||
PLX, PLX_W,
|
||||
TYX, SXY,
|
||||
)
|
||||
val ChangesY = Set(
|
||||
DEY, INY, TAY, LDY
|
||||
DEY, INY, LDY,
|
||||
DEY_W, INY_W, LDY_W,
|
||||
TAY,
|
||||
PLY, PLY_W,
|
||||
TXY, SXY,
|
||||
)
|
||||
val ChangesIZ = Set(
|
||||
DEZ, INZ, TAZ, LDZ,
|
||||
)
|
||||
val ChangesS = Set(
|
||||
PHA, PLA, PHP, PLP, TXS,
|
||||
PHX, PHY, PLX, PLY, TAS, LAS
|
||||
PHA, PLA,
|
||||
PHA_W, PLA_W,
|
||||
PHP, PLP, TXS,
|
||||
PHX, PHY, PLX, PLY,
|
||||
PHX_W, PHY_W, PLX_W, PLY_W,
|
||||
TAS, LAS,
|
||||
PHZ,
|
||||
PHB, PHD, PHK, PLB, PLD, RTL,
|
||||
PEA, PEI, PER,
|
||||
XCE, TCS, TYS,
|
||||
)
|
||||
val ChangesMemoryAlways = Set(
|
||||
STA, STY, STZ, STX,
|
||||
STA_W, STY_W, STZ_W, STX_W,
|
||||
TRB, TSB,
|
||||
SAX, DCP, ISC,
|
||||
SLO, RLA, SRE, RRA,
|
||||
AHX, SHY, SHX, TAS, LAS
|
||||
AHX, SHY, SHX, TAS, LAS,
|
||||
COP,
|
||||
)
|
||||
val ChangesMemoryIfNotImplied = Set(
|
||||
DEC, INC, ASL, ROL, LSR, ROR
|
||||
DEC, INC, ASL, ROL, LSR, ROR,
|
||||
DEC_W, INC_W, ASL_W, ROL_W, LSR_W, ROR_W,
|
||||
)
|
||||
val ReadsMemoryIfNotImpliedOrImmediate = Set(
|
||||
LDY, CPX, CPY,
|
||||
LDY, CPX, CPY, BIT,
|
||||
LDY_W, CPX_W, CPY_W, BIT_W,
|
||||
ORA, AND, EOR, ADC, LDA, CMP, SBC,
|
||||
ORA_W, AND_W, EOR_W, ADC_W, LDA_W, CMP_W, SBC_W,
|
||||
ASL, ROL, LSR, ROR, LDX, DEC, INC,
|
||||
ASL_W, ROL_W, LSR_W, ROR_W, LDX_W, DEC_W, INC_W,
|
||||
SLO, RLA, SRE, RRA, LAX, DCP, ISC,
|
||||
LAS,
|
||||
TRB, TSB
|
||||
TRB, TSB,
|
||||
TRB_W, TSB_W,
|
||||
)
|
||||
|
||||
val AccessesWordInMemory = Set(
|
||||
LDA_W, LDX_W, LDY_W,
|
||||
STA_W, STX_W, STY_W,
|
||||
CMP_W, CPX_W, CPY_W,
|
||||
DEC_W, INC_W, ASL_W, ROL_W, LSR_W, ROR_W,
|
||||
ORA_W, AND_W, EOR_W, ADC_W, SBC_W,
|
||||
TSB_W, TRB_W, BIT_W,
|
||||
PHW,
|
||||
)
|
||||
|
||||
val AccessesWordInMemoryAlwaysIfNotImplied = Set(
|
||||
)
|
||||
|
||||
val OverwritesA = Set(
|
||||
LDA, PLA, TXA, TYA,
|
||||
LAX, LAS
|
||||
LDA, PLA,
|
||||
LDA_W, PLA_W,
|
||||
TXA, TYA,
|
||||
LAX, LAS,
|
||||
TBA, TZA,
|
||||
HuSAX, SAY,
|
||||
TDC, TSC,
|
||||
)
|
||||
val OverwritesAH = Set(
|
||||
LDA_W, PLA_W,
|
||||
TDC, TSC,
|
||||
)
|
||||
val OverwritesX = Set(
|
||||
TAX, LDX, TSX, PLX,
|
||||
LAX, LAS
|
||||
LAX, LAS,
|
||||
TYX, HuSAX, SXY,
|
||||
)
|
||||
val OverwritesY = Set(
|
||||
TAY, LDY, PLY
|
||||
TAY, LDY, PLY,
|
||||
TSY, TXY, SAY, SXY,
|
||||
)
|
||||
val OverwritesC = Set(CLC, SEC, PLP)
|
||||
val OverwritesIZ = Set(
|
||||
TAZ, LDZ, PLZ,
|
||||
)
|
||||
val OverwritesC = Set(CLC, SEC, PLP, XCE)
|
||||
val OverwritesD = Set(CLD, SED, PLP)
|
||||
val OverwritesI = Set(CLI, SEI, PLP)
|
||||
val OverwritesV = Set(CLV, PLP)
|
||||
val ConcernsAAlways = ReadsAAlways ++ ChangesAAlways
|
||||
val ConcernsAHAlways = ReadsAHAlways ++ ChangesAHAlways
|
||||
val ConcernsAIfImplied = ReadsAIfImplied ++ ChangesAIfImplied
|
||||
val ConcernsAHIfImplied = ReadsAHIfImplied ++ ChangesAHIfImplied
|
||||
val ConcernsXAlways = ReadsXAlways | ChangesX
|
||||
val ConcernsYAlways = ReadsYAlways | ChangesY
|
||||
val ConcernsIZAlways = ReadsIZAlways | ChangesIZ
|
||||
|
||||
val ChangesStack = Set(
|
||||
PHA, PLA, PHP, PLP,
|
||||
PHX, PLX, PHY, PLY,
|
||||
PHA_W, PLA_W,
|
||||
PHX_W, PLX_W, PHY_W, PLY_W,
|
||||
TXS,
|
||||
JSR, RTS, RTI,
|
||||
TAS, LAS,
|
||||
PHW, PHZ, PLZ,
|
||||
TYS, TCS,
|
||||
RTL, BSR,
|
||||
PHB, PHD, PHK, PLB, PLD,
|
||||
PEA, PEI, PER,
|
||||
XCE,
|
||||
)
|
||||
|
||||
val ConcernsStack = ChangesStack + TSX
|
||||
val ConcernsStackAlways = ChangesStack ++ Set(TSX, TSY, TSC)
|
||||
val ConcernsS = ChangesS ++ Set(TSX, TSY, TSC)
|
||||
|
||||
val ChangesNAndZ = Set(
|
||||
ADC, AND, ASL, BIT, CMP, CPX, CPY, DEC, DEX, DEY, EOR, INC, INX, INY, LDA,
|
||||
ADC_W, AND_W, ASL_W, BIT_W, CMP_W, CPX_W, CPY_W, DEC_W, DEX_W, DEY_W, EOR_W, INC_W, INX_W, INY_W, LDA_W,
|
||||
LDX, LDY, LSR, ORA, PLP, ROL, ROR, SBC, TAX, TAY, TXA, TYA,
|
||||
LDX_W, LDY_W, LSR_W, ORA_W, ROL_W, ROR_W, SBC_W,
|
||||
LAX, SBX, ANC, ALR, ARR, DCP, ISC, RLA, RRA, SLO, SRE, SAX,
|
||||
TSB, TRB // These two do not change N, but lets pretend they do for simplicity
|
||||
TSB, TRB, // These two do not change N, but lets pretend they do for simplicity
|
||||
TSB_W, TRB_W,
|
||||
NEG, ASR,
|
||||
CPZ, DEZ, INZ, LDZ,
|
||||
REP, SEP, // People usually don't use there to change N or Z, but let's assume they do
|
||||
)
|
||||
val ChangesC = Set(
|
||||
CLC, SEC, ADC, ASL, CMP, CPX, CPY, LSR, PLP, ROL, ROR, SBC,
|
||||
SBX, ANC, ALR, ARR, DCP, ISC, RLA, RRA, SLO, SRE
|
||||
PLP, CLC, SEC,
|
||||
ADC, ASL, CMP, CPX, CPY, LSR, ROL, ROR, SBC,
|
||||
ADC_W, ASL_W, CMP_W, CPX_W, CPY_W, LSR_W, ROL_W, ROR_W, SBC_W,
|
||||
SBX, ANC, ALR, ARR, DCP, ISC, RLA, RRA, SLO, SRE,
|
||||
CPZ, ASR,
|
||||
XCE,
|
||||
REP, SEP, // People usually don't use there to change C, but let's assume they do
|
||||
)
|
||||
val ChangesV = Set(
|
||||
ADC, BIT, PLP, SBC,
|
||||
ARR, ISC, RRA,
|
||||
REP, SEP, // People usually don't use there to change V, but let's assume they do
|
||||
)
|
||||
|
||||
val SupportsAbsoluteX = Set(
|
||||
ORA, AND, EOR, ADC, CMP, SBC,
|
||||
ORA_W, AND_W, EOR_W, ADC_W, CMP_W, SBC_W,
|
||||
ASL, ROL, LSR, ROR, DEC, INC,
|
||||
ASL_W, ROL_W, LSR_W, ROR_W, DEC_W, INC_W,
|
||||
SLO, RLA, SRE, RRA, DCP, ISC,
|
||||
STA, LDA, LDY, STZ, SHY,
|
||||
STA, LDA, LDY, STZ,
|
||||
STA_W, LDA_W, LDY_W, STZ_W,
|
||||
SHY,
|
||||
)
|
||||
|
||||
val SupportsAbsoluteY = Set(
|
||||
ORA, AND, EOR, ADC, CMP, SBC,
|
||||
ORA_W, AND_W, EOR_W, ADC_W, CMP_W, SBC_W,
|
||||
SLO, RLA, SRE, RRA, DCP, ISC,
|
||||
STA, LDA, LDX,
|
||||
STA_W, LDA_W, LDX_W,
|
||||
LAX, AHX, SHX, TAS, LAS,
|
||||
)
|
||||
|
||||
val SupportsAbsolute = Set(
|
||||
ORA, AND, EOR, ADC, STA, LDA, CMP, SBC,
|
||||
ORA_W, AND_W, EOR_W, ADC_W, STA_W, LDA_W, CMP_W, SBC_W,
|
||||
ASL, ROL, LSR, ROR, STX, LDX, DEC, INC,
|
||||
ASL_W, ROL_W, LSR_W, ROR_W, STX_W, LDX_W, DEC_W, INC_W,
|
||||
SLO, RLA, SRE, RRA, SAX, LAX, DCP, ISC,
|
||||
STY, LDY,
|
||||
BIT, JMP, JSR,
|
||||
STZ, TRB, TSB,
|
||||
LDZ,
|
||||
)
|
||||
|
||||
val SupportsZeroPageIndirect = Set(ORA, AND, EOR, ADC, STA, LDA, CMP, SBC)
|
||||
val SupportsIndexedZ = Set(
|
||||
ORA, AND, EOR, ADC, STA, LDA, CMP, SBC,
|
||||
ORA_W, AND_W, EOR_W, ADC_W, STA_W, LDA_W, CMP_W, SBC_W,
|
||||
)
|
||||
|
||||
val SupportsLongIndexedZ = Set(
|
||||
ORA, AND, EOR, ADC, STA, LDA, CMP, SBC,
|
||||
ORA_W, AND_W, EOR_W, ADC_W, STA_W, LDA_W, CMP_W, SBC_W,
|
||||
)
|
||||
|
||||
val ShortConditionalBranching = Set(BEQ, BNE, BMI, BPL, BVC, BVS, BCC, BCS)
|
||||
val ShortBranching = ShortConditionalBranching + BRA
|
||||
val AllDirectJumps = ShortBranching + JMP
|
||||
val AllDirectJumps = ShortBranching ++ Set(JMP, BRL)
|
||||
val AllLinear = Set(
|
||||
ORA, AND, EOR,
|
||||
ORA_W, AND_W, EOR_W,
|
||||
ADC, SBC, CMP, CPX, CPY,
|
||||
ADC_W, SBC_W, CMP_W, CPX_W, CPY_W,
|
||||
DEC, DEX, DEY, INC, INX, INY,
|
||||
DEC_W, DEX_W, DEY_W, INC_W, INX_W, INY_W,
|
||||
ASL, ROL, LSR, ROR,
|
||||
ASL_W, ROL_W, LSR_W, ROR_W,
|
||||
LDA, STA, LDX, STX, LDY, STY,
|
||||
LDA_W, STA_W, LDX_W, STX_W, LDY_W, STY_W,
|
||||
TAX, TXA, TAY, TYA, TXS, TSX,
|
||||
PLA, PLP, PHA, PHP,
|
||||
BIT, NOP,
|
||||
PLA_W, PHA_W,
|
||||
BIT, BIT_W, NOP,
|
||||
CLC, SEC, CLD, SED, CLI, SEI, CLV,
|
||||
STZ, PHX, PHY, PLX, PLY, TSB, TRB,
|
||||
STZ_W, PHX_W, PHY_W, PLX_W, PLY_W, TSB_W, TRB_W,
|
||||
SLO, RLA, SRE, RRA, SAX, LAX, DCP, ISC,
|
||||
ANC, ALR, ARR, XAA, LXA, SBX,
|
||||
CPZ, LDZ, INZ, DEZ,
|
||||
TAZ, TZA, TYS, TSY,
|
||||
TBA,
|
||||
PLZ, PHZ, PHW,
|
||||
CLA, CLX, CLY,
|
||||
CSH, CSL,
|
||||
TXY, TYX, XBA,
|
||||
PHD, PHB, PHK,
|
||||
DISCARD_AF, DISCARD_XF, DISCARD_YF)
|
||||
|
||||
val NoopDiscardsFlags = Set(DISCARD_AF, DISCARD_XF, DISCARD_YF)
|
||||
@ -165,6 +332,11 @@ object OpcodeClasses {
|
||||
|
||||
object AssemblyLine {
|
||||
|
||||
val accu8: AssemblyLine = AssemblyLine.immediate(SEP, 0x20)
|
||||
val accu16: AssemblyLine = AssemblyLine.immediate(REP, 0x20)
|
||||
val index8: AssemblyLine = AssemblyLine.immediate(SEP, 0x10)
|
||||
val index16: AssemblyLine = AssemblyLine.immediate(REP, 0x10)
|
||||
|
||||
def treatment(lines: List[AssemblyLine], state: State.Value): Treatment.Value =
|
||||
lines.map(_.treatment(state)).foldLeft(Treatment.Unchanged)(_ ~ _)
|
||||
|
||||
@ -216,7 +388,12 @@ object AssemblyLine {
|
||||
case v@RelativeVariable(_, _, _, true) =>
|
||||
List(AssemblyLine.zeropage(opcode, v.toAddress + offset))
|
||||
case v: VariableInMemory => List(AssemblyLine.absolute(opcode, v.toAddress + offset))
|
||||
case v: StackVariable => List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(opcode, v.baseOffset + offset + ctx.extraStackOffset))
|
||||
case v: StackVariable =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes)) {
|
||||
List(AssemblyLine.stackRelative(opcode, v.baseOffset + offset + ctx.extraStackOffset))
|
||||
} else {
|
||||
List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(opcode, v.baseOffset + offset + ctx.extraStackOffset))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,6 +406,10 @@ object AssemblyLine {
|
||||
def absolute(opcode: Opcode.Value, addr: Constant) =
|
||||
AssemblyLine(opcode, AddrMode.Absolute, addr)
|
||||
|
||||
def absoluteOrLongAbsolute(opcode: Opcode.Value, thing: ThingInMemory, options: CompilationOptions) =
|
||||
if (thing.isFar(options)) AssemblyLine(opcode, AddrMode.LongAbsolute, thing.toAddress)
|
||||
else AssemblyLine(opcode, AddrMode.Absolute, thing.toAddress)
|
||||
|
||||
def absolute(opcode: Opcode.Value, thing: ThingInMemory, offset: Int = 0) =
|
||||
AssemblyLine(opcode, AddrMode.Absolute, thing.toAddress + offset)
|
||||
|
||||
@ -258,6 +439,9 @@ object AssemblyLine {
|
||||
|
||||
def indexedY(opcode: Opcode.Value, thing: ThingInMemory, offset: Int = 0) =
|
||||
AssemblyLine(opcode, AddrMode.IndexedY, thing.toAddress + offset)
|
||||
|
||||
def stackRelative(opcode: Opcode.Value, addr: Int) =
|
||||
AssemblyLine(opcode, AddrMode.Stack, NumericConstant(addr & 0xff, 1))
|
||||
}
|
||||
|
||||
case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var parameter: Constant, elidable: Boolean = true) {
|
||||
@ -270,43 +454,65 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
|
||||
|
||||
def reads(state: State.Value): Boolean = state match {
|
||||
case A => if (addrMode == Implied) ReadsAIfImplied(opcode) else ReadsAAlways(opcode)
|
||||
case X => addrMode == AbsoluteX || addrMode == ZeroPageX || addrMode == IndexedX || ReadsXAlways(opcode)
|
||||
case Y => addrMode == AbsoluteY || addrMode == ZeroPageY || addrMode == IndexedY || ReadsYAlways(opcode)
|
||||
case AH => if (addrMode == Implied) ReadsAHIfImplied(opcode) else ReadsAHAlways(opcode)
|
||||
case X => addrMode == AbsoluteX || addrMode == LongAbsoluteX || addrMode == ZeroPageX || addrMode == IndexedX || ReadsXAlways(opcode)
|
||||
case Y => addrMode == AbsoluteY || addrMode == ZeroPageY || addrMode == IndexedY || addrMode == LongIndexedY || ReadsYAlways(opcode)
|
||||
case C => ReadsC(opcode)
|
||||
case D => ReadsD(opcode)
|
||||
case N => ReadsN(opcode)
|
||||
case V => ReadsV(opcode)
|
||||
case Z => ReadsZ(opcode)
|
||||
case IZ => addrMode == IndexedZ || ReadsIZAlways(opcode)
|
||||
case M => ReadsM(opcode)
|
||||
case W => ReadsW(opcode)
|
||||
}
|
||||
|
||||
def treatment(state: State.Value): Treatment.Value = opcode match {
|
||||
case LABEL => Unchanged // TODO: ???
|
||||
case NOP => Unchanged
|
||||
case JSR | JMP | BEQ | BNE | BMI | BPL | BRK | BCC | BVC | BCS | BVS => Changed
|
||||
case JSR | JMP | BEQ | BNE | BMI | BPL | BRK | BCC | BVC | BCS | BVS | BSR => Changed
|
||||
case CLC => if (state == C) Cleared else Unchanged
|
||||
case SEC => if (state == C) Set else Unchanged
|
||||
case CLV => if (state == V) Cleared else Unchanged
|
||||
case CLD => if (state == D) Cleared else Unchanged
|
||||
case SED => if (state == D) Set else Unchanged
|
||||
case SEP => parameter match {
|
||||
case NumericConstant(n, _) =>
|
||||
if (isAffectedBySepRep(state, n)) Set else Unchanged
|
||||
case _ => Changed
|
||||
}
|
||||
case REP => parameter match {
|
||||
case NumericConstant(n, _) =>
|
||||
if (isAffectedBySepRep(state, n)) Cleared else Unchanged
|
||||
case _ => Changed
|
||||
}
|
||||
case XCE => Changed
|
||||
case _ => state match { // TODO: smart detection of constants
|
||||
case A =>
|
||||
if (ChangesAAlways(opcode) || addrMode == Implied && ChangesAIfImplied(opcode))
|
||||
Changed
|
||||
else
|
||||
Unchanged
|
||||
case AH =>
|
||||
if (ChangesAHAlways(opcode) || addrMode == Implied && ChangesAHIfImplied(opcode))
|
||||
Changed
|
||||
else
|
||||
Unchanged
|
||||
case X => if (ChangesX(opcode)) Changed else Unchanged
|
||||
case Y => if (ChangesY(opcode)) Changed else Unchanged
|
||||
case IZ => if (ChangesIZ(opcode)) Changed else Unchanged
|
||||
case C => if (ChangesC(opcode)) Changed else Unchanged
|
||||
case V => if (ChangesV(opcode)) Changed else Unchanged
|
||||
case N | Z => if (ChangesNAndZ(opcode)) Changed else Unchanged
|
||||
case D => Unchanged
|
||||
case W | M | D => Unchanged
|
||||
}
|
||||
}
|
||||
|
||||
def sizeInBytes: Int = addrMode match {
|
||||
case Implied => 1
|
||||
case Relative | ZeroPageX | ZeroPage | ZeroPageY | ZeroPageIndirect | IndexedX | IndexedY | Immediate => 2
|
||||
case AbsoluteIndexedX | AbsoluteX | Absolute | AbsoluteY | Indirect => 3
|
||||
case Relative | ZeroPageX | ZeroPage | ZeroPageY | IndexedZ | IndexedX | IndexedY | IndexedSY | Stack | LongIndexedY | LongIndexedZ | Immediate => 2
|
||||
case AbsoluteIndexedX | AbsoluteX | Absolute | AbsoluteY | Indirect | LongRelative | WordImmediate => 3
|
||||
case LongAbsolute | LongAbsoluteX | LongIndirect => 4
|
||||
case DoesNotExist => 0
|
||||
}
|
||||
|
||||
@ -314,10 +520,17 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
|
||||
case Implied => 1000
|
||||
case Relative | Immediate => 2000
|
||||
case ZeroPage => 2001
|
||||
case ZeroPageX | ZeroPageY => 2002
|
||||
case IndexedX | IndexedY => 2003
|
||||
case Absolute => 3000
|
||||
case AbsoluteX | AbsoluteY | Indirect => 3001
|
||||
case Stack | ZeroPageX | ZeroPageY => 2002
|
||||
case IndexedX | IndexedY | IndexedZ => 2003
|
||||
case IndexedSY | LongIndexedY | LongIndexedZ => 2004
|
||||
case WordImmediate => 3000
|
||||
case Absolute => 3001
|
||||
case AbsoluteX | AbsoluteY | Indirect => 3002
|
||||
case AbsoluteIndexedX => 3003
|
||||
case LongAbsolute => 4000
|
||||
case LongAbsoluteX => 4001
|
||||
case LongIndirect => 4002
|
||||
case TripleAbsolute => 7000
|
||||
case DoesNotExist => 1
|
||||
}
|
||||
|
||||
@ -329,6 +542,10 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
|
||||
} else if (addrMode == DoesNotExist) {
|
||||
s" ; $opcode"
|
||||
} else {
|
||||
s" $opcode ${AddrMode.addrModeToString(addrMode, parameter.toString)}"
|
||||
val op = opcode match {
|
||||
case HuSAX => "SAX"
|
||||
case _ => opcode.toString
|
||||
}
|
||||
s" $op ${AddrMode.addrModeToString(addrMode, parameter.toString)}"
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,38 @@ import millfork.error.ErrorReporting
|
||||
import millfork.node.Position
|
||||
|
||||
object State extends Enumeration {
|
||||
val A, X, Y, Z, D, C, N, V = Value
|
||||
val
|
||||
// standard 6502 8-bit registers
|
||||
A, X, Y,
|
||||
// hi bytes of registers on 65816
|
||||
AH, XH, YH,
|
||||
// extra 65816 registers
|
||||
DP, DBL, PB,
|
||||
// extra register of both 65816 (it's called the high byte of DB) and 65CE02 (it's called B)
|
||||
DBH,
|
||||
// extra 65CE02 register
|
||||
IZ,
|
||||
// 8 extra HuC6280 MMU registers
|
||||
MM,
|
||||
// standard 6502 flags
|
||||
Z, D, C, N, V,
|
||||
// extra 65816 flags; W means X flag, not to confuse with the X register
|
||||
E, M, W = Value
|
||||
|
||||
|
||||
|
||||
def isAffectedBySepRep(state: State.Value, n: Long): Boolean = {
|
||||
state match {
|
||||
case C => (n & 1) == 0
|
||||
case Z => (n & 2) == 0
|
||||
case D => (n & 8) == 0
|
||||
case W | XH | YH => (n & 0x10) == 0
|
||||
case M => (n & 0x20) == 0
|
||||
case V => (n & 0x40) == 0
|
||||
case N => (n & 0x80) == 0
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Treatment extends Enumeration {
|
||||
@ -38,14 +69,92 @@ object Opcode extends Enumeration {
|
||||
SBC, SEC, SED, SEI, STA, STX, STY,
|
||||
TAX, TAY, TXA, TXS, TSX, TYA,
|
||||
|
||||
// illegals:
|
||||
LXA, XAA, ANC, ARR, ALR, SBX,
|
||||
LAX, SAX, RLA, RRA, SLO, SRE, DCP, ISC,
|
||||
TAS, LAS, SHX, SHY, AHX,
|
||||
|
||||
// 65C02:
|
||||
STZ, PHX, PHY, PLX, PLY,
|
||||
BRA, TRB, TSB, STP, WAI,
|
||||
// BBR, BBS, RMB, SMB,
|
||||
|
||||
// 65CE02:
|
||||
CPZ, LDZ, DEZ, INZ,
|
||||
PHW,
|
||||
// DEW, INW, ASW, ROW, // aliases for DEC_W, INC_W, ASL_W, ROL_W (?)
|
||||
NEG, ASR,
|
||||
TAZ, TZA, PHZ, PLZ,
|
||||
TSY, TYS,
|
||||
TAB, TBA,
|
||||
// CLE, SEE,
|
||||
BSR,
|
||||
// MAP,
|
||||
|
||||
//HuC6280:
|
||||
CLA, CLX, CLY,
|
||||
CSH, CSL,
|
||||
SAY, SXY, HuSAX,
|
||||
// SET,
|
||||
// ST0, ST1, ST2,
|
||||
// BSR, // the same as on 65CE02
|
||||
TAM, TMA,
|
||||
// TAI, TIA, TDD, TIN, TII, // memcpy instructions
|
||||
TST,
|
||||
|
||||
//65816:
|
||||
BRL,
|
||||
COP,
|
||||
// MVN, MVP,
|
||||
PEA, PEI, PER,
|
||||
PHB, PHD, PHK, PLB, PLD, // there's no PLK for the same reason Intel removed POP CS from 80186
|
||||
REP, SEP,
|
||||
RTL,
|
||||
TCD, TDC, TSC, TCS,
|
||||
TXY, TYX, XBA,
|
||||
XCE,
|
||||
DEC_W, INC_W, ROL_W, ROR_W, ASL_W, LSR_W,
|
||||
ORA_W, AND_W, EOR_W, ADC_W, LDA_W, STA_W, CMP_W, SBC_W, STZ_W, BIT_W, TRB_W, TSB_W,
|
||||
LDX_W, LDY_W, STX_W, STY_W, CPX_W, CPY_W,
|
||||
INX_W, INY_W, DEX_W, DEY_W,
|
||||
PHA_W, PLA_W,
|
||||
PHX_W, PHY_W, PLY_W, PLX_W,
|
||||
|
||||
DISCARD_AF, DISCARD_XF, DISCARD_YF,
|
||||
LABEL = Value
|
||||
|
||||
def widen(opcode: Opcode.Value): Option[Opcode.Value] = opcode match {
|
||||
case ORA => Some(ORA_W)
|
||||
case AND => Some(AND_W)
|
||||
case EOR => Some(EOR_W)
|
||||
case ADC => Some(ADC_W)
|
||||
case SBC => Some(SBC_W)
|
||||
case CMP => Some(CMP_W)
|
||||
case LDA => Some(LDA_W)
|
||||
case STA => Some(STA_W)
|
||||
case STZ => Some(STZ_W)
|
||||
|
||||
case LDX => Some(LDX_W)
|
||||
case LDY => Some(LDY_W)
|
||||
case STX => Some(STX_W)
|
||||
case STY => Some(STY_W)
|
||||
case INX => Some(INX_W)
|
||||
case INY => Some(INX_W)
|
||||
case DEX => Some(DEX_W)
|
||||
case DEY => Some(DEY_W)
|
||||
case CPX => Some(CPX_W)
|
||||
case CPY => Some(CPY_W)
|
||||
|
||||
case INC => Some(INC_W)
|
||||
case DEC => Some(DEC_W)
|
||||
case ROL => Some(ROL_W)
|
||||
case ROR => Some(ROR_W)
|
||||
case ASL => Some(ASL_W)
|
||||
case LSR => Some(LSR_W)
|
||||
|
||||
case _ => None
|
||||
}
|
||||
|
||||
def lookup(opcode: String, position: Option[Position]): Opcode.Value = opcode.toUpperCase(Locale.ROOT) match {
|
||||
case "ADC" => ADC
|
||||
case "AHX" => AHX
|
||||
@ -56,8 +165,10 @@ object Opcode extends Enumeration {
|
||||
case "ARR" => ARR
|
||||
case "ASL" => ASL
|
||||
case "ASO" => SLO
|
||||
case "ASR" => ASR
|
||||
case "ASW" => ASL_W
|
||||
case "AXA" => AHX
|
||||
case "AXS" => SBX // TODO: could mean SAX
|
||||
case "AXS" => SBX // could mean SAX
|
||||
case "BCC" => BCC
|
||||
case "BCS" => BCS
|
||||
case "BEQ" => BEQ
|
||||
@ -67,6 +178,8 @@ object Opcode extends Enumeration {
|
||||
case "BPL" => BPL
|
||||
case "BRA" => BRA
|
||||
case "BRK" => BRK
|
||||
case "BRL" => BRL
|
||||
case "BSR" => BSR
|
||||
case "BVC" => BVC
|
||||
case "BVS" => BVS
|
||||
case "CLC" => CLC
|
||||
@ -74,18 +187,24 @@ object Opcode extends Enumeration {
|
||||
case "CLI" => CLI
|
||||
case "CLV" => CLV
|
||||
case "CMP" => CMP
|
||||
case "COP" => COP
|
||||
case "CPX" => CPX
|
||||
case "CPY" => CPY
|
||||
case "CPZ" => CPZ
|
||||
case "DCM" => DCP
|
||||
case "DCP" => DCP
|
||||
case "DEC" => DEC
|
||||
case "DEW" => DEC_W
|
||||
case "DEX" => DEX
|
||||
case "DEY" => DEY
|
||||
case "DEZ" => DEZ
|
||||
case "EOR" => EOR
|
||||
case "INC" => INC
|
||||
case "INS" => ISC
|
||||
case "INW" => INC_W
|
||||
case "INX" => INX
|
||||
case "INY" => INY
|
||||
case "INZ" => INZ
|
||||
case "ISC" => ISC
|
||||
case "JMP" => JMP
|
||||
case "JSR" => JSR
|
||||
@ -94,33 +213,48 @@ object Opcode extends Enumeration {
|
||||
case "LDA" => LDA
|
||||
case "LDX" => LDX
|
||||
case "LDY" => LDY
|
||||
case "LDZ" => LDZ
|
||||
case "LSE" => SRE
|
||||
case "LSR" => LSR
|
||||
case "LXA" => LXA
|
||||
case "NEG" => NEG
|
||||
case "NOP" => NOP
|
||||
case "OAL" => LXA
|
||||
case "ORA" => ORA
|
||||
case "PEA" => PEA
|
||||
case "PEI" => PEI
|
||||
case "PER" => PER
|
||||
case "PHA" => PHA
|
||||
case "PHB" => PHB
|
||||
case "PHD" => PHD
|
||||
case "PHK" => PHK
|
||||
case "PHP" => PHP
|
||||
case "PHW" => PHW
|
||||
case "PHX" => PHX
|
||||
case "PHY" => PHY
|
||||
case "PLA" => PLA
|
||||
case "PLB" => PLB
|
||||
case "PLD" => PLD
|
||||
case "PLP" => PLP
|
||||
case "PLX" => PLX
|
||||
case "PLY" => PLY
|
||||
case "REP" => REP
|
||||
case "RLA" => RLA
|
||||
case "ROL" => ROL
|
||||
case "ROR" => ROR
|
||||
case "ROW" => ROR_W // TODO: is this correct?
|
||||
case "RRA" => RRA
|
||||
case "RTI" => RTI
|
||||
case "RTL" => RTL
|
||||
case "RTS" => RTS
|
||||
case "SAX" => SAX // TODO: could mean SBX
|
||||
case "SAY" => SHY
|
||||
case "SAX" => SAX // could mean SBX; also, HuC6280 has another SAX that means something else
|
||||
case "SAY" => SAY // could mean SHY
|
||||
case "SBC" => SBC
|
||||
case "SBX" => SBX
|
||||
case "SEC" => SEC
|
||||
case "SED" => SED
|
||||
case "SEI" => SEI
|
||||
case "SEP" => SEP
|
||||
case "SHX" => SHX
|
||||
case "SHY" => SHY
|
||||
case "SLO" => SLO
|
||||
@ -130,18 +264,36 @@ object Opcode extends Enumeration {
|
||||
case "STX" => STX
|
||||
case "STY" => STY
|
||||
case "STZ" => STZ
|
||||
case "TAB" => TAB
|
||||
case "TAM" => TAM
|
||||
case "TAS" => TAS
|
||||
case "TAX" => TAX
|
||||
case "TAY" => TAY
|
||||
case "TAZ" => TAZ
|
||||
case "TBA" => TBA
|
||||
case "TCD" => TCD
|
||||
case "TDC" => TDC
|
||||
case "TCS" => TCS
|
||||
case "TSC" => TSC
|
||||
case "TMA" => TMA
|
||||
case "TRB" => TRB
|
||||
case "TSB" => TSB
|
||||
case "TSX" => TSX
|
||||
case "TSY" => TSY
|
||||
case "TXA" => TXA
|
||||
case "TXS" => TXS
|
||||
case "TXY" => TXY
|
||||
case "TYA" => TYA
|
||||
case "TYS" => TYS
|
||||
case "TYX" => TYX
|
||||
case "TZA" => TZA
|
||||
case "WAI" => WAI
|
||||
case "XAA" => XAA
|
||||
case "XAS" => SHX
|
||||
case "XBA" => XBA
|
||||
case "XCE" => XCE
|
||||
|
||||
// TODO: add all of those
|
||||
case _ =>
|
||||
ErrorReporting.error(s"Invalid opcode `$opcode`", position)
|
||||
LABEL
|
||||
@ -152,39 +304,51 @@ object Opcode extends Enumeration {
|
||||
object AddrMode extends Enumeration {
|
||||
val Implied,
|
||||
Immediate,
|
||||
WordImmediate,
|
||||
Relative,
|
||||
LongRelative,
|
||||
ZeroPage,
|
||||
ZeroPageX,
|
||||
ZeroPageY,
|
||||
Absolute,
|
||||
AbsoluteX,
|
||||
AbsoluteY,
|
||||
LongAbsolute,
|
||||
LongAbsoluteX,
|
||||
Indirect,
|
||||
LongIndirect,
|
||||
IndexedX,
|
||||
IndexedY,
|
||||
IndexedSY,
|
||||
IndexedZ,
|
||||
Stack,
|
||||
LongIndexedY,
|
||||
LongIndexedZ,
|
||||
AbsoluteIndexedX,
|
||||
ZeroPageIndirect,
|
||||
TripleAbsolute,
|
||||
Undecided,
|
||||
DoesNotExist = Value
|
||||
|
||||
|
||||
def argumentLength(a: AddrMode.Value): Int = a match {
|
||||
case Absolute | AbsoluteX | AbsoluteY | Indirect =>
|
||||
2
|
||||
case _ =>
|
||||
1
|
||||
}
|
||||
|
||||
def addrModeToString(am: AddrMode.Value, argument: String): String = {
|
||||
am match {
|
||||
case Implied => ""
|
||||
case Immediate => "#" + argument
|
||||
case WordImmediate => "##" + argument
|
||||
case AbsoluteX | ZeroPageX => argument + ", X"
|
||||
case AbsoluteY | ZeroPageY => argument + ", Y"
|
||||
case IndexedX | AbsoluteIndexedX => "(" + argument + ", X)"
|
||||
case Stack => argument + ", S"
|
||||
case IndexedY => "(" + argument + "), Y"
|
||||
case Indirect | ZeroPageIndirect => "(" + argument + ")"
|
||||
case IndexedSY => "(" + argument + ", S), Y"
|
||||
case IndexedZ => "(" + argument + "), Z"
|
||||
case Indirect => "(" + argument + ")"
|
||||
case LongIndexedY => "[" + argument + "], Y"
|
||||
case LongIndexedZ => "[" + argument + "], Z"
|
||||
case LongIndirect => "[" + argument + "]"
|
||||
case ZeroPage => argument // + "\t;zp"
|
||||
case LongAbsolute => "FAR " + argument
|
||||
case LongAbsoluteX => "FAR " + argument + ", X"
|
||||
case _ => argument;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import java.util.concurrent.atomic.AtomicInteger
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.Opcode._
|
||||
import millfork.assembly.OpcodeClasses._
|
||||
import millfork.assembly._
|
||||
import millfork.assembly.{opt, _}
|
||||
import millfork.env._
|
||||
|
||||
/**
|
||||
@ -745,6 +745,21 @@ object AlwaysGoodOptimizations {
|
||||
(Elidable & HasOpcodeIn(Set(CLV)) & DoesntMatterWhatItDoesWith(State.V)) ~~> (_ => Nil),
|
||||
)
|
||||
|
||||
val SimplifiableStackOperation = new RuleBasedAssemblyOptimization("Simplifiable stack operation",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & HasOpcode(TSX)) ~
|
||||
(Elidable & HasOpcode(INX)) ~
|
||||
(Elidable & HasOpcode(TXS)) ~
|
||||
(Elidable & HasOpcode(PHA) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~~> (_ => List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(STA, 0x101))),
|
||||
(Elidable & HasOpcode(TSX)) ~
|
||||
(Elidable & HasOpcode(INX)) ~
|
||||
(Elidable & HasOpcode(TXS) & DoesntMatterWhatItDoesWith(State.Z, State.N, State.A)) ~~> (_ => List(AssemblyLine.implied(PLA))),
|
||||
(Elidable & HasOpcode(TSX)) ~
|
||||
(Elidable & HasOpcode(INX)) ~
|
||||
(Elidable & HasOpcode(TXS)) ~
|
||||
(ConcernsA & Not(ConcernsStack) & Linear & DoesntMatterWhatItDoesWith(State.Z, State.N, State.A)) ~~> (code => List(code.last, AssemblyLine.implied(PLA))),
|
||||
)
|
||||
|
||||
val SimplifiableBitOpsSequence = new RuleBasedAssemblyOptimization("Simplifiable sequence of bit operations",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
(Elidable & HasOpcode(EOR) & MatchImmediate(0)) ~
|
||||
@ -909,7 +924,7 @@ object AlwaysGoodOptimizations {
|
||||
val jump = Elidable & HasOpcodeIn(Set(JMP, if (firstSet) BCS else BCC, if (zeroIfSet) BEQ else BNE)) & MatchParameter(1)
|
||||
val elseLabel = Elidable & HasOpcode(LABEL) & MatchParameter(0)
|
||||
val afterLabel = Elidable & HasOpcode(LABEL) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.C, State.N, State.V, State.Z)
|
||||
val store = Elidable & (Not(ReadsC) & Linear | HasOpcodeIn(Set(RTS, JSR, RTI)))
|
||||
val store = Elidable & (Not(ReadsC) & Linear | HasOpcodeIn(Set(RTS, JSR, RTI, RTL, BSR)))
|
||||
val secondReturn = (Elidable & HasOpcodeIn(Set(RTS, RTI) | NoopDiscardsFlags)).*.capture(6)
|
||||
val where = Where { ctx =>
|
||||
ctx.get[List[AssemblyLine]](4) == ctx.get[List[AssemblyLine]](5) ||
|
||||
@ -1366,7 +1381,7 @@ object AlwaysGoodOptimizations {
|
||||
first.head.parameter == second.head.parameter &&
|
||||
(first.head.addrMode == Immediate) == (second.head.addrMode == Immediate) && first.tail.zip(second.tail).forall(p => {
|
||||
p._1.opcode == p._2.opcode && p._1.parameter.quickSimplify == p._2.parameter.quickSimplify && (p._1.addrMode == Immediate) == (p._2.addrMode == Immediate)
|
||||
}) && (for (s1 <- first; s2 <- between) yield HelperCheckers.memoryAccessDoesntOverlap(s1.addrMode, s1.parameter, s2.addrMode, s2.parameter)).forall(identity) && {
|
||||
}) && (for (s1 <- first; s2 <- between) yield HelperCheckers.memoryAccessDoesntOverlap(s1, s2)).forall(identity) && {
|
||||
var currentD = false
|
||||
var currentCDefined = false
|
||||
var noAdditionDependency = true
|
||||
@ -1415,10 +1430,10 @@ object AlwaysGoodOptimizations {
|
||||
true
|
||||
}) ~
|
||||
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~
|
||||
(Elidable & MatchParameter(6) & HasAddrModeIn(Set(ZeroPageIndirect, IndexedY))) ~~> { (code, ctx) =>
|
||||
(Elidable & MatchParameter(6) & HasAddrModeIn(Set(IndexedZ, IndexedY))) ~~> { (code, ctx) =>
|
||||
val addr = ctx.get[Int](2)
|
||||
val last = code.last
|
||||
code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == ZeroPageIndirect) Absolute else AbsoluteY)
|
||||
code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == IndexedZ) Absolute else AbsoluteY)
|
||||
},
|
||||
|
||||
(HasOpcode(STA) & MatchA(0) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(4)) ~
|
||||
@ -1437,10 +1452,10 @@ object AlwaysGoodOptimizations {
|
||||
true
|
||||
}) ~
|
||||
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~
|
||||
(Elidable & MatchParameter(6) & HasAddrModeIn(Set(ZeroPageIndirect, IndexedY))) ~~> { (code, ctx) =>
|
||||
(Elidable & MatchParameter(6) & HasAddrModeIn(Set(IndexedZ, IndexedY))) ~~> { (code, ctx) =>
|
||||
val addr = ctx.get[Int](2)
|
||||
val last = code.last
|
||||
code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == ZeroPageIndirect) Absolute else AbsoluteY)
|
||||
code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == IndexedZ) Absolute else AbsoluteY)
|
||||
},
|
||||
)
|
||||
|
||||
|
20
src/main/scala/millfork/assembly/opt/CE02Optimizations.scala
Normal file
20
src/main/scala/millfork/assembly/opt/CE02Optimizations.scala
Normal file
@ -0,0 +1,20 @@
|
||||
package millfork.assembly.opt
|
||||
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.AssemblyLine
|
||||
import millfork.assembly.Opcode._
|
||||
import millfork.assembly.OpcodeClasses._
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object CE02Optimizations {
|
||||
|
||||
val UseAsr = new RuleBasedAssemblyOptimization("Use 65CE02 instruction ASR",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
(Elidable & HasOpcode(CMP) & HasImmediate(0x80)) ~
|
||||
(Elidable & HasOpcode(ROR)) ~~> (_ => List(AssemblyLine.implied(ASR))),
|
||||
)
|
||||
|
||||
val All: List[AssemblyOptimization] = List(UseAsr)
|
||||
}
|
@ -102,10 +102,15 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
|
||||
case AssemblyLine(_, AbsoluteY, _, _) :: xs if loaded != Some(Y) => false
|
||||
case AssemblyLine(_, ZeroPageY, _, _) :: xs if loaded != Some(Y) => false
|
||||
case AssemblyLine(_, IndexedY, _, _) :: xs if dir == Y2X || loaded != Some(Y) => false
|
||||
case AssemblyLine(_, LongIndexedY, _, _) :: xs if dir == Y2X || loaded != Some(Y) => false
|
||||
case AssemblyLine(_, AbsoluteX, _, _) :: xs if loaded != Some(X) => false
|
||||
case AssemblyLine(_, LongAbsoluteX, _, _) :: xs if loaded != Some(X) => false
|
||||
case AssemblyLine(_, ZeroPageX, _, _) :: xs if loaded != Some(X) => false
|
||||
case AssemblyLine(_, IndexedX, _, _) :: xs if dir == X2Y || loaded != Some(X) => false
|
||||
case AssemblyLine(_, AbsoluteIndexedX, _, _) :: xs if dir == X2Y => false
|
||||
case AssemblyLine(SHX | SHY | AHX, _, _, _) :: xs => false
|
||||
case AssemblyLine(TXY, _, _, e) :: xs => e && loaded == Some(X) && canOptimize(xs, dir, Some(Y))
|
||||
case AssemblyLine(TYX, _, _, e) :: xs => e && loaded == Some(Y) && canOptimize(xs, dir, Some(X))
|
||||
|
||||
// using a wrong index register for one instruction is fine
|
||||
case AssemblyLine(LDY | TAY, _, _, _) :: AssemblyLine(_, IndexedY, _, _) :: xs if dir == Y2X =>
|
||||
@ -121,13 +126,13 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
|
||||
case AssemblyLine(LDX | TAX, _, _, _) :: AssemblyLine(INX | DEX, _, _, _) :: AssemblyLine(INC | DEC | ASL | ROL | ROR | LSR | STZ, AbsoluteX | ZeroPageX, _, _) :: xs if dir == X2Y =>
|
||||
canOptimize(xs, dir, None)
|
||||
|
||||
case AssemblyLine(INC | DEC | ASL | ROL | ROR | LSR | STZ, AbsoluteX | ZeroPageX, _, _) :: xs if dir == X2Y => false
|
||||
case AssemblyLine(INC | DEC | ASL | ROL | ROR | LSR | STZ | LDZ | BIT, AbsoluteX | ZeroPageX, _, _) :: xs if dir == X2Y => false
|
||||
|
||||
case AssemblyLine(LAX, _, _, _) :: xs => false
|
||||
case AssemblyLine(JSR, _, _, _) :: xs => false // TODO
|
||||
case AssemblyLine(JMP, _, _, _) :: xs => canOptimize(xs, dir, None)
|
||||
case AssemblyLine(JSR | BSR, _, _, _) :: xs => false // TODO
|
||||
case AssemblyLine(JMP, Absolute, _, _) :: xs => canOptimize(xs, dir, None) // TODO
|
||||
case AssemblyLine(op, _, _, _) :: xs if OpcodeClasses.ShortBranching(op) => canOptimize(xs, dir, None)
|
||||
case AssemblyLine(RTS, _, _, _) :: xs => canOptimize(xs, dir, None)
|
||||
case AssemblyLine(RTS | RTL | BRA | BRL, _, _, _) :: xs => canOptimize(xs, dir, None)
|
||||
case AssemblyLine(LABEL, _, _, _) :: xs => canOptimize(xs, dir, None)
|
||||
case AssemblyLine(DISCARD_XF, _, _, _) :: xs => canOptimize(xs, dir, loaded.filter(_ != X))
|
||||
case AssemblyLine(DISCARD_YF, _, _, _) :: xs => canOptimize(xs, dir, loaded.filter(_ != Y))
|
||||
@ -137,9 +142,9 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
|
||||
(e || dir == Y2X) && canOptimize(xs, dir, Some(X))
|
||||
case AssemblyLine(TAY | LDY | PLY, _, _, e) :: xs =>
|
||||
(e || dir == X2Y) && canOptimize(xs, dir, Some(Y))
|
||||
case AssemblyLine(TXA | STX | PHX | CPX | INX | DEX, _, _, e) :: xs =>
|
||||
case AssemblyLine(TXA | STX | PHX | CPX | INX | DEX | HuSAX, _, _, e) :: xs =>
|
||||
(e || dir == Y2X) && loaded == Some(X) && canOptimize(xs, dir, Some(X))
|
||||
case AssemblyLine(TYA | STY | PHY | CPY | INY | DEY, _, _, e) :: xs =>
|
||||
case AssemblyLine(TYA | STY | PHY | CPY | INY | DEY | SAY, _, _, e) :: xs =>
|
||||
(e || dir == X2Y) && loaded == Some(Y) && canOptimize(xs, dir, Some(Y))
|
||||
|
||||
case AssemblyLine(SAX | TXS | SBX, _, _, _) :: xs => dir == Y2X && loaded == Some(X) && canOptimize(xs, dir, Some(X))
|
||||
@ -167,17 +172,20 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
|
||||
:: xs => a :: i :: b :: switchX2Y(xs)
|
||||
case (x@AssemblyLine(TAX, _, _, _)) :: xs => x.copy(opcode = TAY) :: switchX2Y(xs)
|
||||
case (x@AssemblyLine(TXA, _, _, _)) :: xs => x.copy(opcode = TYA) :: switchX2Y(xs)
|
||||
case (x@AssemblyLine(TXY | TYX, _, _, _)) :: xs => x.copy(opcode = TYX) :: switchX2Y(xs) // keep the transfer for the flags
|
||||
case (x@AssemblyLine(STX, _, _, _)) :: xs => x.copy(opcode = STY) :: switchX2Y(xs)
|
||||
case (x@AssemblyLine(LDX, _, _, _)) :: xs => x.copy(opcode = LDY) :: switchX2Y(xs)
|
||||
case (x@AssemblyLine(INX, _, _, _)) :: xs => x.copy(opcode = INY) :: switchX2Y(xs)
|
||||
case (x@AssemblyLine(DEX, _, _, _)) :: xs => x.copy(opcode = DEY) :: switchX2Y(xs)
|
||||
case (x@AssemblyLine(CPX, _, _, _)) :: xs => x.copy(opcode = CPY) :: switchX2Y(xs)
|
||||
case (x@AssemblyLine(HuSAX, _, _, _)) :: xs => x.copy(opcode = SAY) :: switchX2Y(xs)
|
||||
|
||||
case AssemblyLine(LAX, _, _, _) :: xs => ErrorReporting.fatal("Unexpected LAX")
|
||||
case AssemblyLine(TXS, _, _, _) :: xs => ErrorReporting.fatal("Unexpected TXS")
|
||||
case AssemblyLine(TSX, _, _, _) :: xs => ErrorReporting.fatal("Unexpected TSX")
|
||||
case AssemblyLine(SBX, _, _, _) :: xs => ErrorReporting.fatal("Unexpected SBX")
|
||||
case AssemblyLine(SAX, _, _, _) :: xs => ErrorReporting.fatal("Unexpected SAX")
|
||||
case AssemblyLine(SXY, _, _, _) :: xs => ErrorReporting.fatal("Unexpected SXY")
|
||||
|
||||
case (x@AssemblyLine(_, AbsoluteX, _, _)) :: xs => x.copy(addrMode = AbsoluteY) :: switchX2Y(xs)
|
||||
case (x@AssemblyLine(_, ZeroPageX, _, _)) :: xs => x.copy(addrMode = ZeroPageY) :: switchX2Y(xs)
|
||||
@ -197,11 +205,14 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
|
||||
:: xs => code.take(3) ++ switchY2X(xs)
|
||||
case (x@AssemblyLine(TAY, _, _, _)) :: xs => x.copy(opcode = TAX) :: switchY2X(xs)
|
||||
case (x@AssemblyLine(TYA, _, _, _)) :: xs => x.copy(opcode = TXA) :: switchY2X(xs)
|
||||
case (x@AssemblyLine(TYX | TXY, _, _, _)) :: xs => x.copy(opcode = TXY) :: switchY2X(xs) // keep the transfer for the flags
|
||||
case (x@AssemblyLine(STY, _, _, _)) :: xs => x.copy(opcode = STX) :: switchY2X(xs)
|
||||
case (x@AssemblyLine(LDY, _, _, _)) :: xs => x.copy(opcode = LDX) :: switchY2X(xs)
|
||||
case (x@AssemblyLine(INY, _, _, _)) :: xs => x.copy(opcode = INX) :: switchY2X(xs)
|
||||
case (x@AssemblyLine(DEY, _, _, _)) :: xs => x.copy(opcode = DEX) :: switchY2X(xs)
|
||||
case (x@AssemblyLine(CPY, _, _, _)) :: xs => x.copy(opcode = CPX) :: switchY2X(xs)
|
||||
case (x@AssemblyLine(SAY, _, _, _)) :: xs => x.copy(opcode = HuSAX) :: switchY2X(xs)
|
||||
case AssemblyLine(SXY, _, _, _) :: xs => ErrorReporting.fatal("Unexpected SXY")
|
||||
|
||||
case (x@AssemblyLine(_, AbsoluteY, _, _)) :: xs => x.copy(addrMode = AbsoluteX) :: switchY2X(xs)
|
||||
case (x@AssemblyLine(_, ZeroPageY, _, _)) :: xs => x.copy(addrMode = ZeroPageX) :: switchY2X(xs)
|
||||
|
@ -1,10 +1,10 @@
|
||||
package millfork.assembly.opt
|
||||
|
||||
import millfork.assembly.{AssemblyLine, Opcode}
|
||||
import millfork.assembly.{AssemblyLine, Opcode, State}
|
||||
import millfork.assembly.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.OpcodeClasses._
|
||||
import millfork.env.{Constant, NormalFunction}
|
||||
import millfork.env._
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
@ -15,22 +15,39 @@ object CmosOptimizations {
|
||||
|
||||
val ZeroStoreAsStz = new RuleBasedAssemblyOptimization("Zero store",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
(HasA(0) & HasOpcode(STA) & Elidable & HasAddrModeIn(StzAddrModes)) ~~> {code =>
|
||||
(HasA(0) & HasZ(0) & HasOpcode(STA) & Elidable & HasAddrModeIn(StzAddrModes)) ~~> {code =>
|
||||
code.head.copy(opcode = STZ) :: Nil
|
||||
},
|
||||
(HasX(0) & HasOpcode(STX) & Elidable & HasAddrModeIn(StzAddrModes)) ~~> {code =>
|
||||
(HasX(0) & HasZ(0) & HasOpcode(STX) & Elidable & HasAddrModeIn(StzAddrModes)) ~~> {code =>
|
||||
code.head.copy(opcode = STZ) :: Nil
|
||||
},
|
||||
(HasY(0) & HasOpcode(STY) & Elidable & HasAddrModeIn(StzAddrModes)) ~~> {code =>
|
||||
(HasY(0) & HasZ(0) & HasOpcode(STY) & Elidable & HasAddrModeIn(StzAddrModes)) ~~> {code =>
|
||||
code.head.copy(opcode = STZ) :: Nil
|
||||
},
|
||||
)
|
||||
|
||||
val SimplerBitFlipping = new RuleBasedAssemblyOptimization("Simpler bit flipping",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & HasOpcode(LDA) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(ORA) & MatchImmediate(1)) ~
|
||||
(Elidable & HasOpcode(STA) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(0) & DoesntMatterWhatItDoesWith(State.A, State.Z, State.N)) ~~> { code =>
|
||||
List(code(1).copy(opcode = Opcode.LDA), code.head.copy(opcode = TSB))
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(AND) & MatchImmediate(1)) ~
|
||||
(Elidable & HasOpcode(STA) & HasAddrModeIn(Set(Absolute, ZeroPage)) & MatchParameter(0) & DoesntMatterWhatItDoesWith(State.A, State.Z, State.N)) ~~> { code =>
|
||||
List(
|
||||
code(1).copy(opcode = Opcode.LDA, parameter = CompoundConstant(MathOperator.Exor, NumericConstant(255, 1), code(1).parameter)),
|
||||
code.head.copy(opcode = TRB))
|
||||
},
|
||||
)
|
||||
|
||||
val OptimizeZeroIndex = new RuleBasedAssemblyOptimization("Optimizing zero index",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
(Elidable & HasY(0) & HasAddrMode(IndexedY) & HasOpcodeIn(SupportsZeroPageIndirect)) ~~> (code => code.map(_.copy(addrMode = ZeroPageIndirect))),
|
||||
(Elidable & HasX(0) & HasAddrMode(IndexedX) & HasOpcodeIn(SupportsZeroPageIndirect)) ~~> (code => code.map(_.copy(addrMode = ZeroPageIndirect))),
|
||||
(Elidable & HasY(0) & HasZ(0) & HasAddrMode(IndexedY) & HasOpcodeIn(SupportsIndexedZ)) ~~> (code => code.map(_.copy(addrMode = IndexedZ))),
|
||||
(Elidable & HasX(0) & HasZ(0) & HasAddrMode(IndexedX) & HasOpcodeIn(SupportsIndexedZ)) ~~> (code => code.map(_.copy(addrMode = IndexedZ))),
|
||||
(Elidable & HasX(0) & HasZ(0) & HasAddrMode(AbsoluteIndexedX) & HasOpcode(JMP)) ~~> (code => code.map(_.copy(addrMode = Indirect))),
|
||||
)
|
||||
|
||||
val All: List[AssemblyOptimization] = List(OptimizeZeroIndex, ZeroStoreAsStz)
|
||||
val All: List[AssemblyOptimization] = List(OptimizeZeroIndex, SimplerBitFlipping, ZeroStoreAsStz)
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package millfork.assembly.opt
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly.{AssemblyLine, OpcodeClasses, State}
|
||||
import millfork.env.{Label, MemoryAddressConstant, NormalFunction, NumericConstant}
|
||||
|
||||
@ -27,6 +28,12 @@ sealed trait Status[T] {
|
||||
|
||||
object Status {
|
||||
|
||||
implicit class BoolStatusOps(val inner: Status[Boolean]) extends AnyVal {
|
||||
def withHiddenHi: Status[Boolean] = inner match {
|
||||
case SingleStatus(false) => inner
|
||||
case _ => AnyStatus()
|
||||
}
|
||||
}
|
||||
implicit class IntStatusOps(val inner: Status[Int]) extends AnyVal {
|
||||
def map[T](f: Int => T): Status[T] = inner match {
|
||||
case SingleStatus(x) => SingleStatus(f(x))
|
||||
@ -88,11 +95,14 @@ case class AnyStatus[T]() extends Status[T] {
|
||||
case class CpuStatus(a: Status[Int] = UnknownStatus(),
|
||||
x: Status[Int] = UnknownStatus(),
|
||||
y: Status[Int] = UnknownStatus(),
|
||||
iz: Status[Int] = UnknownStatus(),
|
||||
z: Status[Boolean] = UnknownStatus(),
|
||||
n: Status[Boolean] = UnknownStatus(),
|
||||
c: Status[Boolean] = UnknownStatus(),
|
||||
v: Status[Boolean] = UnknownStatus(),
|
||||
d: Status[Boolean] = UnknownStatus(),
|
||||
m: Status[Boolean] = UnknownStatus(),
|
||||
w: Status[Boolean] = UnknownStatus()
|
||||
) {
|
||||
|
||||
override def toString: String = s"A=$a,X=$x,Y=$y,Z=$z,N=$n,C=$c,V=$v,D=$d"
|
||||
@ -107,47 +117,65 @@ case class CpuStatus(a: Status[Int] = UnknownStatus(),
|
||||
a = this.a ~ that.a,
|
||||
x = this.x ~ that.x,
|
||||
y = this.y ~ that.y,
|
||||
iz = this.iz ~ that.iz,
|
||||
z = this.z ~ that.z,
|
||||
n = this.n ~ that.n,
|
||||
c = this.c ~ that.c,
|
||||
v = this.v ~ that.v,
|
||||
d = this.d ~ that.d,
|
||||
m = this.m ~ that.m,
|
||||
w = this.w ~ that.w,
|
||||
)
|
||||
|
||||
def hasClear(state: State.Value): Boolean = state match {
|
||||
case State.A => a.contains(0)
|
||||
case State.X => x.contains(0)
|
||||
case State.Y => y.contains(0)
|
||||
case State.IZ => iz.contains(0)
|
||||
case State.Z => z.contains(false)
|
||||
case State.N => n.contains(false)
|
||||
case State.C => c.contains(false)
|
||||
case State.V => v.contains(false)
|
||||
case State.D => d.contains(false)
|
||||
case State.M => m.contains(false)
|
||||
case State.W => w.contains(false)
|
||||
case _ => false
|
||||
}
|
||||
|
||||
def hasSet(state: State.Value): Boolean = state match {
|
||||
case State.A => false
|
||||
case State.X => false
|
||||
case State.Y => false
|
||||
case State.IZ => false
|
||||
case State.Z => z.contains(true)
|
||||
case State.N => n.contains(true)
|
||||
case State.C => c.contains(true)
|
||||
case State.V => v.contains(true)
|
||||
case State.D => d.contains(true)
|
||||
case State.M => m.contains(true)
|
||||
case State.W => w.contains(true)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
object CoarseFlowAnalyzer {
|
||||
//noinspection RedundantNewCaseClass
|
||||
def analyze(f: NormalFunction, code: List[AssemblyLine]): List[CpuStatus] = {
|
||||
val flagArray = Array.fill[CpuStatus](code.length)(CpuStatus())
|
||||
def analyze(f: NormalFunction, code: List[AssemblyLine], compilationOptions: CompilationOptions): List[CpuStatus] = {
|
||||
val emptyIz: Status[Int] = if (compilationOptions.flag(CompilationFlag.Emit65CE02Opcodes)) UnknownStatus() else SingleStatus(0)
|
||||
val emptyStatus = CpuStatus(iz = emptyIz)
|
||||
val flagArray = Array.fill[CpuStatus](code.length)(emptyStatus)
|
||||
val codeArray = code.toArray
|
||||
val initialStatus = new CpuStatus(d = SingleStatus(false))
|
||||
val initialStatus = new CpuStatus(
|
||||
d = SingleStatus(false),
|
||||
m = SingleStatus(true),
|
||||
w = SingleStatus(true),
|
||||
iz = emptyIz
|
||||
)
|
||||
|
||||
var changed = true
|
||||
while (changed) {
|
||||
changed = false
|
||||
var currentStatus: CpuStatus = if (f.interrupt) CpuStatus() else initialStatus
|
||||
var currentStatus: CpuStatus = if (f.interrupt) emptyStatus else initialStatus
|
||||
for (i <- codeArray.indices) {
|
||||
import millfork.assembly.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
@ -161,7 +189,7 @@ object CoarseFlowAnalyzer {
|
||||
currentStatus = codeArray.indices.flatMap(j => codeArray(j) match {
|
||||
case AssemblyLine(_, _, MemoryAddressConstant(Label(L)), _) => Some(flagArray(j))
|
||||
case _ => None
|
||||
}).fold(CpuStatus())(_ ~ _)
|
||||
}).fold(emptyStatus)(_ ~ _)
|
||||
|
||||
case AssemblyLine(BCC, _, _, _) =>
|
||||
currentStatus = currentStatus.copy(c = currentStatus.c ~ SingleStatus(true))
|
||||
@ -191,6 +219,25 @@ object CoarseFlowAnalyzer {
|
||||
case AssemblyLine(CLV, _, _, _) =>
|
||||
currentStatus = currentStatus.copy(v = SingleStatus(false))
|
||||
|
||||
case AssemblyLine(REP, Immediate, NumericConstant(nn, _), _) =>
|
||||
if ((nn & 1) != 0) currentStatus = currentStatus.copy(c = SingleStatus(false))
|
||||
if ((nn & 2) != 0) currentStatus = currentStatus.copy(z = SingleStatus(false))
|
||||
if ((nn & 8) != 0) currentStatus = currentStatus.copy(d = SingleStatus(false))
|
||||
if ((nn & 0x10) != 0) currentStatus = currentStatus.copy(w = SingleStatus(false))
|
||||
if ((nn & 0x20) != 0) currentStatus = currentStatus.copy(m = SingleStatus(false))
|
||||
if ((nn & 0x40) != 0) currentStatus = currentStatus.copy(v = SingleStatus(false))
|
||||
if ((nn & 0x80) != 0) currentStatus = currentStatus.copy(n = SingleStatus(false))
|
||||
case AssemblyLine(SEP, Immediate, NumericConstant(nn, _), _) =>
|
||||
if ((nn & 1) != 0) currentStatus = currentStatus.copy(c = SingleStatus(true))
|
||||
if ((nn & 2) != 0) currentStatus = currentStatus.copy(z = SingleStatus(true))
|
||||
if ((nn & 8) != 0) currentStatus = currentStatus.copy(d = SingleStatus(true))
|
||||
if ((nn & 0x10) != 0) currentStatus = currentStatus.copy(w = SingleStatus(true))
|
||||
if ((nn & 0x20) != 0) currentStatus = currentStatus.copy(m = SingleStatus(true))
|
||||
if ((nn & 0x40) != 0) currentStatus = currentStatus.copy(v = SingleStatus(true))
|
||||
if ((nn & 0x80) != 0) currentStatus = currentStatus.copy(n = SingleStatus(true))
|
||||
case AssemblyLine(XCE, _, _, _) =>
|
||||
currentStatus = currentStatus.copy(c = AnyStatus(), m = AnyStatus(), x = AnyStatus())
|
||||
|
||||
case AssemblyLine(JSR, _, _, _) =>
|
||||
currentStatus = initialStatus
|
||||
|
||||
@ -203,9 +250,9 @@ object CoarseFlowAnalyzer {
|
||||
case AssemblyLine(LDA, Immediate, NumericConstant(nn, _), _) =>
|
||||
val n = nn.toInt & 0xff
|
||||
currentStatus = currentStatus.nz(n).copy(a = SingleStatus(n))
|
||||
case AssemblyLine(LAX, Immediate, NumericConstant(nn, _), _) =>
|
||||
case AssemblyLine(LDZ, Immediate, NumericConstant(nn, _), _) =>
|
||||
val n = nn.toInt & 0xff
|
||||
currentStatus = currentStatus.nz(n).copy(a = SingleStatus(n), x = SingleStatus(n))
|
||||
currentStatus = currentStatus.nz(n).copy(iz = SingleStatus(n))
|
||||
|
||||
case AssemblyLine(ADC, Immediate, NumericConstant(nn, _), _) =>
|
||||
val n = nn.toInt
|
||||
@ -231,6 +278,21 @@ object CoarseFlowAnalyzer {
|
||||
c = currentStatus.a.map(i => (i & n & 1) == 0),
|
||||
a = currentStatus.a.map(i => (i & n & 0xff) >> 1))
|
||||
|
||||
|
||||
case AssemblyLine(ADC_W, WordImmediate, NumericConstant(nn, _), _) =>
|
||||
val n = nn.toInt & 0xff
|
||||
val newA = currentStatus.a.adc(n, currentStatus.c, currentStatus.d)
|
||||
currentStatus = currentStatus.copy(n = AnyStatus(), z = newA.z().withHiddenHi, a = newA, c = AnyStatus(), v = AnyStatus())
|
||||
case AssemblyLine(EOR_W, WordImmediate, NumericConstant(nn, _), _) =>
|
||||
val n = nn.toInt & 0xff
|
||||
currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.a.z(_ ^ n).withHiddenHi, a = currentStatus.a.map(_ ^ n))
|
||||
case AssemblyLine(AND_W, WordImmediate, NumericConstant(nn, _), _) =>
|
||||
val n = nn.toInt & 0xff
|
||||
currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.a.z(_ & n).withHiddenHi, a = currentStatus.a.map(_ & n))
|
||||
case AssemblyLine(ORA_W, WordImmediate, NumericConstant(nn, _), _) =>
|
||||
val n = nn.toInt & 0xff
|
||||
currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.a.z(_ | n).withHiddenHi, a = currentStatus.a.map(_ | n))
|
||||
|
||||
case AssemblyLine(INX, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(n = currentStatus.x.n(_ + 1), z = currentStatus.x.z(_ + 1), x = currentStatus.x.map(v => (v + 1) & 0xff))
|
||||
case AssemblyLine(DEX, Implied, _, _) =>
|
||||
@ -243,6 +305,22 @@ object CoarseFlowAnalyzer {
|
||||
currentStatus = currentStatus.copy(n = currentStatus.a.n(_ + 1), z = currentStatus.a.z(_ + 1), a = currentStatus.a.map(v => (v + 1) & 0xff))
|
||||
case AssemblyLine(DEC, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(n = currentStatus.a.n(_ - 1), z = currentStatus.a.z(_ - 1), a = currentStatus.a.map(v => (v - 1) & 0xff))
|
||||
case AssemblyLine(NEG, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(n = currentStatus.a.n(256 - _), z = currentStatus.a.z(256 - _), a = currentStatus.a.map(v => (256 - v) & 0xff))
|
||||
|
||||
case AssemblyLine(INX_W, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.x.z(_ + 1).withHiddenHi, x = currentStatus.x.map(v => (v + 1) & 0xff))
|
||||
case AssemblyLine(DEX_W, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.x.z(_ - 1).withHiddenHi, x = currentStatus.x.map(v => (v - 1) & 0xff))
|
||||
case AssemblyLine(INY_W, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.y.z(_ + 1).withHiddenHi, y = currentStatus.y.map(v => (v + 1) & 0xff))
|
||||
case AssemblyLine(DEY_W, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.y.z(_ - 1).withHiddenHi, y = currentStatus.y.map(v => (v - 1) & 0xff))
|
||||
case AssemblyLine(INC_W, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.a.z(_ + 1).withHiddenHi, a = currentStatus.a.map(v => (v + 1) & 0xff))
|
||||
case AssemblyLine(DEC_W, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(n = AnyStatus(), z = currentStatus.a.z(_ - 1).withHiddenHi, a = currentStatus.a.map(v => (v - 1) & 0xff))
|
||||
|
||||
case AssemblyLine(TAX, _, _, _) =>
|
||||
currentStatus = currentStatus.copy(x = currentStatus.a, n = currentStatus.a.n(), z = currentStatus.a.z())
|
||||
case AssemblyLine(TXA, _, _, _) =>
|
||||
@ -251,11 +329,22 @@ object CoarseFlowAnalyzer {
|
||||
currentStatus = currentStatus.copy(y = currentStatus.a, n = currentStatus.a.n(), z = currentStatus.a.z())
|
||||
case AssemblyLine(TYA, _, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = currentStatus.y, n = currentStatus.y.n(), z = currentStatus.y.z())
|
||||
case AssemblyLine(TAZ, _, _, _) =>
|
||||
currentStatus = currentStatus.copy(iz = currentStatus.a, n = currentStatus.a.n(), z = currentStatus.a.z())
|
||||
case AssemblyLine(TZA, _, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = currentStatus.iz, n = currentStatus.iz.n(), z = currentStatus.iz.z())
|
||||
|
||||
case AssemblyLine(ASL, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = currentStatus.a.map(v => (v << 1) & 0xff), n = currentStatus.a.n(_ << 1), z = currentStatus.a.z(_ << 1),c = currentStatus.a.map(a => a.&(0xff).!=(0)))
|
||||
case AssemblyLine(LSR, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = currentStatus.a.map(a => a.>>(1).&(0x7f)), n = currentStatus.a.n(a => a.>>(1).&(0x7f)), z = currentStatus.a.z(a => a.>>(1).&(0x7f)),c = currentStatus.a.map(a => a.&(1).!=(0)))
|
||||
case AssemblyLine(ASR, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = currentStatus.a.map(a => a.toByte.>>(1).&(0xff)), n = currentStatus.a.n(a => a.toByte.>>(1).&(0xff)), z = currentStatus.a.z(a => a.toByte.>>(1).&(0xff)),c = currentStatus.a.map(a => a.&(1).!=(0)))
|
||||
|
||||
case AssemblyLine(ASL_W, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = currentStatus.a.map(v => (v << 1) & 0xff), n = AnyStatus(), z = currentStatus.a.z(_ << 1).withHiddenHi, c = AnyStatus())
|
||||
case AssemblyLine(LSR_W, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = AnyStatus(), n = AnyStatus(), z = currentStatus.a.z(a => a.>>(1).&(0x7f)).withHiddenHi, c = currentStatus.a.map(a => a.&(1).!=(0)))
|
||||
|
||||
case AssemblyLine(opcode, addrMode, parameter, _) =>
|
||||
if (OpcodeClasses.ChangesX(opcode)) currentStatus = currentStatus.copy(x = AnyStatus())
|
||||
|
@ -39,7 +39,7 @@ object FlowAnalyzer {
|
||||
if (options.flag(CompilationFlag.DetailedFlowAnalysis)) {
|
||||
() => QuantumFlowAnalyzer.analyze(f, code).map(_.collapse)
|
||||
} else {
|
||||
() => CoarseFlowAnalyzer.analyze(f, code)
|
||||
() => CoarseFlowAnalyzer.analyze(f, code, options)
|
||||
}
|
||||
case FlowInfoRequirement.BackwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement =>
|
||||
() => List.fill(code.size)(EmptyCpuStatus)
|
||||
|
@ -0,0 +1,21 @@
|
||||
package millfork.assembly.opt
|
||||
|
||||
import millfork.assembly.AssemblyLine
|
||||
import millfork.assembly.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.env.NumericConstant
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object HudsonOptimizations {
|
||||
|
||||
val All: List[AssemblyOptimization] = List()
|
||||
|
||||
def removeLoadZero(code: List[AssemblyLine]): List[AssemblyLine] = code.map{
|
||||
case AssemblyLine(LDA, Immediate, NumericConstant(0, _), true) => AssemblyLine.implied(CLA)
|
||||
case AssemblyLine(LDX, Immediate, NumericConstant(0, _), true) => AssemblyLine.implied(CLX)
|
||||
case AssemblyLine(LDY, Immediate, NumericConstant(0, _), true) => AssemblyLine.implied(CLY)
|
||||
case l => l
|
||||
}
|
||||
}
|
@ -90,7 +90,7 @@ object LoopUnrolling {
|
||||
(Elidable & HasOpcode(LDX) & MatchNumericImmediate(Start)).capture(Initialization) ~
|
||||
(Elidable & HasOpcode(BEQ) & MatchParameter(Skip)) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
|
||||
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI))) & Not(ChangesX)).*.capture(Body) ~
|
||||
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL))) & Not(ChangesX)).*.capture(Body) ~
|
||||
(Elidable & HasOpcodeIn(Set(DEX, INX))).capture(Step)
|
||||
).capture(BodyWithStep) ~
|
||||
(Elidable & HasOpcode(CPX) & MatchNumericImmediate(End)).? ~
|
||||
@ -104,7 +104,7 @@ object LoopUnrolling {
|
||||
},
|
||||
(Elidable & HasOpcode(LDX) & MatchNumericImmediate(Start)).capture(Initialization) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
|
||||
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI))) & Not(ChangesX)).*.capture(Body) ~
|
||||
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL))) & Not(ChangesX)).*.capture(Body) ~
|
||||
(Elidable & HasOpcodeIn(Set(DEX, INX))).capture(Step)
|
||||
).capture(BodyWithStep) ~
|
||||
(Elidable & HasOpcode(CPX) & MatchNumericImmediate(End)).? ~
|
||||
@ -118,7 +118,7 @@ object LoopUnrolling {
|
||||
(Elidable & HasOpcode(LDY) & MatchNumericImmediate(Start)).capture(Initialization) ~
|
||||
(Elidable & HasOpcode(BEQ) & MatchParameter(Skip)) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
|
||||
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI))) & Not(ChangesY)).*.capture(Body) ~
|
||||
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL))) & Not(ChangesY)).*.capture(Body) ~
|
||||
(Elidable & HasOpcodeIn(Set(DEY, INY))).capture(Step)
|
||||
).capture(BodyWithStep) ~
|
||||
(Elidable & HasOpcode(CPY) & MatchNumericImmediate(End)).? ~
|
||||
@ -132,7 +132,7 @@ object LoopUnrolling {
|
||||
},
|
||||
(Elidable & HasOpcode(LDY) & MatchNumericImmediate(Start)).capture(Initialization) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~
|
||||
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI))) & Not(ChangesY)).*.capture(Body) ~
|
||||
((Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL))) & Not(ChangesY)).*.capture(Body) ~
|
||||
(Elidable & HasOpcodeIn(Set(DEY, INY))).capture(Step)
|
||||
).capture(BodyWithStep) ~
|
||||
(Elidable & HasOpcode(CPY) & MatchNumericImmediate(End)).? ~
|
||||
|
@ -1,7 +1,9 @@
|
||||
package millfork.assembly.opt
|
||||
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.{AssemblyLine, Opcode, OpcodeClasses, State}
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.Register
|
||||
|
||||
import scala.collection.immutable
|
||||
@ -33,13 +35,17 @@ case object UnknownImportance extends Importance {
|
||||
|
||||
//noinspection RedundantNewCaseClass
|
||||
case class CpuImportance(a: Importance = UnknownImportance,
|
||||
ah: Importance = UnknownImportance,
|
||||
x: Importance = UnknownImportance,
|
||||
y: Importance = UnknownImportance,
|
||||
iz: Importance = UnknownImportance,
|
||||
n: Importance = UnknownImportance,
|
||||
z: Importance = UnknownImportance,
|
||||
v: Importance = UnknownImportance,
|
||||
c: Importance = UnknownImportance,
|
||||
d: Importance = UnknownImportance,
|
||||
m: Importance = UnknownImportance,
|
||||
w: Importance = UnknownImportance,
|
||||
) {
|
||||
override def toString: String = s"A=$a,X=$x,Y=$y,Z=$z,N=$n,C=$c,V=$v,D=$d"
|
||||
|
||||
@ -47,38 +53,48 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
||||
a = this.a ~ that.a,
|
||||
x = this.x ~ that.x,
|
||||
y = this.y ~ that.y,
|
||||
iz = this.iz ~ that.iz,
|
||||
z = this.z ~ that.z,
|
||||
n = this.n ~ that.n,
|
||||
c = this.c ~ that.c,
|
||||
v = this.v ~ that.v,
|
||||
d = this.d ~ that.d,
|
||||
m = this.m ~ that.m,
|
||||
w = this.w ~ that.w,
|
||||
)
|
||||
|
||||
def isUnimportant(state: State.Value): Boolean = state match {
|
||||
// UnknownImportance is usually an effect of unreachable code
|
||||
case State.A => a != Important
|
||||
case State.AH => ah != Important
|
||||
case State.X => x != Important
|
||||
case State.Y => y != Important
|
||||
case State.IZ => iz != Important
|
||||
case State.Z => z != Important
|
||||
case State.N => n != Important
|
||||
case State.C => c != Important
|
||||
case State.V => v != Important
|
||||
case State.D => d != Important
|
||||
case State.M => m != Important
|
||||
case State.W => w != Important
|
||||
}
|
||||
}
|
||||
|
||||
object ReverseFlowAnalyzer {
|
||||
|
||||
val aluAdders = Set(Opcode.ADC, Opcode.SBC, Opcode.ISC, Opcode.DCP)
|
||||
val aluAdders = Set(Opcode.ADC, Opcode.SBC, Opcode.ISC, Opcode.DCP, Opcode.ADC_W, Opcode.SBC_W)
|
||||
|
||||
//noinspection RedundantNewCaseClass
|
||||
def analyze(f: NormalFunction, code: List[AssemblyLine]): List[CpuImportance] = {
|
||||
val importanceArray = Array.fill[CpuImportance](code.length)(new CpuImportance())
|
||||
val codeArray = code.toArray
|
||||
val initialStatus = new CpuStatus(d = SingleStatus(false))
|
||||
|
||||
var changed = true
|
||||
val finalImportance = new CpuImportance(a = Important, x = Important, y = Important, c = Important, v = Important, d = Important, z = Important, n = Important)
|
||||
val finalImportance = new CpuImportance(
|
||||
a = Important, ah = Important,
|
||||
x = Important, y = Important, iz = Important,
|
||||
c = Important, v = Important, d = Important, z = Important, n = Important,
|
||||
m = Important, w = Important)
|
||||
changed = true
|
||||
while (changed) {
|
||||
changed = false
|
||||
@ -91,7 +107,7 @@ object ReverseFlowAnalyzer {
|
||||
importanceArray(i) = currentImportance
|
||||
}
|
||||
codeArray(i) match {
|
||||
case AssemblyLine(opcode, Relative, MemoryAddressConstant(Label(l)), _) if OpcodeClasses.ShortBranching(opcode) =>
|
||||
case AssemblyLine(opcode, Relative | LongRelative, MemoryAddressConstant(Label(l)), _) if OpcodeClasses.ShortConditionalBranching(opcode) =>
|
||||
val L = l
|
||||
val labelIndex = codeArray.indexWhere {
|
||||
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(L)), _) => true
|
||||
@ -101,21 +117,27 @@ object ReverseFlowAnalyzer {
|
||||
case _ =>
|
||||
}
|
||||
codeArray(i) match {
|
||||
case AssemblyLine(JSR | JMP, Absolute, MemoryAddressConstant(fun:FunctionInMemory), _) =>
|
||||
case AssemblyLine(JSR | JMP, Absolute | LongAbsolute, MemoryAddressConstant(fun:FunctionInMemory), _) =>
|
||||
var result = new CpuImportance(
|
||||
a = Unimportant,
|
||||
ah = Unimportant,
|
||||
x = Unimportant,
|
||||
y = Unimportant,
|
||||
iz = Unimportant,
|
||||
z = Unimportant,
|
||||
n = Unimportant,
|
||||
c = Unimportant,
|
||||
v = Unimportant,
|
||||
d = Important)
|
||||
d = Important,
|
||||
m = Important,
|
||||
w = Important)
|
||||
fun.params match {
|
||||
case AssemblyParamSignature(params) =>
|
||||
params.foreach(_.variable match {
|
||||
case RegisterVariable(Register.A, _) =>
|
||||
result = result.copy(a = Important)
|
||||
case RegisterVariable(Register.AW, _) =>
|
||||
result = result.copy(a = Important, ah = Important)
|
||||
case RegisterVariable(Register.X, _) =>
|
||||
result = result.copy(x = Important)
|
||||
case RegisterVariable(Register.Y, _) =>
|
||||
@ -131,16 +153,16 @@ object ReverseFlowAnalyzer {
|
||||
case _ =>
|
||||
}
|
||||
currentImportance = result
|
||||
case AssemblyLine(JSR | BRK, _, _, _) =>
|
||||
case AssemblyLine(JSR | BRK | COP, _, _, _) =>
|
||||
currentImportance = finalImportance
|
||||
case AssemblyLine(JMP | BRA, Absolute | Relative, MemoryAddressConstant(Label(l)), _) =>
|
||||
case AssemblyLine(JMP | BRA, Absolute | Relative | LongAbsolute | LongRelative, MemoryAddressConstant(Label(l)), _) =>
|
||||
val L = l
|
||||
val labelIndex = codeArray.indexWhere {
|
||||
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(L)), _) => true
|
||||
case _ => false
|
||||
}
|
||||
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex)
|
||||
case AssemblyLine(JMP, Indirect | AbsoluteIndexedX, _, _) =>
|
||||
case AssemblyLine(JMP, Indirect | AbsoluteIndexedX | LongIndirect, _, _) =>
|
||||
currentImportance = finalImportance
|
||||
case AssemblyLine(BNE | BEQ, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(z = Important)
|
||||
@ -148,16 +170,50 @@ object ReverseFlowAnalyzer {
|
||||
currentImportance = currentImportance.copy(n = Important)
|
||||
case AssemblyLine(SED | CLD, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(d = Unimportant)
|
||||
case AssemblyLine(RTS, _, _, _) =>
|
||||
case AssemblyLine(RTS | RTL, _, _, _) =>
|
||||
currentImportance = finalImportance
|
||||
case AssemblyLine(TAX, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(a = currentImportance.x ~ currentImportance.a ~ currentImportance.n ~ currentImportance.z, x = Unimportant, n = Unimportant, z = Unimportant, m = Important, w = Important)
|
||||
case AssemblyLine(TAY, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(a = currentImportance.y ~ currentImportance.a ~ currentImportance.n ~ currentImportance.z, y = Unimportant, n = Unimportant, z = Unimportant, m = Important, w = Important)
|
||||
case AssemblyLine(TXA, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(x = currentImportance.a ~ currentImportance.x ~ currentImportance.n ~ currentImportance.z, a = Unimportant, n = Unimportant, z = Unimportant, m = Important, w = Important)
|
||||
case AssemblyLine(TYA, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(y = currentImportance.a ~ currentImportance.y ~ currentImportance.n ~ currentImportance.z, a = Unimportant, n = Unimportant, z = Unimportant, m = Important, w = Important)
|
||||
case AssemblyLine(TAZ, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(a = currentImportance.iz ~ currentImportance.a ~ currentImportance.n ~ currentImportance.z, iz = Unimportant, n = Unimportant, z = Unimportant, m = Important, w = Important)
|
||||
case AssemblyLine(TZA, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(iz = currentImportance.a ~ currentImportance.iz ~ currentImportance.n ~ currentImportance.z, a = Unimportant, n = Unimportant, z = Unimportant, m = Important, w = Important)
|
||||
case AssemblyLine(TXY, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(x = currentImportance.y ~ currentImportance.x ~ currentImportance.n ~ currentImportance.z, y = Unimportant, n = Unimportant, z = Unimportant, m = Important, w = Important)
|
||||
case AssemblyLine(TYX, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(y = currentImportance.x ~ currentImportance.y ~ currentImportance.n ~ currentImportance.z, x = Unimportant, n = Unimportant, z = Unimportant, m = Important, w = Important)
|
||||
case AssemblyLine(HuSAX, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(a = currentImportance.x, x = currentImportance.a, m = Important, w = Important)
|
||||
case AssemblyLine(SAY, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(y = currentImportance.a, a = currentImportance.y, m = Important, w = Important)
|
||||
case AssemblyLine(SXY, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(y = currentImportance.x, x = currentImportance.y, m = Important, w = Important)
|
||||
case AssemblyLine(RTI, _, _, _) =>
|
||||
currentImportance = new CpuImportance(a = Unimportant, x = Unimportant, y = Unimportant, z = Unimportant, n = Unimportant, c = Unimportant, v = Unimportant, d = Unimportant)
|
||||
currentImportance = new CpuImportance(
|
||||
a = Unimportant, ah = Unimportant,
|
||||
x = Unimportant, y = Unimportant, iz = Unimportant,
|
||||
z = Unimportant, n = Unimportant, c = Unimportant, v = Unimportant, d = Unimportant,
|
||||
m = Unimportant, w = Unimportant)
|
||||
case AssemblyLine(DISCARD_XF, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(x = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant)
|
||||
case AssemblyLine(DISCARD_YF, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(y = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant)
|
||||
currentImportance = currentImportance.copy(y = Unimportant, iz = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant)
|
||||
case AssemblyLine(DISCARD_AF, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(a = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant)
|
||||
case AssemblyLine(REP | SEP, _, NumericConstant(n, _), _) =>
|
||||
if ((n & 1) != 0) currentImportance = currentImportance.copy(c = Unimportant)
|
||||
if ((n & 2) != 0) currentImportance = currentImportance.copy(z = Unimportant)
|
||||
if ((n & 8) != 0) currentImportance = currentImportance.copy(d = Unimportant)
|
||||
if ((n & 0x10) != 0) currentImportance = currentImportance.copy(w = Unimportant)
|
||||
if ((n & 0x20) != 0) currentImportance = currentImportance.copy(m = Unimportant)
|
||||
if ((n & 0x40) != 0) currentImportance = currentImportance.copy(v = Unimportant)
|
||||
if ((n & 0x80) != 0) currentImportance = currentImportance.copy(n = Unimportant)
|
||||
case AssemblyLine(opcode, addrMode, _, _) =>
|
||||
val reallyIgnoreC =
|
||||
currentImportance.c == Unimportant &&
|
||||
@ -177,18 +233,27 @@ object ReverseFlowAnalyzer {
|
||||
if (OpcodeClasses.ChangesV(opcode)) currentImportance = currentImportance.copy(v = Unimportant)
|
||||
if (OpcodeClasses.ChangesNAndZ(opcode)) currentImportance = currentImportance.copy(n = Unimportant, z = Unimportant)
|
||||
if (OpcodeClasses.OverwritesA(opcode)) currentImportance = currentImportance.copy(a = Unimportant)
|
||||
if (OpcodeClasses.OverwritesAH(opcode)) currentImportance = currentImportance.copy(ah = Unimportant)
|
||||
if (OpcodeClasses.OverwritesX(opcode)) currentImportance = currentImportance.copy(x = Unimportant)
|
||||
if (OpcodeClasses.OverwritesY(opcode)) currentImportance = currentImportance.copy(y = Unimportant)
|
||||
if (OpcodeClasses.OverwritesIZ(opcode)) currentImportance = currentImportance.copy(iz = Unimportant)
|
||||
if (OpcodeClasses.ReadsC(opcode) && !reallyIgnoreC) currentImportance = currentImportance.copy(c = Important)
|
||||
if (OpcodeClasses.ReadsD(opcode)) currentImportance = currentImportance.copy(d = Important)
|
||||
if (OpcodeClasses.ReadsV(opcode)) currentImportance = currentImportance.copy(v = Important)
|
||||
if (OpcodeClasses.ReadsXAlways(opcode)) currentImportance = currentImportance.copy(x = Important)
|
||||
if (OpcodeClasses.ReadsYAlways(opcode)) currentImportance = currentImportance.copy(y = Important)
|
||||
if (OpcodeClasses.ReadsIZAlways(opcode)) currentImportance = currentImportance.copy(iz = Important)
|
||||
if (OpcodeClasses.ReadsM(opcode)) currentImportance = currentImportance.copy(m = Important)
|
||||
if (OpcodeClasses.ReadsW(opcode)) currentImportance = currentImportance.copy(w = Important)
|
||||
if (OpcodeClasses.ReadsAAlways(opcode) && !reallyIgnoreA) currentImportance = currentImportance.copy(a = Important)
|
||||
if (OpcodeClasses.ReadsAHAlways(opcode)) currentImportance = currentImportance.copy(ah = Important)
|
||||
if (OpcodeClasses.ReadsAIfImplied(opcode) && addrMode == Implied) currentImportance = currentImportance.copy(a = Important)
|
||||
if (addrMode == AbsoluteX || addrMode == IndexedX || addrMode == ZeroPageX || addrMode == AbsoluteIndexedX)
|
||||
if (OpcodeClasses.ReadsAHIfImplied(opcode) && addrMode == Implied) currentImportance = currentImportance.copy(ah = Important)
|
||||
if (addrMode == AbsoluteX || addrMode == LongAbsoluteX || addrMode == IndexedX || addrMode == ZeroPageX || addrMode == AbsoluteIndexedX)
|
||||
currentImportance = currentImportance.copy(x = Important)
|
||||
if (addrMode == AbsoluteY || addrMode == IndexedY || addrMode == ZeroPageY)
|
||||
if (addrMode == IndexedZ /*|| addrMode == LongIndexedZ*/)
|
||||
currentImportance = currentImportance.copy(iz = Important)
|
||||
if (addrMode == AbsoluteY || addrMode == IndexedY || addrMode == ZeroPageY || addrMode == LongIndexedY || addrMode == IndexedSY)
|
||||
currentImportance = currentImportance.copy(y = Important)
|
||||
}
|
||||
}
|
||||
|
@ -142,15 +142,16 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions) {
|
||||
val labels = mutable.Set[String]()
|
||||
val jumps = mutable.Set[String]()
|
||||
get[List[AssemblyLine]](i).foreach {
|
||||
case AssemblyLine(Opcode.RTS | Opcode.RTI | Opcode.BRK, _, _, _) =>
|
||||
// JSR and BSR are allowed
|
||||
case AssemblyLine(Opcode.RTS | Opcode.RTI | Opcode.RTL | Opcode.BRK, _, _, _) =>
|
||||
return false
|
||||
case AssemblyLine(Opcode.JMP, AddrMode.Indirect, _, _) =>
|
||||
case AssemblyLine(Opcode.JMP, AddrMode.Indirect | AddrMode.AbsoluteIndexedX | AddrMode.LongIndirect, _, _) =>
|
||||
return false
|
||||
case AssemblyLine(Opcode.LABEL, _, MemoryAddressConstant(Label(l)), _) =>
|
||||
labels += l
|
||||
case AssemblyLine(Opcode.JMP, AddrMode.Absolute, MemoryAddressConstant(Label(l)), _) =>
|
||||
jumps += l
|
||||
case AssemblyLine(Opcode.JMP, AddrMode.Absolute, _, _) =>
|
||||
case AssemblyLine(Opcode.JMP, AddrMode.Absolute | AddrMode.LongAbsolute, _, _) =>
|
||||
return false
|
||||
case AssemblyLine(_, AddrMode.Relative, MemoryAddressConstant(Label(l)), _) =>
|
||||
jumps += l
|
||||
@ -191,25 +192,29 @@ trait AssemblyPattern {
|
||||
}
|
||||
object HelperCheckers {
|
||||
import AddrMode._
|
||||
private val badAddrModes = Set(IndexedX, IndexedY, ZeroPageIndirect, AbsoluteIndexedX)
|
||||
private val goodAddrModes = Set(Implied, Immediate, Relative)
|
||||
private val badAddrModes = Set(IndexedX, IndexedY, IndexedZ, LongIndexedY, LongIndexedZ, IndexedSY, Indirect, TripleAbsolute, Stack)
|
||||
private val goodAddrModes = Set(Implied, Immediate, WordImmediate, Relative, LongRelative)
|
||||
|
||||
def memoryAccessDoesntOverlap(l1: AssemblyLine, l2: AssemblyLine): Boolean = {
|
||||
memoryAccessDoesntOverlap(l1.addrMode, l1.parameter, l2.addrMode, l2.parameter)
|
||||
}
|
||||
|
||||
def memoryAccessDoesntOverlap(a1: AddrMode.Value, p1: Constant, a2: AddrMode.Value, p2: Constant): Boolean = {
|
||||
val a1 = l1.addrMode
|
||||
val a2 = l2.addrMode
|
||||
if (badAddrModes(a1) || badAddrModes(a2)) return false
|
||||
if (goodAddrModes(a1) || goodAddrModes(a2)) return true
|
||||
if ((a1 == IndexedSY) != (a2 == IndexedSY)) return true // bold assertion, but usually true
|
||||
val p1 = l1.parameter
|
||||
val p2 = l2.parameter
|
||||
val w1 = OpcodeClasses.AccessesWordInMemory(l1.opcode)
|
||||
val w2 = OpcodeClasses.AccessesWordInMemory(l2.opcode)
|
||||
def handleKnownDistance(distance: Short): Boolean = {
|
||||
val indexingAddrModes = Set(AbsoluteIndexedX, AbsoluteX, ZeroPageX, AbsoluteY, ZeroPageY)
|
||||
// `distance` is the distance between the first byte that can be addressed by l1 (b1) and the first byte that can be addressed by l2 (b2): (b2-b1)
|
||||
val indexingAddrModes = Set(AbsoluteIndexedX, AbsoluteX, ZeroPageX, AbsoluteY, ZeroPageY, LongAbsoluteX)
|
||||
val a1Indexing = indexingAddrModes(a1)
|
||||
val a2Indexing = indexingAddrModes(a2)
|
||||
(a1Indexing, a2Indexing) match {
|
||||
case (false, false) => distance != 0
|
||||
case (true, false) => distance > 255 || distance < 0
|
||||
case (false, true) => distance > 0 || distance < -255
|
||||
case (true, true) => distance > 255 || distance < -255
|
||||
case (false, false) => distance != 0 && (distance != 1 || !w1) && (distance != -1 || !w2)
|
||||
case (true, false) => distance > 255 || distance < 0 && (distance != 256 || !w1) && (distance != -1 || !w2)
|
||||
case (false, true) => distance > 0 || distance < -255 && (distance != 1 || !w1) && (distance != -256 || !w2)
|
||||
case (true, true) => distance > 255 || distance < -255 && (distance != 265 || !w1) && (distance != -256 || !w2)
|
||||
}
|
||||
}
|
||||
|
||||
@ -436,7 +441,7 @@ case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) ext
|
||||
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
|
||||
val s1s = ctx.get[List[AssemblyLine]](ix1)
|
||||
val s2s = ctx.get[List[AssemblyLine]](ix2)
|
||||
if (s1s.forall(s1 => s2s.forall(s2 => HelperCheckers.memoryAccessDoesntOverlap(s1.addrMode, s1.parameter, s2.addrMode, s2.parameter)))) Some(code) else None
|
||||
if (s1s.forall(s1 => s2s.forall(s2 => HelperCheckers.memoryAccessDoesntOverlap(s1, s2)))) Some(code) else None
|
||||
}
|
||||
}
|
||||
|
||||
@ -502,6 +507,14 @@ case class HasY(value: Int) extends AssemblyLinePattern {
|
||||
flowInfo.statusBefore.y.contains(value)
|
||||
}
|
||||
|
||||
case class HasZ(value: Int) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
flowInfo.statusBefore.iz.contains(value)
|
||||
}
|
||||
|
||||
case class DoesntMatterWhatItDoesWith(states: State.Value*) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertBackward(needsFlowInfo)
|
||||
@ -520,6 +533,38 @@ case class HasSet(state: State.Value) extends AssemblyLinePattern {
|
||||
flowInfo.hasSet(state)
|
||||
}
|
||||
|
||||
object HasAccu8 extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
flowInfo.hasSet(State.M)
|
||||
}
|
||||
|
||||
object HasAccu16 extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
flowInfo.hasClear(State.M)
|
||||
}
|
||||
|
||||
object HasIndex8 extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
flowInfo.hasSet(State.W)
|
||||
}
|
||||
|
||||
object HasIndex16 extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
flowInfo.hasClear(State.W)
|
||||
}
|
||||
|
||||
case class HasClear(state: State.Value) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
@ -598,6 +643,11 @@ case object ReadsA extends TrivialAssemblyLinePattern {
|
||||
OpcodeClasses.ReadsAAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ReadsAIfImplied(line.opcode)
|
||||
}
|
||||
|
||||
case object ReadsAH extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ReadsAHAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ReadsAHIfImplied(line.opcode)
|
||||
}
|
||||
|
||||
case object ReadsMemory extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
line.addrMode match {
|
||||
@ -632,38 +682,76 @@ case object ConcernsA extends TrivialAssemblyLinePattern {
|
||||
OpcodeClasses.ConcernsAAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ConcernsAIfImplied(line.opcode)
|
||||
}
|
||||
|
||||
case object ConcernsAH extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ConcernsAHAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ConcernsAHIfImplied(line.opcode)
|
||||
}
|
||||
|
||||
case object ConcernsX extends TrivialAssemblyLinePattern {
|
||||
val XAddrModes = Set(AddrMode.AbsoluteX, AddrMode.IndexedX, AddrMode.ZeroPageX)
|
||||
val XAddrModes = Set(AddrMode.AbsoluteX, AddrMode.AbsoluteIndexedX, AddrMode.LongAbsoluteX, AddrMode.IndexedX, AddrMode.ZeroPageX)
|
||||
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ConcernsXAlways(line.opcode) || XAddrModes(line.addrMode)
|
||||
}
|
||||
|
||||
case object ConcernsY extends TrivialAssemblyLinePattern {
|
||||
val YAddrModes = Set(AddrMode.AbsoluteY, AddrMode.IndexedY, AddrMode.ZeroPageY)
|
||||
val YAddrModes = Set(AddrMode.AbsoluteY, AddrMode.IndexedSY, AddrMode.IndexedY, AddrMode.LongIndexedY, AddrMode.ZeroPageY)
|
||||
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ConcernsYAlways(line.opcode) || YAddrModes(line.addrMode)
|
||||
}
|
||||
|
||||
case object ConcernsStack extends TrivialAssemblyLinePattern {
|
||||
val SAddrModes = Set(AddrMode.IndexedSY, AddrMode.Stack)
|
||||
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ConcernsStackAlways(line.opcode) || SAddrModes(line.addrMode)
|
||||
}
|
||||
|
||||
case object ConcernsIZ extends TrivialAssemblyLinePattern {
|
||||
val IZAddrModes = Set(AddrMode.IndexedZ, AddrMode.LongIndexedZ)
|
||||
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ConcernsIZAlways(line.opcode) || IZAddrModes(line.addrMode)
|
||||
}
|
||||
|
||||
case object ChangesA extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ChangesAAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ChangesAIfImplied(line.opcode)
|
||||
}
|
||||
|
||||
case object ChangesAH extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ChangesAHAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ChangesAHIfImplied(line.opcode)
|
||||
}
|
||||
|
||||
case object ChangesM extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean = line match {
|
||||
case AssemblyLine(Opcode.SEP | Opcode.REP, AddrMode.Immediate, NumericConstant(n, _), _) => (n & 0x20) != 0
|
||||
case AssemblyLine(Opcode.SEP | Opcode.REP | Opcode.PLP | Opcode.XCE, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
case object ChangesW extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean = line match {
|
||||
case AssemblyLine(Opcode.SEP | Opcode.REP, AddrMode.Immediate, NumericConstant(n, _), _) => (n & 0x10) != 0
|
||||
case AssemblyLine(Opcode.SEP | Opcode.REP | Opcode.PLP | Opcode.XCE, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
case object ChangesMemory extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode)
|
||||
}
|
||||
|
||||
case class DoesntChangeMemoryAt(addrMode1: Int, param1: Int) extends AssemblyLinePattern {
|
||||
case class DoesntChangeMemoryAt(addrMode1: Int, param1: Int, opcode: Opcode.Value = Opcode.NOP) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
|
||||
val p1 = ctx.get[Constant](param1)
|
||||
val p2 = line.parameter
|
||||
val a1 = ctx.get[AddrMode.Value](addrMode1)
|
||||
val a2 = line.addrMode
|
||||
val changesSomeMemory = OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode)
|
||||
!changesSomeMemory || HelperCheckers.memoryAccessDoesntOverlap(a1, p1, a2, p2)
|
||||
// TODO: NOP
|
||||
// this will break if the actual instruction was 16-bit
|
||||
!changesSomeMemory || HelperCheckers.memoryAccessDoesntOverlap(AssemblyLine(opcode, a1, p1), line)
|
||||
}
|
||||
}
|
||||
|
||||
@ -675,10 +763,10 @@ case object ConcernsMemory extends TrivialAssemblyLinePattern {
|
||||
case class DoesNotConcernMemoryAt(addrMode1: Int, param1: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
|
||||
val p1 = ctx.get[Constant](param1)
|
||||
val p2 = line.parameter
|
||||
val a1 = ctx.get[AddrMode.Value](addrMode1)
|
||||
val a2 = line.addrMode
|
||||
HelperCheckers.memoryAccessDoesntOverlap(a1, p1, a2, p2)
|
||||
// TODO: NOP
|
||||
// this will break if the actual instruction was 16-bit
|
||||
HelperCheckers.memoryAccessDoesntOverlap(AssemblyLine(Opcode.NOP, a1, p1), line)
|
||||
}
|
||||
}
|
||||
|
||||
@ -788,8 +876,11 @@ case class MatchNumericImmediate(i: Int) extends AssemblyLinePattern {
|
||||
case class DoesntChangeIndexingInAddrMode(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
ctx.get[AddrMode.Value](i) match {
|
||||
case AddrMode.ZeroPageX | AddrMode.AbsoluteX | AddrMode.IndexedX | AddrMode.AbsoluteIndexedX => !OpcodeClasses.ChangesX.contains(line.opcode)
|
||||
case AddrMode.ZeroPageY | AddrMode.AbsoluteY | AddrMode.IndexedY => !OpcodeClasses.ChangesY.contains(line.opcode)
|
||||
case AddrMode.ZeroPageX | AddrMode.AbsoluteX | AddrMode.LongAbsoluteX | AddrMode.IndexedX | AddrMode.AbsoluteIndexedX => !OpcodeClasses.ChangesX.contains(line.opcode)
|
||||
case AddrMode.ZeroPageY | AddrMode.AbsoluteY | AddrMode.IndexedY | AddrMode.LongIndexedY => !OpcodeClasses.ChangesY.contains(line.opcode)
|
||||
case AddrMode.IndexedZ | AddrMode.LongIndexedZ => !OpcodeClasses.ChangesIZ.contains(line.opcode)
|
||||
case AddrMode.Stack => !OpcodeClasses.ChangesS.contains(line.opcode)
|
||||
case AddrMode.IndexedSY => !OpcodeClasses.ChangesS.contains(line.opcode) && !OpcodeClasses.ChangesY.contains(line.opcode)
|
||||
case _ => true
|
||||
}
|
||||
|
||||
|
195
src/main/scala/millfork/assembly/opt/SixteenOptimizations.scala
Normal file
195
src/main/scala/millfork/assembly/opt/SixteenOptimizations.scala
Normal file
@ -0,0 +1,195 @@
|
||||
package millfork.assembly.opt
|
||||
import millfork.assembly.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.OpcodeClasses._
|
||||
import millfork.assembly.{AssemblyLine, OpcodeClasses, State}
|
||||
import millfork.env.{Constant, NumericConstant}
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object SixteenOptimizations {
|
||||
|
||||
val AccumulatorSwapping = new RuleBasedAssemblyOptimization("Accumulator swapping",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
(Elidable & HasOpcode(PHA) & HasAccu8 & DoesntMatterWhatItDoesWith(State.AH, State.A, State.N, State.Z)) ~
|
||||
(Linear & Not(ConcernsStack)) ~
|
||||
(Elidable & HasOpcode(PLA) & DoesntMatterWhatItDoesWith(State.AH)) ~~> { code =>
|
||||
AssemblyLine.implied(XBA) :: (code.tail.init :+ AssemblyLine.implied(XBA))
|
||||
},
|
||||
(Elidable & HasOpcode(TAX) & HasAccu8 & HasIndex8 & DoesntMatterWhatItDoesWith(State.AH, State.A, State.N, State.Z)) ~
|
||||
(Linear & Not(ConcernsX)) ~
|
||||
(Elidable & HasOpcode(TXA) & DoesntMatterWhatItDoesWith(State.AH, State.X)) ~~> { code =>
|
||||
AssemblyLine.implied(XBA) :: (code.tail.init :+ AssemblyLine.implied(XBA))
|
||||
},
|
||||
(Elidable & HasOpcode(TAY) & HasAccu8 & HasIndex8 & DoesntMatterWhatItDoesWith(State.AH, State.A, State.N, State.Z)) ~
|
||||
(Linear & Not(ConcernsY)) ~
|
||||
(Elidable & HasOpcode(TYA) & DoesntMatterWhatItDoesWith(State.AH, State.Y)) ~~> { code =>
|
||||
AssemblyLine.implied(XBA) :: (code.tail.init :+ AssemblyLine.implied(XBA))
|
||||
},
|
||||
)
|
||||
|
||||
val RepSepWeakening = new RuleBasedAssemblyOptimization("REP/SEP weakening",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & HasImmediate(0)) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.C)) ~
|
||||
Where(c => c.get[Int](0).&(0x1).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xFE
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.Z)) ~
|
||||
Where(c => c.get[Int](0).&(0x2).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xFD
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.D)) ~
|
||||
Where(c => c.get[Int](0).&(0x8).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xF7
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.W)) ~
|
||||
Where(c => c.get[Int](0).&(0x10).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xEF
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.M)) ~
|
||||
Where(c => c.get[Int](0).&(0x20).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xDF
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.V)) ~
|
||||
Where(c => c.get[Int](0).&(0x40).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xBF
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
(Elidable & HasOpcodeIn(Set(SEP, REP)) & MatchNumericImmediate(0) & DoesntMatterWhatItDoesWith(State.N)) ~
|
||||
Where(c => c.get[Int](0).&(0x80).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0x7F
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(code.head.opcode, i))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(SEP) & HasSet(State.C) & MatchNumericImmediate(0)) ~
|
||||
Where(c => c.get[Int](0).&(0x1).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xFE
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(SEP, i))
|
||||
},
|
||||
(Elidable & HasOpcode(REP) & HasClear(State.C) & MatchNumericImmediate(0)) ~
|
||||
Where(c => c.get[Int](0).&(0x1).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xFE
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(REP, i))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(SEP) & HasSet(State.W) & MatchNumericImmediate(0)) ~
|
||||
Where(c => c.get[Int](0).&(0x10).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xEF
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(SEP, i))
|
||||
},
|
||||
(Elidable & HasOpcode(REP) & HasClear(State.W) & MatchNumericImmediate(0)) ~
|
||||
Where(c => c.get[Int](0).&(0x10).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xEF
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(REP, i))
|
||||
},
|
||||
|
||||
(Elidable & HasOpcode(SEP) & HasSet(State.M) & MatchNumericImmediate(0)) ~
|
||||
Where(c => c.get[Int](0).&(0x20).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xDF
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(SEP, i))
|
||||
},
|
||||
(Elidable & HasOpcode(REP) & HasClear(State.M) & MatchNumericImmediate(0)) ~
|
||||
Where(c => c.get[Int](0).&(0x20).!=(0)) ~~> { (code, ctx) =>
|
||||
val i = ctx.get[Int](0) & 0xDF
|
||||
if (i == 0) Nil else List(AssemblyLine.immediate(REP, i))
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
val PointlessLoadAfterLoadOrStore = new RuleBasedAssemblyOptimization("Pointless 16-bit load after load or store",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
|
||||
(HasOpcodeIn(Set(LDA_W, STA_W)) & HasAddrMode(WordImmediate) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesA) & Not(ChangesAH)).* ~
|
||||
(Elidable & HasOpcode(LDA_W) & HasAddrMode(WordImmediate) & MatchParameter(1)) ~~> (_.init),
|
||||
|
||||
(HasOpcodeIn(Set(LDA_W, STA_W)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesA) & Not(ChangesAH) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1, LDA_W)).* ~
|
||||
(Elidable & HasOpcode(LDA_W) & MatchAddrMode(0) & MatchParameter(1)) ~~> (_.init),
|
||||
)
|
||||
|
||||
val OptimizeZeroIndex = new RuleBasedAssemblyOptimization("Optimizing zero index for far pointers",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
(Elidable & HasY(0) /*& HasZ(0)*/ & HasIndex8 & HasAddrMode(LongIndexedY) & HasOpcodeIn(SupportsLongIndexedZ)) ~~> (code => code.map(_.copy(addrMode = LongIndexedZ))),
|
||||
)
|
||||
|
||||
private val SupportsStackAddressing = Set(
|
||||
ADC, AND, EOR, ORA, LDA, STA, SBC, CMP,
|
||||
)
|
||||
|
||||
val OptimizeStackRelative = new RuleBasedAssemblyOptimization("Optimizing stack variables",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
|
||||
((Elidable & HasOpcode(TSX)) ~
|
||||
(Elidable & HasOpcode(INX)).*.captureLength(60) ~
|
||||
(Elidable & HasOpcode(DEX) & DoesntMatterWhatItDoesWith(State.N, State.Z)).*.captureLength(61)).capture(3) ~
|
||||
(Not(ConcernsX) & Not(ChangesS)).*.capture(2) ~
|
||||
(Elidable & SupportsStackAddressing & HasAddrMode(AbsoluteX) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.X)) ~
|
||||
Where(ctx => ctx.get[Constant](1) match {
|
||||
case NumericConstant(x, _) => x >= 0x100 && x <= 0x1ff
|
||||
case _ => false
|
||||
}) ~~> { (code, ctx) =>
|
||||
ctx.get[List[AssemblyLine]](2) ++ List(
|
||||
code.last.copy(addrMode = Stack, parameter = (ctx.get[Constant](1) + ctx.get[Int](60) - ctx.get[Int](61)-0x100).quickSimplify)
|
||||
)
|
||||
},
|
||||
|
||||
((Elidable & HasOpcode(TSX)) ~
|
||||
(Elidable & HasOpcode(INX)).*.captureLength(60) ~
|
||||
(Elidable & HasOpcode(DEX) & DoesntMatterWhatItDoesWith(State.N, State.Z)).*.captureLength(61)).capture(3) ~
|
||||
(Not(ConcernsX) & Not(ChangesS)).*.capture(2) ~
|
||||
(Elidable & SupportsStackAddressing & HasAddrMode(AbsoluteX) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
Where(ctx => ctx.get[Constant](1) match {
|
||||
case NumericConstant(x, _) => x >= 0x100 && x <= 0x1ff
|
||||
case _ => false
|
||||
}) ~~> { (code, ctx) =>
|
||||
ctx.get[List[AssemblyLine]](2) ++ List(
|
||||
code.last.copy(addrMode = Stack, parameter = (ctx.get[Constant](1) + ctx.get[Int](60) - ctx.get[Int](61)-0x100).quickSimplify)
|
||||
) ++ ctx.get[List[AssemblyLine]](3)
|
||||
},
|
||||
|
||||
((Elidable & HasOpcode(TSX)) ~
|
||||
(Elidable & HasOpcode(INX)).*.captureLength(60) ~
|
||||
(Elidable & HasOpcode(DEX) & DoesntMatterWhatItDoesWith(State.N, State.Z)).*.captureLength(61)).capture(3) ~
|
||||
(Not(ConcernsX) & Not(ChangesS)).*.capture(2) ~
|
||||
(Elidable & HasOpcode(LDY) & HasAddrMode(AbsoluteX) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.X, State.A)) ~
|
||||
Where(ctx => ctx.get[Constant](1) match {
|
||||
case NumericConstant(x, _) => x >= 0x100 && x <= 0x1ff
|
||||
case _ => false
|
||||
}) ~~> { (code, ctx) =>
|
||||
ctx.get[List[AssemblyLine]](2) ++ List(
|
||||
AssemblyLine(LDA, Stack,(ctx.get[Constant](1) + ctx.get[Int](60) - ctx.get[Int](61)-0x100).quickSimplify),
|
||||
AssemblyLine.implied(TAY)
|
||||
)
|
||||
},
|
||||
|
||||
((Elidable & HasOpcode(TSX)) ~
|
||||
(Elidable & HasOpcode(INX)).*.captureLength(60) ~
|
||||
(Elidable & HasOpcode(DEX) & DoesntMatterWhatItDoesWith(State.N, State.Z)).*.captureLength(61)).capture(3) ~
|
||||
(Not(ConcernsX) & Not(ChangesS)).*.capture(2) ~
|
||||
(Elidable & HasOpcode(LDY) & HasAddrMode(AbsoluteX) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.A)) ~
|
||||
Where(ctx => ctx.get[Constant](1) match {
|
||||
case NumericConstant(x, _) => x >= 0x100 && x <= 0x1ff
|
||||
case _ => false
|
||||
}) ~~> { (code, ctx) =>
|
||||
ctx.get[List[AssemblyLine]](2) ++ List(
|
||||
AssemblyLine(LDA, Stack,(ctx.get[Constant](1) + ctx.get[Int](60) - ctx.get[Int](61)-0x100).quickSimplify),
|
||||
AssemblyLine.implied(TAY)
|
||||
) ++ ctx.get[List[AssemblyLine]](3)
|
||||
},
|
||||
)
|
||||
|
||||
// TODO: rewrite most 8-bit optimizations that are applicable to 16-bit code
|
||||
|
||||
val AllForEmulation: List[AssemblyOptimization] = List(AccumulatorSwapping, OptimizeZeroIndex, RepSepWeakening, OptimizeStackRelative)
|
||||
|
||||
val AllForNative: List[AssemblyOptimization] = List(PointlessLoadAfterLoadOrStore)
|
||||
|
||||
val All: List[AssemblyOptimization] = AllForEmulation ++ AllForNative
|
||||
}
|
@ -15,49 +15,87 @@ import scala.collection.mutable.ListBuffer
|
||||
*/
|
||||
object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
|
||||
case class Features(
|
||||
blastProcessing: Boolean,
|
||||
izIsAlwaysZero: Boolean,
|
||||
indexRegisterTransfers: Boolean,
|
||||
identityArray: Constant)
|
||||
|
||||
// If any of these opcodes is present within a method,
|
||||
// then it's too hard to assign any variable to a register.
|
||||
private val opcodesThatAlwaysPrecludeXAllocation = Set(
|
||||
JSR, STX, TXA, INX, DEX, CPX,
|
||||
LDX_W, STX_W, CPX_W, DEX_W, INX_W,
|
||||
PHX, PLX,
|
||||
SBX, SAX, LXA, XAA, AHX, SHX, SHY, LAS, TAS)
|
||||
SBX, SAX, LXA, XAA, AHX, SHX, SHY, LAS, TAS,
|
||||
HuSAX, SXY, TXY, TXY,
|
||||
SEP, REP,
|
||||
)
|
||||
|
||||
private val opcodesThatAlwaysPrecludeYAllocation = Set(
|
||||
JSR, STY, TYA, INY, DEY, CPY,
|
||||
LDY_W, STY_W, CPY_W, DEY_W, INY_W,
|
||||
PHY, PLY,
|
||||
AHX, SHX, SHY, LAS, TAS)
|
||||
AHX, SHX, SHY, LAS, TAS,
|
||||
SAY, SXY, TXY, TYX,
|
||||
SEP, REP,
|
||||
)
|
||||
|
||||
private val opcodesThatAlwaysPrecludeZAllocation = Set(
|
||||
JSR, STZ, TZA, INZ, DEZ, CPZ,
|
||||
PHZ, PLZ,
|
||||
SEP, REP,
|
||||
)
|
||||
|
||||
private val opcodesThatAlwaysPrecludeAAllocation = Set(
|
||||
JSR, PHA, PLA,
|
||||
ADC, SBC, ORA, EOR, AND,
|
||||
ADC, SBC, ORA, EOR, AND, BIT,
|
||||
ADC_W, SBC_W, ORA_W, EOR_W, AND_W, BIT_W,
|
||||
RRA, RLA, ISC, SLO, SRE,
|
||||
ALR, ARR, ANC, SBX, LXA, XAA,
|
||||
AHX, SHX, SHY, LAS, TAS
|
||||
AHX, SHX, SHY, LAS, TAS,
|
||||
HuSAX, SAY,
|
||||
TCD, TDC, TSC, TCS,
|
||||
SEP, REP,
|
||||
)
|
||||
|
||||
// If any of these opcodes is used on a variable
|
||||
// then it's too hard to assign that variable to a register.
|
||||
// Also, LDY prevents assigning a variable to X and LDX and LAX prevent assigning a variable to Y.
|
||||
private val opcodesThatCannotBeUsedWithIndexRegistersAsParameters = Set(
|
||||
LDX_W, LDY_W, LDA_W,
|
||||
BIT, CPX, CPY, STY,
|
||||
BIT_W, CPX_W, CPY_W, STY_W,
|
||||
EOR, ORA, AND, ADC, SBC, CMP,
|
||||
EOR_W, ORA_W, AND_W, ADC_W, SBC_W, CMP_W,
|
||||
ROL, ROR, LSR, ASL, STX,
|
||||
ROL_W, ROR_W, LSR_W, ASL_W, STX_W,
|
||||
INC_W, DEC_W,
|
||||
SAX, SLO, SRE, ISC, DCP, RLA, RRA,
|
||||
AHX, SHY, SHX, LAS, TAS,
|
||||
TRB, TSB)
|
||||
TRB, TSB,
|
||||
ASR,
|
||||
)
|
||||
|
||||
private val opcodesThatCannotBeUsedWithAccumulatorAsParameter = Set(
|
||||
BIT, CPX, CPY,
|
||||
BIT_W, CPX_W, CPY_W,
|
||||
EOR, ORA, AND, ADC, SBC, CMP, STA,
|
||||
EOR_W, ORA_W, AND_W, ADC_W, SBC_W, CMP_W, STA_W,
|
||||
INC_W, DEC_W,
|
||||
SAX, SLO, SRE, ISC, DCP, RLA, RRA,
|
||||
AHX, SHY, SHX, LAS, TAS,
|
||||
TRB, TSB)
|
||||
TRB, TSB,
|
||||
ASR,
|
||||
)
|
||||
|
||||
private val opcodesCommutative = Set(AND, ORA, EOR, ADC)
|
||||
private val opcodesIdentityTable = Set(AND, ORA, EOR, CMP, ADC, SBC)
|
||||
|
||||
private val LdxAddrModes = Set(ZeroPage, Absolute, Immediate, AbsoluteY, ZeroPageY)
|
||||
private val LdyAddrModes = Set(ZeroPage, Absolute, Immediate, AbsoluteX, ZeroPageX)
|
||||
private val LdzAddrModes = Set(Absolute, Immediate, AbsoluteX)
|
||||
private val CpxyzAddrModes = Set(Absolute, Immediate, ZeroPage)
|
||||
|
||||
override def name = "Allocating variables to index registers"
|
||||
|
||||
@ -97,13 +135,20 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
val importances = ReverseFlowAnalyzer.analyze(f, code)
|
||||
val blastProcessing = options.flag(CompilationFlag.OptimizeForSonicSpeed)
|
||||
val identityArray = f.environment.maybeGet[ThingInMemory]("identity$").map(MemoryAddressConstant).getOrElse(Constant.Zero)
|
||||
val izIsAlwaysZero = !options.flag(CompilationFlag.Emit65CE02Opcodes)
|
||||
val features = Features(
|
||||
blastProcessing =options.flag(CompilationFlag.OptimizeForSonicSpeed),
|
||||
izIsAlwaysZero = !options.flag(CompilationFlag.Emit65CE02Opcodes),
|
||||
indexRegisterTransfers = options.flag(CompilationFlag.EmitEmulation65816Opcodes),
|
||||
identityArray = f.environment.maybeGet[ThingInMemory]("identity$").map(MemoryAddressConstant).getOrElse(Constant.Zero)
|
||||
)
|
||||
|
||||
val xCandidates = variablesWithLifetimes.filter {
|
||||
case (vName, range) =>
|
||||
importances(range.start).x != Important
|
||||
}.flatMap {
|
||||
case (vName, range) =>
|
||||
canBeInlined(Some(vName), None, blastProcessing, code.zip(importances).slice(range.start, range.end)).map { score =>
|
||||
canBeInlined(Some(vName), None, None, features, code.zip(importances).slice(range.start, range.end)).map { score =>
|
||||
(vName, range, if (variablesWithRegisterHint(vName)) score + 16 else score)
|
||||
}
|
||||
}
|
||||
@ -113,7 +158,17 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
importances(range.start).y != Important
|
||||
}.flatMap {
|
||||
case (vName, range) =>
|
||||
canBeInlined(None, Some(vName), blastProcessing, code.zip(importances).slice(range.start, range.end)).map { score =>
|
||||
canBeInlined(None, Some(vName), None, features, code.zip(importances).slice(range.start, range.end)).map { score =>
|
||||
(vName, range, if (variablesWithRegisterHint(vName)) score + 16 else score)
|
||||
}
|
||||
}
|
||||
|
||||
val zCandidates = if (izIsAlwaysZero) Nil else variablesWithLifetimes.filter {
|
||||
case (vName, range) =>
|
||||
importances(range.start).iz != Important
|
||||
}.flatMap {
|
||||
case (vName, range) =>
|
||||
canBeInlined(None, None, Some(vName), features, code.zip(importances).slice(range.start, range.end)).map { score =>
|
||||
(vName, range, if (variablesWithRegisterHint(vName)) score + 16 else score)
|
||||
}
|
||||
}
|
||||
@ -134,27 +189,33 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
}
|
||||
// println(s"X: $xCandidates")
|
||||
// println(s"Y: $yCandidates")
|
||||
// println(s"Z: $zCandidates")
|
||||
// println(s"A: $aCandidates")
|
||||
|
||||
val xCandidateSets = NonOverlappingIntervals.apply[(String, Range, Int)](xCandidates, _._2.start, _._2.end)
|
||||
val yCandidateSets = NonOverlappingIntervals.apply[(String, Range, Int)](yCandidates, _._2.start, _._2.end)
|
||||
val zCandidateSets = NonOverlappingIntervals.apply[(String, Range, Int)](zCandidates, _._2.start, _._2.end)
|
||||
val aCandidateSets = NonOverlappingIntervals.apply[(String, Range, Int)](aCandidates, _._2.start, _._2.end)
|
||||
|
||||
val variants = for {
|
||||
vx <- xCandidateSets.par
|
||||
vy <- yCandidateSets
|
||||
vz <- zCandidateSets
|
||||
va <- aCandidateSets
|
||||
if (vx & vz).isEmpty
|
||||
if (vz & vy).isEmpty
|
||||
if (va & vz).isEmpty
|
||||
if (vx & vy).isEmpty
|
||||
if (vx & va).isEmpty
|
||||
if (va & vy).isEmpty
|
||||
score = vx.toSeq.map(_._3).sum + vy.toSeq.map(_._3).sum + va.toSeq.map(_._3).sum
|
||||
} yield (score, vx, vy, va)
|
||||
score = vx.toSeq.map(_._3).sum + vy.toSeq.map(_._3).sum + va.toSeq.map(_._3).sum + vz.toSeq.map(_._3).sum
|
||||
} yield (score, vx, vy, vz, va)
|
||||
|
||||
if (variants.isEmpty) {
|
||||
return code
|
||||
}
|
||||
|
||||
val (_, bestXs, bestYs, bestAs) = variants.maxBy(_._1)
|
||||
val (_, bestXs, bestYs, bestZs, bestAs) = variants.maxBy(_._1)
|
||||
|
||||
def reportOptimizedBlock[T](oldCode: List[(AssemblyLine, T)], newCode: List[AssemblyLine]): Unit = {
|
||||
oldCode.foreach(l => ErrorReporting.trace(l._1.toString))
|
||||
@ -162,9 +223,10 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
newCode.foreach(l => ErrorReporting.trace(l.toString))
|
||||
}
|
||||
|
||||
if (bestXs.nonEmpty || bestYs.nonEmpty || bestAs.nonEmpty) {
|
||||
if (bestXs.nonEmpty || bestYs.nonEmpty || bestZs.nonEmpty || bestAs.nonEmpty) {
|
||||
bestXs.foreach(v => f.environment.removeVariable(v._1))
|
||||
bestYs.foreach(v => f.environment.removeVariable(v._1))
|
||||
bestZs.foreach(v => f.environment.removeVariable(v._1))
|
||||
bestAs.foreach(v => f.environment.removeVariable(v._1))
|
||||
val output = ListBuffer[AssemblyLine]()
|
||||
var i = 0
|
||||
@ -174,7 +236,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
case (v, range, _) =>
|
||||
ErrorReporting.debug(s"Inlining $v to register X")
|
||||
val oldCode = code.zip(importances).slice(range.start, range.end)
|
||||
val newCode = inlineVars(Some(v), None, None, identityArray, oldCode)
|
||||
val newCode = inlineVars(Some(v), None, None, None, features, oldCode)
|
||||
reportOptimizedBlock(oldCode, newCode)
|
||||
output ++= newCode
|
||||
i = range.end
|
||||
@ -185,7 +247,19 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
case (v, range, _) =>
|
||||
ErrorReporting.debug(s"Inlining $v to register Y")
|
||||
val oldCode = code.zip(importances).slice(range.start, range.end)
|
||||
val newCode = inlineVars(None, Some(v), None, identityArray, oldCode)
|
||||
val newCode = inlineVars(None, Some(v), None, None, features, oldCode)
|
||||
reportOptimizedBlock(oldCode, newCode)
|
||||
output ++= newCode
|
||||
i = range.end
|
||||
done = true
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
bestZs.find(_._2.start == i).foreach {
|
||||
case (v, range, _) =>
|
||||
ErrorReporting.debug(s"Inlining $v to register Z")
|
||||
val oldCode = code.zip(importances).slice(range.start, range.end)
|
||||
val newCode = inlineVars(None, None, Some(v), None, features, oldCode)
|
||||
reportOptimizedBlock(oldCode, newCode)
|
||||
output ++= newCode
|
||||
i = range.end
|
||||
@ -197,7 +271,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
case (v, range, _) =>
|
||||
ErrorReporting.debug(s"Inlining $v to register A")
|
||||
val oldCode = code.zip(importances).slice(range.start, range.end)
|
||||
val newCode = inlineVars(None, None, Some(v), identityArray, oldCode)
|
||||
val newCode = inlineVars(None, None, None, Some(v), features, oldCode)
|
||||
reportOptimizedBlock(oldCode, newCode)
|
||||
output ++= newCode
|
||||
i = range.end
|
||||
@ -215,28 +289,42 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def canBeInlined(xCandidate: Option[String], yCandidate: Option[String], blastProcessing: Boolean, lines: List[(AssemblyLine, CpuImportance)]): Option[Int] = {
|
||||
// TODO: STA has different flag behaviour than TAX, keep it in mind!
|
||||
def canBeInlined(xCandidate: Option[String], yCandidate: Option[String], zCandidate: Option[String], features: Features, lines: List[(AssemblyLine, CpuImportance)]): Option[Int] = {
|
||||
val vx = xCandidate.getOrElse("-")
|
||||
val vy = yCandidate.getOrElse("-")
|
||||
val vz = zCandidate.getOrElse("-")
|
||||
lines match {
|
||||
case (AssemblyLine(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _), _), _) :: xs
|
||||
if th.name == vx || th.name == vy =>
|
||||
if th.name == vx || th.name == vy || th.name == vz =>
|
||||
// if an address of a variable is used, then that variable cannot be assigned to a register
|
||||
None
|
||||
case (AssemblyLine(_, Immediate, HalfWordConstant(MemoryAddressConstant(th), _), _), _) :: xs
|
||||
if th.name == vx || th.name == vy =>
|
||||
if th.name == vx || th.name == vy || th.name == vz =>
|
||||
// if an address of a variable is used, then that variable cannot be assigned to a register
|
||||
None
|
||||
|
||||
case (AssemblyLine(_, AbsoluteX | AbsoluteY | ZeroPageX | ZeroPageY | IndexedY | IndexedX | ZeroPageIndirect | Indirect | AbsoluteIndexedX, MemoryAddressConstant(th), _), _) :: xs =>
|
||||
case (AssemblyLine(_, AbsoluteX | AbsoluteY | LongAbsoluteX |
|
||||
ZeroPageX | ZeroPageY |
|
||||
IndexedY | IndexedX | IndexedZ |
|
||||
LongIndexedY | LongIndexedZ |
|
||||
Indirect | LongIndirect |
|
||||
AbsoluteIndexedX, MemoryAddressConstant(th), _), _) :: xs =>
|
||||
// if a variable is used as an array or a pointer, then it cannot be assigned to a register
|
||||
if (th.name == vx || th.name == vy) {
|
||||
if (th.name == vx || th.name == vy || th.name == vz) {
|
||||
None
|
||||
} else {
|
||||
canBeInlined(xCandidate, yCandidate, blastProcessing, xs)
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
}
|
||||
|
||||
case (AssemblyLine(STY | LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vx =>
|
||||
if (features.indexRegisterTransfers) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 2)
|
||||
else None
|
||||
|
||||
case (AssemblyLine(STX | LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs if th.name == vy =>
|
||||
if (features.indexRegisterTransfers) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 2)
|
||||
else None
|
||||
|
||||
case (AssemblyLine(opcode, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vx && (opcode == LDY || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
// if a variable is used by some opcodes, then it cannot be assigned to a register
|
||||
@ -247,15 +335,20 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
// if a variable is used by some opcodes, then it cannot be assigned to a register
|
||||
None
|
||||
|
||||
case (AssemblyLine(opcode, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vz && (opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
// if a variable is used by some opcodes, then it cannot be assigned to a register
|
||||
None
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), imp) :: xs
|
||||
if xCandidate.isDefined =>
|
||||
// if a register is populated with a different variable, then this variable cannot be assigned to that register
|
||||
// removing LDX saves 3 cycles
|
||||
if (elidable && th.name == vx) {
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 3)
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 3)
|
||||
} else {
|
||||
canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 1)
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 1)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@ -266,7 +359,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
// LAX = LDX-LDA, and since LDX simplifies to nothing and LDA simplifies to TXA,
|
||||
// LAX simplifies to TXA, saving two bytes
|
||||
if (elidable && th.name == vx) {
|
||||
canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 2)
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 2)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -277,9 +370,20 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
// sometimes that LDX has to be converted into CPX#0
|
||||
if (elidable && th.name == vy) {
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 3)
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 3)
|
||||
} else {
|
||||
canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 1)
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 1)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
case (AssemblyLine(LDZ, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), imp) :: xs if zCandidate.isDefined =>
|
||||
if (elidable && th.name == vz) {
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 3)
|
||||
} else {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 1)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@ -293,33 +397,46 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
// if a register is populated with something else than a variable, then no variable cannot be assigned to that register
|
||||
None
|
||||
|
||||
case (AssemblyLine(LDZ, _, _, _), _) :: xs if zCandidate.isDefined =>
|
||||
// if a register is populated with something else than a variable, then no variable cannot be assigned to that register
|
||||
None
|
||||
|
||||
case (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable),_) :: xs
|
||||
if opcodesIdentityTable(op) =>
|
||||
if (th.name == vx || th.name == vy) {
|
||||
if (elidable) canBeInlined(xCandidate, yCandidate, blastProcessing, xs)
|
||||
if (elidable) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
else None
|
||||
} else canBeInlined(xCandidate, yCandidate, blastProcessing, xs)
|
||||
} else {
|
||||
if (th.name == vz) None
|
||||
else canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
}
|
||||
|
||||
case (AssemblyLine(LDA, _, _, elidable),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable2),_) :: xs
|
||||
if opcodesCommutative(op) =>
|
||||
if (th.name == vx || th.name == vy) {
|
||||
if (elidable && elidable2) canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 2)
|
||||
if (elidable && elidable2) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 2)
|
||||
else None
|
||||
} else canBeInlined(xCandidate, yCandidate, blastProcessing, xs)
|
||||
} else {
|
||||
if (th.name == vz) None
|
||||
else canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
}
|
||||
|
||||
case (AssemblyLine(LDA, _, _, elidable),_) :: (AssemblyLine(CLC, _, _, _),_) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidable2),_) :: xs
|
||||
if opcodesCommutative(op) =>
|
||||
if (th.name == vx || th.name == vy) {
|
||||
if (elidable && elidable2) canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 2)
|
||||
if (elidable && elidable2) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 2)
|
||||
else None
|
||||
} else canBeInlined(xCandidate, yCandidate, blastProcessing, xs)
|
||||
} else {
|
||||
if (th.name == vz) None
|
||||
else canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
}
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: (AssemblyLine(TAX, _, _, elidable2), _) :: xs
|
||||
if xCandidate.isDefined =>
|
||||
// a variable cannot be inlined if there is TAX not after LDA of that variable
|
||||
// but LDA-TAX can be simplified to TXA
|
||||
if (elidable && elidable2 && th.name == vx) {
|
||||
canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 3)
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 3)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -329,21 +446,43 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
// a variable cannot be inlined if there is TAY not after LDA of that variable
|
||||
// but LDA-TAY can be simplified to TYA
|
||||
if (elidable && elidable2 && th.name == vy) {
|
||||
canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 3)
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 3)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
case (AssemblyLine(LDA | STA | INC | DEC | STZ, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: xs =>
|
||||
// changing LDA->TXA, STA->TAX, INC->INX, DEC->DEX, STZ->LDA saves 2 bytes
|
||||
if (th.name == vy || th.name == vx) {
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: (AssemblyLine(TAZ, _, _, elidable2), _) :: xs
|
||||
if zCandidate.isDefined =>
|
||||
// a variable cannot be inlined if there is TAZ not after LDA of that variable
|
||||
// but LDA-TAZ can be simplified to TZA
|
||||
if (elidable && elidable2 && th.name == vy) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 3)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
case (AssemblyLine(LDA | STA | INC | DEC, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: xs =>
|
||||
// changing LDA->TXA, STA->TAX, INC->INX, DEC->DEX saves 2 bytes
|
||||
if (th.name == vy || th.name == vx || th.name == vz) {
|
||||
if (elidable) {
|
||||
canBeInlined(xCandidate, yCandidate, blastProcessing, xs).map(_ + 2)
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 2)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
canBeInlined(xCandidate, yCandidate, blastProcessing, xs)
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
}
|
||||
|
||||
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), elidable), _) :: xs =>
|
||||
// changing STZ->LDX saves 2 bytes
|
||||
if (th.name == vy || th.name == vx) {
|
||||
if (elidable && features.izIsAlwaysZero) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + 2)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
}
|
||||
|
||||
case (AssemblyLine(TAX, _, _, _), _) :: xs if xCandidate.isDefined =>
|
||||
@ -354,17 +493,23 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
// a variable cannot be inlined if there is TAY not after LDA of that variable
|
||||
None
|
||||
|
||||
case (AssemblyLine(TAZ, _, _, _), _) :: xs if zCandidate.isDefined =>
|
||||
// a variable cannot be inlined if there is TAZ not after LDA of that variable
|
||||
None
|
||||
|
||||
case (AssemblyLine(LABEL, _, _, _), _) :: xs =>
|
||||
// labels always end the initial section
|
||||
canBeInlined(xCandidate, yCandidate, blastProcessing, xs)
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
|
||||
case (x, _) :: xs =>
|
||||
if (xCandidate.isDefined && opcodesThatAlwaysPrecludeXAllocation(x.opcode)) {
|
||||
None
|
||||
} else if (yCandidate.isDefined && opcodesThatAlwaysPrecludeYAllocation(x.opcode)) {
|
||||
None
|
||||
} else if (zCandidate.isDefined && opcodesThatAlwaysPrecludeZAllocation(x.opcode)) {
|
||||
None
|
||||
} else {
|
||||
canBeInlined(xCandidate, yCandidate, blastProcessing, xs)
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
}
|
||||
|
||||
case Nil => Some(0)
|
||||
@ -397,7 +542,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
// if an address of a variable is used, then that variable cannot be assigned to a register
|
||||
None
|
||||
|
||||
case (AssemblyLine(_, AbsoluteX | AbsoluteY | ZeroPageX | ZeroPageY | IndexedY | IndexedX | ZeroPageIndirect | Indirect | AbsoluteIndexedX, MemoryAddressConstant(th), _),_) :: xs
|
||||
case (AssemblyLine(_, AbsoluteX | AbsoluteY | ZeroPageX | ZeroPageY | IndexedY | IndexedX | IndexedZ | Indirect | AbsoluteIndexedX, MemoryAddressConstant(th), _),_) :: xs
|
||||
if th.name == candidate =>
|
||||
// if a variable is used as an array or a pointer, then it cannot be assigned to a register
|
||||
None
|
||||
@ -508,178 +653,237 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
}
|
||||
}
|
||||
|
||||
def inlineVars(xCandidate: Option[String], yCandidate: Option[String], aCandidate: Option[String], identityArray: Constant, lines: List[(AssemblyLine, CpuImportance)]): List[AssemblyLine] = {
|
||||
def inlineVars(xCandidate: Option[String], yCandidate: Option[String], zCandidate: Option[String], aCandidate: Option[String], features: Features, lines: List[(AssemblyLine, CpuImportance)]): List[AssemblyLine] = {
|
||||
val vx = xCandidate.getOrElse("-")
|
||||
val vy = yCandidate.getOrElse("-")
|
||||
val vz = zCandidate.getOrElse("-")
|
||||
val va = aCandidate.getOrElse("-")
|
||||
lines match {
|
||||
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vx =>
|
||||
AssemblyLine.implied(INX) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(INX) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vy =>
|
||||
AssemblyLine.implied(INY) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(INY) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vz =>
|
||||
AssemblyLine.implied(INZ) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vx =>
|
||||
AssemblyLine.implied(DEX) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(DEX) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vy =>
|
||||
AssemblyLine.implied(DEY) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(DEY) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vz =>
|
||||
AssemblyLine.implied(DEZ) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(opcode@(DEC | INC | ROL | ROR | ASL | LSR), Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == va =>
|
||||
AssemblyLine.implied(opcode) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(opcode) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _), imp) :: xs
|
||||
if th.name == vx =>
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
} else {
|
||||
AssemblyLine.immediate(CPX, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.immediate(CPX, 0) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
}
|
||||
|
||||
case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vx =>
|
||||
AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (l@AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if opcodesIdentityTable(op) && th.name == vx =>
|
||||
l.copy(addrMode = AbsoluteX, parameter = identityArray) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
l.copy(addrMode = AbsoluteX, parameter = features.identityArray) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (l@AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if opcodesIdentityTable(op) && th.name == vy =>
|
||||
l.copy(addrMode = AbsoluteY, parameter = identityArray) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
l.copy(addrMode = AbsoluteY, parameter = features.identityArray) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (l@AssemblyLine(LDA, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if opcodesCommutative(op) && th.name == va =>
|
||||
l.copy(opcode = op) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
l.copy(opcode = op) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (l@AssemblyLine(LDA, _, _, _), _) :: (clc@AssemblyLine(CLC, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if opcodesCommutative(op) && th.name == va =>
|
||||
l.copy(opcode = op) :: clc :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
l.copy(opcode = op) :: clc :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (l@AssemblyLine(LDA, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if opcodesCommutative(op) && th.name == vx =>
|
||||
AssemblyLine.implied(TXA) :: l.copy(opcode = op) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TXA) :: l.copy(opcode = op) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (l@AssemblyLine(LDA, _, _, _), _) :: (clc@AssemblyLine(CLC, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if opcodesCommutative(op) && th.name == vx =>
|
||||
AssemblyLine.implied(TXA) :: l.copy(opcode = op) :: clc :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TXA) :: l.copy(opcode = op) :: clc :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (l@AssemblyLine(LDA, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if opcodesCommutative(op) && th.name == vy =>
|
||||
AssemblyLine.implied(TYA) :: l.copy(opcode = op) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TYA) :: l.copy(opcode = op) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (l@AssemblyLine(LDA, _, _, _), _) :: (clc@AssemblyLine(CLC, _, _, _), _) :: (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if opcodesCommutative(op) && th.name == vy =>
|
||||
AssemblyLine.implied(TYA) :: l.copy(opcode = op) :: clc :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TYA) :: l.copy(opcode = op) :: clc :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDA | STA, Absolute | ZeroPage, MemoryAddressConstant(th), _), imp) :: xs
|
||||
if th.name == va =>
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
} else {
|
||||
AssemblyLine.immediate(CMP, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.immediate(CMP, 0) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
}
|
||||
|
||||
case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == va =>
|
||||
AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _), imp) :: xs
|
||||
if th.name == vy =>
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
} else {
|
||||
AssemblyLine.immediate(CPY, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.immediate(CPY, 0) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
}
|
||||
|
||||
case (AssemblyLine(LDZ, Absolute | ZeroPage, MemoryAddressConstant(th), _), imp) :: xs
|
||||
if th.name == vz =>
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
} else {
|
||||
AssemblyLine.immediate(CPZ, 0) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
}
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TAX, _, _, true), _) :: xs
|
||||
if th.name == vx =>
|
||||
// these TXA's may get optimized away by a different optimization
|
||||
AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TAY, _, _, true), _) :: xs
|
||||
if th.name == vy =>
|
||||
// these TYA's may get optimized away by a different optimization
|
||||
AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TAZ, _, _, true), _) :: xs
|
||||
if th.name == vz =>
|
||||
// these TZA's may get optimized away by a different optimization
|
||||
AssemblyLine.implied(TZA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TXA, _, _, true), _) :: xs
|
||||
if th.name == va =>
|
||||
// these TAX's may get optimized away by a different optimization
|
||||
AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: (AssemblyLine(TYA, _, _, true), _) :: xs
|
||||
if th.name == va =>
|
||||
// these TAY's may get optimized away by a different optimization
|
||||
AssemblyLine.implied(TAY) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TAY) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDA, am, param, true), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: xs
|
||||
if th.name == vx && LdxAddrModes(am) =>
|
||||
// these TXA's may get optimized away by a different optimization
|
||||
AssemblyLine(LDX, am, param) :: AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine(LDX, am, param) :: AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDA, am, param, true), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: xs
|
||||
if th.name == vy && LdyAddrModes(am) =>
|
||||
// these TYA's may get optimized away by a different optimization
|
||||
AssemblyLine(LDY, am, param) :: AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine(LDY, am, param) :: AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDA, am, param, true), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), true), _) :: xs
|
||||
if th.name == vz && LdzAddrModes(am) =>
|
||||
// these TZA's may get optimized away by a different optimization
|
||||
AssemblyLine(LDZ, am, param) :: AssemblyLine.implied(TZA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: (AssemblyLine(CMP, am, param, true), _) :: xs
|
||||
if th.name == vx && doesntUseXOrY(am) =>
|
||||
if th.name == vx && CpxyzAddrModes(am) =>
|
||||
// ditto
|
||||
AssemblyLine.implied(TXA) :: AssemblyLine(CPX, am, param) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TXA) :: AssemblyLine(CPX, am, param) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: (AssemblyLine(CMP, am, param, true), _) :: xs
|
||||
if th.name == vy && doesntUseXOrY(am) =>
|
||||
if th.name == vy && CpxyzAddrModes(am) =>
|
||||
// ditto
|
||||
AssemblyLine.implied(TYA) :: AssemblyLine(CPY, am, param) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TYA) :: AssemblyLine(CPY, am, param) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: (AssemblyLine(CMP, am, param, true), _) :: xs
|
||||
if th.name == vy && CpxyzAddrModes(am) =>
|
||||
// ditto
|
||||
AssemblyLine.implied(TZA) :: AssemblyLine(CPZ, am, param) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vx =>
|
||||
AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vy =>
|
||||
AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vx =>
|
||||
AssemblyLine.implied(TXY) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vy =>
|
||||
AssemblyLine.implied(TYX) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vz =>
|
||||
AssemblyLine.implied(TZA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == va =>
|
||||
AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == va =>
|
||||
AssemblyLine.implied(TAY) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TAY) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vx =>
|
||||
AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TAX) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vy =>
|
||||
AssemblyLine.implied(TAY) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TAY) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vz =>
|
||||
AssemblyLine.implied(TAZ) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == va =>
|
||||
AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TXA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == va =>
|
||||
AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.implied(TYA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vy =>
|
||||
AssemblyLine.implied(TXY) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vx =>
|
||||
AssemblyLine.implied(TYX) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vx =>
|
||||
AssemblyLine.immediate(LDX, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
if (features.izIsAlwaysZero) AssemblyLine.immediate(LDX, 0) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
else ErrorReporting.fatal("Unexpected STZ")
|
||||
|
||||
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == vy =>
|
||||
AssemblyLine.immediate(LDY, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
if (features.izIsAlwaysZero) AssemblyLine.immediate(LDY, 0) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
else ErrorReporting.fatal("Unexpected STZ")
|
||||
|
||||
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _), _) :: xs
|
||||
if th.name == va =>
|
||||
AssemblyLine.immediate(LDA, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
if (features.izIsAlwaysZero) AssemblyLine.immediate(LDA, 0) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
else AssemblyLine.implied(TZA) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(TAX, _, _, _), _) :: xs if xCandidate.isDefined =>
|
||||
ErrorReporting.fatal("Unexpected TAX")
|
||||
@ -687,20 +891,26 @@ object VariableToRegisterOptimization extends AssemblyOptimization {
|
||||
case (AssemblyLine(TAY, _, _, _), _) :: xs if yCandidate.isDefined =>
|
||||
ErrorReporting.fatal("Unexpected TAY")
|
||||
|
||||
case (AssemblyLine(TAZ, _, _, _), _) :: xs if zCandidate.isDefined =>
|
||||
ErrorReporting.fatal("Unexpected TAZ")
|
||||
|
||||
case (AssemblyLine(TXA, _, _, _), _) :: xs if aCandidate.isDefined =>
|
||||
AssemblyLine.immediate(CPX, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.immediate(CPX, 0) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (AssemblyLine(TYA, _, _, _), _) :: xs if aCandidate.isDefined =>
|
||||
AssemblyLine.immediate(CPY, 0) :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
AssemblyLine.immediate(CPY, 0) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (x, _) :: xs => x :: inlineVars(xCandidate, yCandidate, aCandidate, identityArray, xs)
|
||||
case (AssemblyLine(TZA, _, _, _), _) :: xs if aCandidate.isDefined =>
|
||||
AssemblyLine.immediate(CPZ, 0) :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case (x, _) :: xs => x :: inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
|
||||
case Nil => Nil
|
||||
}
|
||||
}
|
||||
|
||||
def doesntUseXOrY(am: AddrMode.Value): Boolean = am match {
|
||||
case Immediate | ZeroPage | Absolute | Relative | Indirect | ZeroPageIndirect => true
|
||||
case Immediate | WordImmediate | ZeroPage | Absolute | LongAbsolute | Relative | LongRelative | Indirect | LongIndirect | Stack | IndexedZ => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
@ -258,6 +258,14 @@ object BuiltIns {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
Nil
|
||||
case Some(NumericConstant(shift, _)) if shift > 0 =>
|
||||
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
targetBytes match {
|
||||
case List(List(AssemblyLine(STA, a1, l, _)), List(AssemblyLine(STA, a2, h, _))) =>
|
||||
if (a1 == a2 && l.+(1).quickSimplify == h) {
|
||||
return List(AssemblyLine.accu16) ++ List.fill(shift.toInt)(AssemblyLine(if (aslRatherThanLsr) ASL_W else LSR_W, a1, l)) ++ List(AssemblyLine.accu8)
|
||||
}
|
||||
}
|
||||
}
|
||||
List.fill(shift.toInt)(if (aslRatherThanLsr) {
|
||||
staTo(ASL, lo) ++ targetBytes.tail.flatMap { b => staTo(ROL, b) }
|
||||
} else {
|
||||
@ -643,15 +651,79 @@ object BuiltIns {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
return Nil
|
||||
case Some(NumericConstant(1, _)) if canUseIncDec && !subtract =>
|
||||
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
|
||||
targetBytes match {
|
||||
case List(List(AssemblyLine(STA, ZeroPage, l, _)), List(AssemblyLine(STA, ZeroPage, h, _))) =>
|
||||
if (l.+(1).quickSimplify == h) {
|
||||
return List(AssemblyLine.zeropage(INC_W, l))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
targetBytes match {
|
||||
case List(List(AssemblyLine(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l, _)), List(AssemblyLine(STA, a2, h, _))) =>
|
||||
if (a1 == a2 && l.+(1).quickSimplify == h) {
|
||||
return List(AssemblyLine.accu16, AssemblyLine(INC_W, a1, l), AssemblyLine.accu8)
|
||||
}
|
||||
}
|
||||
}
|
||||
val label = MfCompiler.nextLabel("in")
|
||||
return staTo(INC, targetBytes.head) ++ targetBytes.tail.flatMap(l => AssemblyLine.relative(BNE, label)::staTo(INC, l)) :+ AssemblyLine.label(label)
|
||||
case Some(NumericConstant(-1, _)) if canUseIncDec && subtract =>
|
||||
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
|
||||
targetBytes match {
|
||||
case List(List(AssemblyLine(STA, ZeroPage, l, _)), List(AssemblyLine(STA, ZeroPage, h, _))) =>
|
||||
if (l.+(1).quickSimplify == h) {
|
||||
return List(AssemblyLine.zeropage(INC_W, l))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
targetBytes match {
|
||||
case List(List(AssemblyLine(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l, _)), List(AssemblyLine(STA, a2, h, _))) =>
|
||||
if (a1 == a2 && l.+(1).quickSimplify == h) {
|
||||
return List(AssemblyLine.accu16, AssemblyLine(INC_W, a1, l), AssemblyLine.accu8)
|
||||
}
|
||||
}
|
||||
}
|
||||
val label = MfCompiler.nextLabel("in")
|
||||
return staTo(INC, targetBytes.head) ++ targetBytes.tail.flatMap(l => AssemblyLine.relative(BNE, label)::staTo(INC, l)) :+ AssemblyLine.label(label)
|
||||
case Some(NumericConstant(1, _)) if canUseIncDec && subtract =>
|
||||
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
|
||||
targetBytes match {
|
||||
case List(List(AssemblyLine(STA, ZeroPage, l, _)), List(AssemblyLine(STA, ZeroPage, h, _))) =>
|
||||
if (l.+(1).quickSimplify == h) {
|
||||
return List(AssemblyLine.zeropage(DEC_W, l))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
targetBytes match {
|
||||
case List(List(AssemblyLine(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l, _)), List(AssemblyLine(STA, a2, h, _))) =>
|
||||
if (a1 == a2 && l.+(1).quickSimplify == h) {
|
||||
return List(AssemblyLine.accu16, AssemblyLine(DEC_W, a1, l), AssemblyLine.accu8)
|
||||
}
|
||||
}
|
||||
}
|
||||
val label = MfCompiler.nextLabel("de")
|
||||
return doDec(targetBytes)
|
||||
case Some(NumericConstant(-1, _)) if canUseIncDec && !subtract =>
|
||||
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
|
||||
targetBytes match {
|
||||
case List(List(AssemblyLine(STA, ZeroPage, l, _)), List(AssemblyLine(STA, ZeroPage, h, _))) =>
|
||||
if (l.+(1).quickSimplify == h) {
|
||||
return List(AssemblyLine.zeropage(DEC_W, l))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
targetBytes match {
|
||||
case List(List(AssemblyLine(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l, _)), List(AssemblyLine(STA, a2, h, _))) =>
|
||||
if (a1 == a2 && l.+(1).quickSimplify == h) {
|
||||
return List(AssemblyLine.accu16, AssemblyLine(DEC_W, a1, l), AssemblyLine.accu8)
|
||||
}
|
||||
}
|
||||
}
|
||||
val label = MfCompiler.nextLabel("de")
|
||||
return doDec(targetBytes)
|
||||
case Some(constant) =>
|
||||
@ -719,6 +791,32 @@ object BuiltIns {
|
||||
}
|
||||
}
|
||||
val addendByteRead = addendByteRead0 ++ List.fill((targetSize - addendByteRead0.size) max 0)(List(AssemblyLine.immediate(LDA, 0)))
|
||||
|
||||
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
(removeTsx(targetBytes), removeTsx(addendByteRead)) match {
|
||||
case (List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))), List(List(AssemblyLine(LDA, Immediate, al, _)), List(AssemblyLine(LDA, Immediate, ah, _)))) =>
|
||||
if (ta1 == ta2 && tl.+(1).quickSimplify == th) {
|
||||
return wrapInSedCldIfNeeded(decimal, List(
|
||||
AssemblyLine.implied(if(subtract) SEC else CLC),
|
||||
AssemblyLine.accu16,
|
||||
AssemblyLine(LDA_W, ta1, tl),
|
||||
AssemblyLine(if(subtract) SBC_W else ADC_W, WordImmediate, ah.asl(8).+(al).quickSimplify),
|
||||
AssemblyLine(STA_W, ta1, tl),
|
||||
AssemblyLine.accu8))
|
||||
}
|
||||
case (List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))), List(List(AssemblyLine(LDA, aa1, al, _)), List(AssemblyLine(LDA, aa2, ah, _)))) =>
|
||||
if (ta1 == ta2 && aa1 == aa2 && tl.+(1).quickSimplify == th && al.+(1).quickSimplify == ah) {
|
||||
return wrapInSedCldIfNeeded(decimal, List(
|
||||
AssemblyLine.accu16,
|
||||
AssemblyLine.implied(if(subtract) SEC else CLC),
|
||||
AssemblyLine(LDA_W, ta1, tl),
|
||||
AssemblyLine(if(subtract) SBC_W else ADC_W, aa1, al),
|
||||
AssemblyLine(STA_W, ta1, tl),
|
||||
AssemblyLine.accu8))
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
val buffer = mutable.ListBuffer[AssemblyLine]()
|
||||
buffer ++= calculateRhs
|
||||
buffer += AssemblyLine.implied(if (subtract) SEC else CLC)
|
||||
@ -819,6 +917,29 @@ object BuiltIns {
|
||||
})
|
||||
}
|
||||
}
|
||||
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
(removeTsx(targetBytes), removeTsx(addendByteRead)) match {
|
||||
case (List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))), List(List(AssemblyLine(LDA, Immediate, al, _)), List(AssemblyLine(LDA, Immediate, ah, _)))) =>
|
||||
if (ta1 == ta2 && tl.+(1).quickSimplify == th) {
|
||||
return List(
|
||||
AssemblyLine.accu16,
|
||||
AssemblyLine(LDA_W, ta1, tl),
|
||||
AssemblyLine(Opcode.widen(operation).get, WordImmediate, ah.asl(8).+(al).quickSimplify),
|
||||
AssemblyLine(STA_W, ta1, tl),
|
||||
AssemblyLine.accu8)
|
||||
}
|
||||
case (List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))), List(List(AssemblyLine(LDA, aa1, al, _)), List(AssemblyLine(LDA, aa2, ah, _)))) =>
|
||||
if (ta1 == ta2 && aa1 == aa2 && tl.+(1).quickSimplify == th && al.+(1).quickSimplify == ah) {
|
||||
return List(
|
||||
AssemblyLine.accu16,
|
||||
AssemblyLine(LDA_W, ta1, tl),
|
||||
AssemblyLine(Opcode.widen(operation).get, aa1, al),
|
||||
AssemblyLine(STA_W, ta1, tl),
|
||||
AssemblyLine.accu8)
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
val AllOnes = (1L << (8 * targetSize)) - 1
|
||||
(operation, env.eval(param)) match {
|
||||
case (EOR, Some(NumericConstant(0, _)))
|
||||
@ -880,4 +1001,10 @@ object BuiltIns {
|
||||
???
|
||||
}
|
||||
}
|
||||
|
||||
private def removeTsx(codes: List[List[AssemblyLine]]): List[List[AssemblyLine]] = codes.map {
|
||||
case List(AssemblyLine(TSX, _, _, _), AssemblyLine(op, AbsoluteX, NumericConstant(nn, _), _)) if nn >= 0x100 && nn <= 0x1ff =>
|
||||
List(AssemblyLine(op, Stack, NumericConstant(nn & 0xff, 1)))
|
||||
case x => x
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,11 @@ object ExpressionCompiler {
|
||||
def compileConstant(ctx: CompilationContext, expr: Constant, target: Variable): List[AssemblyLine] = {
|
||||
target match {
|
||||
case RegisterVariable(Register.A, _) => List(AssemblyLine(LDA, Immediate, expr))
|
||||
case RegisterVariable(Register.AW, _) =>
|
||||
List(
|
||||
AssemblyLine.accu16,
|
||||
AssemblyLine(LDA_W, WordImmediate, expr),
|
||||
AssemblyLine.accu8)
|
||||
case RegisterVariable(Register.X, _) => List(AssemblyLine(LDX, Immediate, expr))
|
||||
case RegisterVariable(Register.Y, _) => List(AssemblyLine(LDY, Immediate, expr))
|
||||
case RegisterVariable(Register.AX, _) => List(
|
||||
@ -221,7 +226,12 @@ object ExpressionCompiler {
|
||||
case 1 =>
|
||||
v match {
|
||||
case mv: VariableInMemory => AssemblyLine.variable(ctx, store, mv)
|
||||
case sv@StackVariable(_, _, offset) => AssemblyLine.implied(transferToA) :: AssemblyLine.implied(TSX) :: AssemblyLine.absoluteX(STA, offset + ctx.extraStackOffset) :: Nil
|
||||
case sv@StackVariable(_, _, offset) =>
|
||||
if (ctx.options.flags(CompilationFlag.EmitEmulation65816Opcodes)) {
|
||||
AssemblyLine.implied(transferToA) :: AssemblyLine.stackRelative(STA, offset + ctx.extraStackOffset) :: Nil
|
||||
} else {
|
||||
AssemblyLine.implied(transferToA) :: AssemblyLine.implied(TSX) :: AssemblyLine.absoluteX(STA, offset + ctx.extraStackOffset) :: Nil
|
||||
}
|
||||
}
|
||||
case s if s > 1 =>
|
||||
v match {
|
||||
@ -373,6 +383,18 @@ object ExpressionCompiler {
|
||||
case source: VariableInMemory =>
|
||||
target match {
|
||||
case RegisterVariable(Register.A, _) => AssemblyLine.variable(ctx, LDA, source)
|
||||
case RegisterVariable(Register.AW, _) =>
|
||||
exprType.size match {
|
||||
case 1 => if (exprType.isSigned) {
|
||||
AssemblyLine.variable(ctx, LDA, source) ++ List(
|
||||
AssemblyLine.implied(PHA)) ++ signExtendA() ++ List(
|
||||
AssemblyLine.implied(XBA),
|
||||
AssemblyLine.implied(PLA))
|
||||
} else List(AssemblyLine.immediate(LDX, 0), AssemblyLine.implied(XBA)) ++ AssemblyLine.variable(ctx, LDA, source) :+ AssemblyLine.immediate(LDX, 0)
|
||||
case 2 =>
|
||||
// TODO: use LDA_W
|
||||
AssemblyLine.variable(ctx, LDA, source, 1) ++ List(AssemblyLine.implied(XBA)) ++ AssemblyLine.variable(ctx, LDA, source)
|
||||
}
|
||||
case RegisterVariable(Register.X, _) => AssemblyLine.variable(ctx, LDX, source)
|
||||
case RegisterVariable(Register.Y, _) => AssemblyLine.variable(ctx, LDY, source)
|
||||
case RegisterVariable(Register.AX, _) =>
|
||||
@ -1021,13 +1043,13 @@ object ExpressionCompiler {
|
||||
// TODO: fix
|
||||
case _ => Nil
|
||||
}
|
||||
secondViaMemory ++ thirdViaRegisters :+ AssemblyLine.absolute(JSR, function)
|
||||
secondViaMemory ++ thirdViaRegisters :+ AssemblyLine.absoluteOrLongAbsolute(JSR, function, ctx.options)
|
||||
case NormalParamSignature(paramVars) =>
|
||||
params.zip(paramVars).flatMap {
|
||||
case (paramExpr, paramVar) =>
|
||||
val callCtx = callingContext(ctx, paramVar)
|
||||
compileAssignment(callCtx, paramExpr, VariableExpression(paramVar.name))
|
||||
} ++ List(AssemblyLine.absolute(JSR, function))
|
||||
} ++ List(AssemblyLine.absoluteOrLongAbsolute(JSR, function, ctx.options))
|
||||
}
|
||||
result
|
||||
}
|
||||
@ -1076,6 +1098,7 @@ object ExpressionCompiler {
|
||||
exprTypeAndVariable.fold(noop) {
|
||||
case (VoidType, _) => ???
|
||||
case (_, RegisterVariable(Register.A, _)) => noop
|
||||
case (_, RegisterVariable(Register.AW, _)) => List(AssemblyLine.implied(XBA), AssemblyLine.implied(TAX), AssemblyLine.implied(XBA))
|
||||
case (_, RegisterVariable(Register.X, _)) => List(AssemblyLine.implied(TAX))
|
||||
case (_, RegisterVariable(Register.Y, _)) => List(AssemblyLine.implied(TAY))
|
||||
case (_, RegisterVariable(Register.AX, _)) =>
|
||||
@ -1083,7 +1106,9 @@ object ExpressionCompiler {
|
||||
noop
|
||||
case (_, RegisterVariable(Register.XA, _)) =>
|
||||
// TODO: sign extension
|
||||
if (ctx.options.flag(CompilationFlag.EmitCmosOpcodes)) {
|
||||
if (ctx.options.flag(CompilationFlag.EmitHudsonOpcodes)) {
|
||||
List(AssemblyLine.implied(HuSAX))
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitCmosOpcodes)) {
|
||||
List(
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.implied(PHX),
|
||||
@ -1105,11 +1130,17 @@ object ExpressionCompiler {
|
||||
AssemblyLine.implied(TXA))
|
||||
case (_, RegisterVariable(Register.AY, _)) =>
|
||||
// TODO: sign extension
|
||||
List(
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.implied(TAY),
|
||||
AssemblyLine.implied(PLA))
|
||||
if (ctx.options.flag(CompilationFlag.EmitHudsonOpcodes)) {
|
||||
List(AssemblyLine.implied(SXY))
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes)) {
|
||||
List(AssemblyLine.implied(TXY))
|
||||
} else {
|
||||
List(
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.implied(TAY),
|
||||
AssemblyLine.implied(PLA))
|
||||
}
|
||||
case (t, v: VariableInMemory) => t.size match {
|
||||
case 1 => v.typ.size match {
|
||||
case 1 =>
|
||||
@ -1230,7 +1261,7 @@ object ExpressionCompiler {
|
||||
if (i < arrayLength) return Nil
|
||||
if (i >= arrayLength) return List(
|
||||
AssemblyLine.implied(PHP),
|
||||
AssemblyLine.absolute(JSR, ctx.env.get[ThingInMemory]("_panic")))
|
||||
AssemblyLine.absoluteOrLongAbsolute(JSR, ctx.env.get[ThingInMemory]("_panic"), ctx.options))
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
@ -1245,7 +1276,7 @@ object ExpressionCompiler {
|
||||
AssemblyLine.implied(PHP),
|
||||
AssemblyLine.immediate(compare, arrayLength),
|
||||
AssemblyLine.relative(BCC, label),
|
||||
AssemblyLine.absolute(JSR, ctx.env.get[ThingInMemory]("_panic")),
|
||||
AssemblyLine.absoluteOrLongAbsolute(JSR, ctx.env.get[ThingInMemory]("_panic"), ctx.options),
|
||||
AssemblyLine.label(label),
|
||||
AssemblyLine.implied(PLP))
|
||||
} else {
|
||||
|
@ -69,7 +69,7 @@ object StatementCompiler {
|
||||
val actualAddrMode = a match {
|
||||
case Absolute if OpcodeClasses.ShortBranching(o) => Relative
|
||||
case IndexedX if o == JMP => AbsoluteIndexedX
|
||||
case Indirect if o != JMP => ZeroPageIndirect
|
||||
case Indirect if o != JMP => IndexedZ
|
||||
case _ => a
|
||||
}
|
||||
List(AssemblyLine(o, actualAddrMode, c, e))
|
||||
|
39
src/main/scala/millfork/env/Thing.scala
vendored
39
src/main/scala/millfork/env/Thing.scala
vendored
@ -1,7 +1,7 @@
|
||||
package millfork.env
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly.Opcode
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node._
|
||||
|
||||
sealed trait Thing {
|
||||
@ -79,6 +79,12 @@ sealed trait TypedThing extends Thing {
|
||||
|
||||
sealed trait ThingInMemory extends Thing {
|
||||
def toAddress: Constant
|
||||
|
||||
var farFlag: Option[Boolean] = None
|
||||
var declaredBank: Option[Int] = None
|
||||
|
||||
def isFar(compilationOptions: CompilationOptions): Boolean
|
||||
def bank(compilationOptions: CompilationOptions): Int
|
||||
}
|
||||
|
||||
sealed trait PreallocableThing extends ThingInMemory {
|
||||
@ -91,6 +97,12 @@ sealed trait PreallocableThing extends ThingInMemory {
|
||||
|
||||
case class Label(name: String) extends ThingInMemory {
|
||||
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
|
||||
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean =
|
||||
compilationOptions.flag(CompilationFlag.LargeCode) && farFlag.getOrElse(true)
|
||||
|
||||
override def bank(compilationOptions: CompilationOptions): Int =
|
||||
declaredBank.getOrElse(compilationOptions.platform.defaultCodeBank)
|
||||
}
|
||||
|
||||
sealed trait Variable extends TypedThing with VariableLikeThing
|
||||
@ -100,8 +112,13 @@ case class BlackHole(typ: Type) extends Variable {
|
||||
}
|
||||
|
||||
sealed trait VariableInMemory extends Variable with ThingInMemory with IndexableThing {
|
||||
|
||||
def zeropage: Boolean
|
||||
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean =
|
||||
!zeropage && farFlag.getOrElse(false)
|
||||
|
||||
override def bank(compilationOptions: CompilationOptions): Int =
|
||||
declaredBank.getOrElse(0)
|
||||
}
|
||||
|
||||
case class RegisterVariable(register: Register.Value, typ: Type) extends Variable {
|
||||
@ -156,14 +173,26 @@ case class UninitializedArray(name: String, sizeInBytes: Int) extends MfArray wi
|
||||
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
|
||||
|
||||
override def alloc = VariableAllocationMethod.Static
|
||||
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||
|
||||
override def bank(compilationOptions: CompilationOptions): Int = declaredBank.getOrElse(0)
|
||||
}
|
||||
|
||||
case class RelativeArray(name: String, address: Constant, sizeInBytes: Int) extends MfArray {
|
||||
override def toAddress: Constant = address
|
||||
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||
|
||||
override def bank(compilationOptions: CompilationOptions): Int = declaredBank.getOrElse(0)
|
||||
}
|
||||
|
||||
case class InitializedArray(name: String, address: Option[Constant], contents: List[Constant]) extends MfArray with PreallocableThing {
|
||||
override def shouldGenerate = true
|
||||
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||
|
||||
override def bank(compilationOptions: CompilationOptions): Int = declaredBank.getOrElse(0)
|
||||
}
|
||||
|
||||
case class RelativeVariable(name: String, address: Constant, typ: Type, zeropage: Boolean) extends VariableInMemory {
|
||||
@ -198,6 +227,12 @@ case class MacroFunction(name: String,
|
||||
|
||||
sealed trait FunctionInMemory extends MangledFunction with ThingInMemory {
|
||||
def environment: Environment
|
||||
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean =
|
||||
compilationOptions.flag(CompilationFlag.LargeCode) && farFlag.getOrElse(true)
|
||||
|
||||
override def bank(compilationOptions: CompilationOptions): Int =
|
||||
declaredBank.getOrElse(compilationOptions.platform.defaultCodeBank)
|
||||
}
|
||||
|
||||
case class ExternFunction(name: String,
|
||||
|
@ -61,7 +61,7 @@ case class HalfWordExpression(expression: Expression, hiByte: Boolean) extends E
|
||||
}
|
||||
|
||||
object Register extends Enumeration {
|
||||
val A, X, Y, AX, AY, YA, XA, XY, YX = Value
|
||||
val A, X, Y, AX, AY, YA, XA, XY, YX, AW = Value
|
||||
}
|
||||
|
||||
//case class Indexing(child: Expression, register: Register.Value) extends Expression
|
||||
|
@ -1,6 +1,6 @@
|
||||
package millfork.output
|
||||
|
||||
import millfork.assembly.opt.{AssemblyOptimization, JumpShortening}
|
||||
import millfork.assembly.opt.{AssemblyOptimization, HudsonOptimizations, JumpShortening}
|
||||
import millfork.assembly.{AddrMode, AssemblyLine, Opcode}
|
||||
import millfork.compiler.{CompilationContext, MfCompiler}
|
||||
import millfork.env._
|
||||
@ -116,6 +116,16 @@ class Assembler(private val program: Program, private val rootEnv: Environment)
|
||||
}
|
||||
}
|
||||
|
||||
def extractBank(c: Constant, options: CompilationOptions): Byte = {
|
||||
c.quickSimplify match {
|
||||
case NumericConstant(nn, _) => nn.>>(16).toInt.&(0xff).toByte
|
||||
case MemoryAddressConstant(th) => th.bank(options).toByte
|
||||
case CompoundConstant(MathOperator.Plus, a, b) => (extractBank(a, options) + extractBank(b, options)).toByte
|
||||
case CompoundConstant(MathOperator.Minus, a, b) => (extractBank(a, options) - extractBank(b, options)).toByte
|
||||
case _ => ErrorReporting.fatal("Failed to extract bank number from constant " + c)
|
||||
}
|
||||
}
|
||||
|
||||
private def parseNormalToDecimalValue(a: Long): Long = {
|
||||
if (a < 0) -parseNormalToDecimalValue(-a)
|
||||
var x = a
|
||||
@ -314,7 +324,7 @@ class Assembler(private val program: Program, private val rootEnv: Environment)
|
||||
ErrorReporting.debug("Compiling: " + f.name, f.position)
|
||||
val unoptimized =
|
||||
MfCompiler.compile(CompilationContext(env = f.environment, function = f, extraStackOffset = 0, options = options)).flatMap {
|
||||
case AssemblyLine(Opcode.JSR, _, p, true) if inlinedFunctions.contains(p.toString) =>
|
||||
case AssemblyLine(Opcode.JSR, AddrMode.Absolute | AddrMode.LongAbsolute, p, true) if inlinedFunctions.contains(p.toString) =>
|
||||
val labelPrefix = MfCompiler.nextLabel("ai")
|
||||
inlinedFunctions(p.toString).map{
|
||||
case line@AssemblyLine(_, _, MemoryAddressConstant(Label(label)), _) =>
|
||||
@ -336,7 +346,10 @@ class Assembler(private val program: Program, private val rootEnv: Environment)
|
||||
val code = optimizations.foldLeft(unoptimized) { (c, opt) =>
|
||||
opt.optimize(f, c, options)
|
||||
}
|
||||
if (optimizations.nonEmpty) JumpShortening(f, JumpShortening(f, code, options), options)
|
||||
if (optimizations.nonEmpty) {
|
||||
val finalCode = if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.removeLoadZero(code) else code
|
||||
JumpShortening(f, JumpShortening(f, finalCode, options), options)
|
||||
}
|
||||
else code
|
||||
}
|
||||
|
||||
@ -361,14 +374,19 @@ class Assembler(private val program: Program, private val rootEnv: Environment)
|
||||
writeByte(0, index, Assembler.opcodeFor(op, Relative, options))
|
||||
writeByte(0, index + 1, param - (index + 2))
|
||||
index += 2
|
||||
case AssemblyLine(op, am@(Immediate | ZeroPage | ZeroPageX | ZeroPageY | IndexedY | IndexedX | ZeroPageIndirect), param, _) =>
|
||||
case AssemblyLine(op, am@(Immediate | ZeroPage | ZeroPageX | ZeroPageY | IndexedY | IndexedX | IndexedZ | LongIndexedY | LongIndexedZ | Stack), param, _) =>
|
||||
writeByte(0, index, Assembler.opcodeFor(op, am, options))
|
||||
writeByte(0, index + 1, param)
|
||||
index += 2
|
||||
case AssemblyLine(op, am@(Absolute | AbsoluteY | AbsoluteX | Indirect | AbsoluteIndexedX), param, _) =>
|
||||
case AssemblyLine(op, am@(WordImmediate | Absolute | AbsoluteY | AbsoluteX | Indirect | AbsoluteIndexedX), param, _) =>
|
||||
writeByte(0, index, Assembler.opcodeFor(op, am, options))
|
||||
writeWord(0, index + 1, param)
|
||||
index += 3
|
||||
case AssemblyLine(op, am@(LongAbsolute | LongAbsoluteX | LongIndirect), param, _) =>
|
||||
writeByte(0, index, Assembler.opcodeFor(op, am, options))
|
||||
writeWord(0, index + 1, param)
|
||||
writeByte(0, index + 3, extractBank(param, options))
|
||||
index += 4
|
||||
}
|
||||
}
|
||||
index
|
||||
@ -379,44 +397,66 @@ object Assembler {
|
||||
val opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val illegalOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val cmosOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val cmosNopOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val ce02Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val hudsonOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val emulation65816Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
val native65816Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
||||
|
||||
def opcodeFor(opcode: Opcode.Value, addrMode: AddrMode.Value, options: CompilationOptions): Byte = {
|
||||
val key = opcode -> addrMode
|
||||
opcodes.get(key) match {
|
||||
case Some(v) => v
|
||||
case None =>
|
||||
illegalOpcodes.get(key) match {
|
||||
case Some(v) =>
|
||||
if (options.flag(CompilationFlag.EmitIllegals)) v
|
||||
else ErrorReporting.fatal("Cannot assemble an illegal opcode " + key)
|
||||
case None =>
|
||||
cmosOpcodes.get(key) match {
|
||||
case Some(v) =>
|
||||
if (options.flag(CompilationFlag.EmitCmosOpcodes)) v
|
||||
else ErrorReporting.fatal("Cannot assemble a CMOS opcode " + key)
|
||||
case None =>
|
||||
ErrorReporting.fatal("Cannot assemble an unknown opcode " + key)
|
||||
}
|
||||
}
|
||||
}
|
||||
opcodes.get(key).foreach(return _)
|
||||
if (options.flag(CompilationFlag.EmitIllegals)) illegalOpcodes.get(key).foreach(return _)
|
||||
if (options.flag(CompilationFlag.EmitCmosOpcodes)) cmosOpcodes.get(key).foreach(return _)
|
||||
if (options.flag(CompilationFlag.EmitCmosNopOpcodes)) cmosNopOpcodes.get(key).foreach(return _)
|
||||
if (options.flag(CompilationFlag.Emit65CE02Opcodes)) ce02Opcodes.get(key).foreach(return _)
|
||||
if (options.flag(CompilationFlag.EmitHudsonOpcodes)) hudsonOpcodes.get(key).foreach(return _)
|
||||
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) emulation65816Opcodes.get(key).foreach(return _)
|
||||
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) native65816Opcodes.get(key).foreach(return _)
|
||||
ErrorReporting.fatal("Cannot assemble an unknown opcode " + key)
|
||||
}
|
||||
|
||||
private def op(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
||||
if (x < 0 || x > 0xff) ???
|
||||
if (x < 0 || x > 0xff) ErrorReporting.fatal("Invalid code for" + (op -> am))
|
||||
opcodes(op -> am) = x.toByte
|
||||
if (am == AddrMode.Relative) opcodes(op -> AddrMode.Immediate) = x.toByte
|
||||
}
|
||||
|
||||
private def cm(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
||||
if (x < 0 || x > 0xff) ???
|
||||
if (x < 0 || x > 0xff) ErrorReporting.fatal("Invalid code for" + (op -> am))
|
||||
cmosOpcodes(op -> am) = x.toByte
|
||||
}
|
||||
|
||||
private def cn(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
||||
if (x < 0 || x > 0xff) ErrorReporting.fatal("Invalid code for" + (op -> am))
|
||||
cmosNopOpcodes(op -> am) = x.toByte
|
||||
}
|
||||
|
||||
private def il(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
||||
if (x < 0 || x > 0xff) ???
|
||||
if (x < 0 || x > 0xff) ErrorReporting.fatal("Invalid code for" + (op -> am))
|
||||
illegalOpcodes(op -> am) = x.toByte
|
||||
}
|
||||
|
||||
private def hu(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
||||
if (x < 0 || x > 0xff) ErrorReporting.fatal("Invalid code for" + (op -> am))
|
||||
hudsonOpcodes(op -> am) = x.toByte
|
||||
}
|
||||
|
||||
private def ce(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
||||
if (x < 0 || x > 0xff) ErrorReporting.fatal("Invalid code for" + (op -> am))
|
||||
ce02Opcodes(op -> am) = x.toByte
|
||||
}
|
||||
|
||||
private def em(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
||||
if (x < 0 || x > 0xff) ErrorReporting.fatal("Invalid code for" + (op -> am))
|
||||
emulation65816Opcodes(op -> am) = x.toByte
|
||||
}
|
||||
|
||||
private def na(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
||||
if (x < 0 || x > 0xff) ErrorReporting.fatal("Invalid code for" + (op -> am))
|
||||
native65816Opcodes(op -> am) = x.toByte
|
||||
}
|
||||
|
||||
def getStandardLegalOpcodes: Set[Int] = opcodes.values.map(_ & 0xff).toSet
|
||||
|
||||
import AddrMode._
|
||||
@ -617,6 +657,7 @@ object Assembler {
|
||||
il(AHX, AbsoluteY, 0x9F)
|
||||
il(SAX, IndexedX, 0x83)
|
||||
il(AHX, IndexedY, 0x93)
|
||||
il(SHY, AbsoluteX, 0x9C)
|
||||
|
||||
il(ANC, Immediate, 0x0B)
|
||||
il(ALR, Immediate, 0x4B)
|
||||
@ -679,10 +720,10 @@ object Assembler {
|
||||
il(NOP, Absolute, 0x5C)
|
||||
il(NOP, AbsoluteX, 0x1C)
|
||||
|
||||
cm(NOP, Immediate, 0x02)
|
||||
cm(NOP, ZeroPage, 0x44)
|
||||
cm(NOP, ZeroPageX, 0x54)
|
||||
cm(NOP, Absolute, 0x5C)
|
||||
cn(NOP, Immediate, 0x02)
|
||||
cn(NOP, ZeroPage, 0x44)
|
||||
cn(NOP, ZeroPageX, 0x54)
|
||||
cn(NOP, Absolute, 0x5C)
|
||||
|
||||
cm(STZ, ZeroPage, 0x64)
|
||||
cm(STZ, ZeroPageX, 0x74)
|
||||
@ -694,14 +735,14 @@ object Assembler {
|
||||
cm(PLX, Implied, 0xFA)
|
||||
cm(PLY, Implied, 0x7A)
|
||||
|
||||
cm(ORA, ZeroPageIndirect, 0x12)
|
||||
cm(AND, ZeroPageIndirect, 0x32)
|
||||
cm(EOR, ZeroPageIndirect, 0x52)
|
||||
cm(ADC, ZeroPageIndirect, 0x72)
|
||||
cm(STA, ZeroPageIndirect, 0x92)
|
||||
cm(LDA, ZeroPageIndirect, 0xB2)
|
||||
cm(CMP, ZeroPageIndirect, 0xD2)
|
||||
cm(SBC, ZeroPageIndirect, 0xF2)
|
||||
cm(ORA, IndexedZ, 0x12)
|
||||
cm(AND, IndexedZ, 0x32)
|
||||
cm(EOR, IndexedZ, 0x52)
|
||||
cm(ADC, IndexedZ, 0x72)
|
||||
cm(STA, IndexedZ, 0x92)
|
||||
cm(LDA, IndexedZ, 0xB2)
|
||||
cm(CMP, IndexedZ, 0xD2)
|
||||
cm(SBC, IndexedZ, 0xF2)
|
||||
|
||||
cm(TSB, ZeroPage, 0x04)
|
||||
cm(TSB, Absolute, 0x0C)
|
||||
@ -717,4 +758,119 @@ object Assembler {
|
||||
cm(WAI, Implied, 0xCB)
|
||||
cm(STP, Implied, 0xDB)
|
||||
|
||||
ce(CPZ, Immediate, 0xC2)
|
||||
ce(CPZ, ZeroPage, 0xD4)
|
||||
ce(CPZ, Absolute, 0xDC)
|
||||
ce(DEZ, Implied, 0x3B)
|
||||
ce(INZ, Implied,0x1B )
|
||||
ce(DEC_W, ZeroPage, 0xC3)
|
||||
ce(INC_W, ZeroPage, 0xE3)
|
||||
ce(ASL_W, Absolute, 0xCB)
|
||||
// TODO: or is it ROL_W?
|
||||
ce(ROR_W, Absolute, 0xEB)
|
||||
ce(ASR, Implied, 0x43)
|
||||
ce(ASR, ZeroPage, 0x44)
|
||||
ce(ASR, ZeroPageX, 0x54)
|
||||
ce(LDZ, Immediate, 0xA3)
|
||||
ce(LDZ, Absolute, 0xAB)
|
||||
ce(LDZ, AbsoluteX, 0xBB)
|
||||
ce(TAB, Implied, 0x5B)
|
||||
ce(TBA, Implied, 0x7B)
|
||||
ce(TAZ, Implied, 0x4B)
|
||||
ce(TZA, Implied, 0x6B)
|
||||
ce(TSY, Implied, 0x0B)
|
||||
ce(TYS, Implied, 0x2B)
|
||||
ce(PHW, WordImmediate, 0xF4)
|
||||
ce(PHW, Absolute, 0xFC)
|
||||
ce(PHZ, Implied, 0xDB)
|
||||
ce(PLZ, Implied, 0xFB)
|
||||
// ce(CLE, Implied, )
|
||||
// ce(SEE, Implied, )
|
||||
// ce(BSR, , )
|
||||
|
||||
hu(CLY, Implied, 0xC2)
|
||||
hu(CLX, Implied, 0x82)
|
||||
hu(CLA, Implied, 0x62)
|
||||
hu(CSH, Implied, 0xD4)
|
||||
hu(CSL, Implied, 0x54)
|
||||
hu(HuSAX, Implied, 0x22)
|
||||
hu(SAY, Implied, 0x42)
|
||||
hu(SXY, Implied, 0x02)
|
||||
hu(TAM, Immediate, 0x53)
|
||||
hu(TMA, Immediate, 0x43)
|
||||
|
||||
em(ORA, Stack, 0x03)
|
||||
em(ORA, IndexedSY, 0x13)
|
||||
na(ORA, LongIndexedZ, 0x07)
|
||||
na(ORA, LongIndexedY, 0x17)
|
||||
na(ORA, LongAbsolute, 0x0F)
|
||||
na(ORA, LongAbsoluteX, 0x1F)
|
||||
em(AND, Stack, 0x23)
|
||||
em(AND, IndexedSY, 0x33)
|
||||
na(AND, LongIndexedZ, 0x27)
|
||||
na(AND, LongIndexedY, 0x37)
|
||||
na(AND, LongAbsolute, 0x2F)
|
||||
na(AND, LongAbsoluteX, 0x3F)
|
||||
em(EOR, Stack, 0x43)
|
||||
em(EOR, IndexedSY, 0x53)
|
||||
na(EOR, LongIndexedZ, 0x47)
|
||||
na(EOR, LongIndexedY, 0x57)
|
||||
na(EOR, LongAbsolute, 0x4F)
|
||||
na(EOR, LongAbsoluteX, 0x5F)
|
||||
em(ADC, Stack, 0x63)
|
||||
em(ADC, IndexedSY, 0x73)
|
||||
na(ADC, LongIndexedZ, 0x67)
|
||||
na(ADC, LongIndexedY, 0x77)
|
||||
na(ADC, LongAbsolute, 0x6F)
|
||||
na(ADC, LongAbsoluteX, 0x7F)
|
||||
em(STA, Stack, 0x83)
|
||||
em(STA, IndexedSY, 0x93)
|
||||
na(STA, LongIndexedZ, 0x87)
|
||||
na(STA, LongIndexedY, 0x97)
|
||||
na(STA, LongAbsolute, 0x8F)
|
||||
na(STA, LongAbsoluteX, 0x9F)
|
||||
em(LDA, Stack, 0xA3)
|
||||
em(LDA, IndexedSY, 0xB3)
|
||||
na(LDA, LongIndexedZ, 0xA7)
|
||||
na(LDA, LongIndexedY, 0xB7)
|
||||
na(LDA, LongAbsolute, 0xAF)
|
||||
na(LDA, LongAbsoluteX, 0xBF)
|
||||
em(CMP, Stack, 0xA3)
|
||||
em(CMP, IndexedSY, 0xB3)
|
||||
na(CMP, LongIndexedZ, 0xA7)
|
||||
na(CMP, LongIndexedY, 0xB7)
|
||||
na(CMP, LongAbsolute, 0xAF)
|
||||
na(CMP, LongAbsoluteX, 0xBF)
|
||||
|
||||
em(COP, Immediate, 0x02)
|
||||
em(XBA, Implied, 0xEB)
|
||||
em(TXY, Implied, 0x9B)
|
||||
em(TYX, Implied, 0xBB)
|
||||
|
||||
|
||||
na(RTL, Implied, 0x6B)
|
||||
na(JMP, LongAbsolute, 0x5C)
|
||||
na(JMP, LongIndirect, 0x7C)
|
||||
na(BRL, LongRelative, 0x82)
|
||||
|
||||
em(PHD, Implied, 0x0B)
|
||||
em(PLD, Implied, 0x2B)
|
||||
em(PHB, Implied, 0x8B)
|
||||
em(PLB, Implied, 0xAB)
|
||||
em(PHK, Implied, 0x4B)
|
||||
|
||||
na(REP, Immediate, 0xC2)
|
||||
na(SEP, Immediate, 0xE2)
|
||||
|
||||
na(XCE, Implied, 0xFB)
|
||||
na(TCD, Implied, 0x5B)
|
||||
na(TDC, Implied, 0x7B)
|
||||
na(TSC, Implied, 0x3B)
|
||||
na(TCS, Implied, 0x1B)
|
||||
|
||||
for {
|
||||
((narrow, am), code) <- emulation65816Opcodes ++ opcodes ++ cmosOpcodes ++ native65816Opcodes
|
||||
wide <- Opcode.widen(narrow)
|
||||
} na(wide, if (am == Immediate) WordImmediate else am, code & 0xff)
|
||||
|
||||
}
|
||||
|
@ -76,12 +76,13 @@ object InliningCalculator {
|
||||
case _ => Nil
|
||||
}
|
||||
|
||||
private val badOpcodes = Set(RTI, RTS, JSR, BRK) ++ OpcodeClasses.ChangesStack
|
||||
private val badOpcodes = Set(RTI, RTS, JSR, BRK, RTL, BSR) ++ OpcodeClasses.ChangesStack
|
||||
private val jumpingRelatedOpcodes = Set(LABEL, JMP) ++ OpcodeClasses.ShortBranching
|
||||
|
||||
def codeForInlining(fname: String, code: List[AssemblyLine]): Option[List[AssemblyLine]] = {
|
||||
if (code.isEmpty) return None
|
||||
if (code.last.opcode != RTS) return None
|
||||
val lastOpcode = code.last.opcode
|
||||
if (lastOpcode != RTS && lastOpcode != RTL) return None
|
||||
var result = code.init
|
||||
while (result.nonEmpty && OpcodeClasses.NoopDiscardsFlags(result.last.opcode)) {
|
||||
result = result.init
|
||||
|
@ -88,7 +88,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
|
||||
for {
|
||||
p <- position()
|
||||
minus <- "-".!.?
|
||||
_ <- P("0x" | "$") ~/ Pass
|
||||
_ <- P("0x" | "0X" | "$") ~/ Pass
|
||||
s <- CharsWhileIn("1234567890abcdefABCDEF", min = 1).!.opaque("<hex digits>")
|
||||
} yield {
|
||||
val abs = Integer.parseInt(s, 16)
|
||||
@ -96,7 +96,31 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
|
||||
LiteralExpression(value, size(value, s.length > 2, s.length > 4)).pos(p)
|
||||
}
|
||||
|
||||
val literalAtom: P[LiteralExpression] = binaryAtom | hexAtom | decimalAtom
|
||||
val octalAtom: P[LiteralExpression] =
|
||||
for {
|
||||
p <- position()
|
||||
minus <- "-".!.?
|
||||
_ <- P("0o" | "0O") ~/ Pass
|
||||
s <- CharsWhileIn("01234567", min = 1).!.opaque("<octal digits>")
|
||||
} yield {
|
||||
val abs = Integer.parseInt(s, 8)
|
||||
val value = sign(abs, minus.isDefined)
|
||||
LiteralExpression(value, size(value, s.length > 3, s.length > 6)).pos(p)
|
||||
}
|
||||
|
||||
val quaternaryAtom: P[LiteralExpression] =
|
||||
for {
|
||||
p <- position()
|
||||
minus <- "-".!.?
|
||||
_ <- P("0q" | "0Q") ~/ Pass
|
||||
s <- CharsWhileIn("0123", min = 1).!.opaque("<quaternary digits>")
|
||||
} yield {
|
||||
val abs = Integer.parseInt(s, 4)
|
||||
val value = sign(abs, minus.isDefined)
|
||||
LiteralExpression(value, size(value, s.length > 4, s.length > 8)).pos(p)
|
||||
}
|
||||
|
||||
val literalAtom: P[LiteralExpression] = binaryAtom | hexAtom | octalAtom | quaternaryAtom | decimalAtom
|
||||
|
||||
val atom: P[Expression] = P(literalAtom | (position() ~ identifier).map { case (p, i) => VariableExpression(i).pos(p) })
|
||||
|
||||
@ -307,13 +331,19 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
|
||||
|
||||
val commaX = HWS ~ "," ~ HWS ~ ("X" | "x") ~ HWS
|
||||
val commaY = HWS ~ "," ~ HWS ~ ("Y" | "y") ~ HWS
|
||||
val commaZ = HWS ~ "," ~ HWS ~ ("Z" | "z") ~ HWS
|
||||
val commaS = HWS ~ "," ~ HWS ~ ("S" | "s") ~ HWS
|
||||
|
||||
def asmParameter: P[(AddrMode.Value, Expression)] = {
|
||||
(SWS ~ (
|
||||
("##" ~ asmExpression).map(AddrMode.WordImmediate -> _) |
|
||||
("#" ~ asmExpression).map(AddrMode.Immediate -> _) |
|
||||
("(" ~ HWS ~ asmExpression ~ HWS ~ ")" ~ commaY).map(AddrMode.IndexedY -> _) |
|
||||
("(" ~ HWS ~ asmExpression ~ commaS ~ ")" ~ commaY).map(AddrMode.IndexedSY -> _) |
|
||||
("(" ~ HWS ~ asmExpression ~ HWS ~ ")" ~ commaZ).map(AddrMode.IndexedZ -> _) |
|
||||
("(" ~ HWS ~ asmExpression ~ commaX ~ ")").map(AddrMode.IndexedX -> _) |
|
||||
("(" ~ HWS ~ asmExpression ~ HWS ~ ")").map(AddrMode.Indirect -> _) |
|
||||
(asmExpression ~ commaS).map(AddrMode.Stack -> _) |
|
||||
(asmExpression ~ commaX).map(AddrMode.AbsoluteX -> _) |
|
||||
(asmExpression ~ commaY).map(AddrMode.AbsoluteY -> _) |
|
||||
asmExpression.map(AddrMode.Absolute -> _)
|
||||
@ -325,7 +355,13 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
|
||||
def asmInstruction: P[ExecutableStatement] = {
|
||||
val lineParser: P[(Boolean, Opcode.Value, (AddrMode.Value, Expression))] = !"}" ~ elidable ~/ asmOpcode ~/ asmParameter
|
||||
lineParser.map { case (elid, op, param) =>
|
||||
AssemblyStatement(op, param._1, param._2, elid)
|
||||
(op, param._1) match {
|
||||
case (Opcode.SAX, AddrMode.Implied) => AssemblyStatement(Opcode.HuSAX, param._1, param._2, elid)
|
||||
case (Opcode.SBX, AddrMode.Immediate) => AssemblyStatement(Opcode.SBX, param._1, param._2, elid)
|
||||
case (Opcode.SAY, AddrMode.AbsoluteX) => AssemblyStatement(Opcode.SHY, param._1, param._2, elid)
|
||||
case (Opcode.SBX, _) => AssemblyStatement(Opcode.SAX, param._1, param._2, elid)
|
||||
case _ => AssemblyStatement(op, param._1, param._2, elid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuUnoptimizedRun}
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuCmosBenchmarkRun}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ import org.scalatest.{FunSuite, Matchers}
|
||||
class StackVarSuite extends FunSuite with Matchers {
|
||||
|
||||
test("Basic stack assignment") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| stack byte a
|
||||
@ -23,7 +23,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Stack byte addition") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| stack byte a
|
||||
@ -41,7 +41,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Complex expressions involving stack variables") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| stack byte a
|
||||
@ -74,7 +74,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
// }
|
||||
|
||||
test("Stack word addition") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| stack word a
|
||||
@ -92,7 +92,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Recursion") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| array output [6] @$c000
|
||||
| byte fails @$c010
|
||||
| void main () {
|
||||
@ -129,7 +129,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
|
||||
|
||||
test("Indexing") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| array output [200] @$c000
|
||||
| void main () {
|
||||
| stack byte a
|
||||
|
@ -1,5 +1,5 @@
|
||||
package millfork.test
|
||||
import millfork.test.emu.EmuBenchmarkRun
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuCmosBenchmarkRun}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
@ -8,7 +8,7 @@ import org.scalatest.{FunSuite, Matchers}
|
||||
class WordMathSuite extends FunSuite with Matchers {
|
||||
|
||||
test("Word addition") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| word output @$c000
|
||||
| word a
|
||||
| void main () {
|
||||
@ -20,7 +20,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word subtraction") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| word output @$c000
|
||||
| word a
|
||||
| void main () {
|
||||
@ -32,7 +32,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word subtraction 2") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| word output @$c000
|
||||
| word a
|
||||
| void main () {
|
||||
@ -44,7 +44,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Byte-to-word addition") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| word output @$c000
|
||||
| word pair
|
||||
| void main () {
|
||||
@ -57,7 +57,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Literal addition") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| output = 640
|
||||
@ -67,7 +67,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Array element addition") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| word output @$c000
|
||||
| word pair
|
||||
| array b[2]
|
||||
@ -84,7 +84,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("nesdev.com example") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| byte output @$c000
|
||||
| array map [256] @$c300
|
||||
| array b[2]
|
||||
@ -102,7 +102,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("hi()/lo()") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCmosBenchmarkRun("""
|
||||
| array output [7] @$c000
|
||||
| void main () {
|
||||
| output[0] = lo(33)
|
||||
|
@ -7,9 +7,18 @@ import millfork.output.MemoryBank
|
||||
*/
|
||||
object EmuCmosBenchmarkRun {
|
||||
def apply(source:String)(verifier: MemoryBank=>Unit) = {
|
||||
println(f"Compiling for NMOS (unoptimized)")
|
||||
val (Timings(_, t0), m0) = EmuUnoptimizedRun.apply2(source)
|
||||
println(f"Compiling for NMOS")
|
||||
val (Timings(_, t1), m1) = EmuOptimizedRun.apply2(source)
|
||||
println(f"Compiling for CMOS")
|
||||
val (Timings(_, t2), m2) = EmuOptimizedCmosRun.apply2(source)
|
||||
println(f"Compiling for HuC6280")
|
||||
EmuOptimizedHudsonRun.apply2(source)
|
||||
println(f"Compiling for 65CE02")
|
||||
EmuOptimized65CE02Run.apply2(source)
|
||||
println(f"Compiling for 65816")
|
||||
EmuOptimized65816Run.apply2(source)
|
||||
println(f"Before optimization: $t0%7d")
|
||||
println(f"After NMOS optimization: $t1%7d")
|
||||
println(f"After CMOS optimization: $t2%7d")
|
||||
|
21
src/test/scala/millfork/test/emu/EmuOptimized65816Run.scala
Normal file
21
src/test/scala/millfork/test/emu/EmuOptimized65816Run.scala
Normal file
@ -0,0 +1,21 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import millfork.assembly.opt.{CmosOptimizations, SixteenOptimizations}
|
||||
import millfork.{Cpu, OptimizationPresets}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object EmuOptimized65816Run extends EmuRun(
|
||||
Cpu.Sixteen,
|
||||
OptimizationPresets.NodeOpt,
|
||||
OptimizationPresets.AssOpt ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good,
|
||||
false)
|
||||
|
||||
|
||||
|
19
src/test/scala/millfork/test/emu/EmuOptimized65CE02Run.scala
Normal file
19
src/test/scala/millfork/test/emu/EmuOptimized65CE02Run.scala
Normal file
@ -0,0 +1,19 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import millfork.assembly.opt.{CE02Optimizations, CmosOptimizations}
|
||||
import millfork.{Cpu, OptimizationPresets}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object EmuOptimized65CE02Run extends EmuRun(
|
||||
Cpu.CE02,
|
||||
OptimizationPresets.NodeOpt,
|
||||
OptimizationPresets.AssOpt ++
|
||||
CmosOptimizations.All ++ CE02Optimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ CE02Optimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ CE02Optimizations.All ++ OptimizationPresets.Good,
|
||||
false)
|
||||
|
||||
|
||||
|
19
src/test/scala/millfork/test/emu/EmuOptimizedHudsonRun.scala
Normal file
19
src/test/scala/millfork/test/emu/EmuOptimizedHudsonRun.scala
Normal file
@ -0,0 +1,19 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import millfork.assembly.opt.{CmosOptimizations, HudsonOptimizations}
|
||||
import millfork.{Cpu, OptimizationPresets}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object EmuOptimizedHudsonRun extends EmuRun(
|
||||
Cpu.HuC6280,
|
||||
OptimizationPresets.NodeOpt,
|
||||
OptimizationPresets.AssOpt ++
|
||||
CmosOptimizations.All ++ HudsonOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ HudsonOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ HudsonOptimizations.All ++ OptimizationPresets.Good,
|
||||
false)
|
||||
|
||||
|
||||
|
@ -97,7 +97,10 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
CompilationFlag.DetailedFlowAnalysis -> quantum,
|
||||
CompilationFlag.InlineFunctions -> this.inline,
|
||||
CompilationFlag.CompactReturnDispatchParams -> true,
|
||||
CompilationFlag.EmitCmosOpcodes -> (platform.cpu == millfork.Cpu.Cmos),
|
||||
CompilationFlag.EmitCmosOpcodes -> millfork.Cpu.CmosCompatible.contains(platform.cpu),
|
||||
CompilationFlag.EmitEmulation65816Opcodes -> (platform.cpu == millfork.Cpu.Sixteen),
|
||||
CompilationFlag.Emit65CE02Opcodes -> (platform.cpu == millfork.Cpu.CE02),
|
||||
CompilationFlag.EmitHudsonOpcodes -> (platform.cpu == millfork.Cpu.HuC6280),
|
||||
CompilationFlag.OptimizeForSonicSpeed -> blastProcessing
|
||||
// CompilationFlag.CheckIndexOutOfBounds -> true,
|
||||
))
|
||||
@ -167,8 +170,11 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
case millfork.Cpu.Mos =>
|
||||
ErrorReporting.fatal("There's no NMOS emulator with decimal mode support")
|
||||
Timings(-1, -1) -> memoryBank
|
||||
case _ =>
|
||||
case millfork.Cpu.StrictMos | millfork.Cpu.StrictRicoh =>
|
||||
runViaSymon(memoryBank, platform.org, CpuBehavior.NMOS_6502)
|
||||
case _ =>
|
||||
ErrorReporting.trace("No emulation support for " + platform.cpu)
|
||||
Timings(-1, -1) -> memoryBank
|
||||
}
|
||||
case f: Failure[_, _] =>
|
||||
println(f)
|
||||
|
@ -10,8 +10,9 @@ class SymonTestRam(mem: MemoryBank) extends Device(0x0000, 0xffff, "RAM") {
|
||||
|
||||
mem.readable(1) = true
|
||||
mem.readable(2) = true
|
||||
mem.readable(0x23) = true
|
||||
|
||||
(0x100 to 0x1ff).foreach { stack =>
|
||||
(0 to 0x1ff).foreach { stack =>
|
||||
mem.writeable(stack) = true
|
||||
mem.readable(stack) = true
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user