1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-12-24 15:29:23 +00:00

Refactoring for the upcoming Z80 support

This commit is contained in:
Karol Stasiak 2018-06-12 22:46:20 +02:00
parent 70818cc3d2
commit 5281b5f527
79 changed files with 1652 additions and 1484 deletions

View File

@ -68,6 +68,17 @@ case class CompilationOptions(platform: Platform, commandLineFlags: Map[Compilat
}
}
object CpuFamily extends Enumeration {
val M6502, Z80, M6809, I8086, M65K, ARM = Value
def forType(cpu: Cpu.Value): CpuFamily.Value = {
import Cpu._
cpu match {
case Mos | StrictMos | Ricoh | StrictRicoh | Cmos | HuC6280 | CE02 | Sixteen => M6502
}
}
}
object Cpu extends Enumeration {
val Mos, StrictMos, Ricoh, StrictRicoh, Cmos, HuC6280, CE02, Sixteen = Value
@ -76,27 +87,27 @@ object Cpu extends Enumeration {
import CompilationFlag._
private val alwaysDefaultFlags = Set(
private val mosAlwaysDefaultFlags = Set(
VariableOverlap, CompactReturnDispatchParams, ZeropagePseudoregister
)
def defaultFlags(x: Cpu.Value): Set[CompilationFlag.Value] = x match {
case StrictMos =>
alwaysDefaultFlags ++ Set(DecimalMode, PreventJmpIndirectBug)
mosAlwaysDefaultFlags ++ Set(DecimalMode, PreventJmpIndirectBug)
case Mos =>
alwaysDefaultFlags ++ Set(DecimalMode, PreventJmpIndirectBug)
mosAlwaysDefaultFlags ++ Set(DecimalMode, PreventJmpIndirectBug)
case Ricoh =>
alwaysDefaultFlags ++ Set(PreventJmpIndirectBug)
mosAlwaysDefaultFlags ++ Set(PreventJmpIndirectBug)
case StrictRicoh =>
alwaysDefaultFlags ++ Set(PreventJmpIndirectBug)
mosAlwaysDefaultFlags ++ Set(PreventJmpIndirectBug)
case Cmos =>
alwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes)
mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes)
case HuC6280 =>
alwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitHudsonOpcodes)
mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitHudsonOpcodes)
case CE02 =>
alwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, Emit65CE02Opcodes)
mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, Emit65CE02Opcodes)
case Sixteen =>
alwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes, ReturnWordsViaAccumulator)
mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes, ReturnWordsViaAccumulator)
}
def fromString(name: String): Cpu.Value = name match {

View File

@ -4,14 +4,16 @@ import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Paths}
import java.util.Locale
import millfork.assembly.opt._
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.opt._
import millfork.buildinfo.BuildInfo
import millfork.cli.{CliParser, CliStatus}
import millfork.compiler.mos.MosCompiler
import millfork.env.Environment
import millfork.error.ErrorReporting
import millfork.node.StandardCallGraph
import millfork.output.Assembler
import millfork.parser.SourceLoadingQueue
import millfork.output.{AbstractAssembler, AssemblerOutput, MosAssembler, MosInliningCalculator}
import millfork.parser.MosSourceLoadingQueue
/**
* @author Karol Stasiak
@ -59,7 +61,6 @@ object Main {
ErrorReporting.fatalQuit("No input files")
}
ErrorReporting.verbosity = c.verbosity.getOrElse(0)
val optLevel = c.optimizationLevel.getOrElse(0)
val platform = Platform.lookupPlatformFile(c.includePath, c.platform.getOrElse {
ErrorReporting.info("No platform selected, defaulting to `c64`")
"c64"
@ -84,52 +85,9 @@ object Main {
// }
// }).toMap
val unoptimized = new SourceLoadingQueue(
initialFilenames = c.inputFileNames,
includePath = c.includePath,
options = options).run()
val program = if (optLevel > 0) {
OptimizationPresets.NodeOpt.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
} else {
unoptimized
val result: AssemblerOutput = CpuFamily.forType(platform.cpu) match {
case CpuFamily.M6502 => assembleForMos(c, platform, options)
}
val callGraph = new StandardCallGraph(program)
val env = new Environment(None, "")
env.collectDeclarations(program, options)
val assemblyOptimizations = optLevel match {
case 0 => Nil
case 1 => OptimizationPresets.QuickPreset
case i if i >= 9 => List(SuperOptimizer)
case _ =>
val goodExtras = List(
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForEmulation else Nil,
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) SixteenOptimizations.AllForNative else Nil,
if (options.flag(CompilationFlag.ZeropagePseudoregister)) ZeropageRegisterOptimizations.All else Nil,
).flatten
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.EmitNative65816Opcodes)) SixteenOptimizations.AllForNative else Nil,
if (options.flag(CompilationFlag.DangerousOptimizations)) DangerousOptimizations.All else Nil,
).flatten
val goodCycle = List.fill(optLevel - 2)(OptimizationPresets.Good ++ goodExtras).flatten
goodCycle ++ OptimizationPresets.AssOpt ++ extras ++ goodCycle
}
// compile
val assembler = new Assembler(program, env, platform)
val result = assembler.assemble(callGraph, assemblyOptimizations, options)
ErrorReporting.assertNoErrors("Codegen failed")
ErrorReporting.debug(f"Unoptimized code size: ${assembler.unoptimizedCodeSize}%5d B")
ErrorReporting.debug(f"Optimized code size: ${assembler.optimizedCodeSize}%5d B")
ErrorReporting.debug(f"Gain: ${(100L * (assembler.unoptimizedCodeSize - assembler.optimizedCodeSize) / assembler.unoptimizedCodeSize.toDouble).round}%5d%%")
ErrorReporting.debug(f"Initialized variables: ${assembler.initializedVariablesSize}%5d B")
if (c.outputAssembly) {
val path = Paths.get(assOutput)
@ -174,6 +132,57 @@ object Main {
}
}
private def assembleForMos(c: Context, platform: Platform, options: CompilationOptions): AssemblerOutput = {
val optLevel = c.optimizationLevel.getOrElse(0)
val unoptimized = new MosSourceLoadingQueue(
initialFilenames = c.inputFileNames,
includePath = c.includePath,
options = options).run()
val program = if (optLevel > 0) {
OptimizationPresets.NodeOpt.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
} else {
unoptimized
}
val callGraph = new StandardCallGraph(program)
val env = new Environment(None, "")
env.collectDeclarations(program, options)
val assemblyOptimizations = optLevel match {
case 0 => Nil
case 1 => OptimizationPresets.QuickPreset
case i if i >= 9 => List(SuperOptimizer)
case _ =>
val goodExtras = List(
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForEmulation else Nil,
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) SixteenOptimizations.AllForNative else Nil,
if (options.flag(CompilationFlag.ZeropagePseudoregister)) ZeropageRegisterOptimizations.All else Nil,
).flatten
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.EmitNative65816Opcodes)) SixteenOptimizations.AllForNative else Nil,
if (options.flag(CompilationFlag.DangerousOptimizations)) DangerousOptimizations.All else Nil,
).flatten
val goodCycle = List.fill(optLevel - 2)(OptimizationPresets.Good ++ goodExtras).flatten
goodCycle ++ OptimizationPresets.AssOpt ++ extras ++ goodCycle
}
// compile
val assembler = new MosAssembler(program, env, platform)
val result = assembler.assemble(callGraph, assemblyOptimizations, options)
ErrorReporting.assertNoErrors("Codegen failed")
ErrorReporting.debug(f"Unoptimized code size: ${assembler.unoptimizedCodeSize}%5d B")
ErrorReporting.debug(f"Optimized code size: ${assembler.optimizedCodeSize}%5d B")
ErrorReporting.debug(f"Gain: ${(100L * (assembler.unoptimizedCodeSize - assembler.optimizedCodeSize) / assembler.unoptimizedCodeSize.toDouble).round}%5d%%")
ErrorReporting.debug(f"Initialized variables: ${assembler.initializedVariablesSize}%5d B")
result
}
private def parser = new CliParser[Context] {
fluff("Main options:", "")

View File

@ -1,6 +1,8 @@
package millfork
import millfork.assembly.opt._
import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.opt._
import millfork.node.opt.{UnreachableCode, UnusedFunctions, UnusedGlobalVariables, UnusedLocalVariables}
/**
@ -13,7 +15,7 @@ object OptimizationPresets {
UnusedLocalVariables,
UnusedGlobalVariables,
)
val AssOpt: List[AssemblyOptimization] = List[AssemblyOptimization](
val AssOpt: List[AssemblyOptimization[AssemblyLine]] = List[AssemblyOptimization[AssemblyLine]](
UnusedLabelRemoval,
AlwaysGoodOptimizations.NonetAddition,
AlwaysGoodOptimizations.NonetBitOp,
@ -135,7 +137,7 @@ object OptimizationPresets {
LaterOptimizations.UseBit,
)
val Good: List[AssemblyOptimization] = List[AssemblyOptimization](
val Good: List[AssemblyOptimization[AssemblyLine]] = List[AssemblyOptimization[AssemblyLine]](
UnusedLabelRemoval,
AlwaysGoodOptimizations.Adc0Optimization,
AlwaysGoodOptimizations.BitPackingUnpacking,
@ -204,7 +206,7 @@ object OptimizationPresets {
VariableToRegisterOptimization,
)
val QuickPreset: List[AssemblyOptimization] = List[AssemblyOptimization](
val QuickPreset: List[AssemblyOptimization[AssemblyLine]] = List[AssemblyOptimization[AssemblyLine]](
UnusedLabelRemoval,
AlwaysGoodOptimizations.Adc0Optimization,
AlwaysGoodOptimizations.BranchInPlaceRemoval,

View File

@ -0,0 +1,12 @@
package millfork.assembly
import millfork.env.Constant
/**
* @author Karol Stasiak
*/
trait AbstractCode {
def sizeInBytes: Int
def isPrintable: Boolean
def parameter: Constant
}

View File

@ -0,0 +1,58 @@
package millfork.assembly
/**
* @author Karol Stasiak
*/
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,
TripleAbsolute,
Undecided,
RawByte,
DoesNotExist = Value
def addrModeToMosString(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 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;
}
}
}

View File

@ -0,0 +1,13 @@
package millfork.assembly
import millfork.CompilationOptions
import millfork.env.NormalFunction
/**
* @author Karol Stasiak
*/
trait AssemblyOptimization[T <: AbstractCode] {
def name: String
def optimize(f: NormalFunction, code: List[T], options: CompilationOptions): List[T]
}

View File

@ -1,9 +1,10 @@
package millfork.assembly
package millfork.assembly.mos
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.Opcode._
import millfork.compiler.{CompilationContext, MfCompiler}
import millfork.assembly.{AbstractCode, AddrMode}
import millfork.assembly.mos.Opcode._
import millfork.compiler.mos.{CompilationContext, MosCompiler}
import millfork.env._
import millfork.{CompilationFlag, CompilationOptions}
//noinspection TypeAnnotation
object OpcodeClasses {
@ -378,7 +379,7 @@ object AssemblyLine {
List(AssemblyLine.immediate(opcode, 0))
} else if (opcodesForZeroedOrSignExtendedVariableOperation(opcode)) {
if (variable.typ.isSigned) {
val label = MfCompiler.nextLabel("sx")
val label = MosCompiler.nextLabel("sx")
AssemblyLine.variable(ctx, opcode, variable, variable.typ.size - 1) ++ List(
AssemblyLine.immediate(ORA, 0x7f),
AssemblyLine.relative(BMI, label),
@ -453,12 +454,12 @@ object AssemblyLine {
AssemblyLine(opcode, AddrMode.Stack, NumericConstant(addr & 0xff, 1))
}
case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var parameter: Constant, elidable: Boolean = true) {
case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var parameter: Constant, elidable: Boolean = true) extends AbstractCode {
import AddrMode._
import State._
import OpcodeClasses._
import State._
import Treatment._
def reads(state: State.Value): Boolean = state match {
@ -521,7 +522,7 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
}
}
def sizeInBytes: Int = addrMode match {
override def sizeInBytes: Int = addrMode match {
case Implied | RawByte => 1
case Relative | ZeroPageX | ZeroPage | ZeroPageY | IndexedZ | IndexedX | IndexedY | IndexedSY | Stack | LongIndexedY | LongIndexedZ | Immediate => 2
case AbsoluteIndexedX | AbsoluteX | Absolute | AbsoluteY | Indirect | LongRelative | WordImmediate => 3
@ -561,6 +562,6 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
case HuSAX => "SAX"
case _ => opcode.toString
}
s" $op ${AddrMode.addrModeToString(addrMode, parameter.toString)}"
s" $op ${AddrMode.addrModeToMosString(addrMode, parameter.toString)}"
}
}

View File

@ -1,4 +1,4 @@
package millfork.assembly
package millfork.assembly.mos
import java.util.Locale
@ -301,56 +301,3 @@ 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,
TripleAbsolute,
Undecided,
RawByte,
DoesNotExist = Value
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 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;
}
}
}

View File

