mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-23 08:29:35 +00:00
Placeholder for future expansion
This commit is contained in:
parent
32bb0d4453
commit
767f0da703
@ -174,6 +174,7 @@ case class CompilationOptions(platform: Platform,
|
||||
log.error("Either Sharp LR35902 or Intel 8080 opcodes have to be enabled")
|
||||
}
|
||||
case CpuFamily.I86 =>
|
||||
case CpuFamily.M6809 =>
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,6 +266,7 @@ object CpuFamily extends Enumeration {
|
||||
case Mos | StrictMos | Ricoh | StrictRicoh | Cmos | HuC6280 | CE02 | Sixteen => M6502
|
||||
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | EZ80 => I80
|
||||
case Intel8086 | Intel80186 => I86
|
||||
case Cpu.Motorola6809 => M6809
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -339,6 +341,10 @@ object Cpu extends Enumeration {
|
||||
* The Intel 80186 or 80188 processor
|
||||
*/
|
||||
val Intel80186: Cpu.Value = Value
|
||||
/**
|
||||
* The Motorola 6809 processor
|
||||
*/
|
||||
val Motorola6809: Cpu.Value = Value
|
||||
|
||||
/**
|
||||
* Processors that can run code for WDC 65C02
|
||||
@ -367,6 +373,8 @@ object Cpu extends Enumeration {
|
||||
|
||||
private val i80AlwaysDefaultFlags = alwaysDefaultFlags
|
||||
|
||||
private val m6809AlwaysDefaultFlags = alwaysDefaultFlags
|
||||
|
||||
def defaultFlags(x: Cpu.Value): Set[CompilationFlag.Value] = x match {
|
||||
case StrictMos =>
|
||||
mosAlwaysDefaultFlags ++ Set(DecimalMode, PreventJmpIndirectBug)
|
||||
@ -396,6 +404,8 @@ object Cpu extends Enumeration {
|
||||
i80AlwaysDefaultFlags ++ Set(EmitExtended80Opcodes, EmitSharpOpcodes)
|
||||
case Intel8086 | Intel80186 =>
|
||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, UseIxForStack, EmitIntel8085Opcodes, EmitIllegals)
|
||||
case Motorola6809 =>
|
||||
m6809AlwaysDefaultFlags
|
||||
}
|
||||
|
||||
def fromString(name: String)(implicit log: Logger): Cpu.Value = name match {
|
||||
@ -453,6 +463,8 @@ object Cpu extends Enumeration {
|
||||
case "intel80286" => Intel80186
|
||||
case "i80286" => Intel80186
|
||||
case "80286" => Intel80186
|
||||
// disabled for now
|
||||
// case "6809" => Motorola6809
|
||||
case _ => log.fatal("Unknown CPU achitecture: " + name)
|
||||
}
|
||||
|
||||
@ -460,6 +472,7 @@ object Cpu extends Enumeration {
|
||||
CpuFamily.forType(cpu) match {
|
||||
case CpuFamily.M6502 => 2
|
||||
case CpuFamily.I80 | CpuFamily.I86 => 4
|
||||
case CpuFamily.M6809 => 2
|
||||
case _ => ???
|
||||
}
|
||||
}
|
||||
|
36
src/main/scala/millfork/assembly/m6809/MAddrMode.scala
Normal file
36
src/main/scala/millfork/assembly/m6809/MAddrMode.scala
Normal file
@ -0,0 +1,36 @@
|
||||
package millfork.assembly.m6809
|
||||
|
||||
import millfork.node.M6809Register
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
sealed trait MAddrMode
|
||||
|
||||
case object Inherent extends MAddrMode
|
||||
|
||||
case object InherentA extends MAddrMode
|
||||
|
||||
case object InherentB extends MAddrMode
|
||||
|
||||
case class TwoRegisters(source: M6809Register.Value, target: M6809Register.Value) extends MAddrMode
|
||||
|
||||
case class RegisterSet(registers: Set[M6809Register.Value]) extends MAddrMode
|
||||
|
||||
case class Absolute(indirect: Boolean) extends MAddrMode
|
||||
|
||||
case class Indexed(base: M6809Register.Value, indirect: Boolean) extends MAddrMode
|
||||
|
||||
case class AccumulatorIndexed(base: M6809Register.Value, indirect: Boolean) extends MAddrMode
|
||||
|
||||
case class PostIncremented(base: M6809Register.Value, amount: Int, indirect: Boolean) extends MAddrMode
|
||||
|
||||
case class PreDecremented(base: M6809Register.Value, amount: Int, indirect: Boolean) extends MAddrMode
|
||||
|
||||
case object Relative extends MAddrMode
|
||||
|
||||
case object Immediate extends MAddrMode
|
||||
|
||||
case object NonExistent extends MAddrMode
|
||||
|
||||
case object RawByte extends MAddrMode
|
76
src/main/scala/millfork/assembly/m6809/MLine.scala
Normal file
76
src/main/scala/millfork/assembly/m6809/MLine.scala
Normal file
@ -0,0 +1,76 @@
|
||||
package millfork.assembly.m6809
|
||||
|
||||
import millfork.assembly.{AbstractCode, Elidability, SourceLine}
|
||||
import millfork.env.{Constant, Label}
|
||||
import millfork.node.Position
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
object MLine0 {
|
||||
|
||||
@inline
|
||||
def unapply(a: MLine): Some[(MOpcode.Value, MAddrMode, Constant)] = Some(a.opcode, a.addrMode, a.parameter)
|
||||
}
|
||||
|
||||
object MLine {
|
||||
|
||||
import MOpcode._
|
||||
|
||||
def label(label: String): MLine = MLine.label(Label(label))
|
||||
|
||||
def label(label: Label): MLine = MLine(LABEL, NonExistent, label.toAddress)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant, elidability: Elidability.Value = Elidability.Elidable, source: Option[SourceLine] = None) extends AbstractCode {
|
||||
|
||||
def pos(s: Option[SourceLine]): MLine = if (s.isEmpty || s == source) this else this.copy(source = s)
|
||||
|
||||
def pos(s1: Option[SourceLine], s2: Option[SourceLine]): MLine = pos(Seq(s1, s2))
|
||||
|
||||
def position(s: Option[Position]): MLine = pos(SourceLine.of(s))
|
||||
|
||||
def positionIfEmpty(s: Option[Position]): MLine = if (s.isEmpty || source.isDefined) this else pos(SourceLine.of(s))
|
||||
|
||||
def pos(s: Seq[Option[SourceLine]]): MLine = pos(SourceLine.merge(s))
|
||||
|
||||
def mergePos(s: Seq[Option[SourceLine]]): MLine = if (s.isEmpty) this else pos(SourceLine.merge(this.source, s))
|
||||
|
||||
def refersTo(name: String): Boolean = parameter.refersTo(name)
|
||||
|
||||
override def sizeInBytes: Int = 1 // TODO
|
||||
|
||||
override def isPrintable: Boolean = true // TODO
|
||||
|
||||
override def toString: String = {
|
||||
if (opcode == MOpcode.LABEL) return parameter + ":"
|
||||
if (opcode == MOpcode.BYTE) return s" FCB $parameter"
|
||||
val suffix = addrMode match {
|
||||
case NonExistent => ""
|
||||
case Inherent => ""
|
||||
case InherentA => "A"
|
||||
case InherentB => "B"
|
||||
case Relative => s" $parameter"
|
||||
case Absolute(false) => s" $parameter"
|
||||
case Indexed(base, false) => s" $parameter,$base"
|
||||
case Indexed(base, true) => s" [$parameter,$base]"
|
||||
case PreDecremented(base, 1, false) => s" ,-$base"
|
||||
case PreDecremented(base, 2, false) => s" ,--$base"
|
||||
case PreDecremented(base, 1, true) => s" [-$base]"
|
||||
case PreDecremented(base, 2, true) => s" [--$base]"
|
||||
case PostIncremented(base, 1, false) => s" ,$base+"
|
||||
case PostIncremented(base, 2, false) => s" ,$base++"
|
||||
case PostIncremented(base, 1, true) => s" [,$base+]"
|
||||
case PostIncremented(base, 2, true) => s" [,$base++]"
|
||||
}
|
||||
s" $opcode$suffix"
|
||||
}
|
||||
}
|
||||
|
35
src/main/scala/millfork/assembly/m6809/MOpcode.scala
Normal file
35
src/main/scala/millfork/assembly/m6809/MOpcode.scala
Normal file
@ -0,0 +1,35 @@
|
||||
package millfork.assembly.m6809
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
object MFlag extends Enumeration {
|
||||
val Z, C, H, N, V = Value
|
||||
}
|
||||
|
||||
object MOpcode extends Enumeration {
|
||||
val ABX, ADCA, ADCB, ADDA, ADDB, ADDD, ANDA, ANDB, ANDCC, ASL, ASR,
|
||||
BITA, BITB,
|
||||
BRA, BRN, BHI, BLS, BCC, BCS, BNE, BEQ, BVC, BVS, BPL, BMI, BGE, BLT, BGT, BLE,
|
||||
CLR, CMPA, CMPB, CMPD, CMPS, CMPU, CMPX, CMPY, COMA, COMB, COM, CWAI,
|
||||
DAA, DEC,
|
||||
EORA, EORB, EXG,
|
||||
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,
|
||||
PSHS, PSHU, PULS, PULU,
|
||||
ROL, ROR, RTI, RTS,
|
||||
SBCA, SBCB, SEX, STA, STB, STD, STS, STU, STX, STY, SUBA, SUBB, SUBD, SWI, SWI2, SWI3, SYNC,
|
||||
TFR, TST,
|
||||
DISCARD_D, DISCARD_X, DISCARD_Y, DISCARD_CC, CHANGED_MEM, BYTE, LABEL = Value
|
||||
|
||||
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
|
||||
}
|
19
src/main/scala/millfork/compiler/m6809/M6809Compiler.scala
Normal file
19
src/main/scala/millfork/compiler/m6809/M6809Compiler.scala
Normal file
@ -0,0 +1,19 @@
|
||||
package millfork.compiler.m6809
|
||||
|
||||
import millfork.assembly.Elidability
|
||||
import millfork.assembly.m6809.{Inherent, MLine, MOpcode, NonExistent}
|
||||
import millfork.compiler.{AbstractCompiler, CompilationContext}
|
||||
import millfork.env.{Constant, Label, MemoryAddressConstant}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object M6809Compiler extends AbstractCompiler[MLine] {
|
||||
override def compile(ctx: CompilationContext): List[MLine] = {
|
||||
ctx.env.nameCheck(ctx.function.code)
|
||||
val label = MLine.label(Label(ctx.function.name)).copy(elidability = Elidability.Fixed)
|
||||
val chunk = packHalves(M6809StatementCompiler.compile(ctx, new M6809StatementPreprocessor(ctx, ctx.function.code)()))
|
||||
// TODO
|
||||
label :: chunk
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
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.MOpcode._
|
||||
import millfork.env.{Label, ThingInMemory}
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object M6809StatementCompiler extends AbstractStatementCompiler[MLine] {
|
||||
def compile(ctx: CompilationContext, statement: ExecutableStatement): (List[MLine], List[MLine]) = {
|
||||
val code: (List[MLine], List[MLine]) = statement match {
|
||||
case ReturnStatement(None) =>
|
||||
// TODO: clean stack
|
||||
// TODO: RTI
|
||||
List(MLine.inherent(RTS)) -> Nil
|
||||
case M6809AssemblyStatement(opcode, addrMode, expression, elidability) =>
|
||||
ctx.env.evalForAsm(expression) match {
|
||||
case Some(e) => List(MLine(opcode, addrMode, e, elidability)) -> Nil
|
||||
case None =>
|
||||
println(statement)
|
||||
???
|
||||
}
|
||||
case WhileStatement(VariableExpression("true"),List(),List(),_) =>
|
||||
Nil -> Nil // TODO
|
||||
case _ =>
|
||||
println(statement)
|
||||
???
|
||||
}
|
||||
code._1.map(_.positionIfEmpty(statement.position)) -> code._2.map(_.positionIfEmpty(statement.position))
|
||||
}
|
||||
|
||||
override def labelChunk(labelName: String): List[MLine] = ???
|
||||
|
||||
override def jmpChunk(label: Label): List[MLine] = ???
|
||||
|
||||
override def branchChunk(opcode: BranchingOpcodeMapping, labelName: String): List[MLine] = ???
|
||||
|
||||
override def compileExpressionForBranching(ctx: CompilationContext, expr: Expression, branching: BranchSpec): List[MLine] = ???
|
||||
|
||||
override def replaceLabel(ctx: CompilationContext, line: MLine, from: String, to: String): MLine = ???
|
||||
|
||||
override def returnAssemblyStatement: ExecutableStatement = ???
|
||||
|
||||
override def callChunk(label: ThingInMemory): List[MLine] = ???
|
||||
|
||||
override def areBlocksLarge(blocks: List[MLine]*): Boolean = ???
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package millfork.compiler.m6809
|
||||
|
||||
import millfork.assembly.m6809.MLine
|
||||
import millfork.compiler.{AbstractStatementPreprocessor, CompilationContext}
|
||||
import millfork.node.{ExecutableStatement, ForStatement}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
class M6809StatementPreprocessor(ctx: CompilationContext, statements: List[ExecutableStatement])
|
||||
extends AbstractStatementPreprocessor(ctx, statements) {
|
||||
override def maybeOptimizeForStatement(f: ForStatement): Option[(ExecutableStatement, VV)] = None
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package millfork.env
|
||||
|
||||
import millfork.assembly.m6809.{MOpcode, NonExistent}
|
||||
import millfork.assembly.{BranchingOpcodeMapping, Elidability}
|
||||
import millfork.{env, _}
|
||||
import millfork.assembly.mos.{AddrMode, Opcode}
|
||||
@ -1806,6 +1807,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
case CpuFamily.M6502 => List(MosAssemblyStatement(Opcode.CHANGED_MEM, AddrMode.DoesNotExist, LiteralExpression(0, 1), Elidability.Fixed))
|
||||
case CpuFamily.I80 => List(Z80AssemblyStatement(ZOpcode.CHANGED_MEM, NoRegisters, None, LiteralExpression(0, 1), Elidability.Fixed))
|
||||
case CpuFamily.I86 => List(Z80AssemblyStatement(ZOpcode.CHANGED_MEM, NoRegisters, None, LiteralExpression(0, 1), Elidability.Fixed))
|
||||
case CpuFamily.M6809 => List(M6809AssemblyStatement(MOpcode.CHANGED_MEM, NonExistent, LiteralExpression(0, 1), Elidability.Fixed))
|
||||
case _ => ???
|
||||
})
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package millfork.node
|
||||
|
||||
import millfork.assembly.Elidability
|
||||
import millfork.assembly.m6809.{MAddrMode, MOpcode}
|
||||
import millfork.assembly.mos.{AddrMode, Opcode}
|
||||
import millfork.assembly.z80.{ZOpcode, ZRegisters}
|
||||
import millfork.env.{Constant, ParamPassingConvention, Type}
|
||||
@ -197,6 +198,15 @@ object ZRegister extends Enumeration {
|
||||
val main7Registers: Set[ZRegister.Value] = Set[ZRegister.Value](ZRegister.A, ZRegister.B, ZRegister.C, ZRegister.D, ZRegister.D, ZRegister.E, ZRegister.H, ZRegister.L)
|
||||
}
|
||||
|
||||
object M6809Register extends Enumeration {
|
||||
val A, B, D, DP, X, Y, U, S, PC, CC = Value
|
||||
|
||||
def registerSize(reg: Value): Int = reg match {
|
||||
case D | X | Y | U | S | PC => 2
|
||||
case A | B | DP | CC => 1
|
||||
}
|
||||
}
|
||||
|
||||
//case class Indexing(child: Expression, register: Register.Value) extends Expression
|
||||
|
||||
case class VariableExpression(name: String) extends LhsExpression {
|
||||
@ -501,6 +511,10 @@ case class Z80AssemblyStatement(opcode: ZOpcode.Value, registers: ZRegisters, of
|
||||
override def getAllExpressions: List[Expression] = List(expression)
|
||||
}
|
||||
|
||||
case class M6809AssemblyStatement(opcode: MOpcode.Value, addrMode: MAddrMode, expression: Expression, elidability: Elidability.Value) extends ExecutableStatement {
|
||||
override def getAllExpressions: List[Expression] = List(expression)
|
||||
}
|
||||
|
||||
case class IfStatement(condition: Expression, thenBranch: List[ExecutableStatement], elseBranch: List[ExecutableStatement]) extends CompoundStatement {
|
||||
override def getAllExpressions: List[Expression] = condition :: (thenBranch ++ elseBranch).flatMap(_.getAllExpressions)
|
||||
|
||||
|
103
src/main/scala/millfork/output/M6809Assembler.scala
Normal file
103
src/main/scala/millfork/output/M6809Assembler.scala
Normal file
@ -0,0 +1,103 @@
|
||||
package millfork.output
|
||||
|
||||
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 scala.collection.mutable
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
class M6809Assembler(program: Program,
|
||||
rootEnv: Environment,
|
||||
platform: Platform) extends AbstractAssembler[MLine](program, rootEnv, platform, M6809InliningCalculator, M6809Compiler) {
|
||||
override def bytePseudoopcode: String = "FCB"
|
||||
|
||||
override def deduplicate(options: CompilationOptions, compiledFunctions: mutable.Map[String, CompiledFunction[MLine]]): Unit = ()
|
||||
|
||||
override def injectLabels(labelMap: Map[String, (Int, Int)], code: List[MLine]): List[MLine] = code
|
||||
|
||||
override def quickSimplify(code: List[MLine]): List[MLine] = code
|
||||
|
||||
override def gatherNiceFunctionProperties(niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], functionName: String, code: List[MLine]): Unit = ()
|
||||
|
||||
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 = {
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
instr match {
|
||||
case MLine0(BYTE, RawByte, c) =>
|
||||
writeByte(bank, index, c)
|
||||
index + 1
|
||||
case MLine0(BYTE, _, _) => log.fatal("BYTE opcode failure")
|
||||
case MLine0(_, RawByte, _) => log.fatal("BYTE opcode failure")
|
||||
case MLine0(LABEL, NonExistent, MemoryAddressConstant(Label(labelName))) =>
|
||||
val bank0 = mem.banks(bank)
|
||||
labelMap(labelName) = bank0.index -> index
|
||||
index
|
||||
case MLine0(op, NonExistent, _) if MOpcode.NoopDiscard(op) =>
|
||||
index
|
||||
case MLine0(op, Inherent, _) if M6809Assembler.inherent.contains(op) =>
|
||||
writeByte(bank, index, M6809Assembler.inherent(op))
|
||||
index + 1
|
||||
case MLine0(op, InherentA, _) if M6809Assembler.inherentA.contains(op) =>
|
||||
writeByte(bank, index, M6809Assembler.inherentA(op))
|
||||
index + 1
|
||||
case MLine0(op, InherentB, _) if M6809Assembler.inherentB.contains(op) =>
|
||||
writeByte(bank, index, M6809Assembler.inherentB(op))
|
||||
index + 1
|
||||
case MLine0(op, Immediate, param) if M6809Assembler.immediate.contains(op) =>
|
||||
writeByte(bank, index, M6809Assembler.immediate(op))
|
||||
writeByte(bank, index + 1, param)
|
||||
index + 1
|
||||
case _ =>
|
||||
// TODO
|
||||
throw new IllegalArgumentException("Not supported: " + instr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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]()
|
||||
|
||||
private def inh(op: MOpcode.Value, value: Int): Unit = inherent(op) = value.toByte
|
||||
|
||||
private def inab(op: MOpcode.Value, value: Int): Unit = {
|
||||
inherentA(op) = value.toByte
|
||||
inherentB(op) = value.+(0x10).toByte
|
||||
}
|
||||
|
||||
private def imm(op: MOpcode.Value, value: Int): Unit = immediate(op) = value.toByte
|
||||
|
||||
import MOpcode._
|
||||
|
||||
inh(ABX, 0x3a)
|
||||
inh(DAA, 0x19)
|
||||
inh(MUL, 0x3d)
|
||||
inh(NOP, 0x12)
|
||||
inh(RTI, 0x3B)
|
||||
inh(RTS, 0x39)
|
||||
inh(SEX, 0x1d)
|
||||
inh(SWI, 0x3f)
|
||||
inh(SYNC, 0x13)
|
||||
|
||||
inab(ASL, 0x48)
|
||||
inab(ASR, 0x47)
|
||||
inab(CLR, 0x4f)
|
||||
inab(COM, 0x43)
|
||||
inab(DEC, 0x4a)
|
||||
inab(INC, 0x48)
|
||||
inab(LSR, 0x44)
|
||||
inab(NEG, 0x40)
|
||||
inab(ROL, 0x49)
|
||||
inab(ROR, 0x46)
|
||||
inab(TST, 0x4d)
|
||||
|
||||
}
|
16
src/main/scala/millfork/output/M6809InliningCalculator.scala
Normal file
16
src/main/scala/millfork/output/M6809InliningCalculator.scala
Normal file
@ -0,0 +1,16 @@
|
||||
package millfork.output
|
||||
|
||||
import millfork.JobContext
|
||||
import millfork.assembly.m6809.MLine
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object M6809InliningCalculator extends AbstractInliningCalculator[MLine] {
|
||||
override def codeForInlining(fname: String, functionsThatCanBeCalledFromInlinedFunctions: Set[String], code: List[MLine]): Option[List[MLine]] = None
|
||||
|
||||
override def inline(code: List[MLine], inlinedFunctions: Map[String, List[MLine]], jobContext: JobContext): List[MLine] = {
|
||||
if (inlinedFunctions.isEmpty) code
|
||||
else ???
|
||||
}
|
||||
}
|
31
src/main/scala/millfork/parser/M6809Parser.scala
Normal file
31
src/main/scala/millfork/parser/M6809Parser.scala
Normal file
@ -0,0 +1,31 @@
|
||||
package millfork.parser
|
||||
|
||||
import fastparse.all
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.m6809.MLine
|
||||
import millfork.node.{ExecutableStatement, ParameterDeclaration, Position, Statement}
|
||||
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) {
|
||||
override def allowIntelHexAtomsInAssembly: Boolean = false
|
||||
|
||||
override def asmParamDefinition: all.P[ParameterDeclaration] = ???
|
||||
|
||||
def fastAlignmentForArrays: MemoryAlignment = WithinPageAlignment
|
||||
|
||||
def fastAlignmentForFunctions: MemoryAlignment = NoAlignment
|
||||
|
||||
override def asmStatement: all.P[ExecutableStatement] = ???
|
||||
|
||||
override def validateAsmFunctionBody(p: Position, flags: Set[String], name: String, statements: Option[List[Statement]]): Unit = {
|
||||
// TODO
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ import org.scalatest.{FunSuite, Matchers}
|
||||
*/
|
||||
class BasicSymonTest extends FunSuite with Matchers {
|
||||
test("Empty test") {
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)(
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086, Cpu.Motorola6809)(
|
||||
"""
|
||||
| void main () {
|
||||
|
|
||||
|
175
src/test/scala/millfork/test/emu/EmuM6809Run.scala
Normal file
175
src/test/scala/millfork/test/emu/EmuM6809Run.scala
Normal file
@ -0,0 +1,175 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.{Files, Paths}
|
||||
|
||||
import fastparse.core.Parsed.{Failure, Success}
|
||||
import millfork._
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.m6809.MLine
|
||||
import millfork.compiler.m6809.M6809Compiler
|
||||
import millfork.compiler.{CompilationContext, LabelGenerator}
|
||||
import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, NormalFunction}
|
||||
import millfork.node.opt.NodeOptimization
|
||||
import millfork.node.{Program, StandardCallGraph}
|
||||
import millfork.output.{M6809Assembler, MemoryBank}
|
||||
import millfork.parser.{PreprocessingResult, Preprocessor, Z80Parser}
|
||||
import org.scalatest.Matchers
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object EmuM6809Run {
|
||||
|
||||
private def preload(cpu: millfork.Cpu.Value, filename: String): Option[Program] = {
|
||||
TestErrorReporting.log.info(s"Loading $filename for $cpu")
|
||||
val source = Files.readAllLines(Paths.get(filename), StandardCharsets.US_ASCII).asScala.mkString("\n")
|
||||
val options = CompilationOptions(EmuPlatform.get(cpu), Map(
|
||||
CompilationFlag.LenientTextEncoding -> true
|
||||
), None, 0, Map(), JobContext(TestErrorReporting.log, new LabelGenerator))
|
||||
val PreprocessingResult(preprocessedSource, features, _) = Preprocessor.preprocessForTest(options, source)
|
||||
TestErrorReporting.log.debug(s"Features: $features")
|
||||
TestErrorReporting.log.info(s"Parsing $filename")
|
||||
val parser = Z80Parser(filename, preprocessedSource, "", options, features, useIntelSyntax = false)
|
||||
parser.toAst match {
|
||||
case Success(x, _) => Some(x)
|
||||
case f: Failure[_, _] =>
|
||||
TestErrorReporting.log.error(f.toString)
|
||||
TestErrorReporting.log.error(f.extra.toString)
|
||||
TestErrorReporting.log.error(f.lastParser.toString)
|
||||
TestErrorReporting.log.error("Syntax error", Some(parser.lastPosition))
|
||||
TestErrorReporting.log.error("Parsing error")
|
||||
???
|
||||
}
|
||||
}
|
||||
|
||||
private lazy val cache: mutable.Map[(millfork.Cpu.Value, String), Option[Program]] = mutable.Map[(millfork.Cpu.Value, String), Option[Program]]()
|
||||
private def get(cpu: millfork.Cpu.Value, path: String): Program =
|
||||
synchronized { cache.getOrElseUpdate(cpu->path, preload(cpu, path)).getOrElse(throw new IllegalStateException()) }
|
||||
|
||||
def cachedMath(cpu: millfork.Cpu.Value): Program = get(cpu, "include/m6809_math.mfk")
|
||||
def cachedStdio(cpu: millfork.Cpu.Value): Program = get(cpu, "src/test/resources/include/dummy_stdio.mfk")
|
||||
}
|
||||
|
||||
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 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,
|
||||
CompilationFlag.EnableInternalTestSyntax -> true,
|
||||
CompilationFlag.InlineFunctions -> this.inline,
|
||||
CompilationFlag.OptimizeStdlib -> this.inline,
|
||||
CompilationFlag.OptimizeForSize -> this.optimizeForSize,
|
||||
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)
|
||||
log.hasErrors = false
|
||||
log.verbosity = 999
|
||||
var effectiveSource = source
|
||||
if (!source.contains("_panic")) effectiveSource += "\n void _panic(){while(true){}}"
|
||||
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"))
|
||||
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)
|
||||
}
|
||||
tmp
|
||||
}
|
||||
val program = nodeOptimizations.foldLeft(withLibraries.applyImportantAliases)((p, opt) => p.applyNodeOptimization(opt, options))
|
||||
val callGraph = new StandardCallGraph(program, log)
|
||||
val env = new Environment(None, "", CpuFamily.I80, options)
|
||||
env.collectDeclarations(program, options)
|
||||
|
||||
val hasOptimizations = assemblyOptimizations.nonEmpty
|
||||
var unoptimizedSize = 0L
|
||||
// print unoptimized asm
|
||||
env.allPreallocatables.foreach {
|
||||
case f: NormalFunction =>
|
||||
val unoptimized = M6809Compiler.compile(CompilationContext(f.environment, f, 0, options, Set()))
|
||||
unoptimizedSize += unoptimized.map(_.sizeInBytes).sum
|
||||
case d: InitializedArray =>
|
||||
unoptimizedSize += d.contents.length
|
||||
case d: InitializedMemoryVariable =>
|
||||
unoptimizedSize += d.typ.size
|
||||
}
|
||||
|
||||
log.assertNoErrors("Compile failed")
|
||||
|
||||
|
||||
// compile
|
||||
val env2 = new Environment(None, "", CpuFamily.I80, 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)
|
||||
println(";;; ---------------------------")
|
||||
assembler.labelMap.foreach { case (l, (_, addr)) => println(f"$l%-15s $$$addr%04x") }
|
||||
|
||||
val optimizedSize = assembler.mem.banks("default").initialized.count(identity).toLong
|
||||
if (unoptimizedSize == optimizedSize) {
|
||||
println(f"Size: $unoptimizedSize%5d B")
|
||||
} else {
|
||||
println(f"Unoptimized size: $unoptimizedSize%5d B")
|
||||
println(f"Optimized size: $optimizedSize%5d B")
|
||||
println(f"Gain: ${(100L * (unoptimizedSize - optimizedSize) / unoptimizedSize.toDouble).round}%5d%%")
|
||||
}
|
||||
|
||||
if (log.hasErrors) {
|
||||
fail("Code generation failed")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
log.clearErrors()
|
||||
timings
|
||||
case f: Failure[_, _] =>
|
||||
println(f)
|
||||
println(f.extra.toString)
|
||||
println(f.lastParser.toString)
|
||||
log.error("Syntax error", Some(parserF.lastPosition))
|
||||
fail("Parsing error")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -14,6 +14,7 @@ object EmuUnoptimizedCrossPlatformRun {
|
||||
val (_, mz) = if (platforms.contains(Cpu.Z80)) EmuUnoptimizedZ80Run.apply2(source) else Timings(-1, -1) -> null
|
||||
val (_, mi) = if (platforms.contains(Cpu.Intel8080)) EmuUnoptimizedIntel8080Run.apply2(source) else Timings(-1, -1) -> null
|
||||
val (_, ms) = if (platforms.contains(Cpu.Sharp)) EmuUnoptimizedSharpRun.apply2(source) else Timings(-1, -1) -> null
|
||||
val (_, mo) = if (platforms.contains(Cpu.Motorola6809)) EmuUnoptimizedM6809Run.apply2(source) else Timings(-1, -1) -> null
|
||||
val (_, mx) = if (Settings.enableIntel8086Tests && platforms.contains(Cpu.Intel8086)) EmuUnoptimizedIntel8086Run.apply2(source) else Timings(-1, -1) -> null
|
||||
if (Settings.enable6502Tests && platforms.contains(Cpu.Mos)) {
|
||||
println(f"Running 6502")
|
||||
@ -43,5 +44,9 @@ object EmuUnoptimizedCrossPlatformRun {
|
||||
println(f"Running 8086")
|
||||
verifier(mx)
|
||||
}
|
||||
if (Settings.enableMotorola6809Tests && platforms.contains(Cpu.Motorola6809)) {
|
||||
println(f"Running 6809")
|
||||
verifier(mx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,3 +25,5 @@ object EmuUnoptimizedIntel8085Run extends EmuZ80Run(Cpu.Intel8085, Nil, Nil)
|
||||
object EmuUnoptimizedIntel8086Run extends EmuI86Run(Nil, Nil)
|
||||
|
||||
object EmuUnoptimizedSharpRun extends EmuZ80Run(Cpu.Sharp, Nil, Nil)
|
||||
|
||||
object EmuUnoptimizedM6809Run extends EmuM6809Run(Cpu.Motorola6809, Nil, Nil)
|
||||
|
@ -48,6 +48,12 @@ object Settings {
|
||||
*/
|
||||
val enableUnemulatedTests: Boolean = true
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
// the following are the core platforms and they are all tested by default
|
||||
|
||||
val enable6502Tests: Boolean = true
|
||||
|
Loading…
Reference in New Issue
Block a user