From cb0718b433efc2f1d66a413d8ac4d762fe7806ae Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Mon, 29 Jul 2019 00:55:24 +0200 Subject: [PATCH] 6809: First bits of the code generator --- COMPILING.md | 1 + build.sbt | 3 + .../scala/millfork/CompilationOptions.scala | 4 +- src/main/scala/millfork/Main.scala | 36 +- .../assembly/BranchingOpcodeMapping.scala | 3 +- .../millfork/assembly/m6809/MAddrMode.scala | 98 ++++- .../scala/millfork/assembly/m6809/MLine.scala | 52 ++- .../millfork/assembly/m6809/MOpcode.scala | 29 +- .../compiler/m6809/M6809Buitins.scala | 181 +++++++++ .../compiler/m6809/M6809Comparisons.scala | 48 +++ .../m6809/M6809ExpressionCompiler.scala | 352 ++++++++++++++++++ .../m6809/M6809StatementCompiler.scala | 42 ++- src/main/scala/millfork/env/Environment.scala | 33 +- src/main/scala/millfork/env/Thing.scala | 6 + src/main/scala/millfork/node/Node.scala | 14 + .../millfork/output/AbstractAssembler.scala | 34 +- .../millfork/output/M6809Assembler.scala | 309 ++++++++++++++- .../scala/millfork/parser/M6809Parser.scala | 168 ++++++++- .../millfork/parser/MSourceLoadingQueue.scala | 25 ++ .../scala/millfork/test/BasicSymonTest.scala | 14 +- .../scala/millfork/test/ByteMathSuite.scala | 4 +- .../millfork/test/M6809AssemblySuite.scala | 39 ++ .../millfork/test/emu/EmuBenchmarkRun.scala | 22 ++ .../scala/millfork/test/emu/EmuM6809Run.scala | 110 ++++-- .../emu/EmuUnoptimizedCrossPlatformRun.scala | 2 +- .../scala/millfork/test/emu/M6809Memory.scala | 36 ++ .../scala/millfork/test/emu/Settings.scala | 2 +- 27 files changed, 1538 insertions(+), 129 deletions(-) create mode 100644 src/main/scala/millfork/compiler/m6809/M6809Buitins.scala create mode 100644 src/main/scala/millfork/compiler/m6809/M6809Comparisons.scala create mode 100644 src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala create mode 100644 src/main/scala/millfork/parser/MSourceLoadingQueue.scala create mode 100644 src/test/scala/millfork/test/M6809AssemblySuite.scala create mode 100644 src/test/scala/millfork/test/emu/M6809Memory.scala diff --git a/COMPILING.md b/COMPILING.md index 3322b53a..a0251b0f 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -57,6 +57,7 @@ Test suite is useful if you plan on modifying the compiler. Some test dependenci https://github.com/sethm/symon/tree/71905fdb1998ee4f142260879504bc46cf27648f https://github.com/andrew-hoffman/halfnes/tree/061 https://github.com/trekawek/coffee-gb/tree/coffee-gb-1.0.0 + https://github.com/sorenroug/osnine-java/tree/1b4e059c5886fe01e8901c70684f7eedefe65010 * for each of them, run `maven package` and `maven install` diff --git a/build.sbt b/build.sbt index 2d301f7c..030e8949 100644 --- a/build.sbt +++ b/build.sbt @@ -22,6 +22,7 @@ libraryDependencies += "NeatMonster" % "Intel8086" % "1.0" % "test" from "https: // https://github.com/sethm/symon/tree/71905fdb1998ee4f142260879504bc46cf27648f // https://github.com/andrew-hoffman/halfnes/tree/061 // https://github.com/trekawek/coffee-gb/tree/coffee-gb-1.0.0 +// https://github.com/sorenroug/osnine-java/tree/1b4e059c5886fe01e8901c70684f7eedefe65010 libraryDependencies += "com.loomcom.symon" % "symon" % "1.3.0-SNAPSHOT" % "test" @@ -29,6 +30,8 @@ libraryDependencies += "com.grapeshot" % "halfnes" % "061" % "test" libraryDependencies += "eu.rekawek.coffeegb" % "coffee-gb" % "1.0.0" % "test" +libraryDependencies += "roug.org.osnine" % "osnine-core" % "1.0-SNAPSHOT" % "test" + mainClass in Compile := Some("millfork.Main") assemblyJarName := "millfork.jar" diff --git a/src/main/scala/millfork/CompilationOptions.scala b/src/main/scala/millfork/CompilationOptions.scala index 17faf7b7..56d7cc75 100644 --- a/src/main/scala/millfork/CompilationOptions.scala +++ b/src/main/scala/millfork/CompilationOptions.scala @@ -470,8 +470,8 @@ object Cpu extends Enumeration { case "intel80286" => Intel80186 case "i80286" => Intel80186 case "80286" => Intel80186 - // disabled for now -// case "6809" => Motorola6809 + // undocumented for now + case "6809" => Motorola6809 case _ => log.fatal("Unknown CPU achitecture: " + name) } diff --git a/src/main/scala/millfork/Main.scala b/src/main/scala/millfork/Main.scala index ba1803b8..862d3616 100644 --- a/src/main/scala/millfork/Main.scala +++ b/src/main/scala/millfork/Main.scala @@ -16,7 +16,7 @@ import millfork.env.Environment import millfork.error.{ConsoleLogger, Logger} import millfork.node.StandardCallGraph import millfork.output._ -import millfork.parser.{MosSourceLoadingQueue, ZSourceLoadingQueue} +import millfork.parser.{MSourceLoadingQueue, MosSourceLoadingQueue, ZSourceLoadingQueue} @@ -82,6 +82,7 @@ object Main { case CpuFamily.M6502 => assembleForMos(c, platform, options) case CpuFamily.I80 => assembleForI80(c, platform, options) case CpuFamily.I86 => assembleForI86(c, platform, options) + case CpuFamily.M6809 => assembleForM6809(c, platform, options) } if (c.outputAssembly) { @@ -298,6 +299,39 @@ object Main { result } + private def assembleForM6809(c: Context, platform: Platform, options: CompilationOptions): AssemblerOutput = { + val optLevel = c.optimizationLevel.getOrElse(0) + val unoptimized = new MSourceLoadingQueue( + 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 { + OptimizationPresets.NodeOpt0.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options)) + } + val callGraph = new StandardCallGraph(program, options.log) + + val env = new Environment(None, "", platform.cpuFamily, options) + env.collectDeclarations(program, options) + + val assemblyOptimizations = optLevel match { + case 0 => Nil + case _ => Nil + } + + // compile + val assembler = new M6809Assembler(program, env, platform) + val result = assembler.assemble(callGraph, assemblyOptimizations, options) + options.log.assertNoErrors("Codegen failed") + options.log.debug(f"Unoptimized code size: ${assembler.unoptimizedCodeSize}%5d B") + options.log.debug(f"Optimized code size: ${assembler.optimizedCodeSize}%5d B") + options.log.debug(f"Gain: ${(100L * (assembler.unoptimizedCodeSize - assembler.optimizedCodeSize) / assembler.unoptimizedCodeSize.toDouble).round}%5d%%") + options.log.debug(f"Initialized variables: ${assembler.initializedVariablesSize}%5d B") + result + } + private def assembleForI86(c: Context, platform: Platform, options: CompilationOptions): AssemblerOutput = { val optLevel = c.optimizationLevel.getOrElse(0) val unoptimized = new ZSourceLoadingQueue( diff --git a/src/main/scala/millfork/assembly/BranchingOpcodeMapping.scala b/src/main/scala/millfork/assembly/BranchingOpcodeMapping.scala index 49688606..63bfb855 100644 --- a/src/main/scala/millfork/assembly/BranchingOpcodeMapping.scala +++ b/src/main/scala/millfork/assembly/BranchingOpcodeMapping.scala @@ -1,11 +1,12 @@ package millfork.assembly +import millfork.assembly.m6809.MOpcode import millfork.assembly.mos.Opcode import millfork.assembly.z80.{ZOpcode, ZRegisters} /** * @author Karol Stasiak */ -case class BranchingOpcodeMapping(mosOpcode: Opcode.Value, z80Flags: ZRegisters) { +case class BranchingOpcodeMapping(mosOpcode: Opcode.Value, z80Flags: ZRegisters, m6809: MOpcode.Value) { } diff --git a/src/main/scala/millfork/assembly/m6809/MAddrMode.scala b/src/main/scala/millfork/assembly/m6809/MAddrMode.scala index b195a90d..3b21d4db 100644 --- a/src/main/scala/millfork/assembly/m6809/MAddrMode.scala +++ b/src/main/scala/millfork/assembly/m6809/MAddrMode.scala @@ -1,36 +1,102 @@ package millfork.assembly.m6809 -import millfork.node.M6809Register +import millfork.node.{M6809Register, Position} /** * @author Karol Stasiak */ -sealed trait MAddrMode +sealed trait MAddrMode { + def makeIndirect(position: Position): MAddrMode +} -case object Inherent extends MAddrMode +case object Inherent extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = ??? +} -case object InherentA extends MAddrMode +case object InherentA extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = ??? +} -case object InherentB extends MAddrMode +case object InherentB extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = ??? +} -case class TwoRegisters(source: M6809Register.Value, target: M6809Register.Value) extends MAddrMode +case class TwoRegisters(source: M6809Register.Value, target: M6809Register.Value) extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = ??? +} -case class RegisterSet(registers: Set[M6809Register.Value]) extends MAddrMode +case class RegisterSet(registers: Set[M6809Register.Value]) extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = ??? + def contains(register: M6809Register.Value): Boolean = { + import M6809Register._ + register match { + case A => registers(A) || registers(D) + case B => registers(B) || registers(D) + case D => registers(D) || (registers(A) && registers(B)) + case _ => registers(register) + } + } +} -case class Absolute(indirect: Boolean) extends MAddrMode +object RegisterSet { + def cleaned(registers: Set[M6809Register.Value]): RegisterSet = { + import M6809Register._ + if (registers(A) && registers(B)) RegisterSet(registers - A - B + D) + else if (registers(D) && registers(B)) RegisterSet(registers - A - B + D) + else if (registers(D) && registers(A)) RegisterSet(registers - A - B + D) + else RegisterSet(registers) -case class Indexed(base: M6809Register.Value, indirect: Boolean) extends MAddrMode + } +} -case class AccumulatorIndexed(base: M6809Register.Value, indirect: Boolean) extends MAddrMode +case class Absolute(indirect: Boolean) extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = copy(indirect = true) +} -case class PostIncremented(base: M6809Register.Value, amount: Int, indirect: Boolean) extends MAddrMode +case class Indexed(base: M6809Register.Value, indirect: Boolean) extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = copy(indirect = true) +} -case class PreDecremented(base: M6809Register.Value, amount: Int, indirect: Boolean) extends MAddrMode +case class AAccumulatorIndexed(base: M6809Register.Value, indirect: Boolean) extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = copy(indirect = true) +} -case object Relative extends MAddrMode +case class BAccumulatorIndexed(base: M6809Register.Value, indirect: Boolean) extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = copy(indirect = true) +} -case object Immediate extends MAddrMode +case class DAccumulatorIndexed(base: M6809Register.Value, indirect: Boolean) extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = copy(indirect = true) +} -case object NonExistent extends MAddrMode +case class PostIncremented(base: M6809Register.Value, amount: Int, indirect: Boolean) extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = copy(indirect = true) +} -case object RawByte extends MAddrMode +case class PreDecremented(base: M6809Register.Value, amount: Int, indirect: Boolean) extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = copy(indirect = true) +} + +case object Relative extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = ??? +} + +case object LongRelative extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = ??? +} + +case object Immediate extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = ??? +} + +case object DirectPage extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = ??? +} + +case object NonExistent extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = ??? +} + +case object RawByte extends MAddrMode { + def makeIndirect(position: Position): MAddrMode = ??? +} diff --git a/src/main/scala/millfork/assembly/m6809/MLine.scala b/src/main/scala/millfork/assembly/m6809/MLine.scala index 45b560a6..0647ea28 100644 --- a/src/main/scala/millfork/assembly/m6809/MLine.scala +++ b/src/main/scala/millfork/assembly/m6809/MLine.scala @@ -1,8 +1,8 @@ package millfork.assembly.m6809 import millfork.assembly.{AbstractCode, Elidability, SourceLine} -import millfork.env.{Constant, Label} -import millfork.node.Position +import millfork.env.{Constant, Label, MemoryVariable, NumericConstant, StackVariable, Variable, VariableInMemory} +import millfork.node.{M6809Register, Position} /** * @author Karol Stasiak @@ -22,11 +22,42 @@ object MLine { def label(label: Label): MLine = MLine(LABEL, NonExistent, label.toAddress) + def longBranch(opcode: MOpcode.Value, label: Label): MLine = MLine(opcode, LongRelative, label.toAddress) + + def longBranch(opcode: MOpcode.Value, label: String): MLine = longBranch(opcode, Label(label)) + + def tfr(source: M6809Register.Value, target: M6809Register.Value): MLine = MLine(TFR, TwoRegisters(source, target), Constant.Zero) + + def pp(opcode: MOpcode.Value, registers: M6809Register.Value*): MLine = MLine(opcode, RegisterSet(registers.toSet), Constant.Zero) + + def indexedS(opcode: MOpcode.Value, offset: Int = 0): MLine = + MLine(opcode, Indexed(M6809Register.S, indirect = false), NumericConstant(offset, 1)) + + def accessAndPullS(opcode: MOpcode.Value): MLine = + MLine(opcode, PostIncremented(M6809Register.S, 1, indirect = false), Constant.Zero) + + def accessAndPullSTwice(opcode: MOpcode.Value): MLine = + MLine(opcode, PostIncremented(M6809Register.S, 2, indirect = false), Constant.Zero) + def inherent(opcode: MOpcode.Value): MLine = MLine(opcode, Inherent, Constant.Zero) def inherentA(opcode: MOpcode.Value): MLine = MLine(opcode, InherentA, Constant.Zero) def inherentB(opcode: MOpcode.Value): MLine = MLine(opcode, InherentB, Constant.Zero) + + def immediate(opcode: MOpcode.Value, param: Constant): MLine = MLine(opcode, Immediate, param) + + def immediate(opcode: MOpcode.Value, param: Int): MLine = MLine(opcode, Immediate, NumericConstant(param, Constant.minimumSize(param))) + + def absolute(opcode: MOpcode.Value, param: Constant): MLine = MLine(opcode, Absolute(false), param) + + def variable(opcode: MOpcode.Value, variable: Variable, offset: Int = 0): MLine = { + variable match { + case v: VariableInMemory => MLine.absolute(opcode, v.toAddress) + case v: StackVariable => MLine(opcode, Indexed(M6809Register.U, indirect = false), NumericConstant(v.baseOffset, 1)) + case _ => ??? + } + } } case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant, elidability: Elidability.Value = Elidability.Elidable, source: Option[SourceLine] = None) extends AbstractCode { @@ -52,15 +83,25 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant override def toString: String = { if (opcode == MOpcode.LABEL) return parameter + ":" if (opcode == MOpcode.BYTE) return s" FCB $parameter" + if (addrMode == LongRelative) return s" L$opcode $parameter" val suffix = addrMode match { case NonExistent => "" case Inherent => "" case InherentA => "A" case InherentB => "B" case Relative => s" $parameter" + case Immediate => s" #$parameter" + case DirectPage => s" <$parameter" // TODO: syntax case Absolute(false) => s" $parameter" + case Absolute(true) => s" [$parameter]" case Indexed(base, false) => s" $parameter,$base" case Indexed(base, true) => s" [$parameter,$base]" + case DAccumulatorIndexed(base, false) => " D,$base" + case DAccumulatorIndexed(base, true) => " [D,$base]" + case AAccumulatorIndexed(base, false) => " A,$base" + case AAccumulatorIndexed(base, true) => " [A,$base]" + case BAccumulatorIndexed(base, false) => " B,$base" + case BAccumulatorIndexed(base, true) => " [B,$base]" case PreDecremented(base, 1, false) => s" ,-$base" case PreDecremented(base, 2, false) => s" ,--$base" case PreDecremented(base, 1, true) => s" [-$base]" @@ -69,6 +110,13 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant case PostIncremented(base, 2, false) => s" ,$base++" case PostIncremented(base, 1, true) => s" [,$base+]" case PostIncremented(base, 2, true) => s" [,$base++]" + case TwoRegisters(source, destination) => s" $source,$destination" + case RegisterSet(set) => + import MOpcode._ + import M6809Register._ + set.toSeq + .filterNot(r => set(D) && (r == A || r == B)) + .sortBy(x => -M6809Register.registerPushMask(x)).map(_.toString).mkString(" ", ",", "") } s" $opcode$suffix" } diff --git a/src/main/scala/millfork/assembly/m6809/MOpcode.scala b/src/main/scala/millfork/assembly/m6809/MOpcode.scala index 324f5e19..64414daa 100644 --- a/src/main/scala/millfork/assembly/m6809/MOpcode.scala +++ b/src/main/scala/millfork/assembly/m6809/MOpcode.scala @@ -1,5 +1,10 @@ package millfork.assembly.m6809 +import java.util.Locale + +import millfork.error.Logger +import millfork.node.Position + /** * @author Karol Stasiak */ @@ -18,7 +23,6 @@ object MOpcode extends Enumeration { INC, JMP, JSR, LDA, LDB, LDD, LDS, LDU, LDX, LDY, LEAS, LEAU, LEAX, LEAY, LSR, - LBRA, LBRN, LBHI, LBLS, LBCC, LBCS, LBNE, LBEQ, LBVC, LBVS, LBPL, LBMI, LBGE, LBLT, LBGT, LBLE, MUL, NEG, NOP, ORA, ORB, ORCC, @@ -28,8 +32,31 @@ object MOpcode extends Enumeration { TFR, TST, DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL = Value + private val toMap: Map[String, MOpcode.Value] = { + val notActualOpcodes: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL) + values.filterNot(notActualOpcodes).map(o => o.toString -> o).toMap ++ Map("BHS" -> BCC, "BLO" -> BCS, "LSL" -> ASL) + } val NoopDiscard: Set[MOpcode.Value] = Set(DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC) val PrefixedBy10: Set[MOpcode.Value] = Set(CMPD, CMPY, LDS, LDY, SWI2) // TODO: branches val PrefixedBy11: Set[MOpcode.Value] = Set(CMPS, CMPU, SWI3) val Prefixed: Set[MOpcode.Value] = PrefixedBy10 ++ PrefixedBy11 + val CanHaveInherentAccumulator: Set[MOpcode.Value] = Set(ASL, ASR, CLR, COM, DEC, INC, LSR, NEG, ROL, ROR, TST) + val Branching: Set[MOpcode.Value] = Set(BRA, BRN, BHI, BLS, BCC, BCS, BNE, BEQ, BVC, BVS, BPL, BMI, BGE, BLT, BGT, BLE) + + def lookup(opcode: String, position: Some[Position], log: Logger): (MOpcode.Value, Option[MAddrMode]) = { + val o = opcode.toUpperCase(Locale.ROOT) + System.out.flush() + if (o.startsWith("LB") && toMap.contains(o.substring(1))) { + toMap(o.substring(1)) -> Some(LongRelative) + } else if (o.endsWith("A") && toMap.contains(o.init) && CanHaveInherentAccumulator(toMap(o.init))) { + toMap(o.init) -> Some(InherentA) + } else if (o.endsWith("B") && toMap.contains(o.init) && CanHaveInherentAccumulator(toMap(o.init))) { + toMap(o.init) -> Some(InherentB) + } else if (toMap.contains(o)) { + toMap(o) -> None + } else { + log.error(s"Invalid opcode: $o", position) + NOP -> None + } + } } diff --git a/src/main/scala/millfork/compiler/m6809/M6809Buitins.scala b/src/main/scala/millfork/compiler/m6809/M6809Buitins.scala new file mode 100644 index 00000000..1a6bf519 --- /dev/null +++ b/src/main/scala/millfork/compiler/m6809/M6809Buitins.scala @@ -0,0 +1,181 @@ +package millfork.compiler.m6809 + +import millfork.assembly.m6809.{Absolute, Immediate, MLine, MLine0, MOpcode} +import millfork.assembly.m6809.MOpcode._ +import millfork.compiler.CompilationContext +import millfork.env.{CompoundConstant, Constant, MathOperator, NumericConstant} +import millfork.node.{Expression, LhsExpression, M6809Register, SumExpression} + +import scala.collection.mutable.ListBuffer + +/** + * @author Karol Stasiak + */ +object M6809Buitins { + + def perform8BitInPlace(ctx: CompilationContext, l: LhsExpression, r: Expression, opcode: MOpcode.Value): List[MLine] = { + val lc = M6809ExpressionCompiler.compileToB(ctx, l) + val rc = M6809ExpressionCompiler.compileToB(ctx, r) + (lc, rc) match { + case (_, List(MLine0(LDB, Immediate, c))) if c.isProvably(1) && opcode == ADDB && lc.last.opcode == LDB => + lc.init ++ List(lc.last.copy(opcode = INC)) + case (_, List(MLine0(LDB, Immediate, c))) if c.isProvably(1) && opcode == SUBB && lc.last.opcode == LDB => + lc.init ++ List(lc.last.copy(opcode = DEC)) + case (List(ldl@MLine0(LDB, Absolute(false), _)), _) if opcode != SUBB => + rc ++ List(ldl.copy(opcode = opcode), ldl.copy(opcode = STB)) + case (_, List(ldr@MLine0(LDB, Absolute(false), _))) if lc.last.opcode == LDB => + lc ++ List(ldr.copy(opcode = opcode), lc.last.copy(opcode = STB)) + case _ if lc.last.opcode == LDB => + lc ++ List(MLine.pp(PSHS, M6809Register.B)) ++ rc ++ List(MLine.accessAndPullS(opcode)) + case _ => + println(lc) + ??? + } + } + + + def split(ctx: CompilationContext, expr: SumExpression): (Constant, List[(Boolean, Expression)]) = { + var constant = Constant.Zero + val variable = ListBuffer[(Boolean, Expression)]() + for((neg, e) <- expr.expressions) { + ctx.env.eval(e) match { + case Some(c) if !neg || !expr.decimal => + constant = CompoundConstant( + (neg, expr.decimal) match { + case (false, false) => MathOperator.Plus + case (true, false) => MathOperator.Minus + case (false, true) => MathOperator.DecimalPlus + case (false, true) => MathOperator.DecimalMinus + }, + constant, c + ).quickSimplify + case _ => variable += (neg -> e) + } + } + constant -> variable.toList + } + + def split(ctx: CompilationContext, params: List[Expression], op: MathOperator.Value, empty: Int): (Constant, List[Expression]) = { + var constant: Constant = NumericConstant(empty, Constant.minimumSize(empty)) + val variable = ListBuffer[Expression]() + for(e <- params) { + ctx.env.eval(e) match { + case Some(c) => + constant = CompoundConstant(op, constant, c).quickSimplify + case _ => variable += e + } + } + constant -> variable.toList + } + + + def complexityB(code: List[MLine]): Int = code match { + case List(MLine0(LDB, _, _)) => 0 + case _ => -1 + } + def complexityD(code: List[MLine]): Int = code match { + case List(MLine0(LDD, _, _)) => 0 + case _ => -1 + } + + def compileByteSum(ctx: CompilationContext, expr: SumExpression, fromScratch: Boolean): List[MLine] = { + val ld = if (expr.decimal) LDA else LDB + val add = if (expr.decimal) ADDA else ADDB + val sub = if (expr.decimal) SUBA else SUBB + val negLine = if (expr.decimal) MLine.inherentA(NEG) else MLine.inherentB(NEG) + val (constant, variable) = split(ctx, expr) + val addendReads = variable + .map(addend => addend._1 -> M6809ExpressionCompiler.compileToB(ctx, addend._2)) + .map(code => complexityB(code._2) -> code).sortBy(_._1).map(_._2) + val result = ListBuffer[MLine]() + for ((neg, load) <- addendReads) { + if (result.isEmpty && fromScratch) { + result ++= load + if (neg) result += MLine.inherent(NEG) + } else { + load match { + case List(l@MLine0(LDB, _, _)) => + if (neg) { + result += l.copy(opcode = sub) + if (expr.decimal) ??? + } else { + result += l.copy(opcode = add) + if (expr.decimal) result += MLine.inherent(DAA) + } + case _ => + if (expr.decimal) { + result += MLine.pp(PSHS, M6809Register.A) + result ++= load + result += MLine.pp(PULS, M6809Register.A) + if (neg) { + ??? + if (expr.decimal) ??? + } else { + result += MLine.accessAndPullS(ADDA) + result += MLine.inherent(DAA) + } + } else { + result += MLine.pp(PSHS, M6809Register.B) + result ++= load + if (neg) result += negLine + result += MLine.accessAndPullS(ADDB) + } + } + } + } + if (!constant.isProvablyZero) { + if (result.isEmpty) { + result += MLine.immediate(ld, constant) + } else { + result += MLine.immediate(add, constant) + if (expr.decimal) result += MLine.inherent(DAA) + } + } + if (expr.decimal) result += MLine.tfr(M6809Register.A, M6809Register.B) + result.toList + } + + def compileByteBitwise(ctx: CompilationContext, params: List[Expression], fromScratch: Boolean, opcode: MOpcode.Value, op: MathOperator.Value, empty: Int): List[MLine] = { + val (constant, variable) = split(ctx, params, op, empty) + + val addendReads = variable + .map(addend => M6809ExpressionCompiler.compileToB(ctx, addend)) + .map(code => complexityB(code) -> code).sortBy(_._1).map(_._2) + val result = ListBuffer[MLine]() + for (load <- addendReads) { + if (result.isEmpty && fromScratch) { + result ++= load + } else { + load match { + case List(l@MLine0(LDB, _, _)) => + result += l.copy(opcode = opcode) + case _ => + result += MLine.pp(PSHS, M6809Register.B) + result ++= load + result += MLine.accessAndPullS(opcode) + } + } + } + if (!constant.isProvably(empty)) { + if (result.isEmpty) { + result += MLine.immediate(LDB, constant) + } else { + result += MLine.immediate(opcode, constant) + } + } + result.toList + } + + def compileWordSum(ctx: CompilationContext, expr: SumExpression, fromScratch: Boolean): List[MLine] = { + ??? + } + def compileWordBitwise(ctx: CompilationContext, + params: List[Expression], + fromScratch: Boolean, + opcodeB: MOpcode.Value, + opcodeA: MOpcode.Value, + op: MathOperator.Value, + empty: Int): List[MLine] = { + ??? + } +} diff --git a/src/main/scala/millfork/compiler/m6809/M6809Comparisons.scala b/src/main/scala/millfork/compiler/m6809/M6809Comparisons.scala new file mode 100644 index 00000000..a34497e6 --- /dev/null +++ b/src/main/scala/millfork/compiler/m6809/M6809Comparisons.scala @@ -0,0 +1,48 @@ +package millfork.compiler.m6809 + +import millfork.assembly.m6809.{MLine, MLine0} +import millfork.compiler.{BranchIfFalse, BranchIfTrue, BranchSpec, ComparisonType, CompilationContext} +import millfork.node.{Expression, M6809Register} +import millfork.assembly.m6809.MOpcode._ + +/** + * @author Karol Stasiak + */ +object M6809Comparisons { + + def isTrivial(lc: List[MLine]): Boolean = lc match { + case List(MLine0(LDB, _, _)) => true + case _ => false + } + + def compile8BitComparison(ctx: CompilationContext, comparisonType: ComparisonType.Value, l: Expression, r: Expression, branches: BranchSpec): scala.List[MLine] = { + val jump = branches match { + case BranchIfFalse(label) => return compile8BitComparison(ctx, ComparisonType.negate(comparisonType), l, r, branches.flip) + case BranchIfTrue(label) => List(MLine.longBranch( + comparisonType match { + case ComparisonType.Equal => BEQ + case ComparisonType.NotEqual => BNE + case ComparisonType.GreaterOrEqualUnsigned => BCC + case ComparisonType.LessUnsigned => BCS + case ComparisonType.GreaterUnsigned => BHI + case ComparisonType.LessOrEqualSigned => BLS + case ComparisonType.GreaterSigned => BGT + case ComparisonType.GreaterOrEqualSigned => BGE + case ComparisonType.LessSigned => BLT + case ComparisonType.LessOrEqualSigned => BLE + }, + label + )) + case _ => Nil + } + val lc = M6809ExpressionCompiler.compileToB(ctx, l) + val rc = M6809ExpressionCompiler.compileToB(ctx, r) + if (isTrivial(lc) && !isTrivial(rc)) return compile8BitComparison(ctx, ComparisonType.flip(comparisonType), r, l, branches) + if (isTrivial(rc)) { + lc ++ rc.map(_.copy(opcode = CMPB)) ++ jump + } else { + rc ++ List(MLine.pp(PSHS, M6809Register.B)) ++ lc ++ List(MLine.accessAndPullS(CMPB)) ++ jump + } + } + +} diff --git a/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala b/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala new file mode 100644 index 00000000..3f63e4aa --- /dev/null +++ b/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala @@ -0,0 +1,352 @@ +package millfork.compiler.m6809 + +import millfork.assembly.m6809.{Indexed, MLine, MOpcode, TwoRegisters} +import millfork.compiler.{AbstractExpressionCompiler, BranchIfFalse, BranchIfTrue, BranchSpec, ComparisonType, CompilationContext, NoBranching} +import millfork.node.{DerefExpression, Expression, FunctionCallExpression, GeneratedConstantExpression, IndexedExpression, LhsExpression, LiteralExpression, M6809Register, SumExpression, VariableExpression} +import millfork.assembly.m6809.MOpcode._ +import millfork.env.{ConstantBooleanType, ConstantPointy, FatBooleanType, MathOperator, MemoryVariable, NormalFunction, NormalParamSignature, NumericConstant, Variable} + +import scala.collection.GenTraversableOnce + +/** + * @author Karol Stasiak + */ +object MExpressionTarget extends Enumeration { + val A, B, D, X, Y, NOTHING = Value + + def toLd(r: Value): MOpcode.Value = r match { + case A => LDA + case B => LDB + case D => LDD + case X => LDX + case Y => LDY + case _ => ??? + } + + def toLea(r: Value): MOpcode.Value = r match { + case X => LEAX + case Y => LEAY + case _ => ??? + } + + def size(r: Value): Int = r match { + case A | B => 1 + case D | X | Y => 2 + case NOTHING => 0 + } + +} + +import MExpressionTarget.toLd + +object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] { + + def compile(ctx: CompilationContext, expr: Expression, target: MExpressionTarget.Value, branches: BranchSpec = BranchSpec.None): List[MLine] = { + val env = ctx.env + val exprType = getExpressionType(ctx, expr) + val targetSize = MExpressionTarget.size(target) + if (branches != NoBranching) { + (exprType, branches) match { + case (FatBooleanType, _) => + return compile(ctx, FunctionCallExpression("!=", List(expr, LiteralExpression(0, 1))), target, branches) + case (ConstantBooleanType(_, false), BranchIfTrue(_)) | (ConstantBooleanType(_, true), BranchIfFalse(_))=> + return compile(ctx, expr, target, NoBranching) + case (ConstantBooleanType(_, true), BranchIfTrue(x)) => + return compile(ctx, expr, target, NoBranching) :+ MLine.longBranch(BRA, x) + case (ConstantBooleanType(_, false), BranchIfFalse(x)) => + return compile(ctx, expr, target, NoBranching) :+ MLine.longBranch(BRA, x) + case _ => () + } + } + env.eval(expr) match { + case Some(c) => + return target match { + case MExpressionTarget.NOTHING => Nil + case _ => List(MLine.immediate(toLd(target), c)) + } + case None => + } + expr match { + case VariableExpression(name) => + val variable = env.get[Variable](name) + exprType.size match { + case 1 => + targetSize match { + case 0 => Nil + case 1 => List(MLine.variable(toLd(target), variable)) + case 2 => List(MLine.variable(LDB, variable)) ++ zeroextendB(ctx, target, exprType.isSigned) + } + case 2 => + targetSize match { + case 0 => Nil + case 2 => List(MLine.variable(toLd(target), variable)) + } + } + case LiteralExpression(c, _) => + target match { + case MExpressionTarget.NOTHING => Nil + case _ => List(MLine.immediate(MExpressionTarget.toLd(target), NumericConstant(c, MExpressionTarget.size(target)))) + } + case DerefExpression(inner, offset, _) => + compileToX(ctx, inner) :+ MLine(toLd(target), Indexed(M6809Register.X, indirect = false), NumericConstant(offset, 2)) + case e@SumExpression(expressions, decimal) => + getArithmeticParamMaxSize(ctx, expressions.map(_._2)) match { + case 1 => M6809Buitins.compileByteSum(ctx, e, fromScratch = true) ++ targetifyB(ctx, target, isSigned = false) + case 2 => M6809Buitins.compileWordSum(ctx, e, fromScratch = false) ++ targetifyD(ctx, target) + } + case fce@FunctionCallExpression(functionName, params) => + functionName match { + case "not" => ??? + case "nonet" => ??? + case "hi" => + compileToD(ctx, params.head) ++ targetifyA(ctx, target, isSigned = false) + case "lo" => + compileToD(ctx, params.head) ++ targetifyB(ctx, target, isSigned = false) + case "call" => ??? + case "*" => ??? + case "*'" => ??? + case "/" => ??? + case "%%" => ??? + case "&" => + getArithmeticParamMaxSize(ctx, params) match { + case 1 => M6809Buitins.compileByteBitwise(ctx, params, fromScratch = true, ANDB, MathOperator.And, 0xff) ++ targetifyB(ctx, target, isSigned = false) + case 2 => M6809Buitins.compileWordBitwise(ctx, params, fromScratch = true, ANDA, ANDB, MathOperator.And, 0xffff) ++ targetifyB(ctx, target, isSigned = false) + } + case "|" => ??? + getArithmeticParamMaxSize(ctx, params) match { + case 1 => M6809Buitins.compileByteBitwise(ctx, params, fromScratch = true, ORB, MathOperator.Or, 0) ++ targetifyB(ctx, target, isSigned = false) + case 2 => M6809Buitins.compileWordBitwise(ctx, params, fromScratch = true, ORA, ORB, MathOperator.Or, 0) ++ targetifyB(ctx, target, isSigned = false) + } + case "^" => ??? + getArithmeticParamMaxSize(ctx, params) match { + case 1 => M6809Buitins.compileByteBitwise(ctx, params, fromScratch = true, EORB, MathOperator.Exor, 0) ++ targetifyB(ctx, target, isSigned = false) + case 2 => M6809Buitins.compileWordBitwise(ctx, params, fromScratch = true, EORA, EORB, MathOperator.Exor, 0) ++ targetifyB(ctx, target, isSigned = false) + } + case "&&" => + assertBool(ctx, "&&", params) + branches match { + case BranchIfFalse(_) => + params.flatMap(compile(ctx, _, target, branches)) + case _ => + val skip = ctx.nextLabel("an") + params.init.flatMap(compile(ctx, _, target, BranchIfFalse(skip))) ++ + compile(ctx, params.last, target, branches) ++ + List(MLine.label(skip)) + } + case "||" => + assertBool(ctx, "||", params) + branches match { + case BranchIfTrue(_) => + params.flatMap(compile(ctx, _, target, branches)) + case _ => + val skip = ctx.nextLabel("or") + params.init.flatMap(compile(ctx, _, target, BranchIfTrue(skip))) ++ + compile(ctx, params.last, target, branches) ++ + List(MLine.label(skip)) + } + case "==" => + val size = params.map(p => getExpressionType(ctx, p).size).max + compileTransitiveRelation(ctx, "==", params, target, branches) { (l, r) => + size match { + case 1 => M6809Comparisons.compile8BitComparison(ctx, ComparisonType.Equal, l, r, branches) + case 2 => ??? + case _ => ??? + } + } + case "!=" => + val size = params.map(p => getExpressionType(ctx, p).size).max + compileTransitiveRelation(ctx, "!=", params, target, branches) { (l, r) => + size match { + case 1 => M6809Comparisons.compile8BitComparison(ctx, ComparisonType.NotEqual, l, r, branches) + case 2 => ??? + case _ => ??? + } + } + case "<" => + val (size, signed) = assertArithmeticComparison(ctx, params) + compileTransitiveRelation(ctx, "<", params, target, branches) { (l, r) => + size match { + case 1 => M6809Comparisons.compile8BitComparison(ctx, if (signed) ComparisonType.LessSigned else ComparisonType.LessUnsigned, l, r, branches) + case _ => ??? + } + } + case ">" => + val (size, signed) = assertArithmeticComparison(ctx, params) + compileTransitiveRelation(ctx, ">", params, target, branches) { (l, r) => + size match { + case 1 => M6809Comparisons.compile8BitComparison(ctx, if (signed) ComparisonType.GreaterSigned else ComparisonType.GreaterUnsigned, l, r, branches) + case _ => ??? + } + } + case "<=" => + val (size, signed) = assertArithmeticComparison(ctx, params) + compileTransitiveRelation(ctx, "<=", params, target, branches) { (l, r) => + size match { + case 1 => M6809Comparisons.compile8BitComparison(ctx, if (signed) ComparisonType.LessOrEqualSigned else ComparisonType.LessOrEqualUnsigned, l, r, branches) + case _ => ??? + } + } + case ">=" => + val (size, signed) = assertArithmeticComparison(ctx, params) + compileTransitiveRelation(ctx, ">=", params, target, branches) { (l, r) => + size match { + case 1 => M6809Comparisons.compile8BitComparison(ctx, if (signed) ComparisonType.GreaterOrEqualSigned else ComparisonType.GreaterOrEqualUnsigned, l, r, branches) + case _ => ??? + } + } + case "<<" => ??? + case ">>" => ??? + case ">>>>" => ??? + case "<<'" => ??? + case ">>'" => ??? + case "+=" => + val (l, r, size) = assertArithmeticAssignmentLike(ctx, params) + size match { + case 1 => M6809Buitins.perform8BitInPlace(ctx, l, r, ADDB) + case _ => ??? + } + case "+'=" => ??? + case "-=" => + val (l, r, size) = assertArithmeticAssignmentLike(ctx, params) + size match { + case 1 => M6809Buitins.perform8BitInPlace(ctx, l, r, SUBB) + case _ => ??? + } + case "-'=" => ??? + case "*=" => ??? + case "*'=" => ??? + case "/=" => ??? + case "%%=" => ??? + case "&=" => + val (l, r, size) = assertArithmeticAssignmentLike(ctx, params) + size match { + case 1 => M6809Buitins.perform8BitInPlace(ctx, l, r, ANDB) + case _ => ??? + } + case "|=" => + val (l, r, size) = assertArithmeticAssignmentLike(ctx, params) + size match { + case 1 => M6809Buitins.perform8BitInPlace(ctx, l, r, ORB) + case _ => ??? + } + case "^=" => + val (l, r, size) = assertArithmeticAssignmentLike(ctx, params) + size match { + case 1 => M6809Buitins.perform8BitInPlace(ctx, l, r, EORB) + case _ => ??? + } + case "<<=" => ??? + case "<<'=" => ??? + case ">>=" => ??? + case ">>'=" => ??? + case ">>>>=" => ??? + case _ => + val f = lookupFunction(ctx, fce) + f.params match { + case NormalParamSignature(Nil) => //ok + case _ => ??? + } + f match { + case nf:NormalFunction => + List(MLine.absolute(JSR, nf.toAddress)) + case _ => ??? + } + } + case _ => ??? + } + } + + def compileToB(ctx: CompilationContext, expr: Expression): List[MLine] = compile(ctx, expr, MExpressionTarget.B) + + def compileToD(ctx: CompilationContext, expr: Expression): List[MLine] = compile(ctx, expr, MExpressionTarget.D) + + def compileToX(ctx: CompilationContext, expr: Expression): List[MLine] = compile(ctx, expr, MExpressionTarget.X) + + def zeroextendB(ctx: CompilationContext, target: MExpressionTarget.Value, isSigned: Boolean): List[MLine] = { + val extendToA = if (isSigned) MLine.immediate(LDA, 0) else MLine.inherent(SEX) + target match { + case MExpressionTarget.D => List(extendToA) + case MExpressionTarget.X => List(extendToA, MLine.tfr(M6809Register.D, M6809Register.X)) + case MExpressionTarget.Y => List(extendToA, MLine.tfr(M6809Register.D, M6809Register.Y)) + case _ => ??? + } + } + + def storeB(ctx: CompilationContext, target: LhsExpression): List[MLine] = { + target match { + case VariableExpression(name) => + val variable = ctx.env.get[Variable](name) + List(MLine.variable(STB, variable)) + case DerefExpression(inner, offset, _) => + compileToX(ctx, inner) :+ MLine(STB, Indexed(M6809Register.X, indirect = false), NumericConstant(offset, 2)) + case IndexedExpression(name, index) => + ctx.env.getPointy(name) match { + case p: ConstantPointy => + compileToX(ctx, index) :+ MLine(STB, Indexed(M6809Register.X, indirect = false), p.value) + } + } + } + + def storeD(ctx: CompilationContext, target: LhsExpression): List[MLine] = { + target match { + case VariableExpression(name) => + val variable = ctx.env.get[Variable](name) + List(MLine.variable(STD, variable)) + case DerefExpression(inner, offset, _) => + compileToX(ctx, inner) :+ MLine(STD, Indexed(M6809Register.X, indirect = false), NumericConstant(offset, 2)) + } + } + + def targetifyB(ctx: CompilationContext, target: MExpressionTarget.Value, isSigned: Boolean): List[MLine] = target match { + case MExpressionTarget.NOTHING => Nil + case MExpressionTarget.B => Nil + case MExpressionTarget.A => List(MLine.tfr(M6809Register.B, M6809Register.A)) + case MExpressionTarget.D | MExpressionTarget.X | MExpressionTarget.Y => zeroextendB(ctx, target, isSigned) + } + + def targetifyA(ctx: CompilationContext, target: MExpressionTarget.Value, isSigned: Boolean): List[MLine] = target match { + case MExpressionTarget.NOTHING => Nil + case MExpressionTarget.A => Nil + case MExpressionTarget.B => List(MLine.tfr(M6809Register.A, M6809Register.B)) + case MExpressionTarget.D | MExpressionTarget.X | MExpressionTarget.Y => + List(MLine.tfr(M6809Register.A, M6809Register.B)) ++ zeroextendB(ctx, target, isSigned) + } + + def targetifyD(ctx: CompilationContext, target: MExpressionTarget.Value): List[MLine] = target match { + case MExpressionTarget.NOTHING => Nil + case MExpressionTarget.D => Nil + case MExpressionTarget.X => List(MLine.tfr(M6809Register.D, M6809Register.X)) + case MExpressionTarget.Y => List(MLine.tfr(M6809Register.D, M6809Register.Y)) + } + + + private def compileTransitiveRelation(ctx: CompilationContext, + operator: String, + params: List[Expression], + target: MExpressionTarget.Value, + branches: BranchSpec)(binary: (Expression, Expression) => List[MLine]): List[MLine] = { + params match { + case List(l, r) => binary(l, r) + case List(_) | Nil => + ctx.log.fatal("") + case _ => + params.tail.init.foreach { e => + if (ctx.env.eval(e).isEmpty) e match { + case VariableExpression(_) => + case LiteralExpression(_, _) => + case GeneratedConstantExpression(_, _) => + case IndexedExpression(_, VariableExpression(_)) => + case IndexedExpression(_, LiteralExpression(_, _)) => + case IndexedExpression(_, GeneratedConstantExpression(_, _)) => + case IndexedExpression(_, SumExpression(sumParams, false)) if isUpToOneVar(sumParams) => + case _ => + ctx.log.warn("A complex expression may be evaluated multiple times", e.position) + } + } + val conjunction = params.init.zip(params.tail).map { + case (l, r) => FunctionCallExpression(operator, List(l, r)) + }.reduceLeft((a, b) => FunctionCallExpression("&&", List(a, b))) + compile(ctx, conjunction, target, branches) + } + } +} diff --git a/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala b/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala index eff6dbf7..f51ad9b4 100644 --- a/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala +++ b/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala @@ -1,11 +1,12 @@ package millfork.compiler.m6809 import millfork.assembly.BranchingOpcodeMapping -import millfork.assembly.m6809.MLine -import millfork.compiler.{AbstractCompiler, AbstractStatementCompiler, BranchSpec, CompilationContext} -import millfork.node.{ExecutableStatement, Expression, M6809AssemblyStatement, ReturnStatement, VariableExpression, WhileStatement} +import millfork.assembly.m6809.{MLine, NonExistent} +import millfork.compiler.{AbstractCompiler, AbstractExpressionCompiler, AbstractStatementCompiler, BranchSpec, CompilationContext} +import millfork.node.{Assignment, DoWhileStatement, ExecutableStatement, Expression, ExpressionStatement, ForEachStatement, ForStatement, IfStatement, M6809AssemblyStatement, ReturnDispatchStatement, ReturnStatement, VariableExpression, WhileStatement} import millfork.assembly.m6809.MOpcode._ import millfork.env.{Label, ThingInMemory} + /** * @author Karol Stasiak */ @@ -23,8 +24,31 @@ object M6809StatementCompiler extends AbstractStatementCompiler[MLine] { println(statement) ??? } - case WhileStatement(VariableExpression("true"),List(),List(),_) => - Nil -> Nil // TODO + case Assignment(destination, source) => + AbstractExpressionCompiler.getExpressionType(ctx, destination).size match { + case 1 => (M6809ExpressionCompiler.compileToB(ctx, source) ++ M6809ExpressionCompiler.storeB(ctx, destination)) -> Nil + case 2 => (M6809ExpressionCompiler.compileToD(ctx, source) ++ M6809ExpressionCompiler.storeD(ctx, destination)) -> Nil + } + case ExpressionStatement(expression) => + M6809ExpressionCompiler.compile(ctx, expression, MExpressionTarget.NOTHING) -> Nil + case s:IfStatement => + compileIfStatement(ctx, s) + case s:WhileStatement => + compileWhileStatement(ctx, s) + case s:DoWhileStatement => + compileDoWhileStatement(ctx, s) + case s:ForStatement => + compileForStatement(ctx, s) + case s:ForEachStatement => + compileForEachStatement(ctx, s) + case M6809AssemblyStatement(opcode, addrMode, expression, elidability) => + ctx.env.evalForAsm(expression) match { + case Some(param) => + List(MLine(opcode, addrMode, param, elidability)) -> Nil + case None => + ctx.log.error("Invalid parameter", expression.position) + Nil -> Nil + } case _ => println(statement) ??? @@ -32,9 +56,9 @@ object M6809StatementCompiler extends AbstractStatementCompiler[MLine] { code._1.map(_.positionIfEmpty(statement.position)) -> code._2.map(_.positionIfEmpty(statement.position)) } - override def labelChunk(labelName: String): List[MLine] = ??? + override def labelChunk(labelName: String): List[MLine] = List(MLine(LABEL, NonExistent, Label(labelName).toAddress)) - override def jmpChunk(label: Label): List[MLine] = ??? + override def jmpChunk(label: Label): List[MLine] = List(MLine.absolute(JMP, label.toAddress)) override def branchChunk(opcode: BranchingOpcodeMapping, labelName: String): List[MLine] = ??? @@ -44,7 +68,7 @@ object M6809StatementCompiler extends AbstractStatementCompiler[MLine] { override def returnAssemblyStatement: ExecutableStatement = ??? - override def callChunk(label: ThingInMemory): List[MLine] = ??? + override def callChunk(label: ThingInMemory): List[MLine] = List(MLine.absolute(JSR, label.toAddress)) - override def areBlocksLarge(blocks: List[MLine]*): Boolean = ??? + override def areBlocksLarge(blocks: List[MLine]*): Boolean = true // TODO } diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index cf6e22cd..13bcd4bc 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -32,6 +32,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa case CpuFamily.M6502 => 0x101 case CpuFamily.I80 => 0 case CpuFamily.I86 => 0 + case CpuFamily.M6809 => 0 } def errorConstant(msg: String, position: Option[Position] = None): Constant = { @@ -449,36 +450,36 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa } addThing(ConstantThing("$0000", NumericConstant(0, 2), p), None) addThing(FlagBooleanType("set_carry", - BranchingOpcodeMapping(Opcode.BCS, IfFlagSet(ZFlag.C)), - BranchingOpcodeMapping(Opcode.BCC, IfFlagClear(ZFlag.C))), + BranchingOpcodeMapping(Opcode.BCS, IfFlagSet(ZFlag.C), MOpcode.BCS), + BranchingOpcodeMapping(Opcode.BCC, IfFlagClear(ZFlag.C), MOpcode.BCC)), None) addThing(FlagBooleanType("clear_carry", - BranchingOpcodeMapping(Opcode.BCC, IfFlagClear(ZFlag.C)), - BranchingOpcodeMapping(Opcode.BCS, IfFlagSet(ZFlag.C))), + BranchingOpcodeMapping(Opcode.BCC, IfFlagClear(ZFlag.C), MOpcode.BCC), + BranchingOpcodeMapping(Opcode.BCS, IfFlagSet(ZFlag.C), MOpcode.BCS)), None) addThing(FlagBooleanType("set_overflow", - BranchingOpcodeMapping(Opcode.BVS, IfFlagSet(ZFlag.P)), - BranchingOpcodeMapping(Opcode.BVC, IfFlagClear(ZFlag.P))), + BranchingOpcodeMapping(Opcode.BVS, IfFlagSet(ZFlag.P), MOpcode.BVS), + BranchingOpcodeMapping(Opcode.BVC, IfFlagClear(ZFlag.P), MOpcode.BVC)), None) addThing(FlagBooleanType("clear_overflow", - BranchingOpcodeMapping(Opcode.BVC, IfFlagClear(ZFlag.P)), - BranchingOpcodeMapping(Opcode.BVS, IfFlagSet(ZFlag.P))), + BranchingOpcodeMapping(Opcode.BVC, IfFlagClear(ZFlag.P), MOpcode.BVC), + BranchingOpcodeMapping(Opcode.BVS, IfFlagSet(ZFlag.P), MOpcode.BVS)), None) addThing(FlagBooleanType("set_zero", - BranchingOpcodeMapping(Opcode.BEQ, IfFlagSet(ZFlag.Z)), - BranchingOpcodeMapping(Opcode.BNE, IfFlagClear(ZFlag.Z))), + BranchingOpcodeMapping(Opcode.BEQ, IfFlagSet(ZFlag.Z), MOpcode.BEQ), + BranchingOpcodeMapping(Opcode.BNE, IfFlagClear(ZFlag.Z), MOpcode.BNE)), None) addThing(FlagBooleanType("clear_zero", - BranchingOpcodeMapping(Opcode.BNE, IfFlagClear(ZFlag.Z)), - BranchingOpcodeMapping(Opcode.BEQ, IfFlagSet(ZFlag.Z))), + BranchingOpcodeMapping(Opcode.BNE, IfFlagClear(ZFlag.Z), MOpcode.BNE), + BranchingOpcodeMapping(Opcode.BEQ, IfFlagSet(ZFlag.Z), MOpcode.BEQ)), None) addThing(FlagBooleanType("set_negative", - BranchingOpcodeMapping(Opcode.BMI, IfFlagSet(ZFlag.S)), - BranchingOpcodeMapping(Opcode.BPL, IfFlagClear(ZFlag.S))), + BranchingOpcodeMapping(Opcode.BMI, IfFlagSet(ZFlag.S), MOpcode.BMI), + BranchingOpcodeMapping(Opcode.BPL, IfFlagClear(ZFlag.S), MOpcode.BPL)), None) addThing(FlagBooleanType("clear_negative", - BranchingOpcodeMapping(Opcode.BPL, IfFlagClear(ZFlag.S)), - BranchingOpcodeMapping(Opcode.BMI, IfFlagSet(ZFlag.S))), + BranchingOpcodeMapping(Opcode.BPL, IfFlagClear(ZFlag.S), MOpcode.BPL), + BranchingOpcodeMapping(Opcode.BMI, IfFlagSet(ZFlag.S), MOpcode.BMI)), None) builtinsAdded = true } diff --git a/src/main/scala/millfork/env/Thing.scala b/src/main/scala/millfork/env/Thing.scala index 049e6d6f..1c89848f 100644 --- a/src/main/scala/millfork/env/Thing.scala +++ b/src/main/scala/millfork/env/Thing.scala @@ -464,6 +464,12 @@ case class ByZRegister(register: ZRegister.Value) extends ParamPassingConvention override def inNonInlinedOnly = false } +case class ByM6809Register(register: M6809Register.Value) extends ParamPassingConvention { + override def inInlinedOnly = false + + override def inNonInlinedOnly = false +} + case class ByVariable(name: String) extends ParamPassingConvention { override def inInlinedOnly = false diff --git a/src/main/scala/millfork/node/Node.scala b/src/main/scala/millfork/node/Node.scala index 783f6e2f..0e9b47f4 100644 --- a/src/main/scala/millfork/node/Node.scala +++ b/src/main/scala/millfork/node/Node.scala @@ -205,6 +205,20 @@ object M6809Register extends Enumeration { case D | X | Y | U | S | PC => 2 case A | B | DP | CC => 1 } + def registerPushMask(register: M6809Register.Value): Int = { + register match { + case CC => 1 + case A => 2 + case B => 4 + case D => 6 + case X => 0x10 + case Y => 0x20 + case U => 0x40 + case S => 0x40 + case PC => 0x80 + case DP => 0 + } + } } //case class Indexing(child: Expression, register: Register.Value) extends Expression diff --git a/src/main/scala/millfork/output/AbstractAssembler.scala b/src/main/scala/millfork/output/AbstractAssembler.scala index 2cf6f31e..585fc505 100644 --- a/src/main/scala/millfork/output/AbstractAssembler.scala +++ b/src/main/scala/millfork/output/AbstractAssembler.scala @@ -70,13 +70,24 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program mem.banks(bank).initialized(addr + 1) = true mem.banks(bank).readable(addr) = true mem.banks(bank).readable(addr + 1) = true - value match { - case NumericConstant(x, _) => - if (x > 0xffff) log.error("Word overflow") - mem.banks(bank).output(addr) = x.toByte - mem.banks(bank).output(addr + 1) = (x >> 8).toByte - case _ => - wordsToWriteLater += ((bank, addr, value)) + if (platform.isBigEndian) { + value match { + case NumericConstant(x, _) => + if (x > 0xffff) log.error("Word overflow") + mem.banks(bank).output(addr) = (x >> 8).toByte + mem.banks(bank).output(addr + 1) = x.toByte + case _ => + wordsToWriteLater += ((bank, addr, value)) + } + } else { + value match { + case NumericConstant(x, _) => + if (x > 0xffff) log.error("Word overflow") + mem.banks(bank).output(addr) = x.toByte + mem.banks(bank).output(addr + 1) = (x >> 8).toByte + case _ => + wordsToWriteLater += ((bank, addr, value)) + } } } @@ -538,8 +549,13 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program } for ((bank, addr, b) <- wordsToWriteLater) { val value = deepConstResolve(b) - mem.banks(bank).output(addr) = value.toByte - mem.banks(bank).output(addr + 1) = value.>>>(8).toByte + if (platform.isBigEndian) { + mem.banks(bank).output(addr) = value.>>>(8).toByte + mem.banks(bank).output(addr + 1) = value.toByte + } else { + mem.banks(bank).output(addr) = value.toByte + mem.banks(bank).output(addr + 1) = value.>>>(8).toByte + } } for (bank <- mem.banks.keys) { diff --git a/src/main/scala/millfork/output/M6809Assembler.scala b/src/main/scala/millfork/output/M6809Assembler.scala index b7149d02..a079c885 100644 --- a/src/main/scala/millfork/output/M6809Assembler.scala +++ b/src/main/scala/millfork/output/M6809Assembler.scala @@ -1,10 +1,11 @@ package millfork.output +import millfork.assembly.SourceLine import millfork.{CompilationOptions, Platform} import millfork.assembly.m6809.{MOpcode, _} import millfork.compiler.m6809.M6809Compiler -import millfork.env.{Environment, Label, MemoryAddressConstant, NormalFunction} -import millfork.node.{NiceFunctionProperty, Program} +import millfork.env.{Environment, Label, MemoryAddressConstant, NormalFunction, NumericConstant} +import millfork.node.{M6809Register, NiceFunctionProperty, Position, Program} import scala.collection.mutable @@ -26,8 +27,54 @@ class M6809Assembler(program: Program, override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[MLine]): List[MLine] = code - override def emitInstruction(bank: String, options: CompilationOptions, index: Int, instr: MLine): Int = { + private def registerCode(register: M6809Register.Value): Int = { + import M6809Register._ + register match { + case D => 0 + case X => 1 + case Y => 2 + case U => 3 + case S => 4 + case PC => 5 + case A => 8 + case B => 9 + case CC => 10 + case DP => 11 + } + } + private def registerPushMask(register: M6809Register.Value, useUserStack: Boolean, position: Option[Position]): Int = { + import M6809Register._ + register match { + case D => 6 + case X => 0x10 + case Y => 0x20 + case U => + 0x40 + case S => + if (!useUserStack) log.error("Can't push/pull the S register onto the system stack", position) + 0x40 + case PC => 0x80 + case A => 2 + case B => 4 + case CC => 1 + case DP => + log.error("Can't push/pull the DP register", position) + 0 + } + } + + override def emitInstruction(bank: String, options: CompilationOptions, startIndex: Int, instr: MLine): Int = { + val position = instr.source.map(sl => Position(sl.moduleName, sl.line, 0, 0)) import millfork.assembly.m6809.MOpcode._ + var index: Int = startIndex + if (MOpcode.PrefixedBy10(instr.opcode)) { + writeByte(bank, index, 0x10) + index += 1 + } + if (MOpcode.PrefixedBy11(instr.opcode)) { + writeByte(bank, index, 0x11) + index += 1 + } instr match { case MLine0(BYTE, RawByte, c) => writeByte(bank, index, c) @@ -40,6 +87,24 @@ class M6809Assembler(program: Program, index case MLine0(op, NonExistent, _) if MOpcode.NoopDiscard(op) => index + case MLine0(TFR, TwoRegisters(source, target), param) + if M6809Register.registerSize(source) == M6809Register.registerSize(target) && param.isProvablyZero => + writeByte(bank, index, 0x1f) + writeByte(bank, index, registerCode(source) * 16 + registerCode(target)) + index + 2 + case MLine0(EXG, TwoRegisters(source, target), param) + if M6809Register.registerSize(source) == M6809Register.registerSize(target) && param.isProvablyZero => + writeByte(bank, index, 0x1e) + writeByte(bank, index, registerCode(source) * 16 + registerCode(target)) + index + 2 + case l@MLine0(op, RegisterSet(set), param) if param.isProvablyZero && M6809Assembler.pushpull.contains(op) => + writeByte(bank, index, M6809Assembler.pushpull(op)) + val useUserStack = op == PULU || op == PSHU + if (useUserStack && set(M6809Register.U)) log.error("Can't push/pull the U register onto the user stack", position) + if (!useUserStack && set(M6809Register.S)) log.error("Can't push/pull the U register onto the system stack", position) + if (set(M6809Register.DP)) log.error("Can't push/pull the DP register", position) + writeByte(bank, index + 1, set.foldLeft(0)((acc,reg) => acc | registerPushMask(reg, useUserStack, position))) + index + 2 case MLine0(op, Inherent, _) if M6809Assembler.inherent.contains(op) => writeByte(bank, index, M6809Assembler.inherent(op)) index + 1 @@ -52,29 +117,158 @@ class M6809Assembler(program: Program, case MLine0(op, Immediate, param) if M6809Assembler.immediate.contains(op) => writeByte(bank, index, M6809Assembler.immediate(op)) writeByte(bank, index + 1, param) - index + 1 + index + 2 + case MLine0(op, Immediate, param) if M6809Assembler.standardByte.contains(op) => + writeByte(bank, index, M6809Assembler.standardByte(op)) + writeByte(bank, index + 1, param) + index + 2 + case MLine0(op, Immediate, param) if M6809Assembler.standardWord.contains(op) => + writeByte(bank, index, M6809Assembler.standardWord(op)) + writeWord(bank, index + 1, param) + index + 3 + case MLine0(op, DirectPage, param) if M6809Assembler.standard.contains(op) => + if (M6809Assembler.standard(op).&(0xf0) == 0x40) { + writeByte(bank, index, M6809Assembler.standard(op) & 0xf) + } else { + writeByte(bank, index, M6809Assembler.standard(op) + 0x10) + } + writeByte(bank, index + 1, param) + index + 2 + case MLine0(op, Absolute(false), param) if M6809Assembler.standard.contains(op) => + writeByte(bank, index, M6809Assembler.standard(op) + 0x30) + writeWord(bank, index + 1, param) + index + 3 + case MLine0(op, Absolute(true), param) if M6809Assembler.indexable.contains(op) => + writeByte(bank, index, M6809Assembler.standard(op) + 0x20) + writeWord(bank, index + 2, param) + index + 3 + case MLine0(op, Indexed(M6809Register.PC, indirect), param) if M6809Assembler.indexable.contains(op) => + ??? + case MLine0(op, Indexed(register, indirect), param) if M6809Assembler.indexable.contains(op) => + writeByte(bank, index, M6809Assembler.indexable(op) + 0x20) + val ri = getRegByte(register, indirect) + param match { + case NumericConstant(n, _) if !indirect && n >= -16 && n <= 15 => + writeByte(bank, index + 1, ri + n.toInt.&(0x1f)) + index + 2 + case NumericConstant(0, _) => + writeByte(bank, index + 1, 0x84 + ri) + index + 2 + case NumericConstant(n, _) if n >= -128 && n <= 127 => + writeByte(bank, index + 1, 0x88 + ri) + writeWord(bank, index + 2, param) + index + 3 + case _ => + writeByte(bank, index + 1, 0x89 + ri) + writeWord(bank, index + 2, param) + index + 4 + } + case MLine0(op, PostIncremented(register, 1, false), param) if M6809Assembler.indexable.contains(op) => + if (!param.isProvablyZero) log.error(s"Index offset has to be zero, not $param", position) + writeByte(bank, index, M6809Assembler.indexable(op) + 0x20) + writeByte(bank, index + 1, 0x80 + getRegByte(register, indirect = false)) + index + 2 + case MLine0(op, PostIncremented(register, 2, indirect), param) if M6809Assembler.indexable.contains(op) => + if (!param.isProvablyZero) log.error(s"Index offset has to be zero, not $param", position) + writeByte(bank, index, M6809Assembler.indexable(op) + 0x20) + writeByte(bank, index + 1, 0x81 + getRegByte(register, indirect)) + index + 2 + case MLine0(op, PreDecremented(register, 1, false), param) if M6809Assembler.indexable.contains(op) => + if (!param.isProvablyZero) log.error(s"Index offset has to be zero, not $param", position) + writeByte(bank, index, M6809Assembler.indexable(op) + 0x20) + writeByte(bank, index + 1, 0x82 + getRegByte(register, indirect = false)) + index + 2 + case MLine0(op, PreDecremented(register, 2, indirect), param) if M6809Assembler.indexable.contains(op) => + if (!param.isProvablyZero) log.error(s"Index offset has to be zero, not $param", position) + writeByte(bank, index, M6809Assembler.indexable(op) + 0x20) + writeByte(bank, index + 1, 0x83 + getRegByte(register, indirect)) + index + 2 + case MLine0(op, BAccumulatorIndexed(register, indirect), param) if M6809Assembler.indexable.contains(op) => + if (!param.isProvablyZero) log.error(s"Index offset has to be zero, not $param", position) + writeByte(bank, index, M6809Assembler.indexable(op) + 0x20) + writeByte(bank, index + 1, 0x85 + getRegByte(register, indirect)) + index + 2 + case MLine0(op, AAccumulatorIndexed(register, indirect), param) if M6809Assembler.indexable.contains(op) => + if (!param.isProvablyZero) log.error(s"Index offset has to be zero, not $param", position) + writeByte(bank, index, M6809Assembler.indexable(op) + 0x20) + writeByte(bank, index + 1, 0x86 + getRegByte(register, indirect)) + index + 2 + case MLine0(op, DAccumulatorIndexed(register, indirect), param) if M6809Assembler.indexable.contains(op) => + if (!param.isProvablyZero) log.error(s"Index offset has to be zero, not $param", position) + writeByte(bank, index, M6809Assembler.indexable(op) + 0x20) + writeByte(bank, index + 1, 0x8b + getRegByte(register, indirect)) + index + 2 + case MLine0(op, Relative, param) if M6809Assembler.branches.contains(op) => + writeByte(bank, index, M6809Assembler.branches(op)) + writeByte(bank, index + 1, param - (index + 2)) + index + 2 + case MLine0(op, LongRelative, param) if M6809Assembler.branches.contains(op) => + writeByte(bank, index, 0x10) + writeByte(bank, index + 1, M6809Assembler.branches(op)) + writeWord(bank, index + 2, param - (index + 4)) + index + 4 case _ => // TODO throw new IllegalArgumentException("Not supported: " + instr) } } + + private def getRegByte(register: M6809Register.Value, indirect: Boolean) = { + (register match { + case M6809Register.X => 0x00 + case M6809Register.Y => 0x20 + case M6809Register.U => 0x40 + case M6809Register.S => 0x60 + }) + (if (indirect) 0x10 else 0) + } } object M6809Assembler { - val inherent: mutable.Map[MOpcode.Value, Byte] = mutable.Map[MOpcode.Value, Byte]() - val inherentA: mutable.Map[MOpcode.Value, Byte] = mutable.Map[MOpcode.Value, Byte]() - val inherentB: mutable.Map[MOpcode.Value, Byte] = mutable.Map[MOpcode.Value, Byte]() - val immediate: mutable.Map[MOpcode.Value, Byte] = mutable.Map[MOpcode.Value, Byte]() + val inherent: mutable.Map[MOpcode.Value, Int] = mutable.Map[MOpcode.Value, Int]() + val inherentA: mutable.Map[MOpcode.Value, Int] = mutable.Map[MOpcode.Value, Int]() + val inherentB: mutable.Map[MOpcode.Value, Int] = mutable.Map[MOpcode.Value, Int]() + val immediate: mutable.Map[MOpcode.Value, Int] = mutable.Map[MOpcode.Value, Int]() + val standardByte: mutable.Map[MOpcode.Value, Int] = mutable.Map[MOpcode.Value, Int]() + val standardWord: mutable.Map[MOpcode.Value, Int] = mutable.Map[MOpcode.Value, Int]() + val standard: mutable.Map[MOpcode.Value, Int] = mutable.Map[MOpcode.Value, Int]() + val indexable: mutable.Map[MOpcode.Value, Int] = mutable.Map[MOpcode.Value, Int]() + val pushpull: mutable.Map[MOpcode.Value, Int] = mutable.Map[MOpcode.Value, Int]() + val branches: mutable.Map[MOpcode.Value, Int] = mutable.Map[MOpcode.Value, Int]() - private def inh(op: MOpcode.Value, value: Int): Unit = inherent(op) = value.toByte + private def br(op: MOpcode.Value, value: Int): Unit = branches(op) = value + + private def inh(op: MOpcode.Value, value: Int): Unit = inherent(op) = value + + private def pp(op: MOpcode.Value, value: Int): Unit = pushpull(op) = value private def inab(op: MOpcode.Value, value: Int): Unit = { - inherentA(op) = value.toByte - inherentB(op) = value.+(0x10).toByte + inherentA(op) = value + inherentB(op) = value.+(0x10) } - private def imm(op: MOpcode.Value, value: Int): Unit = immediate(op) = value.toByte + private def imm(op: MOpcode.Value, value: Int): Unit = immediate(op) = value + + private def stb(op: MOpcode.Value, value: Int): Unit = { + standardByte(op) = value + standard(op) = value + indexable(op) = value + } + + private def stw(op: MOpcode.Value, value: Int): Unit = { + standardWord(op) = value + standard(op) = value + indexable(op) = value + } + + private def stm(op: MOpcode.Value, value: Int): Unit = { + standard(op) = value + indexable(op) = value + } + + private def lea(op: MOpcode.Value, value: Int): Unit = { + indexable(op) = value + } import MOpcode._ @@ -86,6 +280,8 @@ object M6809Assembler { inh(RTS, 0x39) inh(SEX, 0x1d) inh(SWI, 0x3f) + inh(SWI2, 0x3f) + inh(SWI3, 0x3f) inh(SYNC, 0x13) inab(ASL, 0x48) @@ -100,4 +296,91 @@ object M6809Assembler { inab(ROR, 0x46) inab(TST, 0x4d) -} \ No newline at end of file + stm(ASL, 0x48) + stm(ASR, 0x47) + stm(CLR, 0x4f) + stm(COM, 0x43) + stm(DEC, 0x4a) + stm(INC, 0x4c) + stm(LSR, 0x44) + stm(NEG, 0x40) + stm(ROL, 0x49) + stm(ROR, 0x46) + stm(TST, 0x4d) + + imm(ANDCC, 0x1c) + imm(CWAI, 0x3c) + imm(ORCC, 0x1a) + + pp(PSHS, 0x34) + pp(PSHU, 0x36) + pp(PULS, 0x35) + pp(PULU, 0x37) + + + stb(ADCA, 0x89) + stb(ADCB, 0xc9) + stb(ADDA, 0x8b) + stb(ADDB, 0xcb) + stw(ADDD, 0xc3) + stb(ANDA, 0x84) + stb(ANDB, 0xc4) + stb(BITA, 0x85) + stb(BITB, 0xc5) + stb(CMPA, 0x81) + stb(CMPB, 0xc1) + stw(CMPD, 0x83) + stw(CMPS, 0x8c) + stw(CMPU, 0x83) + stw(CMPX, 0x8c) + stw(CMPY, 0x8c) + stb(EORA, 0x88) + stb(EORB, 0xc8) + stm(JMP, 0x4e) + stm(JSR, 0x8d) + stb(LDA, 0x86) + stb(LDB, 0xc6) + stw(LDD, 0xcc) + stw(LDS, 0xce) + stw(LDU, 0xce) + stw(LDX, 0x8e) + stw(LDY, 0x8e) + lea(LEAS, 0x12) + lea(LEAU, 0x13) + lea(LEAX, 0x10) + lea(LEAY, 0x11) + stb(ORA, 0x8A) + stb(ORB, 0xcA) + stb(SBCA, 0x82) + stb(SBCB, 0xc2) + stm(STA, 0x87) + stm(STB, 0xc7) + stm(STD, 0xcd) + stm(STS, 0xcf) + stm(STU, 0xcf) + stm(STX, 0x8f) + stm(STY, 0x8f) + stb(SUBA, 0x80) + stb(SUBB, 0xc0) + stw(SUBD, 0x83) + + br(BRA, 0x20) + br(BRN, 0x21) + br(BHI, 0x22) + br(BLS, 0x23) + br(BCC, 0x24) + br(BCS, 0x25) + br(BNE, 0x26) + br(BEQ, 0x27) + br(BVC, 0x28) + br(BVS, 0x29) + br(BPL, 0x2a) + br(BMI, 0x2b) + br(BGE, 0x2c) + br(BLT, 0x2d) + br(BGT, 0x2e) + br(BLE, 0x2f) + + +} + diff --git a/src/main/scala/millfork/parser/M6809Parser.scala b/src/main/scala/millfork/parser/M6809Parser.scala index 2f087d47..138811a5 100644 --- a/src/main/scala/millfork/parser/M6809Parser.scala +++ b/src/main/scala/millfork/parser/M6809Parser.scala @@ -1,31 +1,175 @@ package millfork.parser -import fastparse.all +import java.util.Locale + +import fastparse.all._ import millfork.CompilationOptions -import millfork.assembly.m6809.MLine -import millfork.node.{ExecutableStatement, ParameterDeclaration, Position, Statement} +import millfork.assembly.Elidability +import millfork.assembly.m6809.{AAccumulatorIndexed, Absolute, BAccumulatorIndexed, DAccumulatorIndexed, DirectPage, Immediate, Indexed, Inherent, InherentA, InherentB, LongRelative, MAddrMode, MLine, MOpcode, NonExistent, PostIncremented, PreDecremented, RegisterSet, Relative, TwoRegisters} +import millfork.env.{ByM6809Register, ParamPassingConvention} +import millfork.node.{ExecutableStatement, Expression, ExpressionStatement, LiteralExpression, M6809AssemblyStatement, M6809Register, ParameterDeclaration, Position, Statement, VariableExpression} import millfork.output.{MemoryAlignment, NoAlignment, WithinPageAlignment} /** * @author Karol Stasiak */ -class M6809Parser(filename: String, - input: String, - currentDirectory: String, - options: CompilationOptions, - featureConstants: Map[String, Long], - useIntelSyntax: Boolean) extends MfParser[MLine](filename, input, currentDirectory, options, featureConstants) { +case class M6809Parser(filename: String, + input: String, + currentDirectory: String, + options: CompilationOptions, + featureConstants: Map[String, Long]) extends MfParser[MLine](filename, input, currentDirectory, options, featureConstants) { + + import MfParser._ + override def allowIntelHexAtomsInAssembly: Boolean = false - override def asmParamDefinition: all.P[ParameterDeclaration] = ??? + val appcSimple: P[ParamPassingConvention] = P(("a" | "b" | "d" | "x" | "y" | "u" | "s" | "dp") ~ !letterOrDigit).!.map { + case "a" => ByM6809Register(M6809Register.A) + case "b" => ByM6809Register(M6809Register.B) + case "d" => ByM6809Register(M6809Register.D) + case "x" => ByM6809Register(M6809Register.X) + case "y" => ByM6809Register(M6809Register.Y) + case x => log.fatal(s"Unknown assembly parameter passing convention: `$x`") + } + override val asmParamDefinition: P[ParameterDeclaration] = for { + p <- position() + typ <- identifier ~ SWS + appc <- appcSimple | appcComplex + } yield ParameterDeclaration(typ, appc).pos(p) def fastAlignmentForArrays: MemoryAlignment = WithinPageAlignment def fastAlignmentForFunctions: MemoryAlignment = NoAlignment - override def asmStatement: all.P[ExecutableStatement] = ??? + val asmOpcode: P[(MOpcode.Value, Option[MAddrMode])] = + (position() ~ (letter.rep ~ ("2" | "3").?).! ).map { case (p, o) => MOpcode.lookup(o, Some(p), log) } + + private def mapRegister(r: String): M6809Register.Value = r.toLowerCase(Locale.ROOT) match { + case "x" => M6809Register.X + case "y" => M6809Register.Y + case "s" => M6809Register.S + case "u" => M6809Register.U + case "a" => M6809Register.A + case "b" => M6809Register.B + case "dp" => M6809Register.DP + case "pc" => M6809Register.PC + case "cc" => M6809Register.CC + } + + val anyRegister: P[M6809Register.Value] = P(("x" | "X" | "y" | "Y" | "s" | "S" | "u" | "U" | "a" | "A" | "b" | "B" | "dp" | "DP" | "cc" | "CC").!).map(mapRegister) + + val indexRegister: P[M6809Register.Value] = P(("x" | "X" | "y" | "Y" | "s" | "S" | "u" | "U" | "pc" | "PC").!).map(mapRegister) + + val asmIndexedAddrMode: P[MAddrMode] = { + (position() ~ "," ~/ HWS ~/ "-".rep.! ~/ HWS ~/ indexRegister ~/ HWS ~/ "+".rep.!).map { + case (p, "-", r, "") => PreDecremented(r, 1, indirect = false) + case (p, "--", r, "") => PreDecremented(r, 2, indirect = false) + case (p, "", r, "+") => PostIncremented(r, 1, indirect = false) + case (p, "", r, "++") => PostIncremented(r, 2, indirect = false) + case (p, "", r, "") => Indexed(r, indirect = false) + case (p, _, _, _) => + log.error("Invalid addressing mode", Some(p)) + Absolute(indirect = false) + } + } + + val asmParameterNoIndirectOrImmediate: P[(MAddrMode, Expression)] = { + for { + expr <- (asmExpression ~/ HWS).? + am <- if (expr.isDefined) asmIndexedAddrMode.?.map(_.getOrElse(Absolute(false))) else asmIndexedAddrMode + } yield { + (expr, am) match { + case (Some(VariableExpression("d" | "D")), Indexed(base, false)) => DAccumulatorIndexed(base, indirect = false) -> LiteralExpression(0, 1) + case (Some(VariableExpression("a" | "A")), Indexed(base, false)) => AAccumulatorIndexed(base, indirect = false) -> LiteralExpression(0, 1) + case (Some(VariableExpression("b" | "B")), Indexed(base, false)) => BAccumulatorIndexed(base, indirect = false) -> LiteralExpression(0, 1) + case _ => am -> expr.getOrElse(LiteralExpression(0, 1)) + } + } + } + + // TODO: directpage addressing mode syntax + val asmParameter: P[(MAddrMode, Expression)] = { + (for { + _ <- SWS + pos <- position() + (a, e) <- + ("#" ~/ HWS ~/ asmExpression).map(Immediate -> _) | + ("<" ~/ HWS ~/ asmExpression).map(DirectPage -> _) | + ("[" ~/ AWS ~/ asmParameterNoIndirectOrImmediate ~/ AWS ~/ "]").map { case (a, e) => a.makeIndirect(pos) -> e } | + asmParameterNoIndirectOrImmediate + } yield { + a -> e + }).?.map(_.getOrElse(Inherent -> LiteralExpression(0, 1))) + } + + + val asmInstruction: P[ExecutableStatement] = { + import MOpcode._ + for { + _ <- !"}" + elid <- elidable + position <- position() + (op, addrModeOverride) <- asmOpcode + (addrMode, param) <- op match { + case TFR | EXG => + (SWS ~/ anyRegister ~/ HWS ~/ "," ~/ HWS ~/ anyRegister).map { case (a, b) => TwoRegisters(a, b) -> LiteralExpression(0, 1) } + case PULS | PULU | PSHS | PSHU => + SWS ~/ anyRegister.rep(sep = HWS ~ "," ~/ HWS).map(regs => RegisterSet.cleaned(regs.toSet) -> LiteralExpression(0, 1)) + case _ => asmParameter + } + } yield { + val effAddrMode = (addrModeOverride, addrMode) match { + case (Some(InherentA), Inherent) => InherentA + case (Some(InherentB), Inherent) => InherentA + case (Some(InherentA | InherentB), _) => + log.error("Inherent accumulator instructions cannot have parameters", Some(position)) + addrMode + case (Some(LongRelative), Absolute(false)) => LongRelative + case (Some(LongRelative), _) => + log.error("Branching instructions cannot have different addressing modes", Some(position)) + addrMode + case (None, Absolute(false)) if MOpcode.Branching(op) => Relative + case (None, _) => addrMode + } + M6809AssemblyStatement(op, effAddrMode, param, elid) + } + } + + // TODO: label and instruction in one line + val asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => M6809AssemblyStatement(MOpcode.LABEL, NonExistent, VariableExpression(l), Elidability.Elidable)) + + val asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall(false)).map(ExpressionStatement) + + val asmStatement: P[ExecutableStatement] = (position("assembly statement") ~ P(asmLabel | asmMacro | arrayContentsForAsm | asmInstruction)).map { case (p, s) => s.pos(p) } + override def validateAsmFunctionBody(p: Position, flags: Set[String], name: String, statements: Option[List[Statement]]): Unit = { - // TODO + statements match { + case Some(Nil) => log.warn("Assembly function `$name` is empty, did you mean RTS, RTI, JMP, BRA or LBRA?", Some(p)) + case Some(xs) => + if (flags("interrupt")) { + if (xs.exists { + case M6809AssemblyStatement(MOpcode.RTS, _, _, _) => true + case _ => false + }) log.warn("Assembly interrupt function `$name` contains RTS, did you mean RTI?", Some(p)) + } else { + if (xs.exists { + case M6809AssemblyStatement(MOpcode.RTI, _, _, _) => true + case _ => false + }) log.warn("Assembly non-interrupt function `$name` contains RTI, did you mean RTS?", Some(p)) + } + if (!name.startsWith("__") && !flags("macro")) { + xs.last match { + case M6809AssemblyStatement(MOpcode.RTS, _, _, _) => () // OK + case M6809AssemblyStatement(MOpcode.RTI, _, _, _) => () // OK + case M6809AssemblyStatement(MOpcode.JMP, _, _, _) => () // OK + case M6809AssemblyStatement(MOpcode.BRA, _, _, _) => () // OK + case _ => + val validReturn = if (flags("interrupt")) "RTI" else "RTS" + log.warn(s"Non-macro assembly function `$name` should end in " + validReturn, Some(p)) + } + } + case None => () + } } } diff --git a/src/main/scala/millfork/parser/MSourceLoadingQueue.scala b/src/main/scala/millfork/parser/MSourceLoadingQueue.scala new file mode 100644 index 00000000..4badbe5d --- /dev/null +++ b/src/main/scala/millfork/parser/MSourceLoadingQueue.scala @@ -0,0 +1,25 @@ +package millfork.parser + +import millfork.assembly.m6809.MLine +import millfork.assembly.z80.ZLine +import millfork.{CompilationFlag, CompilationOptions} + +/** + * @author Karol Stasiak + */ +class MSourceLoadingQueue(initialFilenames: List[String], + includePath: List[String], + options: CompilationOptions) extends AbstractSourceLoadingQueue[MLine](initialFilenames, includePath, options) { + + override def createParser(filename: String, src: String, parentDir: String, featureConstants: Map[String, Long], pragmas: Set[String]): MfParser[MLine] = { + new M6809Parser(filename, src, parentDir, options, featureConstants) + } + + override def standardModules: IndexedSeq[String] = IndexedSeq.empty + + def enqueueStandardModules(): Unit = { + // TODO + } + + override val supportedPragmas: Set[String] = Set("intel_syntax", "zilog_syntax") +} diff --git a/src/test/scala/millfork/test/BasicSymonTest.scala b/src/test/scala/millfork/test/BasicSymonTest.scala index 8c181506..7e52b418 100644 --- a/src/test/scala/millfork/test/BasicSymonTest.scala +++ b/src/test/scala/millfork/test/BasicSymonTest.scala @@ -80,13 +80,13 @@ class BasicSymonTest extends FunSuite with Matchers { output += 1 } """ - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)(src){m => + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086, Cpu.Motorola6809)(src){m => m.readByte(0xc000) should equal(src.count(_ == '+')) } } test("Byte assignment") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086, Cpu.Motorola6809)( """ | byte output @$c000 | void main () { @@ -98,7 +98,7 @@ class BasicSymonTest extends FunSuite with Matchers { } test("Preallocated variables") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086, Cpu.Motorola6809)( """ | array output [2] @$c000 | byte number = 4 @@ -114,7 +114,7 @@ class BasicSymonTest extends FunSuite with Matchers { } test("Preallocated variables 2") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086, Cpu.Motorola6809)( """ | word output @$c000 | word number = 344 @@ -127,7 +127,7 @@ class BasicSymonTest extends FunSuite with Matchers { } test("Else if") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086, Cpu.Motorola6809)( """ | byte output @$c000 | void main () { @@ -145,7 +145,7 @@ class BasicSymonTest extends FunSuite with Matchers { } test("Segment syntax") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086, Cpu.Motorola6809)( """ | segment(default)byte output @$c000 | segment(default)array x[3] @@ -174,7 +174,7 @@ class BasicSymonTest extends FunSuite with Matchers { } test("Deep alias test") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8086)( + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8086, Cpu.Motorola6809)( """ | word output @$c000 | alias a = output diff --git a/src/test/scala/millfork/test/ByteMathSuite.scala b/src/test/scala/millfork/test/ByteMathSuite.scala index 181840bc..4786de0c 100644 --- a/src/test/scala/millfork/test/ByteMathSuite.scala +++ b/src/test/scala/millfork/test/ByteMathSuite.scala @@ -41,7 +41,7 @@ class ByteMathSuite extends FunSuite with Matchers with AppendedClues { } test("Byte addition") { - EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086, Cpu.Motorola6809)( """ | byte output @$c000, a | void main () { @@ -52,7 +52,7 @@ class ByteMathSuite extends FunSuite with Matchers with AppendedClues { } test("Byte addition 2") { - EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086, Cpu.Motorola6809)( """ | byte output @$c000, a | void main () { diff --git a/src/test/scala/millfork/test/M6809AssemblySuite.scala b/src/test/scala/millfork/test/M6809AssemblySuite.scala new file mode 100644 index 00000000..973943b0 --- /dev/null +++ b/src/test/scala/millfork/test/M6809AssemblySuite.scala @@ -0,0 +1,39 @@ +package millfork.test + +import millfork.test.emu._ +import org.scalatest.{FunSuite, Matchers} + +/** + * @author Karol Stasiak + */ +class M6809AssemblySuite extends FunSuite with Matchers { + + test("Common instructions") { + // TODO: handle more + EmuUnoptimizedM6809Run( + """ + | asm void main () { + | rts + | inca + | inc 0 + | bra main.addr + | lda #3 + | lda ,x + | lda 1,x + | pshs a,b + | puls a,b + | tfr a,b + | swi2 + | swi3 + | ldx [d,x] + | leas ,x++ + | cmpb ,--y + | ldu [4] + | ldd #3465 + | stb <55 + | rts + | } + """.stripMargin) + } + +} diff --git a/src/test/scala/millfork/test/emu/EmuBenchmarkRun.scala b/src/test/scala/millfork/test/emu/EmuBenchmarkRun.scala index 1c244584..4f02b7f5 100644 --- a/src/test/scala/millfork/test/emu/EmuBenchmarkRun.scala +++ b/src/test/scala/millfork/test/emu/EmuBenchmarkRun.scala @@ -125,6 +125,25 @@ object EmuIntel8086BenchmarkRun { } } +object EmuMotorola6809BenchmarkRun { + def apply(source: String)(verifier: MemoryBank => Unit): Unit = { + val (Timings(t0, _), m0) = EmuUnoptimizedM6809Run.apply2(source) +// val (Timings(t1, _), m1) = EmuOptimizedIntel8086Run.apply2(source) +// val (Timings(t2, _), m2) = EmuOptimizedInlinedIntel8086Run.apply2(source) + println(f"Before optimization: $t0%7d") +// println(f"After optimization: $t1%7d") +// println(f"After inlining: $t2%7d") +// println(f"Gain: ${(100L * (t0 - t1) / t0.toDouble).round}%7d%%") +// println(f"Gain with inlining: ${(100L * (t0 - t2) / t0.toDouble).round}%7d%%") + println(f"Running 6809 unoptimized") + verifier(m0) +// println(f"Running 6809 optimized") +// verifier(m1) +// println(f"Running 6809 optimized inlined") +// verifier(m2) + } +} + object EmuCrossPlatformBenchmarkRun { def apply(platforms: millfork.Cpu.Value*)(source: String)(verifier: MemoryBank => Unit): Unit = { if (platforms.isEmpty) { @@ -160,5 +179,8 @@ object EmuCrossPlatformBenchmarkRun { if (Settings.enableIntel8086Tests && platforms.contains(millfork.Cpu.Intel8086)) { EmuIntel8086BenchmarkRun.apply(source)(verifier) } + if (Settings.enableMotorola6809Tests && platforms.contains(millfork.Cpu.Motorola6809)) { + EmuMotorola6809BenchmarkRun.apply(source)(verifier) + } } } diff --git a/src/test/scala/millfork/test/emu/EmuM6809Run.scala b/src/test/scala/millfork/test/emu/EmuM6809Run.scala index b7a97f9f..add2d61f 100644 --- a/src/test/scala/millfork/test/emu/EmuM6809Run.scala +++ b/src/test/scala/millfork/test/emu/EmuM6809Run.scala @@ -10,10 +10,12 @@ import millfork.assembly.m6809.MLine import millfork.compiler.m6809.M6809Compiler import millfork.compiler.{CompilationContext, LabelGenerator} import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, NormalFunction} +import millfork.error.ConsoleLogger import millfork.node.opt.NodeOptimization import millfork.node.{Program, StandardCallGraph} import millfork.output.{M6809Assembler, MemoryBank} -import millfork.parser.{PreprocessingResult, Preprocessor, Z80Parser} +import millfork.parser.{M6809Parser, MosParser, PreprocessingResult, Preprocessor, Z80Parser} +import org.roug.osnine.{BusStraight, MC6809} import org.scalatest.Matchers import scala.collection.JavaConverters._ @@ -55,59 +57,71 @@ object EmuM6809Run { } class EmuM6809Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], assemblyOptimizations: List[AssemblyOptimization[MLine]]) extends Matchers { - def inline: Boolean = false - - def optimizeForSize: Boolean = false - - private val TooManyCycles: Long = 1500000 def apply(source: String): MemoryBank = { apply2(source)._2 } + def emitIllegals = false + + def inline = false + + def blastProcessing = false + + def optimizeForSize = false + + private val TooManyCycles: Long = 1000000 + + private def formatBool(b: Int, c: Char): Char = if (b != 0) c else '-' + + def apply2(source: String): (Timings, MemoryBank) = { Console.out.flush() Console.err.flush() val log = TestErrorReporting.log println(source) val platform = EmuPlatform.get(cpu) - val extraFlags = Map( - CompilationFlag.DangerousOptimizations -> true, + val options = CompilationOptions(platform, Map( CompilationFlag.EnableInternalTestSyntax -> true, + CompilationFlag.DecimalMode -> true, + CompilationFlag.LenientTextEncoding -> true, + CompilationFlag.EmitIllegals -> this.emitIllegals, CompilationFlag.InlineFunctions -> this.inline, CompilationFlag.OptimizeStdlib -> this.inline, - CompilationFlag.OptimizeForSize -> this.optimizeForSize, + CompilationFlag.InterproceduralOptimization -> true, + CompilationFlag.CompactReturnDispatchParams -> true, CompilationFlag.SubroutineExtraction -> optimizeForSize, - CompilationFlag.EmitIllegals -> false, - CompilationFlag.LenientTextEncoding -> true) - val options = CompilationOptions(platform, millfork.Cpu.defaultFlags(cpu).map(_ -> true).toMap ++ extraFlags, None, 0, Map(), JobContext(log, new LabelGenerator)) - println(cpu) - println(options.flags.filter(_._2).keys.toSeq.sorted) + CompilationFlag.OptimizeForSize -> optimizeForSize, + CompilationFlag.OptimizeForSpeed -> blastProcessing, + CompilationFlag.OptimizeForSonicSpeed -> blastProcessing + // CompilationFlag.CheckIndexOutOfBounds -> true, + ), None, 0, Map(), JobContext(log, new LabelGenerator)) log.hasErrors = false log.verbosity = 999 var effectiveSource = source if (!source.contains("_panic")) effectiveSource += "\n void _panic(){while(true){}}" +// if (source.contains("call(")) effectiveSource += "\nnoinline asm word call(word d) {\n\n}\n" log.setSource(Some(effectiveSource.linesIterator.toIndexedSeq)) - val PreprocessingResult(preprocessedSource, features, pragmas) = Preprocessor.preprocessForTest(options, effectiveSource) - // tests use Intel syntax only when forced to: - val parserF = Z80Parser("", preprocessedSource, "", options, features, pragmas.contains("intel_syntax")) + val PreprocessingResult(preprocessedSource, features, _) = Preprocessor.preprocessForTest(options, effectiveSource) + val parserF = M6809Parser("", preprocessedSource, "", options, features) parserF.toAst match { case Success(unoptimized, _) => log.assertNoErrors("Parse failed") - // prepare val withLibraries = { var tmp = unoptimized -// tmp += EmuM6809Run.cachedMath(cpu) // TODO: add this only after you implement maths - if (source.contains("import stdio")) { - tmp += EmuM6809Run.cachedStdio(cpu) - } + if(source.contains("import stdio")) + tmp += EmuRun.cachedStdio + if(!options.flag(CompilationFlag.DecimalMode) && (source.contains("+'") || source.contains("-'") || source.contains("<<'") || source.contains("*'"))) + tmp += EmuRun.cachedBcd tmp } val program = nodeOptimizations.foldLeft(withLibraries.applyImportantAliases)((p, opt) => p.applyNodeOptimization(opt, options)) + program.checkSegments(log, platform.codeAllocators.keySet) + log.assertNoErrors("Failed") val callGraph = new StandardCallGraph(program, log) - val env = new Environment(None, "", CpuFamily.I80, options) + val env = new Environment(None, "", CpuFamily.M6809, options) env.collectDeclarations(program, options) val hasOptimizations = assemblyOptimizations.nonEmpty @@ -127,12 +141,12 @@ class EmuM6809Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizat // compile - val env2 = new Environment(None, "", CpuFamily.I80, options) + val env2 = new Environment(None, "", CpuFamily.M6502, options) env2.collectDeclarations(program, options) val assembler = new M6809Assembler(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) + output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println) println(";;; ---------------------------") assembler.labelMap.foreach { case (l, (_, addr)) => println(f"$l%-15s $$$addr%04x") } @@ -150,17 +164,12 @@ class EmuM6809Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizat } val memoryBank = assembler.mem.banks("default") - (0x1f0 until 0x200).foreach(i => memoryBank.readable(i) = true) - (0xff00 to 0xffff).foreach{i => - memoryBank.readable(i) = true - memoryBank.writeable(i) = true - } - - (0x200 until 0x2000).takeWhile(memoryBank.occupied(_)).map(memoryBank.output).grouped(16).map(_.map(i => f"$i%02x").mkString(" ")).foreach(log.debug(_)) - val timings = platform.cpu match { - case _ => - Timings(-1, -1) -> memoryBank + if (source.contains("return [")) { + for (_ <- 0 until 10; i <- 0xfffe.to(0, -1)) { + if (memoryBank.readable(i)) memoryBank.readable(i + 1) = true + } } + val timings = run(log, memoryBank, platform.codeAllocators("default").startAt) log.clearErrors() timings case f: Failure[_, _] => @@ -168,8 +177,37 @@ class EmuM6809Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizat println(f.extra.toString) println(f.lastParser.toString) log.error("Syntax error", Some(parserF.lastPosition)) - fail("Parsing error") + fail("syntax error") } } + private def debugState(q: MC6809): Unit = + println(f"D=${q.d.get()}%04X X=${q.x.get()}%04X Y=${q.y.get()}%04X U=${q.u.get()}%04X S=${q.s.get()}%04X PC=${q.pc.get()}%04X " + + formatBool(q.cc.bit_e, 'E') +formatBool(q.cc.bit_f, 'F') + formatBool(q.cc.bit_h, 'H') + + formatBool(q.cc.bit_i, 'I') + formatBool(q.cc.bit_n, 'N') + formatBool(q.cc.bit_z, 'Z') + + formatBool(q.cc.bit_v, 'V') + formatBool(q.cc.bit_c, 'C')) + + def run(log: ConsoleLogger, memoryBank: MemoryBank, startAt: Int): (Timings, MemoryBank) = { + (0x200 until 0x2000).takeWhile(memoryBank.occupied(_)).map(memoryBank.output).grouped(16).map(_.map(i => f"$i%02x").mkString(" ")).foreach(log.debug(_)) + val bus = new BusStraight() + bus.addMemorySegment(new M6809Memory(memoryBank, startAt)) + val cpu = new MC6809(bus) + bus.clearNMI() + cpu.pc.set(startAt) + cpu.s.set(0xfff0) + +// val method = classOf[MC6809].getDeclaredMethod("setTraceInstructions", classOf[Boolean]) +// method.setAccessible(true) +// method.invoke(cpu, true.asInstanceOf[AnyRef]) + +// debugState(cpu) + while (cpu.pc.get() > 2) { + cpu.execute() +// debugState(cpu) + bus.getCycleCounter should be < TooManyCycles + } + Timings(bus.getCycleCounter, bus.getCycleCounter) -> memoryBank + } } + + diff --git a/src/test/scala/millfork/test/emu/EmuUnoptimizedCrossPlatformRun.scala b/src/test/scala/millfork/test/emu/EmuUnoptimizedCrossPlatformRun.scala index 4c6bd4b7..e204dd16 100644 --- a/src/test/scala/millfork/test/emu/EmuUnoptimizedCrossPlatformRun.scala +++ b/src/test/scala/millfork/test/emu/EmuUnoptimizedCrossPlatformRun.scala @@ -46,7 +46,7 @@ object EmuUnoptimizedCrossPlatformRun { } if (Settings.enableMotorola6809Tests && platforms.contains(Cpu.Motorola6809)) { println(f"Running 6809") - verifier(mx) + verifier(mo) } } } diff --git a/src/test/scala/millfork/test/emu/M6809Memory.scala b/src/test/scala/millfork/test/emu/M6809Memory.scala new file mode 100644 index 00000000..6f48b62c --- /dev/null +++ b/src/test/scala/millfork/test/emu/M6809Memory.scala @@ -0,0 +1,36 @@ +package millfork.test.emu + +import millfork.output.MemoryBank +import org.roug.osnine.{Bus8Motorola, MemorySegment} + +/** + * @author Karol Stasiak + */ +class M6809Memory(memoryBank: MemoryBank, resetVector: Int) extends MemorySegment(0, 0xffff) { + + for(i <- 0xc000 to 0xffff) { + memoryBank.readable(i) = true + memoryBank.writeable(i) = true + } + + for(i <- 0 to 9) { + memoryBank.readable(i) = true + memoryBank.writeable(i) = true + } + memoryBank.output(0xfffe) = resetVector.>>(8).toByte + memoryBank.output(0xffff) = resetVector.toByte + + override def load(addr: Int): Int = { + if (!memoryBank.readable(addr)) { + ??? + } + memoryBank.readByte(addr) + } + + override def store(addr: Int, `val`: Int): Unit = { + if (!memoryBank.writeable(addr)) { + ??? + } + memoryBank.output(addr) = `val`.toByte + } +} diff --git a/src/test/scala/millfork/test/emu/Settings.scala b/src/test/scala/millfork/test/emu/Settings.scala index d5c923c1..d77d89f6 100644 --- a/src/test/scala/millfork/test/emu/Settings.scala +++ b/src/test/scala/millfork/test/emu/Settings.scala @@ -52,7 +52,7 @@ object Settings { * Should the Motorola 6809 tests be enabled? * Motorola 6809 emulation is not yet implemented, so keep this false for the time being. */ - val enableMotorola6809Tests: Boolean = false + val enableMotorola6809Tests: Boolean = true // the following are the core platforms and they are all tested by default