mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-17 01:37:44 +00:00
6809: First bits of the code generator
This commit is contained in:
parent
b2afddf05b
commit
cb0718b433
@ -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`
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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) {
|
||||
|
||||
}
|
||||
|
@ -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 = ???
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
181
src/main/scala/millfork/compiler/m6809/M6809Buitins.scala
Normal file
181
src/main/scala/millfork/compiler/m6809/M6809Buitins.scala
Normal file
@ -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] = {
|
||||
???
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
33
src/main/scala/millfork/env/Environment.scala
vendored
33
src/main/scala/millfork/env/Environment.scala
vendored
@ -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
|
||||
}
|
||||
|
6
src/main/scala/millfork/env/Thing.scala
vendored
6
src/main/scala/millfork/env/Thing.scala
vendored
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
||||
}
|
||||
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)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -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 => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
25
src/main/scala/millfork/parser/MSourceLoadingQueue.scala
Normal file
25
src/main/scala/millfork/parser/MSourceLoadingQueue.scala
Normal file
@ -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")
|
||||
}
|
@ -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
|
||||
|
@ -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 () {
|
||||
|
39
src/test/scala/millfork/test/M6809AssemblySuite.scala
Normal file
39
src/test/scala/millfork/test/M6809AssemblySuite.scala
Normal file
@ -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)
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -46,7 +46,7 @@ object EmuUnoptimizedCrossPlatformRun {
|
||||
}
|
||||
if (Settings.enableMotorola6809Tests && platforms.contains(Cpu.Motorola6809)) {
|
||||
println(f"Running 6809")
|
||||
verifier(mx)
|
||||
verifier(mo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
36
src/test/scala/millfork/test/emu/M6809Memory.scala
Normal file
36
src/test/scala/millfork/test/emu/M6809Memory.scala
Normal file
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user