1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-25 19:29:49 +00:00

6809: First bits of the code generator

This commit is contained in:
Karol Stasiak 2019-07-29 00:55:24 +02:00
parent b2afddf05b
commit cb0718b433
27 changed files with 1538 additions and 129 deletions

View File

@ -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`

View File

@ -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"

View File

@ -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)
}

View File

@ -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(

View File

@ -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) {
}

View File

@ -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 = ???
}

View File

@ -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"
}

View File

@ -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
}
}
}

View 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] = {
???
}
}

View File

@ -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
}
}
}

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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)
}

View File

@ -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 => ()
}
}
}

View 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")
}

View File

@ -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

View File

@ -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 () {

View 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)
}
}

View File

@ -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)
}
}
}

View File

@ -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
}
}

View File

@ -46,7 +46,7 @@ object EmuUnoptimizedCrossPlatformRun {
}
if (Settings.enableMotorola6809Tests && platforms.contains(Cpu.Motorola6809)) {
println(f"Running 6809")
verifier(mx)
verifier(mo)
}
}
}

View 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
}
}

View File

@ -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