@ -1,11 +1,12 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import java.util.concurrent.atomic.AtomicInteger
import millfork.assembly.AddrMode
import millfork.assembly.AddrMode._
import millfork.assembly.Opcode._
import millfork.assembly.OpcodeClasses._
import millfork.assembly.{opt, _}
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.OpcodeClasses._
import millfork.assembly.mos.{opt, _}
import millfork.env._
/**

View File

@ -1,9 +1,8 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly.AddrMode._
import millfork.assembly.AssemblyLine
import millfork.assembly.Opcode._
import millfork.assembly.OpcodeClasses._
import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.Opcode._
/**
* @author Karol Stasiak
@ -16,5 +15,5 @@ object CE02Optimizations {
(Elidable & HasOpcode(ROR)) ~~> (_ => List(AssemblyLine.implied(ASR))),
)
val All: List[AssemblyOptimization] = List(UseAsr)
val All: List[AssemblyOptimization[AssemblyLine]] = List(UseAsr)
}

View File

@ -1,7 +1,8 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly.{AssemblyLine, OpcodeClasses}
import millfork.assembly.mos.{AssemblyLine, OpcodeClasses}
import millfork.assembly.AssemblyOptimization
import millfork.env.NormalFunction
import millfork.error.ErrorReporting
@ -12,7 +13,7 @@ import millfork.error.ErrorReporting
object ChangeIndexRegisterOptimizationPreferringX2Y extends ChangeIndexRegisterOptimization(true)
object ChangeIndexRegisterOptimizationPreferringY2X extends ChangeIndexRegisterOptimization(false)
class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimization {
class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimization[AssemblyLine] {
object IndexReg extends Enumeration {
val X, Y = Value
@ -25,7 +26,7 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
import IndexReg._
import IndexDirection._
import millfork.assembly.AddrMode._
import millfork.assembly.Opcode._
import millfork.assembly.mos.Opcode._
type IndexReg = IndexReg.Value
type IndexDirection = IndexDirection.Value

View File

@ -1,9 +1,10 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly.{AssemblyLine, Opcode, State}
import millfork.assembly.Opcode._
import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.{AssemblyLine, Opcode, State}
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
import millfork.assembly.OpcodeClasses._
import millfork.assembly.mos.OpcodeClasses._
import millfork.env._
/**
@ -49,5 +50,5 @@ object CmosOptimizations {
(Elidable & HasX(0) & HasZ(0) & HasAddrMode(AbsoluteIndexedX) & HasOpcode(JMP)) ~~> (code => code.map(_.copy(addrMode = Indirect))),
)
val All: List[AssemblyOptimization] = List(OptimizeZeroIndex, SimplerBitFlipping, ZeroStoreAsStz)
val All: List[AssemblyOptimization[AssemblyLine]] = List(OptimizeZeroIndex, SimplerBitFlipping, ZeroStoreAsStz)
}

View File

@ -1,7 +1,8 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.OpcodeClasses
import millfork.env.{Label, MemoryAddressConstant, NormalFunction, NumericConstant}
/**
@ -32,7 +33,7 @@ object CoarseFlowAnalyzer {
changed = false
var currentStatus: CpuStatus = functionStartStatus
for (i <- codeArray.indices) {
import millfork.assembly.Opcode._
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
if (flagArray(i) != currentStatus) {
changed = true

View File

@ -1,6 +1,6 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly.State
import millfork.assembly.mos.State
/**
* @author Karol Stasiak

View File

@ -1,7 +1,8 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly._
import millfork.assembly.Opcode._
import millfork.assembly.mos._
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
import millfork.env._
@ -55,5 +56,5 @@ object DangerousOptimizations {
},
)
val All: List[AssemblyOptimization] = List(ConstantIndexOffsetPropagation)
val All: List[AssemblyOptimization[AssemblyLine]] = List(ConstantIndexOffsetPropagation)
}

View File

@ -1,9 +1,10 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly.AssemblyLine
import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.AssemblyLine
import millfork.env._
import millfork.assembly.Opcode._
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
import millfork.error.ErrorReporting
@ -12,7 +13,7 @@ import scala.collection.{immutable, mutable}
/**
* @author Karol Stasiak
*/
object EmptyMemoryStoreRemoval extends AssemblyOptimization {
object EmptyMemoryStoreRemoval extends AssemblyOptimization[AssemblyLine] {
override def name = "Removing pointless stores to automatic variables"
private val storeAddrModes = Set(Absolute, ZeroPage, AbsoluteX, AbsoluteY, ZeroPageX, ZeroPageY)

View File

@ -1,7 +1,7 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.{AssemblyLine, Opcode, State}
import millfork.CompilationOptions
import millfork.assembly.mos.{AssemblyLine, Opcode, State}
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
/**

View File

@ -1,7 +1,7 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly.Opcode
import millfork.assembly.Opcode._
import millfork.assembly.mos.Opcode
import millfork.assembly.mos.Opcode._
/**
* @author Karol Stasiak

View File

@ -1,7 +1,7 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly.Opcode
import millfork.assembly.Opcode._
import millfork.assembly.mos.Opcode
import millfork.assembly.mos.Opcode._
/**
* @author Karol Stasiak

View File

@ -1,7 +1,7 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly.Opcode
import millfork.assembly.Opcode._
import millfork.assembly.mos.Opcode
import millfork.assembly.mos.Opcode._
/**
* @author Karol Stasiak

View File

@ -1,7 +1,8 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly.AssemblyLine
import millfork.assembly.Opcode._
import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
import millfork.env.NumericConstant
@ -10,7 +11,7 @@ import millfork.env.NumericConstant
*/
object HudsonOptimizations {
val All: List[AssemblyOptimization] = List()
val All: List[AssemblyOptimization[AssemblyLine]] = List()
def removeLoadZero(code: List[AssemblyLine]): List[AssemblyLine] = code.map{
case AssemblyLine(LDA, Immediate, NumericConstant(0, _), true) => AssemblyLine.implied(CLA)

View File

@ -1,13 +1,13 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import java.util.concurrent.atomic.AtomicInteger
import millfork.assembly.Opcode._
import millfork.assembly.{AddrMode, AssemblyLine, Opcode}
import millfork.compiler.MfCompiler
import millfork.assembly.mos.{AssemblyLine, Opcode}
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
import millfork.error.ErrorReporting
import millfork.{CompilationFlag, CompilationOptions}
import millfork.CompilationOptions
/**
* @author Karol Stasiak

View File

@ -1,8 +1,9 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly.mos.AssemblyLine
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.{AddrMode, AssemblyLine, Opcode}
import millfork.assembly.Opcode._
import millfork.assembly.AddrMode
import millfork.assembly.mos.Opcode._
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
import millfork.error.ErrorReporting

View File

@ -1,9 +1,10 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly._
import millfork.assembly.Opcode._
import millfork.assembly.mos._
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
import millfork.assembly.OpcodeClasses._
import millfork.assembly.mos.OpcodeClasses._
import millfork.env.{Constant, NormalFunction, NumericConstant}
/**

View File

@ -1,19 +1,17 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.{CompilationFlag, CompilationOptions, NonOverlappingIntervals}
import millfork.assembly.{AddrMode, AssemblyLine, OpcodeClasses}
import millfork.assembly.Opcode._
import millfork.CompilationOptions
import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
import millfork.assembly.mos.{AssemblyLine, OpcodeClasses}
import millfork.env._
import millfork.error.ErrorReporting
import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
/**
* @author Karol Stasiak
*/
object LocalVariableReadOptimization extends AssemblyOptimization {
object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine] {
override def name: String = "Local variable read optimization"

View File

@ -1,11 +1,11 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import java.util.concurrent.atomic.AtomicInteger
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.AssemblyLine
import millfork.assembly.OpcodeClasses._
import millfork.assembly.Opcode._
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.OpcodeClasses._
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
import millfork.env.{Constant, Label, MemoryAddressConstant}

View File

@ -1,10 +1,11 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly._
import millfork.assembly.mos._
import millfork.env._
import millfork.error.ErrorReporting
import millfork.node.Register
import millfork.node.MosRegister
import scala.collection.immutable
@ -112,7 +113,7 @@ object ReverseFlowAnalyzer {
changed = false
var currentImportance: CpuImportance = finalImportance
for (i <- codeArray.indices.reverse) {
import millfork.assembly.Opcode._
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
if (importanceArray(i) != currentImportance) {
changed = true
@ -151,19 +152,19 @@ object ReverseFlowAnalyzer {
fun.params match {
case AssemblyParamSignature(params) =>
params.foreach(_.variable match {
case RegisterVariable(Register.A, _) =>
case RegisterVariable(MosRegister.A, _) =>
result = result.copy(a = Important)
case RegisterVariable(Register.AW, _) =>
case RegisterVariable(MosRegister.AW, _) =>
result = result.copy(a = Important, ah = Important)
case RegisterVariable(Register.X, _) =>
case RegisterVariable(MosRegister.X, _) =>
result = result.copy(x = Important)
case RegisterVariable(Register.Y, _) =>
case RegisterVariable(MosRegister.Y, _) =>
result = result.copy(y = Important)
case RegisterVariable(Register.AX | Register.XA, _) =>
case RegisterVariable(MosRegister.AX | MosRegister.XA, _) =>
result = result.copy(a = Important, x = Important)
case RegisterVariable(Register.YA | Register.YA, _) =>
case RegisterVariable(MosRegister.YA | MosRegister.YA, _) =>
result = result.copy(a = Important, y = Important)
case RegisterVariable(Register.XY | Register.YX, _) =>
case RegisterVariable(MosRegister.XY | MosRegister.YX, _) =>
result = result.copy(x = Important, y = Important)
case _ =>
})

View File

@ -1,7 +1,7 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly.Opcode
import millfork.assembly.Opcode._
import millfork.assembly.mos.Opcode
import millfork.assembly.mos.Opcode._
/**
* @author Karol Stasiak

View File

@ -1,7 +1,8 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.assembly.mos._
import millfork.env._
import millfork.error.ErrorReporting
@ -26,7 +27,7 @@ object FlowInfoRequirement extends Enumeration {
}
}
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRule*) extends AssemblyOptimization {
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRule*) extends AssemblyOptimization[AssemblyLine] {
rules.foreach(_.pattern.validate(needsFlowInfo))

View File

@ -1,9 +1,10 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.{CompilationFlag, CompilationOptions, NonOverlappingIntervals}
import millfork.assembly.{AddrMode, AssemblyLine, Opcode, OpcodeClasses}
import millfork.assembly.Opcode._
import millfork.assembly._
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
import millfork.assembly.mos.{AssemblyLine, Opcode}
import millfork.env._
import millfork.error.ErrorReporting
@ -13,7 +14,7 @@ import scala.collection.mutable.ListBuffer
/**
* @author Karol Stasiak
*/
object SingleAssignmentVariableOptimization extends AssemblyOptimization {
object SingleAssignmentVariableOptimization extends AssemblyOptimization[AssemblyLine] {
private val BadOpcodes = Set(RTS, JSR, JMP, RTI)
private val GoodOpcodes = Set(

View File

@ -1,8 +1,10 @@
package millfork.assembly.opt
import millfork.assembly.Opcode._
package millfork.assembly.mos.opt
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
import millfork.assembly.OpcodeClasses._
import millfork.assembly.{AssemblyLine, OpcodeClasses, State}
import millfork.assembly.mos.OpcodeClasses._
import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.{AssemblyLine, OpcodeClasses, State}
import millfork.env.{Constant, NumericConstant}
/**
* @author Karol Stasiak
@ -216,7 +218,7 @@ object SixteenOptimizations {
// TODO: rewrite most 8-bit optimizations that are applicable to 16-bit code
val AllForEmulation: List[AssemblyOptimization] = List(
val AllForEmulation: List[AssemblyOptimization[AssemblyLine]] = List(
AccumulatorSwapping,
OptimizeStackRelative,
OptimizeZeroIndex,
@ -224,9 +226,9 @@ object SixteenOptimizations {
RepSepWeakening,
)
val AllForNative: List[AssemblyOptimization] = List(
val AllForNative: List[AssemblyOptimization[AssemblyLine]] = List(
PointlessLoadAfterLoadOrStore
)
val All: List[AssemblyOptimization] = AllForEmulation ++ AllForNative
val All: List[AssemblyOptimization[AssemblyLine]] = AllForEmulation ++ AllForNative
}

View File

@ -1,4 +1,4 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
/**
* @author Karol Stasiak

View File

@ -1,7 +1,8 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.{CompilationFlag, CompilationOptions, OptimizationPresets}
import millfork.assembly.{AddrMode, AssemblyLine, Opcode}
import millfork.assembly.{AddrMode, AssemblyOptimization}
import millfork.assembly.mos.{AssemblyLine, Opcode}
import millfork.env.NormalFunction
import millfork.error.ErrorReporting
@ -10,7 +11,7 @@ import scala.collection.mutable
/**
* @author Karol Stasiak
*/
object SuperOptimizer extends AssemblyOptimization {
object SuperOptimizer extends AssemblyOptimization[AssemblyLine] {
def optimize(m: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine] = {
val oldVerbosity = ErrorReporting.verbosity
@ -44,8 +45,8 @@ object SuperOptimizer extends AssemblyOptimization {
ChangeIndexRegisterOptimizationPreferringX2Y,
ChangeIndexRegisterOptimizationPreferringY2X)
val seenSoFar = mutable.Set[CodeView]()
val queue = mutable.Queue[(List[AssemblyOptimization], List[AssemblyLine])]()
val leaves = mutable.ListBuffer[(List[AssemblyOptimization], List[AssemblyLine])]()
val queue = mutable.Queue[(List[AssemblyOptimization[AssemblyLine]], List[AssemblyLine])]()
val leaves = mutable.ListBuffer[(List[AssemblyOptimization[AssemblyLine]], List[AssemblyLine])]()
val quickScrub = List(
UnusedLabelRemoval,

View File

@ -1,11 +1,12 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import java.util.concurrent.atomic.AtomicInteger
import millfork.assembly._
import millfork.assembly.Opcode._
import millfork.assembly.mos._
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
import millfork.assembly.OpcodeClasses._
import millfork.assembly.mos.OpcodeClasses._
import millfork.env.{Constant, NormalFunction, NumericConstant}
/**
@ -515,7 +516,7 @@ object UndocumentedOptimizations {
(Elidable & HasOpcode(ANC) & DoesntMatterWhatItDoesWith(State.C)) ~~> (_.map(_.copy(opcode = AND))),
)
val All: List[AssemblyOptimization] = List(
val All: List[AssemblyOptimization[AssemblyLine]] = List(
UseLax,
UseSax,
UseSbx,

View File

@ -1,16 +1,16 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly.AddrMode._
import millfork.assembly.Opcode._
import millfork.assembly.{AddrMode, AssemblyLine}
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.Opcode._
import millfork.assembly.AssemblyOptimization
import millfork.env._
import millfork.error.ErrorReporting
/**
* @author Karol Stasiak
*/
object UnusedLabelRemoval extends AssemblyOptimization {
object UnusedLabelRemoval extends AssemblyOptimization[AssemblyLine] {
override def optimize(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine] = {
val usedLabels = code.flatMap {

View File

@ -1,6 +1,6 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly.AssemblyLine
import millfork.assembly.mos.AssemblyLine
import millfork.env._
import millfork.error.ErrorReporting

View File

@ -1,8 +1,9 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.{CompilationFlag, CompilationOptions, NonOverlappingIntervals}
import millfork.assembly.{AddrMode, AssemblyLine, OpcodeClasses}
import millfork.assembly.Opcode._
import millfork.assembly.{AddrMode, AssemblyOptimization}
import millfork.assembly.mos._
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
import millfork.env._
import millfork.error.ErrorReporting
@ -12,7 +13,7 @@ import scala.collection.mutable.ListBuffer
/**
* @author Karol Stasiak
*/
object VariableToRegisterOptimization extends AssemblyOptimization {
object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine] {
object CyclesAndBytes {
val Zero = CyclesAndBytes(0, 0)

View File

@ -1,8 +1,9 @@
package millfork.assembly.opt
package millfork.assembly.mos.opt
import millfork.assembly.Opcode._
import millfork.assembly.mos.Opcode._
import millfork.assembly.AddrMode._
import millfork.assembly.AssemblyLine
import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.AssemblyLine
import millfork.env.{CompoundConstant, Constant, MathOperator}
/**
@ -74,7 +75,7 @@ object ZeropageRegisterOptimizations {
(Elidable & HasOpcode(STA) & RefersTo("__reg", 1) & DoesntMatterWhatItDoesWithReg(1)) ~~> (_.tail),
)
val All: List[AssemblyOptimization] = List(
val All: List[AssemblyOptimization[AssemblyLine]] = List(
ConstantMultiplication,
DeadRegStore,
DeadRegStoreFromFlow,

View File

@ -1,14 +0,0 @@
package millfork.assembly.opt
import millfork.CompilationOptions
import millfork.assembly.AssemblyLine
import millfork.env.NormalFunction
/**
* @author Karol Stasiak
*/
trait AssemblyOptimization {
def name: String
def optimize(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine]
}

View File

@ -0,0 +1,12 @@
package millfork.compiler
import millfork.assembly.AbstractCode
import millfork.compiler.mos.CompilationContext
/**
* @author Karol Stasiak
*/
abstract class AbstractCompiler[T <: AbstractCode] {
def nextLabel(prefix: String): String
def compile(ctx: CompilationContext): List[T]
}

View File

@ -0,0 +1,99 @@
package millfork.compiler
import millfork.env._
import millfork.node._
import millfork.error.ErrorReporting
import millfork.assembly.AbstractCode
import millfork.compiler.mos.CompilationContext
/**
* @author Karol Stasiak
*/
class AbstractExpressionCompiler[T <: AbstractCode] {
def getExpressionType(ctx: CompilationContext, expr: Expression): Type = {
val env = ctx.env
val b = env.get[Type]("byte")
val bool = env.get[Type]("bool$")
val v = env.get[Type]("void")
val w = env.get[Type]("word")
expr match {
case LiteralExpression(value, size) =>
size match {
case 1 => b
case 2 => w
case 3 => env.get[Type]("farword")
case 4 => env.get[Type]("long")
}
case VariableExpression(name) =>
env.get[TypedThing](name, expr.position).typ
case HalfWordExpression(param, _) =>
getExpressionType(ctx, param)
b
case IndexedExpression(_, _) => b
case SeparateBytesExpression(hi, lo) =>
if (getExpressionType(ctx, hi).size > 1) ErrorReporting.error("Hi byte too large", hi.position)
if (getExpressionType(ctx, lo).size > 1) ErrorReporting.error("Lo byte too large", lo.position)
w
case SumExpression(params, _) => params.map { case (_, e) => getExpressionType(ctx, e).size }.max match {
case 1 => b
case 2 => w
case _ => ErrorReporting.error("Adding values bigger than words", expr.position); w
}
case FunctionCallExpression("nonet", params) => w
case FunctionCallExpression("not", params) => bool
case FunctionCallExpression("hi", params) => b
case FunctionCallExpression("lo", params) => b
case FunctionCallExpression("*", params) => b
case FunctionCallExpression("|" | "&" | "^", params) => params.map { e => getExpressionType(ctx, e).size }.max match {
case 1 => b
case 2 => w
case _ => ErrorReporting.error("Adding values bigger than words", expr.position); w
}
case FunctionCallExpression("<<", List(a1, a2)) =>
if (getExpressionType(ctx, a2).size > 1) ErrorReporting.error("Shift amount too large", a2.position)
getExpressionType(ctx, a1)
case FunctionCallExpression(">>", List(a1, a2)) =>
if (getExpressionType(ctx, a2).size > 1) ErrorReporting.error("Shift amount too large", a2.position)
getExpressionType(ctx, a1)
case FunctionCallExpression("<<'", params) => b
case FunctionCallExpression(">>'", params) => b
case FunctionCallExpression(">>>>", params) => b
case FunctionCallExpression("&&", params) => bool
case FunctionCallExpression("||", params) => bool
case FunctionCallExpression("^^", params) => bool
case FunctionCallExpression("==", params) => bool
case FunctionCallExpression("!=", params) => bool
case FunctionCallExpression("<", params) => bool
case FunctionCallExpression(">", params) => bool
case FunctionCallExpression("<=", params) => bool
case FunctionCallExpression(">=", params) => bool
case FunctionCallExpression("+=", params) => v
case FunctionCallExpression("-=", params) => v
case FunctionCallExpression("*=", params) => v
case FunctionCallExpression("+'=", params) => v
case FunctionCallExpression("-'=", params) => v
case FunctionCallExpression("*'=", params) => v
case FunctionCallExpression("|=", params) => v
case FunctionCallExpression("&=", params) => v
case FunctionCallExpression("^=", params) => v
case FunctionCallExpression("<<=", params) => v
case FunctionCallExpression(">>=", params) => v
case FunctionCallExpression("<<'=", params) => v
case FunctionCallExpression(">>'=", params) => v
case f@FunctionCallExpression(name, params) =>
ctx.env.maybeGet[Type](name) match {
case Some(typ) =>
typ
case None =>
lookupFunction(ctx, f).returnType
}
}
}
def lookupFunction(ctx: CompilationContext, f: FunctionCallExpression): MangledFunction = {
val paramsWithTypes = f.expressions.map(x => getExpressionType(ctx, x) -> x)
ctx.env.lookupFunction(f.functionName, paramsWithTypes).getOrElse(
ErrorReporting.fatal(s"Cannot find function `${f.functionName}` with given params `${paramsWithTypes.map(_._1).mkString("(", ",", ")")}`", f.position))
}
}

View File

@ -1,4 +1,4 @@
package millfork.compiler
package millfork.compiler.mos
/**
* @author Karol Stasiak

View File

@ -1,13 +1,13 @@
package millfork.compiler
package millfork.compiler.mos
import millfork.{CompilationFlag, CompilationOptions}
import millfork.CompilationFlag
import millfork.assembly.AddrMode._
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
import millfork.assembly._
import millfork.env._
import millfork.node._
import millfork.assembly.Opcode._
import millfork.assembly.AddrMode._
import millfork.assembly.opt.ConcernsY
import millfork.error.ErrorReporting
import millfork.node._
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
@ -55,15 +55,15 @@ object BuiltIns {
case (p: ConstantPointy, _, _, _, None) =>
Nil -> List(AssemblyLine.absolute(opcode, p.value + constantPart))
case (p: ConstantPointy, _, 1, IndexChoice.RequireX | IndexChoice.PreferX, Some(v)) =>
ExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(Register.X, b)), NoBranching) -> List(AssemblyLine.absoluteX(opcode, p.value + constantPart))
MosExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(MosRegister.X, b)), NoBranching) -> List(AssemblyLine.absoluteX(opcode, p.value + constantPart))
case (p: ConstantPointy, _, 1, IndexChoice.PreferY, Some(v)) =>
ExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(Register.Y, b)), NoBranching) -> List(AssemblyLine.absoluteY(opcode, p.value + constantPart))
MosExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(MosRegister.Y, b)), NoBranching) -> List(AssemblyLine.absoluteY(opcode, p.value + constantPart))
case (p: VariablePointy, 0 | 1, _, IndexChoice.PreferX | IndexChoice.PreferY, _) =>
ExpressionCompiler.compile(ctx, index, Some(b -> RegisterVariable(Register.Y, b)), NoBranching) -> List(AssemblyLine.indexedY(opcode, p.addr))
MosExpressionCompiler.compile(ctx, index, Some(b -> RegisterVariable(MosRegister.Y, b)), NoBranching) -> List(AssemblyLine.indexedY(opcode, p.addr))
case (p: ConstantPointy, _, 2, IndexChoice.PreferX | IndexChoice.PreferY, Some(v)) =>
ExpressionCompiler.prepareWordIndexing(ctx, p, index) -> List(AssemblyLine.indexedY(opcode, env.get[VariableInMemory]("__reg")))
MosExpressionCompiler.prepareWordIndexing(ctx, p, index) -> List(AssemblyLine.indexedY(opcode, env.get[VariableInMemory]("__reg")))
case (p: VariablePointy, 2, _, IndexChoice.PreferX | IndexChoice.PreferY, _) =>
ExpressionCompiler.prepareWordIndexing(ctx, p, index) -> List(AssemblyLine.indexedY(opcode, env.get[VariableInMemory]("__reg")))
MosExpressionCompiler.prepareWordIndexing(ctx, p, index) -> List(AssemblyLine.indexedY(opcode, env.get[VariableInMemory]("__reg")))
case _ =>
ErrorReporting.error("Invalid index for simple operation argument", index.position)
Nil -> Nil
@ -73,11 +73,11 @@ object BuiltIns {
case _: FunctionCallExpression | _:SumExpression if commutative =>
// TODO: is it ok?
if (ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes)) {
return List(AssemblyLine.implied(PHA)) ++ ExpressionCompiler.compile(ctx.addStack(1), source, Some(b -> RegisterVariable(Register.A, b)), NoBranching) ++ wrapInSedCldIfNeeded(decimal, List(
return List(AssemblyLine.implied(PHA)) ++ MosExpressionCompiler.compile(ctx.addStack(1), source, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching) ++ wrapInSedCldIfNeeded(decimal, List(
AssemblyLine.stackRelative(opcode, 1),
AssemblyLine.implied(PHX)))
} else {
return List(AssemblyLine.implied(PHA)) ++ ExpressionCompiler.compile(ctx.addStack(1), source, Some(b -> RegisterVariable(Register.A, b)), NoBranching) ++ wrapInSedCldIfNeeded(decimal, List(
return List(AssemblyLine.implied(PHA)) ++ MosExpressionCompiler.compile(ctx.addStack(1), source, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching) ++ wrapInSedCldIfNeeded(decimal, List(
AssemblyLine.implied(TSX),
AssemblyLine.absoluteX(opcode, 0x101),
AssemblyLine.implied(INX),
@ -130,7 +130,7 @@ object BuiltIns {
val normalizedParams = sortedParams
val h = normalizedParams.head
val firstParamCompiled = ExpressionCompiler.compile(ctx, h._2, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
val firstParamCompiled = MosExpressionCompiler.compile(ctx, h._2, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
val firstParamSignCompiled = if (h._1) {
// TODO: check if decimal subtraction works correctly here
List(AssemblyLine.immediate(EOR, 0xff), AssemblyLine.implied(SEC), AssemblyLine.immediate(ADC, 0))
@ -169,7 +169,7 @@ object BuiltIns {
val sortedParams = params.sortBy { expr => simplicity(ctx.env, expr) }
val h = sortedParams.head
val firstParamCompiled = ExpressionCompiler.compile(ctx, h, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
val firstParamCompiled = MosExpressionCompiler.compile(ctx, h, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
val remainingParamsCompiled = sortedParams.tail.flatMap { p =>
simpleOperation(opcode, ctx, p, IndexChoice.PreferY, preserveA = true, commutative = true)
@ -180,17 +180,17 @@ object BuiltIns {
def compileShiftOps(opcode: Opcode.Value, ctx: CompilationContext, l: Expression, r: Expression): List[AssemblyLine] = {
val b = ctx.env.get[Type]("byte")
val firstParamCompiled = ExpressionCompiler.compile(ctx, l, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
val firstParamCompiled = MosExpressionCompiler.compile(ctx, l, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
ctx.env.eval(r) match {
case Some(NumericConstant(0, _)) =>
ExpressionCompiler.compile(ctx, l, None, NoBranching)
MosExpressionCompiler.compile(ctx, l, None, NoBranching)
case Some(NumericConstant(v, _)) if v > 0 =>
firstParamCompiled ++ List.fill(v.toInt)(AssemblyLine.implied(opcode))
case _ =>
val compileCounter = ExpressionCompiler.preserveRegisterIfNeeded(ctx, Register.A,
ExpressionCompiler.compile(ctx, r, Some(b -> RegisterVariable(Register.X, b)), NoBranching))
val labelSkip = MfCompiler.nextLabel("ss")
val labelRepeat = MfCompiler.nextLabel("sr")
val compileCounter = MosExpressionCompiler.preserveRegisterIfNeeded(ctx, MosRegister.A,
MosExpressionCompiler.compile(ctx, r, Some(b -> RegisterVariable(MosRegister.X, b)), NoBranching))
val labelSkip = MosCompiler.nextLabel("ss")
val labelRepeat = MosCompiler.nextLabel("sr")
val loop = List(
AssemblyLine.relative(BEQ, labelSkip),
AssemblyLine.label(labelRepeat),
@ -216,7 +216,7 @@ object BuiltIns {
}
env.eval(rhs) match {
case Some(NumericConstant(0, _)) =>
ExpressionCompiler.compile(ctx, lhs, None, NoBranching)
MosExpressionCompiler.compile(ctx, lhs, None, NoBranching)
case Some(NumericConstant(shift, _)) if shift > 0 =>
if (ctx.options.flag(CompilationFlag.RorWarning))
ErrorReporting.warn("ROR instruction generated", ctx.options, lhs.position)
@ -230,15 +230,15 @@ object BuiltIns {
def compileInPlaceByteShiftOps(opcode: Opcode.Value, ctx: CompilationContext, lhs: LhsExpression, rhs: Expression): List[AssemblyLine] = {
val env = ctx.env
val b = env.get[Type]("byte")
val firstParamCompiled = ExpressionCompiler.compile(ctx, lhs, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
val firstParamCompiled = MosExpressionCompiler.compile(ctx, lhs, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
env.eval(rhs) match {
case Some(NumericConstant(0, _)) =>
ExpressionCompiler.compile(ctx, lhs, None, NoBranching)
MosExpressionCompiler.compile(ctx, lhs, None, NoBranching)
case Some(NumericConstant(v, _)) if v > 0 =>
val result = simpleOperation(opcode, ctx, lhs, IndexChoice.RequireX, preserveA = true, commutative = false)
result ++ List.fill(v.toInt - 1)(result.last)
case _ =>
compileShiftOps(opcode, ctx, lhs, rhs) ++ ExpressionCompiler.compileByteStorage(ctx, Register.A, lhs)
compileShiftOps(opcode, ctx, lhs, rhs) ++ MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, lhs)
}
}
@ -251,7 +251,7 @@ object BuiltIns {
// TODO: this probably breaks in case of complex split word expressions
env.eval(rhs) match {
case Some(NumericConstant(0, _)) =>
ExpressionCompiler.compile(ctx, lhs, None, NoBranching)
MosExpressionCompiler.compile(ctx, lhs, None, NoBranching)
case Some(NumericConstant(shift, _)) if shift > 0 =>
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
targetBytes match {
@ -273,16 +273,16 @@ object BuiltIns {
val usesX = targetBytes.exists(_.exists(_.concernsX))
val usesY = targetBytes.exists(_.exists(_.concernsY))
val (register, decrease) = (usesX, usesY) match {
case (true, false) => Register.Y -> DEY
case (false, true) => Register.X -> DEX
case (false, false) => Register.X -> DEX
case (true, false) => MosRegister.Y -> DEY
case (false, true) => MosRegister.X -> DEX
case (false, false) => MosRegister.X -> DEX
case (true, true) => ???
}
val compileCounter = ExpressionCompiler.preserveRegisterIfNeeded(ctx, Register.A,
ExpressionCompiler.compile(ctx, rhs, Some(b -> RegisterVariable(register, b)), NoBranching))
val labelSkip = MfCompiler.nextLabel("ss")
val labelRepeat = MfCompiler.nextLabel("sr")
val compileCounter = MosExpressionCompiler.preserveRegisterIfNeeded(ctx, MosRegister.A,
MosExpressionCompiler.compile(ctx, rhs, Some(b -> RegisterVariable(register, b)), NoBranching))
val labelSkip = MosCompiler.nextLabel("ss")
val labelRepeat = MosCompiler.nextLabel("sr")
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
targetBytes match {
@ -323,7 +323,7 @@ object BuiltIns {
if (simplicity(env, lhs) >= 'J' && simplicity(env, rhs) < 'J') {
return compileByteComparison(ctx, ComparisonType.flip(compType), rhs, lhs, branches)
}
val firstParamCompiled = ExpressionCompiler.compile(ctx, lhs, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
val firstParamCompiled = MosExpressionCompiler.compile(ctx, lhs, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
val maybeConstant = env.eval(rhs)
maybeConstant match {
case Some(NumericConstant(0, _)) =>
@ -395,7 +395,7 @@ object BuiltIns {
case ComparisonType.LessOrEqualUnsigned =>
List(AssemblyLine.relative(BCC, Label(label)), AssemblyLine.relative(BEQ, Label(label)))
case ComparisonType.GreaterUnsigned =>
val x = MfCompiler.nextLabel("co")
val x = MosCompiler.nextLabel("co")
List(
AssemblyLine.relative(BEQ, x),
AssemblyLine.relative(BCS, Label(label)),
@ -408,7 +408,7 @@ object BuiltIns {
case ComparisonType.LessOrEqualSigned =>
List(AssemblyLine.relative(BMI, Label(label)), AssemblyLine.relative(BEQ, Label(label)))
case ComparisonType.GreaterSigned =>
val x = MfCompiler.nextLabel("co")
val x = MosCompiler.nextLabel("co")
List(
AssemblyLine.relative(BEQ, x),
AssemblyLine.relative(BPL, Label(label)),
@ -479,8 +479,8 @@ object BuiltIns {
ErrorReporting.error("Too complex expressions in comparison", lhs.position)
(Nil, Nil, Nil, Nil)
}
val lType = ExpressionCompiler.getExpressionType(ctx, lhs)
val rType = ExpressionCompiler.getExpressionType(ctx, rhs)
val lType = MosExpressionCompiler.getExpressionType(ctx, lhs)
val rType = MosExpressionCompiler.getExpressionType(ctx, rhs)
val compactEqualityComparison = if (ctx.options.flag(CompilationFlag.OptimizeForSpeed)) {
None
} else if (lType.size == 1 && !lType.isSigned) {
@ -495,7 +495,7 @@ object BuiltIns {
compactEqualityComparison match {
case Some(code) => code :+ AssemblyLine.relative(BEQ, Label(x))
case None =>
val innerLabel = MfCompiler.nextLabel("cp")
val innerLabel = MosCompiler.nextLabel("cp")
staTo(LDA, ll) ++
staTo(CMP, rl) ++
List(AssemblyLine.relative(BNE, innerLabel)) ++
@ -519,7 +519,7 @@ object BuiltIns {
}
case ComparisonType.LessUnsigned =>
val innerLabel = MfCompiler.nextLabel("cp")
val innerLabel = MosCompiler.nextLabel("cp")
staTo(LDA, lh) ++
staTo(CMP, rh) ++
List(
@ -532,7 +532,7 @@ object BuiltIns {
AssemblyLine.label(innerLabel))
case ComparisonType.LessOrEqualUnsigned =>
val innerLabel = MfCompiler.nextLabel("cp")
val innerLabel = MosCompiler.nextLabel("cp")
staTo(LDA, rh) ++
staTo(CMP, lh) ++
List(AssemblyLine.relative(BCC, innerLabel),
@ -543,7 +543,7 @@ object BuiltIns {
AssemblyLine.label(innerLabel))
case ComparisonType.GreaterUnsigned =>
val innerLabel = MfCompiler.nextLabel("cp")
val innerLabel = MosCompiler.nextLabel("cp")
staTo(LDA, rh) ++
staTo(CMP, lh) ++
List(AssemblyLine.relative(BCC, Label(x)),
@ -554,7 +554,7 @@ object BuiltIns {
AssemblyLine.label(innerLabel))
case ComparisonType.GreaterOrEqualUnsigned =>
val innerLabel = MfCompiler.nextLabel("cp")
val innerLabel = MosCompiler.nextLabel("cp")
staTo(LDA, lh) ++
staTo(CMP, rh) ++
List(AssemblyLine.relative(BCC, innerLabel),
@ -570,7 +570,7 @@ object BuiltIns {
}
def compileLongComparison(ctx: CompilationContext, compType: ComparisonType.Value, lhs: Expression, rhs: Expression, size:Int, branches: BranchSpec, alreadyFlipped: Boolean = false): List[AssemblyLine] = {
val rType = ExpressionCompiler.getExpressionType(ctx, rhs)
val rType = MosExpressionCompiler.getExpressionType(ctx, rhs)
if (rType.size < size && rType.isSigned) {
if (alreadyFlipped) ???
else return compileLongComparison(ctx, ComparisonType.flip(compType), rhs, lhs, size, branches, alreadyFlipped = true)
@ -617,7 +617,7 @@ object BuiltIns {
case _ =>
effectiveComparisonType match {
case ComparisonType.Equal =>
val innerLabel = MfCompiler.nextLabel("cp")
val innerLabel = MosCompiler.nextLabel("cp")
val bytewise = l.zip(r).map{
case (staL, staR) => staTo(LDA, staL) ++ staTo(CMP, staR)
}
@ -658,13 +658,13 @@ object BuiltIns {
val b = ctx.env.get[Type]("byte")
ctx.env.eval(addend) match {
case Some(NumericConstant(0, _)) =>
ExpressionCompiler.compile(ctx, v, None, NoBranching) ++ (AssemblyLine.immediate(LDA, 0) :: ExpressionCompiler.compileByteStorage(ctx, Register.A, v))
MosExpressionCompiler.compile(ctx, v, None, NoBranching) ++ (AssemblyLine.immediate(LDA, 0) :: MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v))
case Some(NumericConstant(1, _)) =>
ExpressionCompiler.compile(ctx, v, None, NoBranching)
MosExpressionCompiler.compile(ctx, v, None, NoBranching)
case Some(NumericConstant(x, _)) =>
compileByteMultiplication(ctx, v, x.toInt) ++ ExpressionCompiler.compileByteStorage(ctx, Register.A, v)
compileByteMultiplication(ctx, v, x.toInt) ++ MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
case _ =>
PseudoregisterBuiltIns.compileByteMultiplication(ctx, Some(v), addend, storeInRegLo = false) ++ ExpressionCompiler.compileByteStorage(ctx, Register.A, v)
PseudoregisterBuiltIns.compileByteMultiplication(ctx, Some(v), addend, storeInRegLo = false) ++ MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
}
}
@ -727,7 +727,7 @@ object BuiltIns {
case _ => false
}
env.eval(addend) match {
case Some(NumericConstant(0, _)) => ExpressionCompiler.compile(ctx, v, None, NoBranching)
case Some(NumericConstant(0, _)) => MosExpressionCompiler.compile(ctx, v, None, NoBranching)
case Some(NumericConstant(1, _)) if lhsIsDirectlyIncrementable && !decimal => if (subtract) {
simpleOperation(DEC, ctx, v, IndexChoice.RequireX, preserveA = false, commutative = true)
} else {
@ -741,25 +741,25 @@ object BuiltIns {
}
case _ =>
if (!subtract && simplicity(env, v) > simplicity(env, addend)) {
val loadRhs = ExpressionCompiler.compile(ctx, addend, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
val loadRhs = MosExpressionCompiler.compile(ctx, addend, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
val modifyAcc = insertBeforeLast(AssemblyLine.implied(CLC), simpleOperation(ADC, ctx, v, IndexChoice.PreferY, preserveA = true, commutative = true, decimal = decimal))
val storeLhs = ExpressionCompiler.compileByteStorage(ctx, Register.A, v)
val storeLhs = MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
loadRhs ++ modifyAcc ++ storeLhs
} else {
val loadLhs = ExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
val loadLhs = MosExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
val modifyLhs = if (subtract) {
insertBeforeLast(AssemblyLine.implied(SEC), simpleOperation(SBC, ctx, addend, IndexChoice.PreferY, preserveA = true, commutative = false, decimal = decimal))
} else {
insertBeforeLast(AssemblyLine.implied(CLC), simpleOperation(ADC, ctx, addend, IndexChoice.PreferY, preserveA = true, commutative = true, decimal = decimal))
}
val storeLhs = ExpressionCompiler.compileByteStorage(ctx, Register.A, v)
val storeLhs = MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
loadLhs ++ modifyLhs ++ storeLhs
}
}
}
private def getIndexerSize(ctx: CompilationContext, indexExpr: Expression) = {
ctx.env.evalVariableAndConstantSubParts(indexExpr)._1.map(v => ExpressionCompiler.getExpressionType(ctx, v)).size
ctx.env.evalVariableAndConstantSubParts(indexExpr)._1.map(v => MosExpressionCompiler.getExpressionType(ctx, v)).size
}
def compileInPlaceWordOrLongAddition(ctx: CompilationContext, lhs: LhsExpression, addend: Expression, subtract: Boolean, decimal: Boolean): List[AssemblyLine] = {
@ -772,7 +772,7 @@ object BuiltIns {
val targetBytes: List[List[AssemblyLine]] = getStorageForEachByte(ctx, lhs)
val lhsIsStack = targetBytes.head.head.opcode == TSX
val targetSize = targetBytes.size
val addendType = ExpressionCompiler.getExpressionType(ctx, addend)
val addendType = MosExpressionCompiler.getExpressionType(ctx, addend)
var addendSize = addendType.size
def isRhsComplex(xs: List[AssemblyLine]): Boolean = xs match {
@ -795,7 +795,7 @@ object BuiltIns {
case Nil => Nil
case x :: Nil => staTo(DEC, x)
case x :: xs =>
val label = MfCompiler.nextLabel("de")
val label = MosCompiler.nextLabel("de")
staTo(LDA, x) ++
List(AssemblyLine.relative(BNE, label)) ++
doDec(xs) ++
@ -805,7 +805,7 @@ object BuiltIns {
val (calculateRhs, addendByteRead0): (List[AssemblyLine], List[List[AssemblyLine]]) = env.eval(addend) match {
case Some(NumericConstant(0, _)) =>
return ExpressionCompiler.compile(ctx, lhs, None, NoBranching)
return MosExpressionCompiler.compile(ctx, lhs, None, NoBranching)
case Some(NumericConstant(1, _)) if canUseIncDec && !subtract =>
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
targetBytes match {
@ -823,7 +823,7 @@ object BuiltIns {
}
}
}
val label = MfCompiler.nextLabel("in")
val label = MosCompiler.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)) {
@ -842,7 +842,7 @@ object BuiltIns {
}
}
}
val label = MfCompiler.nextLabel("in")
val label = MosCompiler.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)) {
@ -861,7 +861,7 @@ object BuiltIns {
}
}
}
val label = MfCompiler.nextLabel("de")
val label = MosCompiler.nextLabel("de")
return doDec(targetBytes)
case Some(NumericConstant(-1, _)) if canUseIncDec && !subtract =>
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
@ -880,7 +880,7 @@ object BuiltIns {
}
}
}
val label = MfCompiler.nextLabel("de")
val label = MosCompiler.nextLabel("de")
return doDec(targetBytes)
case Some(constant) =>
addendSize = targetSize
@ -888,7 +888,7 @@ object BuiltIns {
case None =>
addendSize match {
case 1 =>
val base = ExpressionCompiler.compile(ctx, addend, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
val base = MosExpressionCompiler.compile(ctx, addend, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
if (subtract) {
if (isRhsComplex(base)) {
if (isRhsStack(base)) {
@ -903,9 +903,9 @@ object BuiltIns {
base -> List(Nil)
}
case 2 =>
val base = ExpressionCompiler.compile(ctx, addend, Some(ExpressionCompiler.getExpressionType(ctx, addend) -> RegisterVariable(Register.AX, w)), NoBranching)
val base = MosExpressionCompiler.compile(ctx, addend, Some(MosExpressionCompiler.getExpressionType(ctx, addend) -> RegisterVariable(MosRegister.AX, w)), NoBranching)
if (isRhsStack(base)) {
val fixedBase = ExpressionCompiler.compile(ctx, addend, Some(ExpressionCompiler.getExpressionType(ctx, addend) -> RegisterVariable(Register.AY, w)), NoBranching)
val fixedBase = MosExpressionCompiler.compile(ctx, addend, Some(MosExpressionCompiler.getExpressionType(ctx, addend) -> RegisterVariable(MosRegister.AY, w)), NoBranching)
if (subtract) {
ErrorReporting.warn("Subtracting a stack-based value", ctx.options)
if (isRhsComplex(base)) {
@ -932,7 +932,7 @@ object BuiltIns {
}
} else {
if (lhsIsStack) {
val fixedBase = ExpressionCompiler.compile(ctx, addend, Some(ExpressionCompiler.getExpressionType(ctx, addend) -> RegisterVariable(Register.AY, w)), NoBranching)
val fixedBase = MosExpressionCompiler.compile(ctx, addend, Some(MosExpressionCompiler.getExpressionType(ctx, addend) -> RegisterVariable(MosRegister.AY, w)), NoBranching)
fixedBase -> List(Nil, List(AssemblyLine.implied(TYA)))
} else {
base -> List(Nil, List(AssemblyLine.implied(TXA)))
@ -1009,7 +1009,7 @@ object BuiltIns {
} else {
if (i >= addendSize) {
if (addendType.isSigned) {
val label = MfCompiler.nextLabel("sx")
val label = MosCompiler.nextLabel("sx")
buffer += AssemblyLine.implied(TXA)
if (i == addendSize) {
buffer += AssemblyLine.immediate(ORA, 0x7f)
@ -1048,14 +1048,14 @@ object BuiltIns {
Nil
case _ =>
if (simplicity(env, v) > simplicity(env, param)) {
val loadRhs = ExpressionCompiler.compile(ctx, param, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
val loadRhs = MosExpressionCompiler.compile(ctx, param, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
val modifyAcc = simpleOperation(operation, ctx, v, IndexChoice.PreferY, preserveA = true, commutative = true)
val storeLhs = ExpressionCompiler.compileByteStorage(ctx, Register.A, v)
val storeLhs = MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
loadRhs ++ modifyAcc ++ storeLhs
} else {
val loadLhs = ExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
val loadLhs = MosExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
val modifyLhs = simpleOperation(operation, ctx, param, IndexChoice.PreferY, preserveA = true, commutative = true)
val storeLhs = ExpressionCompiler.compileByteStorage(ctx, Register.A, v)
val storeLhs = MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
loadLhs ++ modifyLhs ++ storeLhs
}
}
@ -1069,7 +1069,7 @@ object BuiltIns {
val targetBytes: List[List[AssemblyLine]] = getStorageForEachByte(ctx, lhs)
val lo = targetBytes.head
val targetSize = targetBytes.size
val paramType = ExpressionCompiler.getExpressionType(ctx, param)
val paramType = MosExpressionCompiler.getExpressionType(ctx, param)
var paramSize = paramType.size
val extendMultipleBytes = targetSize > paramSize + 1
val extendAtLeastOneByte = targetSize > paramSize
@ -1080,10 +1080,10 @@ object BuiltIns {
case None =>
paramSize match {
case 1 =>
val base = ExpressionCompiler.compile(ctx, param, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
val base = MosExpressionCompiler.compile(ctx, param, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
base -> List(Nil)
case 2 =>
val base = ExpressionCompiler.compile(ctx, param, Some(ExpressionCompiler.getExpressionType(ctx, param) -> RegisterVariable(Register.AX, w)), NoBranching)
val base = MosExpressionCompiler.compile(ctx, param, Some(MosExpressionCompiler.getExpressionType(ctx, param) -> RegisterVariable(MosRegister.AX, w)), NoBranching)
base -> List(Nil, List(AssemblyLine.implied(TXA)))
case _ => Nil -> (param match {
case vv: VariableExpression =>
@ -1120,7 +1120,7 @@ object BuiltIns {
case (EOR, Some(NumericConstant(0, _)))
| (ORA, Some(NumericConstant(0, _)))
| (AND, Some(NumericConstant(AllOnes, _))) =>
ExpressionCompiler.compile(ctx, lhs, None, NoBranching)
MosExpressionCompiler.compile(ctx, lhs, None, NoBranching)
case _ =>
val buffer = mutable.ListBuffer[AssemblyLine]()
buffer ++= calculateRhs
@ -1132,7 +1132,7 @@ object BuiltIns {
}
} else {
if (paramType.isSigned) {
val label = MfCompiler.nextLabel("sx")
val label = MosCompiler.nextLabel("sx")
buffer += AssemblyLine.implied(TXA)
if (i == paramSize) {
buffer += AssemblyLine.immediate(ORA, 0x7f)
@ -1163,7 +1163,7 @@ object BuiltIns {
val variable = env.get[Variable](v.name)
List.tabulate(variable.typ.size) { i => AssemblyLine.variable(ctx, STA, variable, i) }
case IndexedExpression(variable, index) =>
List(ExpressionCompiler.compileByteStorage(ctx, Register.A, lhs))
List(MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, lhs))
case SeparateBytesExpression(h: LhsExpression, l: LhsExpression) =>
if (simplicity(ctx.env, h) < 'J' || simplicity(ctx.env, l) < 'J') {
// a[b]:c[d] is the most complex expression that doesn't cause the following warning
@ -1171,7 +1171,7 @@ object BuiltIns {
}
List(
getStorageForEachByte(ctx, l).head,
ExpressionCompiler.preserveRegisterIfNeeded(ctx, Register.A, getStorageForEachByte(ctx, h).head))
MosExpressionCompiler.preserveRegisterIfNeeded(ctx, MosRegister.A, getStorageForEachByte(ctx, h).head))
case _ =>
???
}
@ -1189,7 +1189,7 @@ object BuiltIns {
if (i < variable.typ.size) {
AssemblyLine.variable(ctx, STA, variable, i)
} else if (variable.typ.isSigned) {
val label = MfCompiler.nextLabel("sx")
val label = MosCompiler.nextLabel("sx")
AssemblyLine.variable(ctx, STA, variable, variable.typ.size - 1) ++ List(
AssemblyLine.immediate(ORA, 0x7F),
AssemblyLine.relative(BMI, label),
@ -1199,7 +1199,7 @@ object BuiltIns {
}
case expr@IndexedExpression(variable, index) =>
List.tabulate(size) { i =>
if (i == 0) ExpressionCompiler.compileByteStorage(ctx, Register.A, expr)
if (i == 0) MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, expr)
else List(AssemblyLine.immediate(STA, 0))
}
case SeparateBytesExpression(h: LhsExpression, l: LhsExpression) =>
@ -1209,7 +1209,7 @@ object BuiltIns {
}
List.tabulate(size) { i =>
if (i == 0) getStorageForEachByte(ctx, l).head
else if (i == 1) ExpressionCompiler.preserveRegisterIfNeeded(ctx, Register.A, getStorageForEachByte(ctx, h).head)
else if (i == 1) MosExpressionCompiler.preserveRegisterIfNeeded(ctx, MosRegister.A, getStorageForEachByte(ctx, h).head)
else List(AssemblyLine.immediate(STA, 0))
}
case _ =>

View File

@ -1,4 +1,4 @@
package millfork.compiler
package millfork.compiler.mos
/**
* @author Karol Stasiak

View File

@ -1,7 +1,7 @@
package millfork.compiler
package millfork.compiler.mos
import millfork.{CompilationFlag, CompilationOptions}
import millfork.env.{Environment, Label, NormalFunction}
import millfork.{CompilationFlag, CompilationOptions}
/**
* @author Karol Stasiak
@ -13,7 +13,7 @@ case class CompilationContext(env: Environment,
breakLabels: Map[String, Label] = Map(),
continueLabels: Map[String, Label] = Map()){
def withInlinedEnv(environment: Environment): CompilationContext = {
val newEnv = new Environment(Some(env), MfCompiler.nextLabel("en"))
val newEnv = new Environment(Some(env), MosCompiler.nextLabel("en"))
newEnv.things ++= environment.things
copy(env = newEnv)
}

View File

@ -1,19 +1,13 @@
package millfork.compiler
package millfork.compiler.mos
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.env._
import millfork.node._
import millfork.assembly.Opcode._
import millfork.CompilationFlag
import millfork.assembly.AddrMode._
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
import millfork.assembly._
import millfork.env.{NumericConstant, RegisterVariable, Type, _}
import millfork.error.ErrorReporting
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import millfork.assembly.AssemblyLine
import millfork.env.{NumericConstant, RegisterVariable, Type}
import millfork.error.ErrorReporting
import millfork.node.{Expression, Register}
import millfork.node.{Expression, MosRegister, _}
/**
* @author Karol Stasiak
@ -34,7 +28,7 @@ object DecimalBuiltIns {
def compileByteShiftRight(ctx: CompilationContext, l: Expression, r: Expression, rotate: Boolean): List[AssemblyLine] = {
val b = ctx.env.get[Type]("byte")
ExpressionCompiler.compile(ctx, l, Some((b, RegisterVariable(Register.A, b))), BranchSpec.None) ++ (ctx.env.eval(r) match {
MosExpressionCompiler.compile(ctx, l, Some((b, RegisterVariable(MosRegister.A, b))), BranchSpec.None) ++ (ctx.env.eval(r) match {
case Some(NumericConstant(0, _)) =>
Nil
case Some(NumericConstant(v, _)) =>
@ -48,11 +42,11 @@ object DecimalBuiltIns {
}
private def shiftOrRotateAccumulatorRight(ctx: CompilationContext, rotate: Boolean, preserveCarry: Boolean) = {
val skipHiDigit = MfCompiler.nextLabel("ds")
val skipLoDigit = MfCompiler.nextLabel("ds")
val skipHiDigit = MosCompiler.nextLabel("ds")
val skipLoDigit = MosCompiler.nextLabel("ds")
val cmos = ctx.options.flags(CompilationFlag.EmitCmosOpcodes)
if (preserveCarry) {
val constantLabel = MfCompiler.nextLabel("c8")
val constantLabel = MosCompiler.nextLabel("c8")
val bit = if (cmos) {
AssemblyLine.immediate(BIT, 8)
} else {
@ -142,7 +136,7 @@ object DecimalBuiltIns {
ErrorReporting.error("Cannot multiply by a non-constant amount", r.position)
return Nil
}
val fullStorage = ExpressionCompiler.compileByteStorage(ctx, Register.A, l)
val fullStorage = MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, l)
val sta = fullStorage.last
if (sta.opcode != STA) ???
val fullLoad = fullStorage.init :+ sta.copy(opcode = LDA)

View File

@ -1,16 +1,10 @@
package millfork.compiler
package millfork.compiler.mos
import java.util.concurrent.atomic.AtomicLong
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
import millfork.env._
import millfork.node.{Register, _}
import millfork.assembly.AddrMode._
import millfork.assembly.Opcode._
import millfork.error.ErrorReporting
import scala.collection.JavaConverters._
import millfork.node.{MosRegister, _}
/**
* @author Karol Stasiak
*/
@ -34,7 +28,7 @@ object MacroExpander {
case DoWhileStatement(b, i, c, n) => DoWhileStatement(b.map(gx), i.map(gx), f(c), n)
case ForStatement(v, start, end, dir, body) => ForStatement(h(v), f(start), f(end), dir, body.map(gx))
case IfStatement(c, t, e) => IfStatement(f(c), t.map(gx), e.map(gx))
case s:AssemblyStatement => s.copy(expression = f(s.expression))
case s:MosAssemblyStatement => s.copy(expression = f(s.expression))
case Assignment(d,s) => Assignment(fx(d), f(s))
case BreakStatement(s) => if (s == paramName) BreakStatement(target.toString) else stmt
case ContinueStatement(s) => if (s == paramName) ContinueStatement(target.toString) else stmt
@ -57,19 +51,19 @@ object MacroExpander {
ctx.env.get[ThingInMemory](vname)
case l: LhsExpression =>
// TODO: ??
ExpressionCompiler.compileByteStorage(ctx, Register.A, l)
MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, l)
case _ =>
ErrorReporting.error("A non-assignable expression was passed to an inlineable function as a `ref` parameter", actualParam.position)
}
actualCode = actualCode.map {
case a@AssemblyStatement(_, _, expr, _) =>
case a@MosAssemblyStatement(_, _, expr, _) =>
a.copy(expression = expr.replaceVariable(ph, actualParam))
case x => x
}
case (AssemblyParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByConstant), actualParam) =>
ctx.env.eval(actualParam).getOrElse(Constant.error("Non-constant expression was passed to an inlineable function as a `const` parameter", actualParam.position))
actualCode = actualCode.map {
case a@AssemblyStatement(_, _, expr, _) =>
case a@MosAssemblyStatement(_, _, expr, _) =>
a.copy(expression = expr.replaceVariable(ph, actualParam))
case x => x
}
@ -78,7 +72,7 @@ object MacroExpander {
ErrorReporting.error("Only one macro assembly function parameter can be passed via a register", position)
}
hadRegisterParam = true
paramPreparation = ExpressionCompiler.compile(ctx, actualParam, Some(typ, v), BranchSpec.None)
paramPreparation = MosExpressionCompiler.compile(ctx, actualParam, Some(typ, v), BranchSpec.None)
case (AssemblyParam(_, _, AssemblyParameterPassingBehaviour.Copy), actualParam) =>
???
case (_, actualParam) =>
@ -101,12 +95,12 @@ object MacroExpander {
// fix local labels:
// TODO: do it even if the labels are in an inline assembly block inside a Millfork function
val localLabels = actualCode.flatMap{
case AssemblyStatement(LABEL, _, VariableExpression(l), _) => Some(l)
case MosAssemblyStatement(LABEL, _, VariableExpression(l), _) => Some(l)
case _ => None
}.toSet
val labelPrefix = MfCompiler.nextLabel("il")
val labelPrefix = MosCompiler.nextLabel("il")
paramPreparation -> actualCode.map{
case s@AssemblyStatement(_, _, VariableExpression(v), _) if localLabels(v) =>
case s@MosAssemblyStatement(_, _, VariableExpression(v), _) if localLabels(v) =>
s.copy(expression = VariableExpression(labelPrefix + v))
case s => s
}

View File

@ -1,31 +1,27 @@
package millfork.compiler
package millfork.compiler.mos
import java.util.concurrent.atomic.AtomicLong
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.CompilationFlag
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
import millfork.compiler.AbstractCompiler
import millfork.env._
import millfork.node.{Register, _}
import millfork.assembly.AddrMode._
import millfork.assembly.Opcode._
import millfork.error.ErrorReporting
import scala.collection.JavaConverters._
/**
* @author Karol Stasiak
*/
//noinspection NotImplementedCode,ScalaUnusedSymbol
object MfCompiler {
object MosCompiler extends AbstractCompiler[AssemblyLine] {
private val labelCounter = new AtomicLong
def nextLabel(prefix: String): String = "." + prefix + "__" + labelCounter.incrementAndGet().formatted("%05d")
def compile(ctx: CompilationContext): List[AssemblyLine] = {
override def compile(ctx: CompilationContext): List[AssemblyLine] = {
ctx.env.nameCheck(ctx.function.code)
val chunk = StatementCompiler.compile(ctx, ctx.function.code)
val chunk = MosStatementCompiler.compile(ctx, ctx.function.code)
val phReg =
if (ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {

View File

@ -1,119 +1,38 @@
package millfork.compiler
package millfork.compiler.mos
import java.util.concurrent.atomic.AtomicLong
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.env._
import millfork.node.{Register, _}
import millfork.CompilationFlag
import millfork.assembly.AddrMode._
import millfork.assembly.Opcode._
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
import millfork.compiler.AbstractExpressionCompiler
import millfork.env._
import millfork.error.ErrorReporting
import millfork.node.{MosRegister, _}
/**
* @author Karol Stasiak
*/
object ExpressionCompiler {
def getExpressionType(ctx: CompilationContext, expr: Expression): Type = {
val env = ctx.env
val b = env.get[Type]("byte")
val bool = env.get[Type]("bool$")
val v = env.get[Type]("void")
val w = env.get[Type]("word")
expr match {
case LiteralExpression(value, size) =>
size match {
case 1 => b
case 2 => w
case 3 => env.get[Type]("farword")
case 4 => env.get[Type]("long")
}
case VariableExpression(name) =>
env.get[TypedThing](name, expr.position).typ
case HalfWordExpression(param, _) =>
getExpressionType(ctx, param)
b
case IndexedExpression(_, _) => b
case SeparateBytesExpression(hi, lo) =>
if (getExpressionType(ctx, hi).size > 1) ErrorReporting.error("Hi byte too large", hi.position)
if (getExpressionType(ctx, lo).size > 1) ErrorReporting.error("Lo byte too large", lo.position)
w
case SumExpression(params, _) => params.map { case (_, e) => getExpressionType(ctx, e).size }.max match {
case 1 => b
case 2 => w
case _ => ErrorReporting.error("Adding values bigger than words", expr.position); w
}
case FunctionCallExpression("nonet", params) => w
case FunctionCallExpression("not", params) => bool
case FunctionCallExpression("hi", params) => b
case FunctionCallExpression("lo", params) => b
case FunctionCallExpression("*", params) => b
case FunctionCallExpression("|" | "&" | "^", params) => params.map { e => getExpressionType(ctx, e).size }.max match {
case 1 => b
case 2 => w
case _ => ErrorReporting.error("Adding values bigger than words", expr.position); w
}
case FunctionCallExpression("<<", List(a1, a2)) =>
if (getExpressionType(ctx, a2).size > 1) ErrorReporting.error("Shift amount too large", a2.position)
getExpressionType(ctx, a1)
case FunctionCallExpression(">>", List(a1, a2)) =>
if (getExpressionType(ctx, a2).size > 1) ErrorReporting.error("Shift amount too large", a2.position)
getExpressionType(ctx, a1)
case FunctionCallExpression("<<'", params) => b
case FunctionCallExpression(">>'", params) => b
case FunctionCallExpression(">>>>", params) => b
case FunctionCallExpression("&&", params) => bool
case FunctionCallExpression("||", params) => bool
case FunctionCallExpression("^^", params) => bool
case FunctionCallExpression("==", params) => bool
case FunctionCallExpression("!=", params) => bool
case FunctionCallExpression("<", params) => bool
case FunctionCallExpression(">", params) => bool
case FunctionCallExpression("<=", params) => bool
case FunctionCallExpression(">=", params) => bool
case FunctionCallExpression("+=", params) => v
case FunctionCallExpression("-=", params) => v
case FunctionCallExpression("*=", params) => v
case FunctionCallExpression("+'=", params) => v
case FunctionCallExpression("-'=", params) => v
case FunctionCallExpression("*'=", params) => v
case FunctionCallExpression("|=", params) => v
case FunctionCallExpression("&=", params) => v
case FunctionCallExpression("^=", params) => v
case FunctionCallExpression("<<=", params) => v
case FunctionCallExpression(">>=", params) => v
case FunctionCallExpression("<<'=", params) => v
case FunctionCallExpression(">>'=", params) => v
case f@FunctionCallExpression(name, params) =>
ctx.env.maybeGet[Type](name) match {
case Some(typ) =>
typ
case None =>
lookupFunction(ctx, f).returnType
}
}
}
object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
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, _) =>
case RegisterVariable(MosRegister.A, _) => List(AssemblyLine(LDA, Immediate, expr))
case RegisterVariable(MosRegister.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(
case RegisterVariable(MosRegister.X, _) => List(AssemblyLine(LDX, Immediate, expr))
case RegisterVariable(MosRegister.Y, _) => List(AssemblyLine(LDY, Immediate, expr))
case RegisterVariable(MosRegister.AX, _) => List(
AssemblyLine(LDA, Immediate, expr.loByte),
AssemblyLine(LDX, Immediate, expr.hiByte))
case RegisterVariable(Register.AY, _) => List(
case RegisterVariable(MosRegister.AY, _) => List(
AssemblyLine(LDA, Immediate, expr.loByte),
AssemblyLine(LDY, Immediate, expr.hiByte))
case RegisterVariable(Register.XA, _) => List(
case RegisterVariable(MosRegister.XA, _) => List(
AssemblyLine(LDA, Immediate, expr.hiByte),
AssemblyLine(LDX, Immediate, expr.loByte))
case RegisterVariable(Register.YA, _) => List(
case RegisterVariable(MosRegister.YA, _) => List(
AssemblyLine(LDA, Immediate, expr.hiByte),
AssemblyLine(LDY, Immediate, expr.loByte))
case m: VariableInMemory =>
@ -160,18 +79,18 @@ object ExpressionCompiler {
case Nil => Nil
}
def preserveRegisterIfNeeded(ctx: CompilationContext, register: Register.Value, code: List[AssemblyLine]): List[AssemblyLine] = {
def preserveRegisterIfNeeded(ctx: CompilationContext, register: MosRegister.Value, code: List[AssemblyLine]): List[AssemblyLine] = {
val state = register match {
case Register.A => State.A
case Register.X => State.X
case Register.Y => State.Y
case MosRegister.A => State.A
case MosRegister.X => State.X
case MosRegister.Y => State.Y
}
val cmos = ctx.options.flag(CompilationFlag.EmitCmosOpcodes)
if (AssemblyLine.treatment(code, state) != Treatment.Unchanged) {
register match {
case Register.A => AssemblyLine.implied(PHA) +: fixTsx(code) :+ AssemblyLine.implied(PLA)
case Register.X => if (cmos) {
case MosRegister.A => AssemblyLine.implied(PHA) +: fixTsx(code) :+ AssemblyLine.implied(PLA)
case MosRegister.X => if (cmos) {
List(
AssemblyLine.implied(PHA),
AssemblyLine.implied(PHX),
@ -190,7 +109,7 @@ object ExpressionCompiler {
AssemblyLine.implied(PLA),
)
}
case Register.Y => if (cmos) {
case MosRegister.Y => if (cmos) {
List(
AssemblyLine.implied(PHA),
AssemblyLine.implied(PHY),
@ -222,7 +141,7 @@ object ExpressionCompiler {
return Nil
}
val reg = ctx.env.get[VariableInMemory]("__reg")
val compileIndex = compile(ctx, indexExpression, Some(ExpressionCompiler.getExpressionType(ctx, indexExpression) -> RegisterVariable(Register.YA, w)), BranchSpec.None)
val compileIndex = compile(ctx, indexExpression, Some(MosExpressionCompiler.getExpressionType(ctx, indexExpression) -> RegisterVariable(MosRegister.YA, w)), BranchSpec.None)
val prepareRegister = pointy match {
case ConstantPointy(addr, _) =>
List(
@ -242,18 +161,18 @@ object ExpressionCompiler {
compileIndex ++ prepareRegister
}
def compileByteStorage(ctx: CompilationContext, register: Register.Value, target: LhsExpression): List[AssemblyLine] = {
def compileByteStorage(ctx: CompilationContext, register: MosRegister.Value, target: LhsExpression): List[AssemblyLine] = {
val env = ctx.env
val b = env.get[Type]("byte")
val store = register match {
case Register.A => STA
case Register.X => STX
case Register.Y => STY
case MosRegister.A => STA
case MosRegister.X => STX
case MosRegister.Y => STY
}
val transferToA = register match {
case Register.A => NOP
case Register.X => TXA
case Register.Y => TYA
case MosRegister.A => NOP
case MosRegister.X => TXA
case MosRegister.Y => TYA
}
target match {
case VariableExpression(name) =>
@ -291,21 +210,21 @@ object ExpressionCompiler {
def storeToArrayAtUnknownIndex(variableIndex: Expression, arrayAddr: Constant) = {
// TODO check typ
val indexRegister = if (register == Register.Y) Register.X else Register.Y
val indexRegister = if (register == MosRegister.Y) MosRegister.X else MosRegister.Y
val calculatingIndex = preserveRegisterIfNeeded(ctx, register, compile(ctx, variableIndex, Some(b, RegisterVariable(indexRegister, b)), NoBranching))
if (register == Register.A) {
if (register == MosRegister.A) {
indexRegister match {
case Register.Y =>
calculatingIndex ++ arrayBoundsCheck(ctx, pointy, Register.Y, indexExpr) ++ List(AssemblyLine.absoluteY(STA, arrayAddr + constIndex))
case Register.X =>
calculatingIndex ++ arrayBoundsCheck(ctx, pointy, Register.X, indexExpr) ++ List(AssemblyLine.absoluteX(STA, arrayAddr + constIndex))
case MosRegister.Y =>
calculatingIndex ++ arrayBoundsCheck(ctx, pointy, MosRegister.Y, indexExpr) ++ List(AssemblyLine.absoluteY(STA, arrayAddr + constIndex))
case MosRegister.X =>
calculatingIndex ++ arrayBoundsCheck(ctx, pointy, MosRegister.X, indexExpr) ++ List(AssemblyLine.absoluteX(STA, arrayAddr + constIndex))
}
} else {
indexRegister match {
case Register.Y =>
calculatingIndex ++ arrayBoundsCheck(ctx, pointy, Register.Y, indexExpr) ++ List(AssemblyLine.implied(transferToA), AssemblyLine.absoluteY(STA, arrayAddr + constIndex))
case Register.X =>
calculatingIndex ++ arrayBoundsCheck(ctx, pointy, Register.X, indexExpr) ++ List(AssemblyLine.implied(transferToA), AssemblyLine.absoluteX(STA, arrayAddr + constIndex))
case MosRegister.Y =>
calculatingIndex ++ arrayBoundsCheck(ctx, pointy, MosRegister.Y, indexExpr) ++ List(AssemblyLine.implied(transferToA), AssemblyLine.absoluteY(STA, arrayAddr + constIndex))
case MosRegister.X =>
calculatingIndex ++ arrayBoundsCheck(ctx, pointy, MosRegister.X, indexExpr) ++ List(AssemblyLine.implied(transferToA), AssemblyLine.absoluteX(STA, arrayAddr + constIndex))
}
}
}
@ -313,9 +232,9 @@ object ExpressionCompiler {
val reg = ctx.env.get[VariableInMemory]("__reg")
val cmos = ctx.options.flag(CompilationFlag.EmitCmosOpcodes)
register match {
case Register.A =>
case MosRegister.A =>
List(AssemblyLine.implied(PHA)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
case Register.X =>
case MosRegister.X =>
if (code.exists(l => OpcodeClasses.ChangesX(l.opcode))) {
if (cmos)
List(AssemblyLine.implied(PHX)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
@ -324,7 +243,7 @@ object ExpressionCompiler {
} else {
code ++ List(AssemblyLine.implied(TXA), AssemblyLine.indexedY(STA, reg))
}
case Register.Y =>
case MosRegister.Y =>
if (cmos)
List(AssemblyLine.implied(PHY)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
else
@ -344,25 +263,25 @@ object ExpressionCompiler {
//TODO: should there be a type check or a zeropage check?
case (pointerVariable:VariablePointy, None, _, 0 | 1) =>
register match {
case Register.A =>
case MosRegister.A =>
List(AssemblyLine.immediate(LDY, constIndex), AssemblyLine.indexedY(STA, pointerVariable.addr))
case Register.Y =>
case MosRegister.Y =>
List(AssemblyLine.implied(TYA), AssemblyLine.immediate(LDY, constIndex), AssemblyLine.indexedY(STA, pointerVariable.addr), AssemblyLine.implied(TAY))
case Register.X =>
case MosRegister.X =>
List(AssemblyLine.immediate(LDY, constIndex), AssemblyLine.implied(TXA), AssemblyLine.indexedY(STA, pointerVariable.addr))
case _ =>
ErrorReporting.error("Cannot store a word in an array", target.position)
Nil
}
case (pointerVariable:VariablePointy, Some(_), _, 0 | 1) =>
val calculatingIndex = compile(ctx, indexExpr, Some(b, RegisterVariable(Register.Y, b)), NoBranching)
val calculatingIndex = compile(ctx, indexExpr, Some(b, RegisterVariable(MosRegister.Y, b)), NoBranching)
register match {
case Register.A =>
preserveRegisterIfNeeded(ctx, Register.A, calculatingIndex) :+ AssemblyLine.indexedY(STA, pointerVariable.addr)
case Register.X =>
preserveRegisterIfNeeded(ctx, Register.X, calculatingIndex) ++ List(AssemblyLine.implied(TXA), AssemblyLine.indexedY(STA, pointerVariable.addr))
case Register.Y =>
AssemblyLine.implied(TYA) :: preserveRegisterIfNeeded(ctx, Register.A, calculatingIndex) ++ List(
case MosRegister.A =>
preserveRegisterIfNeeded(ctx, MosRegister.A, calculatingIndex) :+ AssemblyLine.indexedY(STA, pointerVariable.addr)
case MosRegister.X =>
preserveRegisterIfNeeded(ctx, MosRegister.X, calculatingIndex) ++ List(AssemblyLine.implied(TXA), AssemblyLine.indexedY(STA, pointerVariable.addr))
case MosRegister.Y =>
AssemblyLine.implied(TYA) :: preserveRegisterIfNeeded(ctx, MosRegister.A, calculatingIndex) ++ List(
AssemblyLine.indexedY(STA, pointerVariable.addr), AssemblyLine.implied(TAY)
)
case _ =>
@ -462,8 +381,8 @@ object ExpressionCompiler {
env.get[TypedThing](name) match {
case source: VariableInMemory =>
target match {
case RegisterVariable(Register.A, _) => AssemblyLine.variable(ctx, LDA, source)
case RegisterVariable(Register.AW, _) =>
case RegisterVariable(MosRegister.A, _) => AssemblyLine.variable(ctx, LDA, source)
case RegisterVariable(MosRegister.AW, _) =>
exprType.size match {
case 1 => if (exprType.isSigned) {
AssemblyLine.variable(ctx, LDA, source) ++ List(
@ -475,9 +394,9 @@ object ExpressionCompiler {
// 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, _) =>
case RegisterVariable(MosRegister.X, _) => AssemblyLine.variable(ctx, LDX, source)
case RegisterVariable(MosRegister.Y, _) => AssemblyLine.variable(ctx, LDY, source)
case RegisterVariable(MosRegister.AX, _) =>
exprType.size match {
case 1 => if (exprType.isSigned) {
AssemblyLine.variable(ctx, LDA, source) ++ List(
@ -488,7 +407,7 @@ object ExpressionCompiler {
case 2 =>
AssemblyLine.variable(ctx, LDA, source) ++ AssemblyLine.variable(ctx, LDX, source, 1)
}
case RegisterVariable(Register.AY, _) =>
case RegisterVariable(MosRegister.AY, _) =>
exprType.size match {
case 1 => if (exprType.isSigned) {
AssemblyLine.variable(ctx, LDA, source) ++ List(
@ -501,7 +420,7 @@ object ExpressionCompiler {
case 2 =>
AssemblyLine.variable(ctx, LDA, source) ++ AssemblyLine.variable(ctx, LDY, source, 1)
}
case RegisterVariable(Register.XA, _) =>
case RegisterVariable(MosRegister.XA, _) =>
exprType.size match {
case 1 => if (exprType.isSigned) {
AssemblyLine.variable(ctx, LDX, source) ++ List(AssemblyLine.implied(TXA)) ++ signExtendA()
@ -510,7 +429,7 @@ object ExpressionCompiler {
case 2 =>
AssemblyLine.variable(ctx, LDX, source) ++ AssemblyLine.variable(ctx,LDA, source, 1)
}
case RegisterVariable(Register.YA, _) =>
case RegisterVariable(MosRegister.YA, _) =>
exprType.size match {
case 1 => if (exprType.isSigned) {
AssemblyLine.variable(ctx, LDY, source) ++ List(AssemblyLine.implied(TYA)) ++ signExtendA()
@ -551,10 +470,10 @@ object ExpressionCompiler {
}
case source@StackVariable(_, sourceType, offset) =>
target match {
case RegisterVariable(Register.A, _) => List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset))
case RegisterVariable(Register.X, _) => List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset), AssemblyLine.implied(TAX))
case RegisterVariable(Register.Y, _) => List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDY, offset + ctx.extraStackOffset))
case RegisterVariable(Register.AX, _) =>
case RegisterVariable(MosRegister.A, _) => List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset))
case RegisterVariable(MosRegister.X, _) => List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset), AssemblyLine.implied(TAX))
case RegisterVariable(MosRegister.Y, _) => List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDY, offset + ctx.extraStackOffset))
case RegisterVariable(MosRegister.AX, _) =>
exprType.size match {
case 1 => if (exprType.isSigned) {
List(
@ -575,10 +494,10 @@ object ExpressionCompiler {
AssemblyLine.implied(TAX),
AssemblyLine.implied(PLA))
}
case RegisterVariable(Register.AY, _) =>
case RegisterVariable(MosRegister.AY, _) =>
exprType.size match {
case 1 => if (exprType.isSigned) {
val label = MfCompiler.nextLabel("sx")
val label = MosCompiler.nextLabel("sx")
??? // TODO
} else {
List(
@ -591,12 +510,12 @@ object ExpressionCompiler {
AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset),
AssemblyLine.absoluteX(LDY, offset + ctx.extraStackOffset + 1))
}
case RegisterVariable(Register.XA, _) =>
case RegisterVariable(MosRegister.XA, _) =>
??? // TODO
case RegisterVariable(Register.YA, _) =>
case RegisterVariable(MosRegister.YA, _) =>
exprType.size match {
case 1 => if (exprType.isSigned) {
val label = MfCompiler.nextLabel("sx")
val label = MosCompiler.nextLabel("sx")
??? // TODO
} else {
List(
@ -654,7 +573,7 @@ object ExpressionCompiler {
val register = target match {
case RegisterVariable(r, _) => r
case _ => Register.A
case _ => MosRegister.A
}
val suffix = target match {
case RegisterVariable(_, _) => Nil
@ -672,31 +591,31 @@ object ExpressionCompiler {
}
}
val load = register match {
case Register.A | Register.AX | Register.AY => LDA
case Register.X => LDX
case Register.Y => LDY
case MosRegister.A | MosRegister.AX | MosRegister.AY => LDA
case MosRegister.X => LDX
case MosRegister.Y => LDY
}
def loadFromArrayAtUnknownIndex(variableIndex: Expression, arrayAddr: Constant) = {
// TODO check typ
val indexRegister = if (register == Register.Y) Register.X else Register.Y
val indexRegister = if (register == MosRegister.Y) MosRegister.X else MosRegister.Y
val calculatingIndex = compile(ctx, variableIndex, Some(b, RegisterVariable(indexRegister, b)), NoBranching)
indexRegister match {
case Register.Y =>
calculatingIndex ++ arrayBoundsCheck(ctx, pointy, Register.Y, indexExpr) ++ List(AssemblyLine.absoluteY(load, arrayAddr + constantIndex))
case Register.X =>
calculatingIndex ++ arrayBoundsCheck(ctx, pointy, Register.X, indexExpr) ++ List(AssemblyLine.absoluteX(load, arrayAddr + constantIndex))
case MosRegister.Y =>
calculatingIndex ++ arrayBoundsCheck(ctx, pointy, MosRegister.Y, indexExpr) ++ List(AssemblyLine.absoluteY(load, arrayAddr + constantIndex))
case MosRegister.X =>
calculatingIndex ++ arrayBoundsCheck(ctx, pointy, MosRegister.X, indexExpr) ++ List(AssemblyLine.absoluteX(load, arrayAddr + constantIndex))
}
}
def loadFromReg() = {
val reg = ctx.env.get[VariableInMemory]("__reg")
register match {
case Register.A =>
case MosRegister.A =>
List(AssemblyLine.indexedY(LDA, reg))
case Register.X =>
case MosRegister.X =>
List(AssemblyLine.indexedY(LDA, reg), AssemblyLine.implied(TAX))
case Register.Y =>
case MosRegister.Y =>
List(AssemblyLine.indexedY(LDA, reg), AssemblyLine.implied(TAY))
}
}
@ -711,21 +630,21 @@ object ExpressionCompiler {
prepareWordIndexing(ctx, a, indexExpr) ++ loadFromReg()
case (p:VariablePointy, None, 0 | 1, _) =>
register match {
case Register.A =>
case MosRegister.A =>
List(AssemblyLine.immediate(LDY, constantIndex), AssemblyLine.indexedY(LDA, p.addr))
case Register.Y =>
case MosRegister.Y =>
List(AssemblyLine.immediate(LDY, constantIndex), AssemblyLine.indexedY(LDA, p.addr), AssemblyLine.implied(TAY))
case Register.X =>
case MosRegister.X =>
List(AssemblyLine.immediate(LDY, constantIndex), AssemblyLine.indexedY(LDA, p.addr), AssemblyLine.implied(TAX))
}
case (p:VariablePointy, Some(_), 0 | 1, _) =>
val calculatingIndex = compile(ctx, indexExpr, Some(b, RegisterVariable(Register.Y, b)), NoBranching)
val calculatingIndex = compile(ctx, indexExpr, Some(b, RegisterVariable(MosRegister.Y, b)), NoBranching)
register match {
case Register.A =>
case MosRegister.A =>
calculatingIndex :+ AssemblyLine.indexedY(LDA, p.addr)
case Register.X =>
case MosRegister.X =>
calculatingIndex ++ List(AssemblyLine.indexedY(LDA, p.addr), AssemblyLine.implied(TAX))
case Register.Y =>
case MosRegister.Y =>
calculatingIndex ++ List(AssemblyLine.indexedY(LDA, p.addr), AssemblyLine.implied(TAY))
}
case _ =>
@ -733,9 +652,9 @@ object ExpressionCompiler {
Nil
}
register match {
case Register.A | Register.X | Register.Y => result ++ suffix
case Register.AX => result :+ AssemblyLine.immediate(LDX, 0)
case Register.AY => result :+ AssemblyLine.immediate(LDY, 0)
case MosRegister.A | MosRegister.X | MosRegister.Y => result ++ suffix
case MosRegister.AX => result :+ AssemblyLine.immediate(LDX, 0)
case MosRegister.AY => result :+ AssemblyLine.immediate(LDY, 0)
}
}
case SumExpression(params, decimal) =>
@ -774,19 +693,19 @@ object ExpressionCompiler {
assertCompatible(exprType, target.typ)
target match {
// TODO: some more complex ones may not work correctly
case RegisterVariable(Register.A | Register.X | Register.Y, _) => compile(ctx, l, exprTypeAndVariable, branches)
case RegisterVariable(Register.AX, _) =>
compile(ctx, l, Some(b -> RegisterVariable(Register.A, b)), branches) ++
preserveRegisterIfNeeded(ctx, Register.A, compile(ctx, h, Some(b -> RegisterVariable(Register.X, b)), branches))
case RegisterVariable(Register.AY, _) =>
compile(ctx, l, Some(b -> RegisterVariable(Register.A, b)), branches) ++
preserveRegisterIfNeeded(ctx, Register.A, compile(ctx, h, Some(b -> RegisterVariable(Register.Y, b)), branches))
case RegisterVariable(Register.XA, _) =>
compile(ctx, l, Some(b -> RegisterVariable(Register.X, b)), branches) ++
compile(ctx, h, Some(b -> RegisterVariable(Register.A, b)), branches)
case RegisterVariable(Register.YA, _) =>
compile(ctx, l, Some(b -> RegisterVariable(Register.Y, b)), branches) ++
compile(ctx, h, Some(b -> RegisterVariable(Register.A, b)), branches)
case RegisterVariable(MosRegister.A | MosRegister.X | MosRegister.Y, _) => compile(ctx, l, exprTypeAndVariable, branches)
case RegisterVariable(MosRegister.AX, _) =>
compile(ctx, l, Some(b -> RegisterVariable(MosRegister.A, b)), branches) ++
preserveRegisterIfNeeded(ctx, MosRegister.A, compile(ctx, h, Some(b -> RegisterVariable(MosRegister.X, b)), branches))
case RegisterVariable(MosRegister.AY, _) =>
compile(ctx, l, Some(b -> RegisterVariable(MosRegister.A, b)), branches) ++
preserveRegisterIfNeeded(ctx, MosRegister.A, compile(ctx, h, Some(b -> RegisterVariable(MosRegister.Y, b)), branches))
case RegisterVariable(MosRegister.XA, _) =>
compile(ctx, l, Some(b -> RegisterVariable(MosRegister.X, b)), branches) ++
compile(ctx, h, Some(b -> RegisterVariable(MosRegister.A, b)), branches)
case RegisterVariable(MosRegister.YA, _) =>
compile(ctx, l, Some(b -> RegisterVariable(MosRegister.Y, b)), branches) ++
compile(ctx, h, Some(b -> RegisterVariable(MosRegister.A, b)), branches)
case target: VariableInMemory =>
target.typ.size match {
case 1 =>
@ -827,7 +746,7 @@ object ExpressionCompiler {
ErrorReporting.error("Invalid parameter type for hi/lo", param.position)
compile(ctx, param, None, BranchSpec.None)
} else {
val compilation = compile(ctx, param, Some(ExpressionCompiler.getExpressionType(ctx, param) -> RegisterVariable(Register.AX, w)), BranchSpec.None)
val compilation = compile(ctx, param, Some(MosExpressionCompiler.getExpressionType(ctx, param) -> RegisterVariable(MosRegister.AX, w)), BranchSpec.None)
if (hi) {
if (typ.size == 2) compilation :+ AssemblyLine.implied(TXA)
else if (typ.isSigned) compilation ++ signExtendA()
@ -853,8 +772,8 @@ object ExpressionCompiler {
case _ =>
ErrorReporting.warn("Unspecified nonet operation, results might be unpredictable", ctx.options, expr.position)
}
val label = MfCompiler.nextLabel("no")
compile(ctx, params.head, Some(b -> RegisterVariable(Register.A, b)), BranchSpec.None) ++ List(
val label = MosCompiler.nextLabel("no")
compile(ctx, params.head, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None) ++ List(
AssemblyLine.immediate(LDX, 0),
AssemblyLine.relative(BCC, label),
AssemblyLine.implied(INX),
@ -867,7 +786,7 @@ object ExpressionCompiler {
case BranchIfFalse(_) =>
params.flatMap(compile(ctx, _, exprTypeAndVariable, branches))
case _ =>
val skip = MfCompiler.nextLabel("an")
val skip = MosCompiler.nextLabel("an")
params.init.flatMap(compile(ctx, _, exprTypeAndVariable, BranchIfFalse(skip))) ++
compile(ctx, params.last, exprTypeAndVariable, branches) ++
List(AssemblyLine.label(skip))
@ -878,7 +797,7 @@ object ExpressionCompiler {
case BranchIfTrue(_) =>
params.flatMap(compile(ctx, _, exprTypeAndVariable, branches))
case _ =>
val skip = MfCompiler.nextLabel("or")
val skip = MosCompiler.nextLabel("or")
params.init.flatMap(compile(ctx, _, exprTypeAndVariable, BranchIfTrue(skip))) ++
compile(ctx, params.last, exprTypeAndVariable, branches) ++
List(AssemblyLine.label(skip))
@ -1096,7 +1015,7 @@ object ExpressionCompiler {
val (l, r, size) = assertAssignmentLike(ctx, params)
size match {
case 1 =>
DecimalBuiltIns.compileByteShiftLeft(ctx, l, r, rotate = false) ++ compileByteStorage(ctx, Register.A, l)
DecimalBuiltIns.compileByteShiftLeft(ctx, l, r, rotate = false) ++ compileByteStorage(ctx, MosRegister.A, l)
case i if i >= 2 =>
l match {
case v: LhsExpression =>
@ -1107,7 +1026,7 @@ object ExpressionCompiler {
val (l, r, size) = assertAssignmentLike(ctx, params)
size match {
case 1 =>
DecimalBuiltIns.compileByteShiftRight(ctx, l, r, rotate = false) ++ compileByteStorage(ctx, Register.A, l)
DecimalBuiltIns.compileByteShiftRight(ctx, l, r, rotate = false) ++ compileByteStorage(ctx, MosRegister.A, l)
case i if i >= 2 =>
l match {
case v: LhsExpression =>
@ -1181,7 +1100,7 @@ object ExpressionCompiler {
case function: MacroFunction =>
val (paramPreparation, statements) = MacroExpander.inlineFunction(ctx, function, params, expr.position)
paramPreparation ++ statements.map {
case AssemblyStatement(opcode, addrMode, expression, elidable) =>
case MosAssemblyStatement(opcode, addrMode, expression, elidable) =>
val param = env.evalForAsm(expression).getOrElse {
ErrorReporting.error("Inlining failed due to non-constant things", expression.position)
Constant.Zero
@ -1271,14 +1190,14 @@ object ExpressionCompiler {
def expressionStorageFromAX(ctx: CompilationContext, exprTypeAndVariable: Option[(Type, Variable)], position: Option[Position]): List[AssemblyLine] = {
exprTypeAndVariable.fold(noop) {
case (VoidType, _) => ErrorReporting.fatal("Cannot assign word to void", position)
case (_, RegisterVariable(Register.A, _)) => noop
case (_, RegisterVariable(Register.AW, _)) => List(AssemblyLine.implied(XBA), AssemblyLine.implied(TXA), AssemblyLine.implied(XBA))
case (_, RegisterVariable(Register.X, _)) => List(AssemblyLine.implied(TAX))
case (_, RegisterVariable(Register.Y, _)) => List(AssemblyLine.implied(TAY))
case (_, RegisterVariable(Register.AX, _)) =>
case (_, RegisterVariable(MosRegister.A, _)) => noop
case (_, RegisterVariable(MosRegister.AW, _)) => List(AssemblyLine.implied(XBA), AssemblyLine.implied(TXA), AssemblyLine.implied(XBA))
case (_, RegisterVariable(MosRegister.X, _)) => List(AssemblyLine.implied(TAX))
case (_, RegisterVariable(MosRegister.Y, _)) => List(AssemblyLine.implied(TAY))
case (_, RegisterVariable(MosRegister.AX, _)) =>
// TODO: sign extension
noop
case (_, RegisterVariable(Register.XA, _)) =>
case (_, RegisterVariable(MosRegister.XA, _)) =>
// TODO: sign extension
if (ctx.options.flag(CompilationFlag.EmitHudsonOpcodes)) {
List(AssemblyLine.implied(HuSAX))
@ -1297,12 +1216,12 @@ object ExpressionCompiler {
AssemblyLine.implied(TAX),
AssemblyLine.implied(PLA)) // fuck this shit
}
case (_, RegisterVariable(Register.YA, _)) =>
case (_, RegisterVariable(MosRegister.YA, _)) =>
// TODO: sign extension
List(
AssemblyLine.implied(TAY),
AssemblyLine.implied(TXA))
case (_, RegisterVariable(Register.AY, _)) =>
case (_, RegisterVariable(MosRegister.AY, _)) =>
// TODO: sign extension
if (ctx.options.flag(CompilationFlag.EmitHudsonOpcodes)) {
List(AssemblyLine.implied(SXY))
@ -1408,23 +1327,17 @@ object ExpressionCompiler {
// TODO check v.typ
compile(ctx, source, Some((getExpressionType(ctx, source), v)), NoBranching)
case SeparateBytesExpression(h: LhsExpression, l: LhsExpression) =>
compile(ctx, source, Some(w, RegisterVariable(Register.AX, w)), NoBranching) ++
compileByteStorage(ctx, Register.A, l) ++ compileByteStorage(ctx, Register.X, h)
compile(ctx, source, Some(w, RegisterVariable(MosRegister.AX, w)), NoBranching) ++
compileByteStorage(ctx, MosRegister.A, l) ++ compileByteStorage(ctx, MosRegister.X, h)
case SeparateBytesExpression(_, _) =>
ErrorReporting.error("Invalid left-hand-side use of `:`")
Nil
case _ =>
compile(ctx, source, Some(b, RegisterVariable(Register.A, b)), NoBranching) ++ compileByteStorage(ctx, Register.A, target)
compile(ctx, source, Some(b, RegisterVariable(MosRegister.A, b)), NoBranching) ++ compileByteStorage(ctx, MosRegister.A, target)
}
}
def lookupFunction(ctx: CompilationContext, f: FunctionCallExpression): MangledFunction = {
val paramsWithTypes = f.expressions.map(x => getExpressionType(ctx, x) -> x)
ctx.env.lookupFunction(f.functionName, paramsWithTypes).getOrElse(
ErrorReporting.fatal(s"Cannot find function `${f.functionName}` with given params `${paramsWithTypes.map(_._1).mkString("(", ",", ")")}`", f.position))
}
def arrayBoundsCheck(ctx: CompilationContext, pointy: Pointy, register: Register.Value, index: Expression): List[AssemblyLine] = {
def arrayBoundsCheck(ctx: CompilationContext, pointy: Pointy, register: MosRegister.Value, index: Expression): List[AssemblyLine] = {
if (!ctx.options.flags(CompilationFlag.CheckIndexOutOfBounds)) return Nil
val arrayLength = pointy match {
case _: VariablePointy => return Nil
@ -1442,11 +1355,11 @@ object ExpressionCompiler {
case _ =>
}
if (arrayLength > 0 && arrayLength < 255) {
val label = MfCompiler.nextLabel("bc")
val label = MosCompiler.nextLabel("bc")
val compare = register match {
case Register.A => CMP
case Register.X => CPX
case Register.Y => CPY
case MosRegister.A => CMP
case MosRegister.X => CPX
case MosRegister.Y => CPY
}
List(
AssemblyLine.implied(PHP),
@ -1461,7 +1374,7 @@ object ExpressionCompiler {
}
private def signExtendA(): List[AssemblyLine] = {
val label = MfCompiler.nextLabel("sx")
val label = MosCompiler.nextLabel("sx")
List(
AssemblyLine.immediate(ORA, 0x7F),
AssemblyLine.relative(BMI, label),

View File

@ -1,19 +1,17 @@
package millfork.compiler
package millfork.compiler.mos
import java.util.concurrent.atomic.AtomicLong
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.env._
import millfork.node.{Register, _}
import millfork.CompilationFlag
import millfork.assembly.AddrMode._
import millfork.assembly.Opcode._
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
import millfork.env._
import millfork.error.ErrorReporting
import millfork.node.{MosRegister, _}
/**
* @author Karol Stasiak
*/
object StatementCompiler {
object MosStatementCompiler {
private def labelChunk(labelName: String) = List(AssemblyLine.label(Label(labelName)))
@ -41,9 +39,9 @@ object StatementCompiler {
AssemblyLine.zeropage(STA, reg)
)
} else Nil
val someRegisterA = Some(b, RegisterVariable(Register.A, b))
val someRegisterAX = Some(w, RegisterVariable(Register.AX, w))
val someRegisterYA = Some(w, RegisterVariable(Register.YA, w))
val someRegisterA = Some(b, RegisterVariable(MosRegister.A, b))
val someRegisterAX = Some(w, RegisterVariable(MosRegister.AX, w))
val someRegisterYA = Some(w, RegisterVariable(MosRegister.YA, w))
val returnInstructions = if (m.interrupt) {
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
if (ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
@ -114,7 +112,7 @@ object StatementCompiler {
})
}
statement match {
case AssemblyStatement(o, a, x, e) =>
case MosAssemblyStatement(o, a, x, e) =>
val c: Constant = x match {
// TODO: hmmm
case VariableExpression(name) =>
@ -143,14 +141,14 @@ object StatementCompiler {
}
}
case Assignment(dest, source) =>
ExpressionCompiler.compileAssignment(ctx, source, dest)
MosExpressionCompiler.compileAssignment(ctx, source, dest)
case ExpressionStatement(e@FunctionCallExpression(name, params)) =>
env.lookupFunction(name, params.map(p => ExpressionCompiler.getExpressionType(ctx, p) -> p)) match {
env.lookupFunction(name, params.map(p => MosExpressionCompiler.getExpressionType(ctx, p) -> p)) match {
case Some(i: MacroFunction) =>
val (paramPreparation, inlinedStatements) = MacroExpander.inlineFunction(ctx, i, params, e.position)
paramPreparation ++ compile(ctx.withInlinedEnv(i.environment), inlinedStatements)
case _ =>
ExpressionCompiler.compile(ctx, e, None, NoBranching)
MosExpressionCompiler.compile(ctx, e, None, NoBranching)
}
case ExpressionStatement(e) =>
e match {
@ -158,7 +156,7 @@ object StatementCompiler {
ErrorReporting.warn("Pointless expression statement", ctx.options, statement.position)
case _ =>
}
ExpressionCompiler.compile(ctx, e, None, NoBranching)
MosExpressionCompiler.compile(ctx, e, None, NoBranching)
case ReturnStatement(None) =>
// TODO: return type check
// TODO: better stackpointer fix
@ -187,9 +185,9 @@ object StatementCompiler {
ErrorReporting.error("Cannot return anything from a void function", statement.position)
stackPointerFixBeforeReturn(ctx) ++ returnInstructions
case 1 =>
ExpressionCompiler.compile(ctx, e, someRegisterA, NoBranching) ++ stackPointerFixBeforeReturn(ctx) ++ returnInstructions
MosExpressionCompiler.compile(ctx, e, someRegisterA, NoBranching) ++ stackPointerFixBeforeReturn(ctx) ++ returnInstructions
case 2 =>
ExpressionCompiler.compile(ctx, e, someRegisterAX, NoBranching) ++ stackPointerFixBeforeReturn(ctx) ++ returnInstructions
MosExpressionCompiler.compile(ctx, e, someRegisterAX, NoBranching) ++ stackPointerFixBeforeReturn(ctx) ++ returnInstructions
}
case _ =>
m.returnType.size match {
@ -197,14 +195,14 @@ object StatementCompiler {
ErrorReporting.error("Cannot return anything from a void function", statement.position)
stackPointerFixBeforeReturn(ctx) ++ List(AssemblyLine.discardAF(), AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
case 1 =>
ExpressionCompiler.compile(ctx, e, someRegisterA, NoBranching) ++ stackPointerFixBeforeReturn(ctx) ++ List(AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
MosExpressionCompiler.compile(ctx, e, someRegisterA, NoBranching) ++ stackPointerFixBeforeReturn(ctx) ++ List(AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
case 2 =>
// TODO: ???
val stackPointerFix = stackPointerFixBeforeReturn(ctx)
if (stackPointerFix.isEmpty) {
ExpressionCompiler.compile(ctx, e, someRegisterAX, NoBranching) ++ List(AssemblyLine.discardYF()) ++ returnInstructions
MosExpressionCompiler.compile(ctx, e, someRegisterAX, NoBranching) ++ List(AssemblyLine.discardYF()) ++ returnInstructions
} else {
ExpressionCompiler.compile(ctx, e, someRegisterYA, NoBranching) ++
MosExpressionCompiler.compile(ctx, e, someRegisterYA, NoBranching) ++
stackPointerFix ++
List(AssemblyLine.implied(TAX), AssemblyLine.implied(TYA), AssemblyLine.discardYF()) ++
returnInstructions
@ -212,103 +210,103 @@ object StatementCompiler {
}
}
case IfStatement(condition, thenPart, elsePart) =>
val condType = ExpressionCompiler.getExpressionType(ctx, condition)
val condType = MosExpressionCompiler.getExpressionType(ctx, condition)
val thenBlock = compile(ctx, thenPart)
val elseBlock = compile(ctx, elsePart)
val largeThenBlock = thenBlock.map(_.sizeInBytes).sum > 100
val largeElseBlock = elseBlock.map(_.sizeInBytes).sum > 100
condType match {
case ConstantBooleanType(_, true) =>
ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching) ++ thenBlock
MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching) ++ thenBlock
case ConstantBooleanType(_, false) =>
ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching) ++ elseBlock
MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching) ++ elseBlock
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
(thenPart, elsePart) match {
case (Nil, Nil) =>
ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
case (Nil, _) =>
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
if (largeElseBlock) {
val middle = MfCompiler.nextLabel("el")
val end = MfCompiler.nextLabel("fi")
val middle = MosCompiler.nextLabel("el")
val end = MosCompiler.nextLabel("fi")
List(conditionBlock, branchChunk(jumpIfFalse, middle), jmpChunk(end), labelChunk(middle), elseBlock, labelChunk(end)).flatten
} else {
val end = MfCompiler.nextLabel("fi")
val end = MosCompiler.nextLabel("fi")
List(conditionBlock, branchChunk(jumpIfTrue, end), elseBlock, labelChunk(end)).flatten
}
case (_, Nil) =>
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
if (largeThenBlock) {
val middle = MfCompiler.nextLabel("th")
val end = MfCompiler.nextLabel("fi")
val middle = MosCompiler.nextLabel("th")
val end = MosCompiler.nextLabel("fi")
List(conditionBlock, branchChunk(jumpIfTrue, middle), jmpChunk(end), labelChunk(middle), thenBlock, labelChunk(end)).flatten
} else {
val end = MfCompiler.nextLabel("fi")
val end = MosCompiler.nextLabel("fi")
List(conditionBlock, branchChunk(jumpIfFalse, end), thenBlock, labelChunk(end)).flatten
}
case _ =>
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
if (largeThenBlock) {
if (largeElseBlock) {
val middleT = MfCompiler.nextLabel("th")
val middleE = MfCompiler.nextLabel("el")
val end = MfCompiler.nextLabel("fi")
val middleT = MosCompiler.nextLabel("th")
val middleE = MosCompiler.nextLabel("el")
val end = MosCompiler.nextLabel("fi")
List(conditionBlock, branchChunk(jumpIfTrue, middleT), jmpChunk(middleE), labelChunk(middleT), thenBlock, jmpChunk(end), labelChunk(middleE), elseBlock, labelChunk(end)).flatten
} else {
val middle = MfCompiler.nextLabel("th")
val end = MfCompiler.nextLabel("fi")
val middle = MosCompiler.nextLabel("th")
val end = MosCompiler.nextLabel("fi")
List(conditionBlock, branchChunk(jumpIfTrue, middle), elseBlock, jmpChunk(end), labelChunk(middle), thenBlock, labelChunk(end)).flatten
}
} else {
val middle = MfCompiler.nextLabel("el")
val end = MfCompiler.nextLabel("fi")
val middle = MosCompiler.nextLabel("el")
val end = MosCompiler.nextLabel("fi")
List(conditionBlock, branchChunk(jumpIfFalse, middle), thenBlock, jmpChunk(end), labelChunk(middle), elseBlock, labelChunk(end)).flatten
}
}
case BuiltInBooleanType =>
(thenPart, elsePart) match {
case (Nil, Nil) =>
ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
case (Nil, _) =>
if (largeElseBlock) {
val middle = MfCompiler.nextLabel("el")
val end = MfCompiler.nextLabel("fi")
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(middle))
val middle = MosCompiler.nextLabel("el")
val end = MosCompiler.nextLabel("fi")
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(middle))
List(conditionBlock, jmpChunk(end), labelChunk(middle), elseBlock, labelChunk(end)).flatten
} else {
val end = MfCompiler.nextLabel("fi")
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(end))
val end = MosCompiler.nextLabel("fi")
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(end))
List(conditionBlock, elseBlock, labelChunk(end)).flatten
}
case (_, Nil) =>
if (largeThenBlock) {
val middle = MfCompiler.nextLabel("th")
val end = MfCompiler.nextLabel("fi")
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle))
val middle = MosCompiler.nextLabel("th")
val end = MosCompiler.nextLabel("fi")
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle))
List(conditionBlock, jmpChunk(end), labelChunk(middle), thenBlock, labelChunk(end)).flatten
} else {
val end = MfCompiler.nextLabel("fi")
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(end))
val end = MosCompiler.nextLabel("fi")
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(end))
List(conditionBlock, thenBlock, labelChunk(end)).flatten
}
case _ =>
if (largeThenBlock) {
if (largeElseBlock) {
val middleT = MfCompiler.nextLabel("th")
val middleE = MfCompiler.nextLabel("el")
val end = MfCompiler.nextLabel("fi")
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middleT))
val middleT = MosCompiler.nextLabel("th")
val middleE = MosCompiler.nextLabel("el")
val end = MosCompiler.nextLabel("fi")
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middleT))
List(conditionBlock, jmpChunk(middleE), labelChunk(middleT), thenBlock, jmpChunk(end), labelChunk(middleE), elseBlock, labelChunk(end)).flatten
} else {
val middle = MfCompiler.nextLabel("th")
val end = MfCompiler.nextLabel("fi")
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle))
val middle = MosCompiler.nextLabel("th")
val end = MosCompiler.nextLabel("fi")
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle))
List(conditionBlock, elseBlock, jmpChunk(end), labelChunk(middle), thenBlock, labelChunk(end)).flatten
}
} else {
val middle = MfCompiler.nextLabel("el")
val end = MfCompiler.nextLabel("fi")
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(middle))
val middle = MosCompiler.nextLabel("el")
val end = MosCompiler.nextLabel("fi")
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(middle))
List(conditionBlock, thenBlock, jmpChunk(end), labelChunk(middle), elseBlock, labelChunk(end)).flatten
}
}
@ -317,11 +315,11 @@ object StatementCompiler {
Nil
}
case WhileStatement(condition, bodyPart, incrementPart, labels) =>
val start = MfCompiler.nextLabel("wh")
val middle = MfCompiler.nextLabel("he")
val inc = MfCompiler.nextLabel("fp")
val end = MfCompiler.nextLabel("ew")
val condType = ExpressionCompiler.getExpressionType(ctx, condition)
val start = MosCompiler.nextLabel("wh")
val middle = MosCompiler.nextLabel("he")
val inc = MosCompiler.nextLabel("fp")
val end = MosCompiler.nextLabel("ew")
val condType = MosExpressionCompiler.getExpressionType(ctx, condition)
val bodyBlock = compile(ctx.addLabels(labels, Label(end), Label(inc)), bodyPart)
val incrementBlock = compile(ctx.addLabels(labels, Label(end), Label(inc)), incrementPart)
val largeBodyBlock = bodyBlock.map(_.sizeInBytes).sum + incrementBlock.map(_.sizeInBytes).sum > 100
@ -331,19 +329,19 @@ object StatementCompiler {
case ConstantBooleanType(_, false) => Nil
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
if (largeBodyBlock) {
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
List(labelChunk(start), conditionBlock, branchChunk(jumpIfTrue, middle), jmpChunk(end), labelChunk(middle), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
} else {
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
List(jmpChunk(middle), labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, labelChunk(middle), conditionBlock, branchChunk(jumpIfTrue, start), labelChunk(end)).flatten
// List(labelChunk(start), conditionBlock, branchChunk(jumpIfFalse, end), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
}
case BuiltInBooleanType =>
if (largeBodyBlock) {
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle))
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle))
List(labelChunk(start), conditionBlock, jmpChunk(end), labelChunk(middle), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
} else {
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(start))
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(start))
List(jmpChunk(middle), labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, labelChunk(middle), conditionBlock, labelChunk(end)).flatten
// List(labelChunk(start), conditionBlock, bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
}
@ -352,21 +350,21 @@ object StatementCompiler {
Nil
}
case DoWhileStatement(bodyPart, incrementPart, condition, labels) =>
val start = MfCompiler.nextLabel("do")
val inc = MfCompiler.nextLabel("fp")
val end = MfCompiler.nextLabel("od")
val condType = ExpressionCompiler.getExpressionType(ctx, condition)
val start = MosCompiler.nextLabel("do")
val inc = MosCompiler.nextLabel("fp")
val end = MosCompiler.nextLabel("od")
val condType = MosExpressionCompiler.getExpressionType(ctx, condition)
val bodyBlock = compile(ctx.addLabels(labels, Label(end), Label(inc)), bodyPart)
val incrementBlock = compile(ctx.addLabels(labels, Label(end), Label(inc)), incrementPart)
val largeBodyBlock = bodyBlock.map(_.sizeInBytes).sum + incrementBlock.map(_.sizeInBytes).sum > 100
condType match {
case ConstantBooleanType(_, true) =>
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
List(labelChunk(start),bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
case ConstantBooleanType(_, false) =>
List(bodyBlock, labelChunk(inc), incrementBlock, labelChunk(end)).flatten
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
if (largeBodyBlock) {
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, branchChunk(jumpIfFalse, end), jmpChunk(start), labelChunk(end)).flatten
} else {
@ -374,10 +372,10 @@ object StatementCompiler {
}
case BuiltInBooleanType =>
if (largeBodyBlock) {
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(end))
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(end))
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, jmpChunk(start), labelChunk(end)).flatten
} else {
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(start))
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(start))
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, labelChunk(end)).flatten
}
case _ =>
@ -395,13 +393,13 @@ object StatementCompiler {
(direction, env.eval(start), env.eval(end)) match {
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e - 1 =>
val end = MfCompiler.nextLabel("of")
val end = MosCompiler.nextLabel("of")
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, f.start) :: f.body) ++ labelChunk(end)
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s >= e =>
Nil
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e =>
val end = MfCompiler.nextLabel("of")
val end = MosCompiler.nextLabel("of")
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, f.start) :: f.body) ++ labelChunk(end)
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s > e =>
Nil
@ -413,7 +411,7 @@ object StatementCompiler {
))
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s == e =>
val end = MfCompiler.nextLabel("of")
val end = MosCompiler.nextLabel("of")
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, LiteralExpression(s, ssize)) :: f.body) ++ labelChunk(end)
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s < e =>
Nil

View File

@ -1,13 +1,12 @@
package millfork.compiler
package millfork.compiler.mos
import millfork.CompilationFlag
import millfork.assembly.AssemblyLine
import millfork.assembly.AddrMode._
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
import millfork.env._
import millfork.error.ErrorReporting
import millfork.node._
import millfork.assembly.Opcode
import millfork.assembly.Opcode._
import millfork.assembly.AddrMode._
/**
* @author Karol Stasiak
@ -21,12 +20,12 @@ object PseudoregisterBuiltIns {
val (variablePart, constPart) = ctx.env.evalVariableAndConstantSubParts(SumExpression(params, decimal = false))
variablePart match {
case None =>
return ExpressionCompiler.compileConstant(ctx, constPart, RegisterVariable(Register.AX, w))
return MosExpressionCompiler.compileConstant(ctx, constPart, RegisterVariable(MosRegister.AX, w))
case Some(v) =>
val typ = ExpressionCompiler.getExpressionType(ctx, v)
val typ = MosExpressionCompiler.getExpressionType(ctx, v)
if (typ.size == 1 && !typ.isSigned) {
val bytePart = ExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(Register.A, b)), BranchSpec.None)
val label = MfCompiler.nextLabel("ah")
val bytePart = MosExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None)
val label = MosCompiler.nextLabel("ah")
return bytePart ++ List(
AssemblyLine.implied(CLC),
AssemblyLine.immediate(ADC, constPart.loByte),
@ -47,7 +46,7 @@ object PseudoregisterBuiltIns {
}
val reg = ctx.env.get[VariableInMemory]("__reg")
val head = params.head match {
case (false, e) => ExpressionCompiler.compile(ctx, e, Some(ExpressionCompiler.getExpressionType(ctx, e) -> reg), BranchSpec.None)
case (false, e) => MosExpressionCompiler.compile(ctx, e, Some(MosExpressionCompiler.getExpressionType(ctx, e) -> reg), BranchSpec.None)
case (true, e) => ???
}
params.tail.foldLeft[List[AssemblyLine]](head){case (code, (sub, param)) => code ++ addToReg(ctx, param, sub, decimal)} ++ List(
@ -65,7 +64,7 @@ object PseudoregisterBuiltIns {
val w = ctx.env.get[Type]("word")
val reg = ctx.env.get[VariableInMemory]("__reg")
// TODO: smarter on 65816
val compileRight = ExpressionCompiler.compile(ctx, r, Some(ExpressionCompiler.getExpressionType(ctx, r) -> reg), BranchSpec.None)
val compileRight = MosExpressionCompiler.compile(ctx, r, Some(MosExpressionCompiler.getExpressionType(ctx, r) -> reg), BranchSpec.None)
val op = if (subtract) SBC else ADC
val prepareCarry = AssemblyLine.implied(if (subtract) SEC else CLC)
compileRight match {
@ -118,7 +117,7 @@ object PseudoregisterBuiltIns {
val b = ctx.env.get[Type]("byte")
val w = ctx.env.get[Type]("word")
val reg = ctx.env.get[VariableInMemory]("__reg")
val head = ExpressionCompiler.compile(ctx, params.head, Some(ExpressionCompiler.getExpressionType(ctx, params.head) -> reg), BranchSpec.None)
val head = MosExpressionCompiler.compile(ctx, params.head, Some(MosExpressionCompiler.getExpressionType(ctx, params.head) -> reg), BranchSpec.None)
params.tail.foldLeft[List[AssemblyLine]](head){case (code, param) => code ++ bitOpReg(ctx, param, op)} ++ List(
AssemblyLine.zeropage(LDA, reg),
AssemblyLine.zeropage(LDX, reg, 1),
@ -134,7 +133,7 @@ object PseudoregisterBuiltIns {
val w = ctx.env.get[Type]("word")
val reg = ctx.env.get[VariableInMemory]("__reg")
// TODO: smarter on 65816
val compileRight = ExpressionCompiler.compile(ctx, r, Some(ExpressionCompiler.getExpressionType(ctx, r) -> reg), BranchSpec.None)
val compileRight = MosExpressionCompiler.compile(ctx, r, Some(MosExpressionCompiler.getExpressionType(ctx, r) -> reg), BranchSpec.None)
compileRight match {
case List(
AssemblyLine(LDA, Immediate, NumericConstant(0, _), _),
@ -187,7 +186,7 @@ object PseudoregisterBuiltIns {
val b = ctx.env.get[Type]("byte")
val w = ctx.env.get[Type]("word")
val reg = ctx.env.get[VariableInMemory]("__reg")
val firstParamCompiled = ExpressionCompiler.compile(ctx, l, Some(ExpressionCompiler.getExpressionType(ctx, l) -> reg), NoBranching)
val firstParamCompiled = MosExpressionCompiler.compile(ctx, l, Some(MosExpressionCompiler.getExpressionType(ctx, l) -> reg), NoBranching)
ctx.env.eval(r) match {
case Some(NumericConstant(0, _)) =>
Nil
@ -204,20 +203,20 @@ object PseudoregisterBuiltIns {
firstParamCompiled ++ List.fill(v.toInt)(cycle).flatten ++ List(AssemblyLine.zeropage(LDA, reg), AssemblyLine.zeropage(LDX, reg, 1))
}
case _ =>
val compileCounter = ExpressionCompiler.compile(ctx, r, Some(b -> RegisterVariable(Register.X, b)), NoBranching)
val compileCounter = MosExpressionCompiler.compile(ctx, r, Some(b -> RegisterVariable(MosRegister.X, b)), NoBranching)
val compileCounterAndPrepareFirstParam = compileCounter match {
case List(AssemblyLine(LDX, _, _, _)) => firstParamCompiled ++ compileCounter
case List(AssemblyLine(LDY, _, _, _), AssemblyLine(LDX, _, _, _)) => firstParamCompiled ++ compileCounter
case _ =>
ExpressionCompiler.compile(ctx, r, Some(b -> RegisterVariable(Register.A, b)), NoBranching) ++
MosExpressionCompiler.compile(ctx, r, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching) ++
List(AssemblyLine.implied(PHA)) ++
firstParamCompiled ++ (
if (ctx.options.flag(CompilationFlag.EmitCmosOpcodes)) List(AssemblyLine.implied(PLX))
else List(AssemblyLine.implied(PLA), AssemblyLine.implied(TAX))
)
}
val labelRepeat = MfCompiler.nextLabel("sr")
val labelSkip = MfCompiler.nextLabel("ss")
val labelRepeat = MosCompiler.nextLabel("sr")
val labelSkip = MosCompiler.nextLabel("ss")
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
compileCounterAndPrepareFirstParam ++ List(
AssemblyLine.relative(BEQ, labelSkip),
@ -255,8 +254,8 @@ object PseudoregisterBuiltIns {
val reg = ctx.env.get[VariableInMemory]("__reg")
val load: List[AssemblyLine] = param1OrRegister match {
case Some(param1) =>
val code1 = ExpressionCompiler.compile(ctx, param1, Some(b -> RegisterVariable(Register.A, b)), BranchSpec.None)
val code2 = ExpressionCompiler.compile(ctx, param2, Some(b -> RegisterVariable(Register.A, b)), BranchSpec.None)
val code1 = MosExpressionCompiler.compile(ctx, param1, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None)
val code2 = MosExpressionCompiler.compile(ctx, param2, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None)
if (!usesRegLo(code2)) {
code1 ++ List(AssemblyLine.zeropage(STA, reg)) ++ code2 ++ List(AssemblyLine.zeropage(STA, reg, 1))
} else if (!usesRegLo(code1)) {
@ -269,7 +268,7 @@ object PseudoregisterBuiltIns {
)
}
case None =>
val code2 = ExpressionCompiler.compile(ctx, param2, Some(b -> RegisterVariable(Register.A, b)), BranchSpec.None)
val code2 = MosExpressionCompiler.compile(ctx, param2, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None)
if (!usesRegLo(code2)) {
List(AssemblyLine.zeropage(STA, reg)) ++ code2 ++ List(AssemblyLine.zeropage(STA, reg, 1))
} else if (!usesRegHi(code2)) {

View File

@ -1,7 +1,8 @@
package millfork.compiler
package millfork.compiler.mos
import millfork.CompilationFlag
import millfork.assembly.{AssemblyLine, OpcodeClasses}
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
import millfork.env._
import millfork.error.ErrorReporting
import millfork.node._
@ -37,7 +38,7 @@ object ReturnDispatch {
}
}
val indexerType = ExpressionCompiler.getExpressionType(ctx, stmt.indexer)
val indexerType = MosExpressionCompiler.getExpressionType(ctx, stmt.indexer)
if (indexerType.size != 1) {
ErrorReporting.error("Return dispatch index expression type has to be a byte", stmt.indexer.position)
}
@ -120,7 +121,7 @@ object ReturnDispatch {
}
while (env.parent.isDefined) env = env.parent.get
val label = MfCompiler.nextLabel("di")
val label = MosCompiler.nextLabel("di")
val paramArrays = stmt.params.indices.map { ix =>
val a = InitializedArray(label + "$" + ix + ".array", None, (paramMins(ix) to paramMaxes(ix)).map { key =>
map(key)._2.lift(ix).getOrElse(LiteralExpression(0, 1))
@ -134,11 +135,11 @@ object ReturnDispatch {
val b = ctx.env.get[Type]("byte")
import millfork.assembly.AddrMode._
import millfork.assembly.Opcode._
import millfork.assembly.mos.Opcode._
val ctxForStoringParams = ctx.neverCheckArrayBounds
val copyParams = stmt.params.zipWithIndex.flatMap { case (paramVar, paramIndex) =>
val storeParam = ExpressionCompiler.compileByteStorage(ctxForStoringParams, Register.A, paramVar)
val storeParam = MosExpressionCompiler.compileByteStorage(ctxForStoringParams, MosRegister.A, paramVar)
if (storeParam.exists(l => OpcodeClasses.ChangesX(l.opcode)))
ErrorReporting.error("Invalid/too complex target parameter variable", paramVar.position)
AssemblyLine.absoluteX(LDA, paramArrays(paramIndex), -paramMins(paramIndex)) :: storeParam
@ -148,10 +149,10 @@ object ReturnDispatch {
val jumpTable = InitializedArray(label + "$jt.array", None, (actualMin to actualMax).flatMap(i => List(lobyte0(map(i)._1), hibyte0(map(i)._1))).toList, ctx.function.declaredBank)
env.registerUnnamedArray(jumpTable)
if (copyParams.isEmpty) {
val loadIndex = ExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(Register.A, b)), BranchSpec.None)
val loadIndex = MosExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None)
loadIndex ++ List(AssemblyLine.implied(ASL), AssemblyLine.implied(TAX)) ++ copyParams :+ AssemblyLine(JMP, AbsoluteIndexedX, jumpTable.toAddress - actualMin * 2)
} else {
val loadIndex = ExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(Register.X, b)), BranchSpec.None)
val loadIndex = MosExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(MosRegister.X, b)), BranchSpec.None)
loadIndex ++ copyParams ++ List(
AssemblyLine.implied(TXA),
AssemblyLine.implied(ASL),
@ -159,7 +160,7 @@ object ReturnDispatch {
AssemblyLine(JMP, AbsoluteIndexedX, jumpTable.toAddress - actualMin * 2))
}
} else {
val loadIndex = ExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(Register.X, b)), BranchSpec.None)
val loadIndex = MosExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(MosRegister.X, b)), BranchSpec.None)
val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte1(map(i)._1)).toList, ctx.function.declaredBank)
val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte1(map(i)._1)).toList, ctx.function.declaredBank)
env.registerUnnamedArray(jumpTableLo)

View File

@ -3,11 +3,10 @@ package millfork.env
import java.util.concurrent.atomic.AtomicLong
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.{Opcode, OpcodeClasses}
import millfork.compiler._
import millfork.assembly.mos.Opcode
import millfork.error.ErrorReporting
import millfork.node._
import millfork.output.{CompiledMemory, MemoryBank, VariableAllocator}
import millfork.output.{CompiledMemory, VariableAllocator}
import scala.collection.mutable
@ -576,7 +575,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
resultType,
params,
env,
executableStatements ++ (if (needsExtraRTS) List(AssemblyStatement.implied(Opcode.RTS, elidable = true)) else Nil)
executableStatements ++ (if (needsExtraRTS) List(MosAssemblyStatement.implied(Opcode.RTS, elidable = true)) else Nil)
)
addThing(mangled, stmt.position)
} else {
@ -1011,7 +1010,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
def nameCheck(nodes: List[_ <:Node]): Unit = nodes.foreach(nameCheck)
def nameCheck(node: Node): Unit = node match {
case _:AssemblyStatement => ()
case _:MosAssemblyStatement => ()
case _:DeclarationStatement => ()
case s:ForStatement =>
checkName[Variable]("Variable", s.variable, s.position)

View File

@ -1,7 +1,7 @@
package millfork.env
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.Opcode
import millfork.assembly.mos.Opcode
import millfork.node._
sealed trait Thing {
@ -122,7 +122,7 @@ sealed trait VariableInMemory extends Variable with ThingInMemory with Indexable
declaredBank.getOrElse("default")
}
case class RegisterVariable(register: Register.Value, typ: Type) extends Variable {
case class RegisterVariable(register: MosRegister.Value, typ: Type) extends Variable {
def name: String = register.toString
}
@ -294,7 +294,7 @@ sealed trait ParamPassingConvention {
def inNonInlinedOnly: Boolean
}
case class ByRegister(register: Register.Value) extends ParamPassingConvention {
case class ByRegister(register: MosRegister.Value) extends ParamPassingConvention {
override def inInlinedOnly = false
override def inNonInlinedOnly = false

View File

@ -1,7 +1,8 @@
package millfork.node
import millfork.assembly.{AddrMode, Opcode}
import millfork.env.{Constant, Label, ParamPassingConvention}
import millfork.assembly.AddrMode
import millfork.assembly.mos.Opcode
import millfork.env.{Constant, ParamPassingConvention}
case class Position(filename: String, line: Int, column: Int, cursor: Int)
@ -64,7 +65,7 @@ case class HalfWordExpression(expression: Expression, hiByte: Boolean) extends E
HalfWordExpression(expression.replaceVariable(variable, actualParam), hiByte)
}
object Register extends Enumeration {
object MosRegister extends Enumeration {
val A, X, Y, AX, AY, YA, XA, XY, YX, AW = Value
}
@ -211,7 +212,7 @@ case class Assignment(destination: LhsExpression, source: Expression) extends Ex
override def getAllExpressions: List[Expression] = List(destination, source)
}
case class AssemblyStatement(opcode: Opcode.Value, addrMode: AddrMode.Value, expression: Expression, elidable: Boolean) extends ExecutableStatement {
case class MosAssemblyStatement(opcode: Opcode.Value, addrMode: AddrMode.Value, expression: Expression, elidable: Boolean) extends ExecutableStatement {
override def getAllExpressions: List[Expression] = List(expression)
}
@ -251,8 +252,8 @@ case class ContinueStatement(label: String) extends ExecutableStatement {
override def getAllExpressions: List[Expression] = Nil
}
object AssemblyStatement {
def implied(opcode: Opcode.Value, elidable: Boolean) = AssemblyStatement(opcode, AddrMode.Implied, LiteralExpression(0, 1), elidable)
object MosAssemblyStatement {
def implied(opcode: Opcode.Value, elidable: Boolean) = MosAssemblyStatement(opcode, AddrMode.Implied, LiteralExpression(0, 1), elidable)
def nonexistent(opcode: Opcode.Value) = AssemblyStatement(opcode, AddrMode.DoesNotExist, LiteralExpression(0, 1), elidable = true)
def nonexistent(opcode: Opcode.Value) = MosAssemblyStatement(opcode, AddrMode.DoesNotExist, LiteralExpression(0, 1), elidable = true)
}

View File

@ -1,7 +1,6 @@
package millfork.node.opt
import millfork.CompilationOptions
import millfork.assembly.AssemblyLine
import millfork.env._
import millfork.error.ErrorReporting
import millfork.node._

View File

@ -1,12 +1,12 @@
package millfork.output
import millfork.assembly.opt.{AssemblyOptimization, HudsonOptimizations, JumpFixing, JumpShortening}
import millfork.assembly.{AddrMode, AssemblyLine, Opcode}
import millfork.compiler.{CompilationContext, MfCompiler}
import millfork.assembly._
import millfork.compiler.AbstractCompiler
import millfork.env._
import millfork.error.ErrorReporting
import millfork.node.{CallGraph, Program}
import millfork._
import millfork.compiler.mos.CompilationContext
import scala.collection.mutable
@ -16,7 +16,11 @@ import scala.collection.mutable
case class AssemblerOutput(code: Map[String, Array[Byte]], asm: Array[String], labels: List[(String, Int)])
class Assembler(private val program: Program, private val rootEnv: Environment, private val platform: Platform) {
abstract class AbstractAssembler[T <: AbstractCode](private val program: Program,
private val rootEnv: Environment,
private val platform: Platform,
private val inliningCalculator: AbstractInliningCalculator[T],
private val compiler: AbstractCompiler[T]) {
private var env = rootEnv.allThings
var unoptimizedCodeSize: Int = 0
@ -165,12 +169,12 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
private def asDecimal(a: Long, b: Long, f: (Long, Long) => Long): Long =
storeDecimalValueInNormalRespresentation(f(parseNormalToDecimalValue(a), parseNormalToDecimalValue(b)))
def assemble(callGraph: CallGraph, optimizations: Seq[AssemblyOptimization], options: CompilationOptions): AssemblerOutput = {
def assemble(callGraph: CallGraph, optimizations: Seq[AssemblyOptimization[T]], options: CompilationOptions): AssemblerOutput = {
val platform = options.platform
val assembly = mutable.ArrayBuffer[String]()
val inliningResult = InliningCalculator.calculate(
val inliningResult = MosInliningCalculator.calculate(
program,
options.flags(CompilationFlag.InlineFunctions) || options.flags(CompilationFlag.OptimizeForSonicSpeed),
if (options.flags(CompilationFlag.OptimizeForSonicSpeed)) 4.0
@ -183,8 +187,8 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
val potentiallyInlineable: Map[String, Int] = inliningResult.potentiallyInlineableFunctions
var nonInlineableFunctions: Set[String] = inliningResult.nonInlineableFunctions
var inlinedFunctions = Map[String, List[AssemblyLine]]()
val compiledFunctions = mutable.Map[String, List[AssemblyLine]]()
var inlinedFunctions = Map[String, List[T]]()
val compiledFunctions = mutable.Map[String, List[T]]()
val recommendedCompilationOrder = callGraph.recommendedCompilationOrder
recommendedCompilationOrder.foreach { f =>
env.maybeGet[NormalFunction](f).foreach { function =>
@ -192,7 +196,7 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
val strippedCodeForInlining = for {
limit <- potentiallyInlineable.get(f)
if code.map(_.sizeInBytes).sum <= limit
s <- InliningCalculator.codeForInlining(f, nonInlineableFunctions, code)
s <- inliningCalculator.codeForInlining(f, nonInlineableFunctions, code)
} yield s
strippedCodeForInlining match {
case Some(c) =>
@ -418,562 +422,33 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
AssemblerOutput(code, assembly.toArray, labelMap.toList)
}
private def compileFunction(f: NormalFunction, optimizations: Seq[AssemblyOptimization], options: CompilationOptions, inlinedFunctions: Map[String, List[AssemblyLine]]): List[AssemblyLine] = {
private def compileFunction(f: NormalFunction, optimizations: Seq[AssemblyOptimization[T]], options: CompilationOptions, inlinedFunctions: Map[String, List[T]]): List[T] = {
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, 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)), _) =>
val newLabel = MemoryAddressConstant(Label(labelPrefix + label))
line.copy(parameter = newLabel)
case l => l
}
case AssemblyLine(Opcode.JMP, AddrMode.Absolute, p, true) if inlinedFunctions.contains(p.toString) =>
val labelPrefix = MfCompiler.nextLabel("ai")
inlinedFunctions(p.toString).map{
case line@AssemblyLine(_, _, MemoryAddressConstant(Label(label)), _) =>
val newLabel = MemoryAddressConstant(Label(labelPrefix + label))
line.copy(parameter = newLabel)
case l => l
} :+ AssemblyLine.implied(Opcode.RTS)
case x => List(x)
}
val unoptimized: List[T] =
inliningCalculator.inline(
compiler.compile(CompilationContext(env = f.environment, function = f, extraStackOffset = 0, options = options)),
inlinedFunctions,
compiler)
unoptimizedCodeSize += unoptimized.map(_.sizeInBytes).sum
val code = optimizations.foldLeft(unoptimized) { (c, opt) =>
opt.optimize(f, c, options)
}
if (optimizations.nonEmpty) {
val finalCode = if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.removeLoadZero(code) else code
JumpShortening(f, JumpShortening(f, JumpFixing(f, finalCode, options), options), options)
}
else JumpFixing(f, code, options)
performFinalOptimizationPass(f, optimizations.nonEmpty, options, code)
}
private def outputFunction(bank: String, code: List[AssemblyLine], startFrom: Int, assOut: mutable.ArrayBuffer[String], options: CompilationOptions): Int = {
def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[T]): List[T]
private def outputFunction(bank: String, code: List[T], startFrom: Int, assOut: mutable.ArrayBuffer[String], options: CompilationOptions): Int = {
var index = startFrom
assOut.append("* = $" + startFrom.toHexString)
import millfork.assembly.AddrMode._
import millfork.assembly.Opcode._
for (instr <- code) {
if (instr.isPrintable) {
assOut.append(instr.toString)
}
instr match {
case AssemblyLine(BYTE, RawByte, c, _) =>
writeByte(bank, index, c)
index += 1
case AssemblyLine(BYTE, _, _, _) => ???
case AssemblyLine(_, RawByte, _, _) => ???
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(labelName)), _) =>
labelMap(labelName) = index
case AssemblyLine(_, DoesNotExist, _, _) =>
()
case AssemblyLine(op, Implied, _, _) =>
writeByte(bank, index, Assembler.opcodeFor(op, Implied, options))
index += 1
case AssemblyLine(op, Relative, param, _) =>
writeByte(bank, index, Assembler.opcodeFor(op, Relative, options))
writeByte(bank, index + 1, AssertByte(param - (index + 2)))
index += 2
case AssemblyLine(op, am@(Immediate | ZeroPage | ZeroPageX | ZeroPageY | IndexedY | IndexedX | IndexedZ | LongIndexedY | LongIndexedZ | Stack), param, _) =>
writeByte(bank, index, Assembler.opcodeFor(op, am, options))
writeByte(bank, index + 1, param)
index += 2
case AssemblyLine(op, am@(WordImmediate | Absolute | AbsoluteY | AbsoluteX | Indirect | AbsoluteIndexedX), param, _) =>
writeByte(bank, index, Assembler.opcodeFor(op, am, options))
writeWord(bank, index + 1, param)
index += 3
case AssemblyLine(op, am@(LongAbsolute | LongAbsoluteX | LongIndirect), param, _) =>
writeByte(bank, index, Assembler.opcodeFor(op, am, options))
writeWord(bank, index + 1, param)
writeByte(bank, index + 3, extractBank(param, options))
index += 4
}
index = emitInstruction(bank, options, index, instr)
}
index
}
}
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).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) 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) 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) 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._
import Opcode._
op(ADC, Immediate, 0x69)
op(ADC, ZeroPage, 0x65)
op(ADC, ZeroPageX, 0x75)
op(ADC, Absolute, 0x6D)
op(ADC, AbsoluteX, 0x7D)
op(ADC, AbsoluteY, 0x79)
op(ADC, IndexedX, 0x61)
op(ADC, IndexedY, 0x71)
op(AND, Immediate, 0x29)
op(AND, ZeroPage, 0x25)
op(AND, ZeroPageX, 0x35)
op(AND, Absolute, 0x2D)
op(AND, AbsoluteX, 0x3D)
op(AND, AbsoluteY, 0x39)
op(AND, IndexedX, 0x21)
op(AND, IndexedY, 0x31)
op(ASL, Implied, 0x0A)
op(ASL, ZeroPage, 0x06)
op(ASL, ZeroPageX, 0x16)
op(ASL, Absolute, 0x0E)
op(ASL, AbsoluteX, 0x1E)
op(BIT, ZeroPage, 0x24)
op(BIT, Absolute, 0x2C)
op(BPL, Relative, 0x10)
op(BMI, Relative, 0x30)
op(BVC, Relative, 0x50)
op(BVS, Relative, 0x70)
op(BCC, Relative, 0x90)
op(BCS, Relative, 0xB0)
op(BNE, Relative, 0xD0)
op(BEQ, Relative, 0xF0)
op(BRK, Implied, 0)
op(CMP, Immediate, 0xC9)
op(CMP, ZeroPage, 0xC5)
op(CMP, ZeroPageX, 0xD5)
op(CMP, Absolute, 0xCD)
op(CMP, AbsoluteX, 0xDD)
op(CMP, AbsoluteY, 0xD9)
op(CMP, IndexedX, 0xC1)
op(CMP, IndexedY, 0xD1)
op(CPX, Immediate, 0xE0)
op(CPX, ZeroPage, 0xE4)
op(CPX, Absolute, 0xEC)
op(CPY, Immediate, 0xC0)
op(CPY, ZeroPage, 0xC4)
op(CPY, Absolute, 0xCC)
op(DEC, ZeroPage, 0xC6)
op(DEC, ZeroPageX, 0xD6)
op(DEC, Absolute, 0xCE)
op(DEC, AbsoluteX, 0xDE)
op(EOR, Immediate, 0x49)
op(EOR, ZeroPage, 0x45)
op(EOR, ZeroPageX, 0x55)
op(EOR, Absolute, 0x4D)
op(EOR, AbsoluteX, 0x5D)
op(EOR, AbsoluteY, 0x59)
op(EOR, IndexedX, 0x41)
op(EOR, IndexedY, 0x51)
op(INC, ZeroPage, 0xE6)
op(INC, ZeroPageX, 0xF6)
op(INC, Absolute, 0xEE)
op(INC, AbsoluteX, 0xFE)
op(CLC, Implied, 0x18)
op(SEC, Implied, 0x38)
op(CLI, Implied, 0x58)
op(SEI, Implied, 0x78)
op(CLV, Implied, 0xB8)
op(CLD, Implied, 0xD8)
op(SED, Implied, 0xF8)
op(JMP, Absolute, 0x4C)
op(JMP, Indirect, 0x6C)
op(JSR, Absolute, 0x20)
op(LDA, Immediate, 0xA9)
op(LDA, ZeroPage, 0xA5)
op(LDA, ZeroPageX, 0xB5)
op(LDA, Absolute, 0xAD)
op(LDA, AbsoluteX, 0xBD)
op(LDA, AbsoluteY, 0xB9)
op(LDA, IndexedX, 0xA1)
op(LDA, IndexedY, 0xB1)
op(LDX, Immediate, 0xA2)
op(LDX, ZeroPage, 0xA6)
op(LDX, ZeroPageY, 0xB6)
op(LDX, Absolute, 0xAE)
op(LDX, AbsoluteY, 0xBE)
op(LDY, Immediate, 0xA0)
op(LDY, ZeroPage, 0xA4)
op(LDY, ZeroPageX, 0xB4)
op(LDY, Absolute, 0xAC)
op(LDY, AbsoluteX, 0xBC)
op(LSR, Implied, 0x4A)
op(LSR, ZeroPage, 0x46)
op(LSR, ZeroPageX, 0x56)
op(LSR, Absolute, 0x4E)
op(LSR, AbsoluteX, 0x5E)
op(NOP, Implied, 0xEA)
op(ORA, Immediate, 0x09)
op(ORA, ZeroPage, 0x05)
op(ORA, ZeroPageX, 0x15)
op(ORA, Absolute, 0x0D)
op(ORA, AbsoluteX, 0x1D)
op(ORA, AbsoluteY, 0x19)
op(ORA, IndexedX, 0x01)
op(ORA, IndexedY, 0x11)
op(TAX, Implied, 0xAA)
op(TXA, Implied, 0x8A)
op(DEX, Implied, 0xCA)
op(INX, Implied, 0xE8)
op(TAY, Implied, 0xA8)
op(TYA, Implied, 0x98)
op(DEY, Implied, 0x88)
op(INY, Implied, 0xC8)
op(ROL, Implied, 0x2A)
op(ROL, ZeroPage, 0x26)
op(ROL, ZeroPageX, 0x36)
op(ROL, Absolute, 0x2E)
op(ROL, AbsoluteX, 0x3E)
op(ROR, Implied, 0x6A)
op(ROR, ZeroPage, 0x66)
op(ROR, ZeroPageX, 0x76)
op(ROR, Absolute, 0x6E)
op(ROR, AbsoluteX, 0x7E)
op(RTI, Implied, 0x40)
op(RTS, Implied, 0x60)
op(SBC, Immediate, 0xE9)
op(SBC, ZeroPage, 0xE5)
op(SBC, ZeroPageX, 0xF5)
op(SBC, Absolute, 0xED)
op(SBC, AbsoluteX, 0xFD)
op(SBC, AbsoluteY, 0xF9)
op(SBC, IndexedX, 0xE1)
op(SBC, IndexedY, 0xF1)
op(STA, ZeroPage, 0x85)
op(STA, ZeroPageX, 0x95)
op(STA, Absolute, 0x8D)
op(STA, AbsoluteX, 0x9D)
op(STA, AbsoluteY, 0x99)
op(STA, IndexedX, 0x81)
op(STA, IndexedY, 0x91)
op(TXS, Implied, 0x9A)
op(TSX, Implied, 0xBA)
op(PHA, Implied, 0x48)
op(PLA, Implied, 0x68)
op(PHP, Implied, 0x08)
op(PLP, Implied, 0x28)
op(STX, ZeroPage, 0x86)
op(STX, ZeroPageY, 0x96)
op(STX, Absolute, 0x8E)
op(STY, ZeroPage, 0x84)
op(STY, ZeroPageX, 0x94)
op(STY, Absolute, 0x8C)
il(LAX, ZeroPage, 0xA7)
il(LAX, ZeroPageY, 0xB7)
il(LAX, Absolute, 0xAF)
il(LAX, AbsoluteY, 0xBF)
il(LAX, IndexedX, 0xA3)
il(LAX, IndexedY, 0xB3)
il(SAX, ZeroPage, 0x87)
il(SAX, ZeroPageY, 0x97)
il(SAX, Absolute, 0x8F)
il(TAS, AbsoluteY, 0x9B)
il(AHX, AbsoluteY, 0x9F)
il(SAX, IndexedX, 0x83)
il(AHX, IndexedY, 0x93)
il(SHY, AbsoluteX, 0x9C)
il(ANC, Immediate, 0x0B)
il(ALR, Immediate, 0x4B)
il(ARR, Immediate, 0x6B)
il(XAA, Immediate, 0x8B)
il(LXA, Immediate, 0xAB)
il(SBX, Immediate, 0xCB)
il(SLO, ZeroPage, 0x07)
il(SLO, ZeroPageX, 0x17)
il(SLO, IndexedX, 0x03)
il(SLO, IndexedY, 0x13)
il(SLO, Absolute, 0x0F)
il(SLO, AbsoluteX, 0x1F)
il(SLO, AbsoluteY, 0x1B)
il(RLA, ZeroPage, 0x27)
il(RLA, ZeroPageX, 0x37)
il(RLA, IndexedX, 0x23)
il(RLA, IndexedY, 0x33)
il(RLA, Absolute, 0x2F)
il(RLA, AbsoluteX, 0x3F)
il(RLA, AbsoluteY, 0x3B)
il(SRE, ZeroPage, 0x47)
il(SRE, ZeroPageX, 0x57)
il(SRE, IndexedX, 0x43)
il(SRE, IndexedY, 0x53)
il(SRE, Absolute, 0x4F)
il(SRE, AbsoluteX, 0x5F)
il(SRE, AbsoluteY, 0x5B)
il(RRA, ZeroPage, 0x67)
il(RRA, ZeroPageX, 0x77)
il(RRA, IndexedX, 0x63)
il(RRA, IndexedY, 0x73)
il(RRA, Absolute, 0x6F)
il(RRA, AbsoluteX, 0x7F)
il(RRA, AbsoluteY, 0x7B)
il(DCP, ZeroPage, 0xC7)
il(DCP, ZeroPageX, 0xD7)
il(DCP, IndexedX, 0xC3)
il(DCP, IndexedY, 0xD3)
il(DCP, Absolute, 0xCF)
il(DCP, AbsoluteX, 0xDF)
il(DCP, AbsoluteY, 0xDB)
il(ISC, ZeroPage, 0xE7)
il(ISC, ZeroPageX, 0xF7)
il(ISC, IndexedX, 0xE3)
il(ISC, IndexedY, 0xF3)
il(ISC, Absolute, 0xEF)
il(ISC, AbsoluteX, 0xFF)
il(ISC, AbsoluteY, 0xFB)
il(NOP, Immediate, 0x80)
il(NOP, ZeroPage, 0x44)
il(NOP, ZeroPageX, 0x54)
il(NOP, Absolute, 0x5C)
il(NOP, AbsoluteX, 0x1C)
cn(NOP, Immediate, 0x02)
cn(NOP, ZeroPage, 0x44)
cn(NOP, ZeroPageX, 0x54)
cn(NOP, Absolute, 0x5C)
cm(STZ, ZeroPage, 0x64)
cm(STZ, ZeroPageX, 0x74)
cm(STZ, Absolute, 0x9C)
cm(STZ, AbsoluteX, 0x9E)
cm(PHX, Implied, 0xDA)
cm(PHY, Implied, 0x5A)
cm(PLX, Implied, 0xFA)
cm(PLY, Implied, 0x7A)
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)
cm(TRB, ZeroPage, 0x14)
cm(TRB, Absolute, 0x1C)
cm(BRA, Relative, 0x80)
cm(BIT, ZeroPageX, 0x34)
cm(BIT, AbsoluteX, 0x3C)
cm(INC, Implied, 0x1A)
cm(DEC, Implied, 0x3A)
cm(JMP, AbsoluteIndexedX, 0x7C)
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)
}
def emitInstruction(bank: String, options: CompilationOptions, index: Int, instr: T): Int
}

View File

@ -0,0 +1,12 @@
package millfork.output
import millfork.assembly.AbstractCode
import millfork.compiler.AbstractCompiler
/**
* @author Karol Stasiak
*/
abstract class AbstractInliningCalculator[T <: AbstractCode] {
def codeForInlining(fname: String, functionsAlreadyKnownToBeNonInlineable: Set[String], code: List[T]): Option[List[T]]
def inline(code: List[T], inlinedFunctions: Map[String, List[T]], compiler: AbstractCompiler[T]): List[T]
}

View File

@ -0,0 +1,549 @@
package millfork.output
import millfork.assembly.mos.opt.{HudsonOptimizations, JumpFixing, JumpShortening}
import millfork.assembly._
import millfork.env._
import millfork.error.ErrorReporting
import millfork.node.Program
import millfork._
import millfork.assembly.mos.{AssemblyLine, Opcode}
import millfork.compiler.mos.MosCompiler
import scala.collection.mutable
/**
* @author Karol Stasiak
*/
class MosAssembler(program: Program,
rootEnv: Environment,
platform: Platform) extends AbstractAssembler[AssemblyLine](program, rootEnv, platform, MosInliningCalculator, MosCompiler) {
override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[AssemblyLine]):List[AssemblyLine] = {
if (actuallyOptimize) {
val finalCode = if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.removeLoadZero(code) else code
JumpShortening(f, JumpShortening(f, JumpFixing(f, finalCode, options), options), options)
}
else JumpFixing(f, code, options)
}
override def emitInstruction(bank: String, options: CompilationOptions, index: Int, instr: AssemblyLine): Int = {
import millfork.assembly.AddrMode._
import millfork.assembly.mos.Opcode._
instr match {
case AssemblyLine(BYTE, RawByte, c, _) =>
writeByte(bank, index, c)
index + 1
case AssemblyLine(BYTE, _, _, _) => ???
case AssemblyLine(_, RawByte, _, _) => ???
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(labelName)), _) =>
labelMap(labelName) = index
index
case AssemblyLine(_, DoesNotExist, _, _) =>
index
case AssemblyLine(op, Implied, _, _) =>
writeByte(bank, index, MosAssembler.opcodeFor(op, Implied, options))
index + 1
case AssemblyLine(op, Relative, param, _) =>
writeByte(bank, index, MosAssembler.opcodeFor(op, Relative, options))
writeByte(bank, index + 1, AssertByte(param - (index + 2)))
index + 2
case AssemblyLine(op, am@(Immediate | ZeroPage | ZeroPageX | ZeroPageY | IndexedY | IndexedX | IndexedZ | LongIndexedY | LongIndexedZ | Stack), param, _) =>
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
writeByte(bank, index + 1, param)
index + 2
case AssemblyLine(op, am@(WordImmediate | Absolute | AbsoluteY | AbsoluteX | Indirect | AbsoluteIndexedX), param, _) =>
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
writeWord(bank, index + 1, param)
index + 3
case AssemblyLine(op, am@(LongAbsolute | LongAbsoluteX | LongIndirect), param, _) =>
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
writeWord(bank, index + 1, param)
writeByte(bank, index + 3, extractBank(param, options))
index + 4
}
}
}
object MosAssembler {
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).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) 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) 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) 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._
import Opcode._
op(ADC, Immediate, 0x69)
op(ADC, ZeroPage, 0x65)
op(ADC, ZeroPageX, 0x75)
op(ADC, Absolute, 0x6D)
op(ADC, AbsoluteX, 0x7D)
op(ADC, AbsoluteY, 0x79)
op(ADC, IndexedX, 0x61)
op(ADC, IndexedY, 0x71)
op(AND, Immediate, 0x29)
op(AND, ZeroPage, 0x25)
op(AND, ZeroPageX, 0x35)
op(AND, Absolute, 0x2D)
op(AND, AbsoluteX, 0x3D)
op(AND, AbsoluteY, 0x39)
op(AND, IndexedX, 0x21)
op(AND, IndexedY, 0x31)
op(ASL, Implied, 0x0A)
op(ASL, ZeroPage, 0x06)
op(ASL, ZeroPageX, 0x16)
op(ASL, Absolute, 0x0E)
op(ASL, AbsoluteX, 0x1E)
op(BIT, ZeroPage, 0x24)
op(BIT, Absolute, 0x2C)
op(BPL, Relative, 0x10)
op(BMI, Relative, 0x30)
op(BVC, Relative, 0x50)
op(BVS, Relative, 0x70)
op(BCC, Relative, 0x90)
op(BCS, Relative, 0xB0)
op(BNE, Relative, 0xD0)
op(BEQ, Relative, 0xF0)
op(BRK, Implied, 0)
op(CMP, Immediate, 0xC9)
op(CMP, ZeroPage, 0xC5)
op(CMP, ZeroPageX, 0xD5)
op(CMP, Absolute, 0xCD)
op(CMP, AbsoluteX, 0xDD)
op(CMP, AbsoluteY, 0xD9)
op(CMP, IndexedX, 0xC1)
op(CMP, IndexedY, 0xD1)
op(CPX, Immediate, 0xE0)
op(CPX, ZeroPage, 0xE4)
op(CPX, Absolute, 0xEC)
op(CPY, Immediate, 0xC0)
op(CPY, ZeroPage, 0xC4)
op(CPY, Absolute, 0xCC)
op(DEC, ZeroPage, 0xC6)
op(DEC, ZeroPageX, 0xD6)
op(DEC, Absolute, 0xCE)
op(DEC, AbsoluteX, 0xDE)
op(EOR, Immediate, 0x49)
op(EOR, ZeroPage, 0x45)
op(EOR, ZeroPageX, 0x55)
op(EOR, Absolute, 0x4D)
op(EOR, AbsoluteX, 0x5D)
op(EOR, AbsoluteY, 0x59)
op(EOR, IndexedX, 0x41)
op(EOR, IndexedY, 0x51)
op(INC, ZeroPage, 0xE6)
op(INC, ZeroPageX, 0xF6)
op(INC, Absolute, 0xEE)
op(INC, AbsoluteX, 0xFE)
op(CLC, Implied, 0x18)
op(SEC, Implied, 0x38)
op(CLI, Implied, 0x58)
op(SEI, Implied, 0x78)
op(CLV, Implied, 0xB8)
op(CLD, Implied, 0xD8)
op(SED, Implied, 0xF8)
op(JMP, Absolute, 0x4C)
op(JMP, Indirect, 0x6C)
op(JSR, Absolute, 0x20)
op(LDA, Immediate, 0xA9)
op(LDA, ZeroPage, 0xA5)
op(LDA, ZeroPageX, 0xB5)
op(LDA, Absolute, 0xAD)
op(LDA, AbsoluteX, 0xBD)
op(LDA, AbsoluteY, 0xB9)
op(LDA, IndexedX, 0xA1)
op(LDA, IndexedY, 0xB1)
op(LDX, Immediate, 0xA2)
op(LDX, ZeroPage, 0xA6)
op(LDX, ZeroPageY, 0xB6)
op(LDX, Absolute, 0xAE)
op(LDX, AbsoluteY, 0xBE)
op(LDY, Immediate, 0xA0)
op(LDY, ZeroPage, 0xA4)
op(LDY, ZeroPageX, 0xB4)
op(LDY, Absolute, 0xAC)
op(LDY, AbsoluteX, 0xBC)
op(LSR, Implied, 0x4A)
op(LSR, ZeroPage, 0x46)
op(LSR, ZeroPageX, 0x56)
op(LSR, Absolute, 0x4E)
op(LSR, AbsoluteX, 0x5E)
op(NOP, Implied, 0xEA)
op(ORA, Immediate, 0x09)
op(ORA, ZeroPage, 0x05)
op(ORA, ZeroPageX, 0x15)
op(ORA, Absolute, 0x0D)
op(ORA, AbsoluteX, 0x1D)
op(ORA, AbsoluteY, 0x19)
op(ORA, IndexedX, 0x01)
op(ORA, IndexedY, 0x11)
op(TAX, Implied, 0xAA)
op(TXA, Implied, 0x8A)
op(DEX, Implied, 0xCA)
op(INX, Implied, 0xE8)
op(TAY, Implied, 0xA8)
op(TYA, Implied, 0x98)
op(DEY, Implied, 0x88)
op(INY, Implied, 0xC8)
op(ROL, Implied, 0x2A)
op(ROL, ZeroPage, 0x26)
op(ROL, ZeroPageX, 0x36)
op(ROL, Absolute, 0x2E)
op(ROL, AbsoluteX, 0x3E)
op(ROR, Implied, 0x6A)
op(ROR, ZeroPage, 0x66)
op(ROR, ZeroPageX, 0x76)
op(ROR, Absolute, 0x6E)
op(ROR, AbsoluteX, 0x7E)
op(RTI, Implied, 0x40)
op(RTS, Implied, 0x60)
op(SBC, Immediate, 0xE9)
op(SBC, ZeroPage, 0xE5)
op(SBC, ZeroPageX, 0xF5)
op(SBC, Absolute, 0xED)
op(SBC, AbsoluteX, 0xFD)
op(SBC, AbsoluteY, 0xF9)
op(SBC, IndexedX, 0xE1)
op(SBC, IndexedY, 0xF1)
op(STA, ZeroPage, 0x85)
op(STA, ZeroPageX, 0x95)
op(STA, Absolute, 0x8D)
op(STA, AbsoluteX, 0x9D)
op(STA, AbsoluteY, 0x99)
op(STA, IndexedX, 0x81)
op(STA, IndexedY, 0x91)
op(TXS, Implied, 0x9A)
op(TSX, Implied, 0xBA)
op(PHA, Implied, 0x48)
op(PLA, Implied, 0x68)
op(PHP, Implied, 0x08)
op(PLP, Implied, 0x28)
op(STX, ZeroPage, 0x86)
op(STX, ZeroPageY, 0x96)
op(STX, Absolute, 0x8E)
op(STY, ZeroPage, 0x84)
op(STY, ZeroPageX, 0x94)
op(STY, Absolute, 0x8C)
il(LAX, ZeroPage, 0xA7)
il(LAX, ZeroPageY, 0xB7)
il(LAX, Absolute, 0xAF)
il(LAX, AbsoluteY, 0xBF)
il(LAX, IndexedX, 0xA3)
il(LAX, IndexedY, 0xB3)
il(SAX, ZeroPage, 0x87)
il(SAX, ZeroPageY, 0x97)
il(SAX, Absolute, 0x8F)
il(TAS, AbsoluteY, 0x9B)
il(AHX, AbsoluteY, 0x9F)
il(SAX, IndexedX, 0x83)
il(AHX, IndexedY, 0x93)
il(SHY, AbsoluteX, 0x9C)
il(ANC, Immediate, 0x0B)
il(ALR, Immediate, 0x4B)
il(ARR, Immediate, 0x6B)
il(XAA, Immediate, 0x8B)
il(LXA, Immediate, 0xAB)
il(SBX, Immediate, 0xCB)
il(SLO, ZeroPage, 0x07)
il(SLO, ZeroPageX, 0x17)
il(SLO, IndexedX, 0x03)
il(SLO, IndexedY, 0x13)
il(SLO, Absolute, 0x0F)
il(SLO, AbsoluteX, 0x1F)
il(SLO, AbsoluteY, 0x1B)
il(RLA, ZeroPage, 0x27)
il(RLA, ZeroPageX, 0x37)
il(RLA, IndexedX, 0x23)
il(RLA, IndexedY, 0x33)
il(RLA, Absolute, 0x2F)
il(RLA, AbsoluteX, 0x3F)
il(RLA, AbsoluteY, 0x3B)
il(SRE, ZeroPage, 0x47)
il(SRE, ZeroPageX, 0x57)
il(SRE, IndexedX, 0x43)
il(SRE, IndexedY, 0x53)
il(SRE, Absolute, 0x4F)
il(SRE, AbsoluteX, 0x5F)
il(SRE, AbsoluteY, 0x5B)
il(RRA, ZeroPage, 0x67)
il(RRA, ZeroPageX, 0x77)
il(RRA, IndexedX, 0x63)
il(RRA, IndexedY, 0x73)
il(RRA, Absolute, 0x6F)
il(RRA, AbsoluteX, 0x7F)
il(RRA, AbsoluteY, 0x7B)
il(DCP, ZeroPage, 0xC7)
il(DCP, ZeroPageX, 0xD7)
il(DCP, IndexedX, 0xC3)
il(DCP, IndexedY, 0xD3)
il(DCP, Absolute, 0xCF)
il(DCP, AbsoluteX, 0xDF)
il(DCP, AbsoluteY, 0xDB)
il(ISC, ZeroPage, 0xE7)
il(ISC, ZeroPageX, 0xF7)
il(ISC, IndexedX, 0xE3)
il(ISC, IndexedY, 0xF3)
il(ISC, Absolute, 0xEF)
il(ISC, AbsoluteX, 0xFF)
il(ISC, AbsoluteY, 0xFB)
il(NOP, Immediate, 0x80)
il(NOP, ZeroPage, 0x44)
il(NOP, ZeroPageX, 0x54)
il(NOP, Absolute, 0x5C)
il(NOP, AbsoluteX, 0x1C)
cn(NOP, Immediate, 0x02)
cn(NOP, ZeroPage, 0x44)
cn(NOP, ZeroPageX, 0x54)
cn(NOP, Absolute, 0x5C)
cm(STZ, ZeroPage, 0x64)
cm(STZ, ZeroPageX, 0x74)
cm(STZ, Absolute, 0x9C)
cm(STZ, AbsoluteX, 0x9E)
cm(PHX, Implied, 0xDA)
cm(PHY, Implied, 0x5A)
cm(PLX, Implied, 0xFA)
cm(PLY, Implied, 0x7A)
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)
cm(TRB, ZeroPage, 0x14)
cm(TRB, Absolute, 0x1C)
cm(BRA, Relative, 0x80)
cm(BIT, ZeroPageX, 0x34)
cm(BIT, AbsoluteX, 0x3C)
cm(INC, Implied, 0x1A)
cm(DEC, Implied, 0x3A)
cm(JMP, AbsoluteIndexedX, 0x7C)
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)
}

View File

@ -1,8 +1,9 @@
package millfork.output
import millfork.assembly.{AddrMode, AssemblyLine, Opcode, OpcodeClasses}
import millfork.assembly.Opcode._
import millfork.compiler.{ExpressionCompiler, MfCompiler}
import millfork.assembly.AddrMode
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
import millfork.compiler.AbstractCompiler
import millfork.env._
import millfork.node._
@ -14,7 +15,7 @@ import scala.collection.mutable
case class InliningResult(potentiallyInlineableFunctions: Map[String, Int], nonInlineableFunctions: Set[String])
object InliningCalculator {
object MosInliningCalculator extends AbstractInliningCalculator[AssemblyLine] {
private val sizes = Seq(64, 64, 8, 6, 5, 5, 4)
@ -69,7 +70,7 @@ object InliningCalculator {
case s: ArrayContents => getAllCalledFunctions(s.getAllExpressions)
case s: FunctionDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.statements.getOrElse(Nil))
case Assignment(VariableExpression(_), expr) => getAllCalledFunctions(expr :: Nil)
case AssemblyStatement(JSR, _, VariableExpression(name), true) => (name -> false) :: Nil
case MosAssemblyStatement(JSR, _, VariableExpression(name), true) => (name -> false) :: Nil
case s: Statement => getAllCalledFunctions(s.getAllExpressions)
case s: VariableExpression => Set(
s.name,
@ -110,4 +111,26 @@ object InliningCalculator {
}) return None
Some(result)
}
def inline(code: List[AssemblyLine], inlinedFunctions: Map[String, List[AssemblyLine]], compiler: AbstractCompiler[AssemblyLine]): List[AssemblyLine] = {
code.flatMap {
case AssemblyLine(Opcode.JSR, AddrMode.Absolute | AddrMode.LongAbsolute, p, true) if inlinedFunctions.contains(p.toString) =>
val labelPrefix = compiler.nextLabel("ai")
inlinedFunctions(p.toString).map {
case line@AssemblyLine(_, _, MemoryAddressConstant(Label(label)), _) =>
val newLabel = MemoryAddressConstant(Label(labelPrefix + label))
line.copy(parameter = newLabel)
case l => l
}
case AssemblyLine(Opcode.JMP, AddrMode.Absolute, p, true) if inlinedFunctions.contains(p.toString) =>
val labelPrefix = compiler.nextLabel("ai")
inlinedFunctions(p.toString).map {
case line@AssemblyLine(_, _, MemoryAddressConstant(Label(label)), _) =>
val newLabel = MemoryAddressConstant(Label(labelPrefix + label))
line.copy(parameter = newLabel)
case l => l
} :+ AssemblyLine.implied(Opcode.RTS)
case x => List(x)
}
}
}

View File

@ -9,26 +9,24 @@ import millfork.node.{ImportStatement, Position, Program}
import scala.collection.mutable
/**
* @author Karol Stasiak
*/
class SourceLoadingQueue(val initialFilenames: List[String], val includePath: List[String], val options: CompilationOptions) {
abstract class AbstractSourceLoadingQueue[T](val initialFilenames: List[String],
val includePath: List[String],
val options: CompilationOptions) {
private val parsedModules = mutable.Map[String, Program]()
private val moduleQueue = mutable.Queue[() => Unit]()
protected val parsedModules: mutable.Map[String, Program] = mutable.Map[String, Program]()
protected val moduleQueue: mutable.Queue[() => Unit] = mutable.Queue[() => Unit]()
val extension: String = ".mfk"
def enqueueStandardModules(): Unit
def run(): Program = {
initialFilenames.foreach { i =>
parseModule(extractName(i), includePath, Right(i), options)
parseModule(extractName(i), includePath, Right(i))
}
options.platform.startingModules.foreach {m =>
moduleQueue.enqueue(() => parseModule(m, includePath, Left(None), options))
}
if (options.flag(CompilationFlag.ZeropagePseudoregister)) {
moduleQueue.enqueue(() => parseModule("zp_reg", includePath, Left(None), options))
moduleQueue.enqueue(() => parseModule(m, includePath, Left(None)))
}
enqueueStandardModules()
while (moduleQueue.nonEmpty) {
if (options.flag(CompilationFlag.SingleThreaded)) {
moduleQueue.dequeueAll(_ => true).foreach(_())
@ -51,13 +49,15 @@ class SourceLoadingQueue(val initialFilenames: List[String], val includePath: Li
ErrorReporting.fatal(s"Module `$moduleName` not found", position)
}
def parseModule(moduleName: String, includePath: List[String], why: Either[Option[Position], String], options: CompilationOptions): Unit = {
def createParser(filename: String, src: String, parentDir: String) : MfParser[T]
def parseModule(moduleName: String, includePath: List[String], why: Either[Option[Position], String]): Unit = {
val filename: String = why.fold(p => lookupModuleFile(includePath, moduleName, p), s => s)
ErrorReporting.debug(s"Parsing $filename")
val path = Paths.get(filename)
val parentDir = path.toFile.getAbsoluteFile.getParent
val src = new String(Files.readAllBytes(path))
val parser = MfParser(filename, src, parentDir, options)
val parser = createParser(filename, src, parentDir)
parser.toAst match {
case Success(prog, _) =>
parsedModules.synchronized {
@ -65,7 +65,7 @@ class SourceLoadingQueue(val initialFilenames: List[String], val includePath: Li
prog.declarations.foreach {
case s@ImportStatement(m) =>
if (!parsedModules.contains(m)) {
moduleQueue.enqueue(() => parseModule(m, parentDir :: includePath, Left(s.position), options))
moduleQueue.enqueue(() => parseModule(m, parentDir :: includePath, Left(s.position)))
}
case _ => ()
}

View File

@ -3,7 +3,7 @@ package millfork.parser
import java.nio.file.{Files, Paths}
import fastparse.all._
import millfork.assembly.{AddrMode, Opcode}
import millfork.assembly.mos.Opcode
import millfork.env._
import millfork.error.ErrorReporting
import millfork.node._
@ -12,7 +12,7 @@ import millfork.{CompilationOptions, SeparatedList}
/**
* @author Karol Stasiak
*/
case class MfParser(filename: String, input: String, currentDirectory: String, options: CompilationOptions) {
abstract class MfParser[T](filename: String, input: String, currentDirectory: String, options: CompilationOptions) {
var lastPosition = Position(filename, 1, 1, 0)
var lastLabel = ""
@ -147,7 +147,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
val atom: P[Expression] = P(literalAtom | (position() ~ identifier).map { case (p, i) => VariableExpression(i).pos(p) })
val mlOperators = List(
val mfOperators = List(
List("+=", "-=", "+'=", "-'=", "^=", "&=", "|=", "*=", "*'=", "<<=", ">>=", "<<'=", ">>'="),
List("||", "^^"),
List("&&"),
@ -167,8 +167,8 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
flags <- flags("const", "static", "volatile", "stack", "register") ~ HWS
typ <- identifier ~ SWS
name <- identifier ~/ HWS ~/ Pass
addr <- ("@" ~/ HWS ~/ mlExpression(1)).?.opaque("<address>") ~ HWS
initialValue <- ("=" ~/ HWS ~/ mlExpression(1)).? ~ HWS
addr <- ("@" ~/ HWS ~/ mfExpression(1)).?.opaque("<address>") ~ HWS
initialValue <- ("=" ~/ HWS ~/ mfExpression(1)).? ~ HWS
_ <- &(EOL) ~/ ""
} yield {
Seq(VariableDeclarationStatement(name, typ,
@ -191,33 +191,9 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
ParameterDeclaration(typ, ByVariable(name)).pos(p)
}
val appcSimple: P[ParamPassingConvention] = P("xy" | "yx" | "ax" | "ay" | "xa" | "ya" | "stack" | "a" | "x" | "y").!.map {
case "xy" => ByRegister(Register.XY)
case "yx" => ByRegister(Register.YX)
case "ax" => ByRegister(Register.AX)
case "ay" => ByRegister(Register.AY)
case "xa" => ByRegister(Register.XA)
case "ya" => ByRegister(Register.YA)
case "a" => ByRegister(Register.A)
case "x" => ByRegister(Register.X)
case "y" => ByRegister(Register.Y)
case x => ErrorReporting.fatal(s"Unknown assembly parameter passing convention: `$x`")
}
def asmParamDefinition: P[ParameterDeclaration]
val appcComplex: P[ParamPassingConvention] = P((("const" | "ref").! ~/ AWS).? ~ AWS ~ identifier) map {
case (None, name) => ByVariable(name)
case (Some("const"), name) => ByConstant(name)
case (Some("ref"), name) => ByReference(name)
case x => ErrorReporting.fatal(s"Unknown assembly parameter passing convention: `$x`")
}
val asmParamDefinition: P[ParameterDeclaration] = for {
p <- position()
typ <- identifier ~ SWS
appc <- appcSimple | appcComplex
} yield ParameterDeclaration(typ, appc).pos(p)
def arrayListElement: P[ArrayContents] = arrayStringContents | arrayLoopContents | mlExpression(nonStatementLevel).map(e => LiteralContents(List(e)))
def arrayListElement: P[ArrayContents] = arrayStringContents | arrayLoopContents | mfExpression(nonStatementLevel).map(e => LiteralContents(List(e)))
def arrayListContents: P[ArrayContents] = ("[" ~/ AWS ~/ arrayListElement.rep(sep = AWS ~ "," ~/ AWS) ~ AWS ~ "]" ~/ Pass).map(c => CombinedContents(c.toList))
@ -265,10 +241,10 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
def arrayLoopContents: P[ArrayContents] = for {
identifier <- "for" ~ SWS ~/ identifier ~/ HWS ~ "," ~/ HWS ~ Pass
start <- mlExpression(nonStatementLevel) ~ HWS ~ "," ~/ HWS ~/ Pass
start <- mfExpression(nonStatementLevel) ~ HWS ~ "," ~/ HWS ~/ Pass
pos <- position()
direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass
end <- mlExpression(nonStatementLevel)
end <- mfExpression(nonStatementLevel)
body <- AWS ~ arrayContents
} yield {
val fixedDirection = direction match {
@ -291,21 +267,21 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
p <- position()
bank <- bankDeclaration
name <- "array" ~ !letterOrDigit ~/ SWS ~ identifier ~ HWS
length <- ("[" ~/ AWS ~/ mlExpression(nonStatementLevel) ~ AWS ~ "]").? ~ HWS
addr <- ("@" ~/ HWS ~/ mlExpression(1)).? ~/ HWS
length <- ("[" ~/ AWS ~/ mfExpression(nonStatementLevel) ~ AWS ~ "]").? ~ HWS
addr <- ("@" ~/ HWS ~/ mfExpression(1)).? ~/ HWS
contents <- ("=" ~/ HWS ~/ arrayContents).? ~/ HWS
} yield Seq(ArrayDeclarationStatement(name, bank, length, addr, contents).pos(p))
def tightMlExpression: P[Expression] = P(mlParenExpr | functionCall | mlIndexedExpression | atom) // TODO
def tightMfExpression: P[Expression] = P(mfParenExpr | functionCall | mfIndexedExpression | atom) // TODO
def tightMlExpressionButNotCall: P[Expression] = P(mlParenExpr | mlIndexedExpression | atom) // TODO
def tightMfExpressionButNotCall: P[Expression] = P(mfParenExpr | mfIndexedExpression | atom) // TODO
def mlExpression(level: Int): P[Expression] = {
val allowedOperators = mlOperators.drop(level).flatten
def mfExpression(level: Int): P[Expression] = {
val allowedOperators = mfOperators.drop(level).flatten
def inner: P[SeparatedList[Expression, String]] = {
for {
head <- tightMlExpression ~/ HWS
head <- tightMfExpression ~/ HWS
maybeOperator <- StringIn(allowedOperators: _*).!.?
maybeTail <- maybeOperator.fold[P[Option[List[(String, Expression)]]]](Pass.map(_ => None))(o => (HWS ~/ inner ~/ HWS).map(x2 => Some((o -> x2.head) :: x2.tail)))
} yield {
@ -314,9 +290,9 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
}
def p(list: SeparatedList[Expression, String], level: Int): Expression =
if (level == mlOperators.length) list.head
if (level == mfOperators.length) list.head
else {
val xs = list.split(mlOperators(level).toSet(_))
val xs = list.split(mfOperators(level).toSet(_))
xs.separators.distinct match {
case Nil =>
if (xs.tail.nonEmpty)
@ -344,32 +320,32 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
inner.map(x => p(x, 0))
}
def mlLhsExpressionSimple: P[LhsExpression] = mlIndexedExpression | (position() ~ identifier).map { case (p, n) => VariableExpression(n).pos(p) }
def mfLhsExpressionSimple: P[LhsExpression] = mfIndexedExpression | (position() ~ identifier).map { case (p, n) => VariableExpression(n).pos(p) }
def mlLhsExpression: P[LhsExpression] = for {
(p, left) <- position() ~ mlLhsExpressionSimple
rightOpt <- (HWS ~ ":" ~/ HWS ~ mlLhsExpressionSimple).?
def mfLhsExpression: P[LhsExpression] = for {
(p, left) <- position() ~ mfLhsExpressionSimple
rightOpt <- (HWS ~ ":" ~/ HWS ~ mfLhsExpressionSimple).?
} yield rightOpt.fold(left)(right => SeparateBytesExpression(left, right).pos(p))
def mlParenExpr: P[Expression] = P("(" ~/ AWS ~/ mlExpression(nonStatementLevel) ~ AWS ~/ ")")
def mfParenExpr: P[Expression] = P("(" ~/ AWS ~/ mfExpression(nonStatementLevel) ~ AWS ~/ ")")
def mlIndexedExpression: P[IndexedExpression] = for {
def mfIndexedExpression: P[IndexedExpression] = for {
p <- position()
array <- identifier
index <- HWS ~ "[" ~/ AWS ~/ mlExpression(nonStatementLevel) ~ AWS ~/ "]"
index <- HWS ~ "[" ~/ AWS ~/ mfExpression(nonStatementLevel) ~ AWS ~/ "]"
} yield IndexedExpression(array, index).pos(p)
def functionCall: P[FunctionCallExpression] = for {
p <- position()
name <- identifier
params <- HWS ~ "(" ~/ AWS ~/ mlExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ ""
params <- HWS ~ "(" ~/ AWS ~/ mfExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ ""
} yield FunctionCallExpression(name, params.toList).pos(p)
val expressionStatement: P[Seq[ExecutableStatement]] = mlExpression(0).map(x => Seq(ExpressionStatement(x)))
val expressionStatement: P[Seq[ExecutableStatement]] = mfExpression(0).map(x => Seq(ExpressionStatement(x)))
val assignmentStatement: P[Seq[ExecutableStatement]] =
(position() ~ mlLhsExpression ~ HWS ~ "=" ~/ HWS ~ mlExpression(1)).map {
(position() ~ mfLhsExpression ~ HWS ~ "=" ~/ HWS ~ mfExpression(1)).map {
case (p, l, r) => Seq(Assignment(l, r).pos(p))
}
@ -386,65 +362,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
def executableStatement: P[Seq[ExecutableStatement]] = (position() ~ P(keywordStatement | expressionStatement)).map { case (p, s) => s.map(_.pos(p)) }
// TODO: label and instruction in one line
def asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => AssemblyStatement(Opcode.LABEL, AddrMode.DoesNotExist, VariableExpression(l), elidable = true))
// def zeropageAddrModeHint: P[Option[Boolean]] = Pass
def asmOpcode: P[Opcode.Value] = (position() ~ letter.rep(exactly = 3).! ~ ("_W" | "_w").?.!).map { case (p, suffix, o) => Opcode.lookup(o + suffix, Some(p)) }
def asmExpression: P[Expression] = (position() ~ NoCut(
("<" ~/ HWS ~ mlExpression(mathLevel)).map(e => HalfWordExpression(e, hiByte = false)) |
(">" ~/ HWS ~ mlExpression(mathLevel)).map(e => HalfWordExpression(e, hiByte = true)) |
mlExpression(mathLevel)
)).map { case (p, e) => e.pos(p) }
private val commaX = HWS ~ "," ~ HWS ~ ("X" | "x") ~ HWS
private val commaY = HWS ~ "," ~ HWS ~ ("Y" | "y") ~ HWS
private val commaZ = HWS ~ "," ~ HWS ~ ("Z" | "z") ~ HWS
private val commaS = HWS ~ "," ~ HWS ~ ("S" | "s") ~ HWS
val farKeyword: P[Unit] = P(("f" | "F") ~ ("a" | "A") ~ ("r" | "R"))
def asmParameter: P[(AddrMode.Value, Expression)] = {
(SWS ~ (
("##" ~ asmExpression).map(AddrMode.WordImmediate -> _) |
("#" ~ asmExpression).map(AddrMode.Immediate -> _) |
("(" ~ HWS ~ asmExpression ~ HWS ~ ")" ~ commaY).map(AddrMode.IndexedY -> _) |
(farKeyword ~ HWS ~ "(" ~ HWS ~ asmExpression ~ HWS ~ ")" ~ commaY).map(AddrMode.LongIndexedY -> _) |
("(" ~ 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 -> _) |
(farKeyword ~ HWS ~ "(" ~ HWS ~ asmExpression ~ HWS ~ ")").map(AddrMode.LongIndexedZ -> _) |
(farKeyword ~ HWS ~ asmExpression ~ commaX).map(AddrMode.LongAbsoluteX -> _) |
(farKeyword ~ HWS ~ asmExpression).map(AddrMode.LongAbsolute -> _) |
(asmExpression ~ commaS).map(AddrMode.Stack -> _) |
(asmExpression ~ commaX).map(AddrMode.AbsoluteX -> _) |
(asmExpression ~ commaY).map(AddrMode.AbsoluteY -> _) |
asmExpression.map(AddrMode.Absolute -> _)
)).?.map(_.getOrElse(AddrMode.Implied -> LiteralExpression(0, 1)))
}
def elidable: P[Boolean] = ("?".! ~/ HWS).?.map(_.isDefined)
def asmInstruction: P[ExecutableStatement] = {
val lineParser: P[(Boolean, Opcode.Value, (AddrMode.Value, Expression))] = !"}" ~ elidable ~/ asmOpcode ~/ asmParameter
lineParser.map { case (elid, op, param) =>
(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 (_, AddrMode.Indirect) if op != Opcode.JMP && op != Opcode.JSR => AssemblyStatement(op, AddrMode.IndexedZ, param._2, elid)
case _ => AssemblyStatement(op, param._1, param._2, elid)
}
}
}
def asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall).map(ExpressionStatement)
def asmStatement: P[ExecutableStatement] = (position("assembly statement") ~ P(asmLabel | asmMacro | arrayContentsForAsm | asmInstruction)).map { case (p, s) => s.pos(p) } // TODO: macros
def asmStatement: P[ExecutableStatement]
def statement: P[Seq[Statement]] = (position() ~ P(keywordStatement | variableDefinition(false) | expressionStatement)).map { case (p, s) => s.map(_.pos(p)) }
@ -455,7 +373,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
def executableStatements: P[Seq[ExecutableStatement]] = ("{" ~/ AWS ~/ executableStatement.rep(sep = NoCut(EOL) ~ !"}" ~/ Pass) ~/ AWS ~ "}").map(_.flatten)
def dispatchLabel: P[ReturnDispatchLabel] =
("default" ~ !letterOrDigit ~/ AWS ~/ ("(" ~/ position("default branch range") ~ AWS ~/ mlExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?).map{
("default" ~ !letterOrDigit ~/ AWS ~/ ("(" ~/ position("default branch range") ~ AWS ~/ mfExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?).map{
case None => DefaultReturnDispatchLabel(None, None)
case Some((_, Seq())) => DefaultReturnDispatchLabel(None, None)
case Some((_, Seq(e))) => DefaultReturnDispatchLabel(None, Some(e))
@ -463,38 +381,38 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
case Some((pos, _)) =>
ErrorReporting.error("Invalid default branch declaration", Some(pos))
DefaultReturnDispatchLabel(None, None)
} | mlExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS).map(exprs => StandardReturnDispatchLabel(exprs.toList))
} | mfExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS).map(exprs => StandardReturnDispatchLabel(exprs.toList))
def dispatchBranch: P[ReturnDispatchBranch] = for {
pos <- position()
l <- dispatchLabel ~/ HWS ~/ "@" ~/ HWS
f <- tightMlExpressionButNotCall ~/ HWS
parameters <- ("(" ~/ position("dispatch actual parameters") ~ AWS ~/ mlExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?
f <- tightMfExpressionButNotCall ~/ HWS
parameters <- ("(" ~/ position("dispatch actual parameters") ~ AWS ~/ mfExpression(nonStatementLevel).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?
} yield ReturnDispatchBranch(l, f, parameters.map(_._2.toList).getOrElse(Nil)).pos(pos)
def dispatchStatementBody: P[Seq[ExecutableStatement]] = for {
indexer <- "[" ~/ AWS ~/ mlExpression(nonStatementLevel) ~/ AWS ~/ "]" ~/ AWS
indexer <- "[" ~/ AWS ~/ mfExpression(nonStatementLevel) ~/ AWS ~/ "]" ~/ AWS
_ <- position("dispatch statement body")
parameters <- ("(" ~/ position("dispatch parameters") ~ AWS ~/ mlLhsExpression.rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?
parameters <- ("(" ~/ position("dispatch parameters") ~ AWS ~/ mfLhsExpression.rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?
_ <- AWS ~/ position("dispatch statement body") ~/ "{" ~/ AWS
branches <- dispatchBranch.rep(sep = EOL ~ !"}" ~/ Pass)
_ <- AWS ~/ "}"
} yield Seq(ReturnDispatchStatement(indexer, parameters.map(_._2.toList).getOrElse(Nil), branches.toList))
def returnOrDispatchStatement: P[Seq[ExecutableStatement]] = "return" ~ !letterOrDigit ~/ HWS ~ (dispatchStatementBody | mlExpression(nonStatementLevel).?.map(ReturnStatement).map(Seq(_)))
def returnOrDispatchStatement: P[Seq[ExecutableStatement]] = "return" ~ !letterOrDigit ~/ HWS ~ (dispatchStatementBody | mfExpression(nonStatementLevel).?.map(ReturnStatement).map(Seq(_)))
def breakStatement: P[Seq[ExecutableStatement]] = ("break" ~ !letterOrDigit ~/ HWS ~ identifier.?).map(l => Seq(BreakStatement(l.getOrElse(""))))
def continueStatement: P[Seq[ExecutableStatement]] = ("continue" ~ !letterOrDigit ~/ HWS ~ identifier.?).map(l => Seq(ContinueStatement(l.getOrElse(""))))
def ifStatement: P[Seq[ExecutableStatement]] = for {
condition <- "if" ~ !letterOrDigit ~/ HWS ~/ mlExpression(nonStatementLevel)
condition <- "if" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel)
thenBranch <- AWS ~/ executableStatements
elseBranch <- (AWS ~ "else" ~/ AWS ~/ (ifStatement | executableStatements)).?
} yield Seq(IfStatement(condition, thenBranch.toList, elseBranch.getOrElse(Nil).toList))
def whileStatement: P[Seq[ExecutableStatement]] = for {
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mlExpression(nonStatementLevel)
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel)
body <- AWS ~ executableStatements
} yield Seq(WhileStatement(condition, body.toList, Nil))
@ -507,9 +425,9 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
def forStatement: P[Seq[ExecutableStatement]] = for {
identifier <- "for" ~ SWS ~/ identifier ~/ HWS ~ "," ~/ HWS ~ Pass
start <- mlExpression(nonStatementLevel) ~ HWS ~ "," ~/ HWS ~/ Pass
start <- mfExpression(nonStatementLevel) ~ HWS ~ "," ~/ HWS ~/ Pass
direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass
end <- mlExpression(nonStatementLevel)
end <- mfExpression(nonStatementLevel)
body <- AWS ~ executableStatements
} yield Seq(ForStatement(identifier, start, end, direction, body.toList))
@ -521,7 +439,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
//noinspection MutatorLikeMethodIsParameterless
def doWhileStatement: P[Seq[ExecutableStatement]] = for {
body <- "do" ~ !letterOrDigit ~/ AWS ~ executableStatements ~/ AWS
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mlExpression(nonStatementLevel)
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel)
} yield Seq(DoWhileStatement(body.toList, Nil, condition))
@ -536,7 +454,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
returnType <- identifier ~ SWS
name <- identifier ~ HWS
params <- "(" ~/ AWS ~/ (if (flags("asm")) asmParamDefinition else paramDefinition).rep(sep = AWS ~ "," ~/ AWS) ~ AWS ~ ")" ~/ AWS
addr <- ("@" ~/ HWS ~/ mlExpression(1)).?.opaque("<address>") ~/ AWS
addr <- ("@" ~/ HWS ~/ mfExpression(1)).?.opaque("<address>") ~/ AWS
statements <- (externFunctionBody | (if (flags("asm")) asmStatements else statements).map(l => Some(l))) ~/ Pass
} yield {
if (flags("interrupt") && flags("macro")) ErrorReporting.error(s"Interrupt function `$name` cannot be macros", Some(p))
@ -550,32 +468,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
if (flags("interrupt") && returnType != "void") ErrorReporting.error("Interrupt function `$name` has to return void", Some(p))
if (addr.isEmpty && statements.isEmpty) ErrorReporting.error("Extern function `$name` must have an address", Some(p))
if (statements.isEmpty && !flags("asm") && params.nonEmpty) ErrorReporting.error("Extern non-asm function `$name` cannot have parameters", Some(p))
if (flags("asm")) statements match {
case Some(Nil) => ErrorReporting.warn("Assembly function `$name` is empty, did you mean RTS or RTI", options, Some(p))
case Some(xs) =>
if (flags("interrupt")) {
if (xs.exists {
case AssemblyStatement(Opcode.RTS, _, _, _) => true
case _ => false
}) ErrorReporting.warn("Assembly interrupt function `$name` contains RTS, did you mean RTI?", options, Some(p))
} else {
if (xs.exists {
case AssemblyStatement(Opcode.RTI, _, _, _) => true
case _ => false
}) ErrorReporting.warn("Assembly non-interrupt function `$name` contains RTI, did you mean RTS?", options, Some(p))
}
if (!name.startsWith("__") && !flags("macro")) {
xs.last match {
case AssemblyStatement(Opcode.RTS, _, _, _) => () // OK
case AssemblyStatement(Opcode.RTI, _, _, _) => () // OK
case AssemblyStatement(Opcode.JMP, _, _, _) => () // OK
case _ =>
val validReturn = if (flags("interrupt")) "RTI" else "RTS"
ErrorReporting.warn(s"Non-macro assembly function `$name` should end in " + validReturn, options, Some(p))
}
}
case None => ()
}
if (flags("asm")) validateAsmFunctionBody(p, flags, name, statements)
Seq(FunctionDeclarationStatement(name, returnType, params.toList,
bank,
addr,
@ -588,6 +481,8 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
flags("reentrant")).pos(p))
}
def validateAsmFunctionBody(p: Position, flags: Set[String], name: String, statements: Option[List[Statement]])
def importStatement: Parser[Seq[ImportStatement]] = ("import" ~ !letterOrDigit ~/ SWS ~/ identifier).map(x => Seq(ImportStatement(x)))
def program: Parser[Program] = for {

View File

@ -0,0 +1,132 @@
package millfork.parser
import fastparse.all._
import millfork.assembly.AddrMode
import millfork.assembly.mos.Opcode
import millfork.assembly.mos.AssemblyLine
import millfork.env._
import millfork.error.ErrorReporting
import millfork.node._
import millfork.CompilationOptions
/**
* @author Karol Stasiak
*/
case class MosParser(filename: String, input: String, currentDirectory: String, options: CompilationOptions) extends MfParser[AssemblyLine](filename, input, currentDirectory, options) {
// TODO: label and instruction in one line
def asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => MosAssemblyStatement(Opcode.LABEL, AddrMode.DoesNotExist, VariableExpression(l), elidable = true))
// def zeropageAddrModeHint: P[Option[Boolean]] = Pass
def asmOpcode: P[Opcode.Value] = (position() ~ letter.rep(exactly = 3).! ~ ("_W" | "_w").?.!).map { case (p, suffix, o) => Opcode.lookup(o + suffix, Some(p)) }
def asmExpression: P[Expression] = (position() ~ NoCut(
("<" ~/ HWS ~ mfExpression(mathLevel)).map(e => HalfWordExpression(e, hiByte = false)) |
(">" ~/ HWS ~ mfExpression(mathLevel)).map(e => HalfWordExpression(e, hiByte = true)) |
mfExpression(mathLevel)
)).map { case (p, e) => e.pos(p) }
private val commaX = HWS ~ "," ~ HWS ~ ("X" | "x") ~ HWS
private val commaY = HWS ~ "," ~ HWS ~ ("Y" | "y") ~ HWS
private val commaZ = HWS ~ "," ~ HWS ~ ("Z" | "z") ~ HWS
private val commaS = HWS ~ "," ~ HWS ~ ("S" | "s") ~ HWS
val farKeyword: P[Unit] = P(("f" | "F") ~ ("a" | "A") ~ ("r" | "R"))
def asmParameter: P[(AddrMode.Value, Expression)] = {
(SWS ~ (
("##" ~ asmExpression).map(AddrMode.WordImmediate -> _) |
("#" ~ asmExpression).map(AddrMode.Immediate -> _) |
("(" ~ HWS ~ asmExpression ~ HWS ~ ")" ~ commaY).map(AddrMode.IndexedY -> _) |
(farKeyword ~ HWS ~ "(" ~ HWS ~ asmExpression ~ HWS ~ ")" ~ commaY).map(AddrMode.LongIndexedY -> _) |
("(" ~ 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 -> _) |
(farKeyword ~ HWS ~ "(" ~ HWS ~ asmExpression ~ HWS ~ ")").map(AddrMode.LongIndexedZ -> _) |
(farKeyword ~ HWS ~ asmExpression ~ commaX).map(AddrMode.LongAbsoluteX -> _) |
(farKeyword ~ HWS ~ asmExpression).map(AddrMode.LongAbsolute -> _) |
(asmExpression ~ commaS).map(AddrMode.Stack -> _) |
(asmExpression ~ commaX).map(AddrMode.AbsoluteX -> _) |
(asmExpression ~ commaY).map(AddrMode.AbsoluteY -> _) |
asmExpression.map(AddrMode.Absolute -> _)
)).?.map(_.getOrElse(AddrMode.Implied -> LiteralExpression(0, 1)))
}
def elidable: P[Boolean] = ("?".! ~/ HWS).?.map(_.isDefined)
def asmInstruction: P[ExecutableStatement] = {
val lineParser: P[(Boolean, Opcode.Value, (AddrMode.Value, Expression))] = !"}" ~ elidable ~/ asmOpcode ~/ asmParameter
lineParser.map { case (elid, op, param) =>
(op, param._1) match {
case (Opcode.SAX, AddrMode.Implied) => MosAssemblyStatement(Opcode.HuSAX, param._1, param._2, elid)
case (Opcode.SBX, AddrMode.Immediate) => MosAssemblyStatement(Opcode.SBX, param._1, param._2, elid)
case (Opcode.SAY, AddrMode.AbsoluteX) => MosAssemblyStatement(Opcode.SHY, param._1, param._2, elid)
case (Opcode.SBX, _) => MosAssemblyStatement(Opcode.SAX, param._1, param._2, elid)
case (_, AddrMode.Indirect) if op != Opcode.JMP && op != Opcode.JSR => MosAssemblyStatement(op, AddrMode.IndexedZ, param._2, elid)
case _ => MosAssemblyStatement(op, param._1, param._2, elid)
}
}
}
def asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall).map(ExpressionStatement)
def asmStatement: P[ExecutableStatement] = (position("assembly statement") ~ P(asmLabel | asmMacro | arrayContentsForAsm | asmInstruction)).map { case (p, s) => s.pos(p) } // TODO: macros
val appcSimple: P[ParamPassingConvention] = P("xy" | "yx" | "ax" | "ay" | "xa" | "ya" | "stack" | "a" | "x" | "y").!.map {
case "xy" => ByRegister(MosRegister.XY)
case "yx" => ByRegister(MosRegister.YX)
case "ax" => ByRegister(MosRegister.AX)
case "ay" => ByRegister(MosRegister.AY)
case "xa" => ByRegister(MosRegister.XA)
case "ya" => ByRegister(MosRegister.YA)
case "a" => ByRegister(MosRegister.A)
case "x" => ByRegister(MosRegister.X)
case "y" => ByRegister(MosRegister.Y)
case x => ErrorReporting.fatal(s"Unknown assembly parameter passing convention: `$x`")
}
val appcComplex: P[ParamPassingConvention] = P((("const" | "ref").! ~/ AWS).? ~ AWS ~ identifier) map {
case (None, name) => ByVariable(name)
case (Some("const"), name) => ByConstant(name)
case (Some("ref"), name) => ByReference(name)
case x => ErrorReporting.fatal(s"Unknown assembly parameter passing convention: `$x`")
}
val asmParamDefinition: P[ParameterDeclaration] = for {
p <- position()
typ <- identifier ~ SWS
appc <- appcSimple | appcComplex
} yield ParameterDeclaration(typ, appc).pos(p)
def validateAsmFunctionBody(p: Position, flags: Set[String], name: String, statements: Option[List[Statement]]): Unit = {
statements match {
case Some(Nil) => ErrorReporting.warn("Assembly function `$name` is empty, did you mean RTS or RTI", options, Some(p))
case Some(xs) =>
if (flags("interrupt")) {
if (xs.exists {
case MosAssemblyStatement(Opcode.RTS, _, _, _) => true
case _ => false
}) ErrorReporting.warn("Assembly interrupt function `$name` contains RTS, did you mean RTI?", options, Some(p))
} else {
if (xs.exists {
case MosAssemblyStatement(Opcode.RTI, _, _, _) => true
case _ => false
}) ErrorReporting.warn("Assembly non-interrupt function `$name` contains RTI, did you mean RTS?", options, Some(p))
}
if (!name.startsWith("__") && !flags("macro")) {
xs.last match {
case MosAssemblyStatement(Opcode.RTS, _, _, _) => () // OK
case MosAssemblyStatement(Opcode.RTI, _, _, _) => () // OK
case MosAssemblyStatement(Opcode.JMP, _, _, _) => () // OK
case _ =>
val validReturn = if (flags("interrupt")) "RTI" else "RTS"
ErrorReporting.warn(s"Non-macro assembly function `$name` should end in " + validReturn, options, Some(p))
}
}
case None => ()
}
}
}

View File

@ -0,0 +1,21 @@
package millfork.parser
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.mos.AssemblyLine
/**
* @author Karol Stasiak
*/
class MosSourceLoadingQueue(initialFilenames: List[String],
includePath: List[String],
options: CompilationOptions) extends AbstractSourceLoadingQueue[AssemblyLine](initialFilenames, includePath, options) {
override def createParser(filename: String, src: String, parentDir: String): MfParser[AssemblyLine] = MosParser(filename, src, parentDir, options)
def enqueueStandardModules(): Unit = {
if (options.flag(CompilationFlag.ZeropagePseudoregister)) {
moduleQueue.enqueue(() => parseModule("zp_reg", includePath, Left(None)))
}
}
}

View File

@ -1,7 +1,7 @@
package millfork.test
import millfork.{Cpu, OptimizationPresets}
import millfork.assembly.opt.{AlwaysGoodOptimizations, DangerousOptimizations}
import millfork.assembly.mos.opt.{AlwaysGoodOptimizations, DangerousOptimizations}
import millfork.test.emu._
import org.scalatest.{FunSuite, Matchers}

View File

@ -1,6 +1,6 @@
package millfork.test
import millfork.assembly.opt.DangerousOptimizations
import millfork.assembly.mos.opt.DangerousOptimizations
import millfork.test.emu.EmuBenchmarkRun
import millfork.{Cpu, OptimizationPresets}
import org.scalatest.{FunSuite, Matchers}

View File

@ -1,7 +1,7 @@
package millfork.test
import millfork.{Cpu, OptimizationPresets}
import millfork.assembly.opt.{AlwaysGoodOptimizations, LaterOptimizations, VariableToRegisterOptimization}
import millfork.assembly.mos.opt.{AlwaysGoodOptimizations, LaterOptimizations, VariableToRegisterOptimization}
import millfork.test.emu.{EmuBenchmarkRun, EmuRun, EmuSuperOptimizedRun, EmuUltraBenchmarkRun}
import org.scalatest.{FunSuite, Matchers}

View File

@ -1,7 +1,7 @@
package millfork.test
import millfork.{Cpu, OptimizationPresets}
import millfork.assembly.opt.{AlwaysGoodOptimizations, DangerousOptimizations}
import millfork.assembly.mos.opt.{AlwaysGoodOptimizations, DangerousOptimizations}
import millfork.test.emu._
import org.scalatest.{FunSuite, Matchers}

View File

@ -1,6 +1,6 @@
package millfork.test
import millfork.assembly.opt.{AlwaysGoodOptimizations, LaterOptimizations, VariableToRegisterOptimization}
import millfork.assembly.mos.opt.{AlwaysGoodOptimizations, LaterOptimizations, VariableToRegisterOptimization}
import millfork.test.emu.{EmuBenchmarkRun, EmuRun, EmuUltraBenchmarkRun}
import millfork.{Cpu, OptimizationPresets}
import org.scalatest.{FunSuite, Matchers}

View File

@ -1,6 +1,6 @@
package millfork.test.emu
import millfork.assembly.opt.{CmosOptimizations, SixteenOptimizations}
import millfork.assembly.mos.opt.{CmosOptimizations, SixteenOptimizations}
import millfork.{Cpu, OptimizationPresets}
/**

View File

@ -1,6 +1,6 @@
package millfork.test.emu
import millfork.assembly.opt.{CE02Optimizations, CmosOptimizations}
import millfork.assembly.mos.opt.{CE02Optimizations, CmosOptimizations}
import millfork.{Cpu, OptimizationPresets}
/**

View File

@ -1,6 +1,6 @@
package millfork.test.emu
import millfork.assembly.opt.{CmosOptimizations, ZeropageRegisterOptimizations}
import millfork.assembly.mos.opt.{CmosOptimizations, ZeropageRegisterOptimizations}
import millfork.{Cpu, OptimizationPresets}
/**

View File

@ -1,6 +1,6 @@
package millfork.test.emu
import millfork.assembly.opt.{CmosOptimizations, HudsonOptimizations}
import millfork.assembly.mos.opt.{CmosOptimizations, HudsonOptimizations}
import millfork.{Cpu, OptimizationPresets}
/**

View File

@ -1,6 +1,6 @@
package millfork.test.emu
import millfork.assembly.opt.{LaterOptimizations, ZeropageRegisterOptimizations}
import millfork.assembly.mos.opt.{LaterOptimizations, ZeropageRegisterOptimizations}
import millfork.{Cpu, OptimizationPresets}
/**

View File

@ -1,6 +1,6 @@
package millfork.test.emu
import millfork.assembly.opt.{LaterOptimizations, ZeropageRegisterOptimizations}
import millfork.assembly.mos.opt.{LaterOptimizations, ZeropageRegisterOptimizations}
import millfork.{Cpu, OptimizationPresets}
/**

View File

@ -7,16 +7,18 @@ import com.grapeshot.halfnes.{CPU, CPURAM}
import com.loomcom.symon.InstructionTable.CpuBehavior
import com.loomcom.symon.{Bus, Cpu, CpuState}
import fastparse.core.Parsed.{Failure, Success}
import millfork.assembly.opt.AssemblyOptimization
import millfork.compiler.{CompilationContext, MfCompiler}
import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.AssemblyLine
import millfork.compiler.mos.{CompilationContext, MosCompiler}
import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, NormalFunction}
import millfork.error.ErrorReporting
import millfork.node.StandardCallGraph
import millfork.node.opt.NodeOptimization
import millfork.output.{Assembler, MemoryBank}
import millfork.parser.MfParser
import millfork.output.{MemoryBank, MosAssembler}
import millfork.parser.MosParser
import millfork.{CompilationFlag, CompilationOptions}
import org.scalatest.Matchers
import scala.collection.JavaConverters._
/**
@ -24,7 +26,7 @@ import scala.collection.JavaConverters._
*/
case class Timings(nmos: Long, cmos: Long)
class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], assemblyOptimizations: List[AssemblyOptimization]) extends Matchers {
class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], assemblyOptimizations: List[AssemblyOptimization[AssemblyLine]]) extends Matchers {
def apply(source: String): MemoryBank = {
apply2(source)._2
@ -116,7 +118,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
if (!source.contains("__reg")) effectiveSource += "\n pointer __reg"
if (source.contains("import zp_reg"))
effectiveSource += Files.readAllLines(Paths.get("include/zp_reg.mfk"), StandardCharsets.US_ASCII).asScala.mkString("\n", "\n", "")
val parserF = MfParser("", effectiveSource, "", options)
val parserF = MosParser("", effectiveSource, "", options)
parserF.toAst match {
case Success(unoptimized, _) =>
ErrorReporting.assertNoErrors("Parse failed")
@ -133,7 +135,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
// print unoptimized asm
env.allPreallocatables.foreach {
case f: NormalFunction =>
val unoptimized = MfCompiler.compile(CompilationContext(f.environment, f, 0, options))
val unoptimized = MosCompiler.compile(CompilationContext(f.environment, f, 0, options))
unoptimizedSize += unoptimized.map(_.sizeInBytes).sum
case d: InitializedArray =>
unoptimizedSize += d.contents.length
@ -147,7 +149,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
// compile
val env2 = new Environment(None, "")
env2.collectDeclarations(program, options)
val assembler = new Assembler(program, env2, platform)
val assembler = new MosAssembler(program, env2, platform)
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
println(";;; compiled: -----------------")
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
@ -222,7 +224,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
cpu.setBus(bus)
cpu.setProgramCounter(org)
cpu.setStackPointer(0xff)
val legal = Assembler.getStandardLegalOpcodes
val legal = MosAssembler.getStandardLegalOpcodes
var countNmos = 0L
var countCmos = 0L

View File

@ -1,6 +1,6 @@
package millfork.test.emu
import millfork.assembly.opt.SuperOptimizer
import millfork.assembly.mos.opt.SuperOptimizer
import millfork.{Cpu, OptimizationPresets}
/**

View File

@ -1,6 +1,6 @@
package millfork.test.emu
import millfork.assembly.opt.SuperOptimizer
import millfork.assembly.mos.opt.SuperOptimizer
import millfork.{Cpu, OptimizationPresets}
/**

View File

@ -1,6 +1,6 @@
package millfork.test.emu
import millfork.assembly.opt.{LaterOptimizations, UndocumentedOptimizations}
import millfork.assembly.mos.opt.{LaterOptimizations, UndocumentedOptimizations}
import millfork.{Cpu, OptimizationPresets}
/**