mirror of https://github.com/KarolS/millfork.git
419 lines
16 KiB
Scala
419 lines
16 KiB
Scala
package millfork.output
|
|
|
|
import millfork.assembly.SourceLine
|
|
import millfork.assembly.m6809.opt.JumpFixing
|
|
import millfork.{CompilationFlag, CompilationOptions, Platform}
|
|
import millfork.assembly.m6809.{MOpcode, _}
|
|
import millfork.compiler.m6809.M6809Compiler
|
|
import millfork.env.{Environment, FunctionInMemory, Label, MemoryAddressConstant, NormalFunction, NumericConstant}
|
|
import millfork.node.{M6809Register, NiceFunctionProperty, Position, 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(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: NormalFunction, code: List[MLine]): Unit = ()
|
|
|
|
override def gatherFunctionOptimizationHints(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: FunctionInMemory): Unit = {
|
|
import NiceFunctionProperty._
|
|
import millfork.node.M6809NiceFunctionProperty._
|
|
val functionName = function.name
|
|
if (function.optimizationHints("preserves_memory")) niceFunctionProperties += DoesntWriteMemory -> functionName
|
|
if (function.optimizationHints("idempotent")) niceFunctionProperties += Idempotent -> functionName
|
|
if (function.optimizationHints("preserves_a")) niceFunctionProperties += DoesntChangeA -> functionName
|
|
if (function.optimizationHints("preserves_b")) niceFunctionProperties += DoesntChangeB -> functionName
|
|
if (function.optimizationHints("preserves_d")) {
|
|
niceFunctionProperties += DoesntChangeA -> functionName
|
|
niceFunctionProperties += DoesntChangeB -> functionName
|
|
}
|
|
if (function.optimizationHints("preserves_x")) niceFunctionProperties += DoesntChangeX -> functionName
|
|
if (function.optimizationHints("preserves_y")) niceFunctionProperties += DoesntChangeY -> functionName
|
|
if (function.optimizationHints("preserves_u")) niceFunctionProperties += DoesntChangeU -> functionName
|
|
if (function.optimizationHints("preserves_dp")) niceFunctionProperties += DoesntChangeDP -> functionName
|
|
if (function.optimizationHints("preserves_c")) niceFunctionProperties += DoesntChangeCF -> functionName
|
|
}
|
|
|
|
override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[MLine]): List[MLine] = {
|
|
JumpFixing(f, code, options)
|
|
}
|
|
|
|
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 = {
|
|
implicit 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)
|
|
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(CHANGED_MEM, NonExistent, MemoryAddressConstant(Label(l))) if l.contains("..brk") =>
|
|
breakpointSet += mem.banks(bank).index -> index
|
|
index
|
|
case MLine0(CHANGED_MEM, _, _) =>
|
|
index
|
|
case MLine0(TFR, TwoRegisters(source, target), param)
|
|
if M6809Register.registerSize(source) == M6809Register.registerSize(target) && param.isProvablyZero =>
|
|
writeByte(bank, index, 0x1f)
|
|
writeByte(bank, index + 1, 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 + 1, 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
|
|
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 + 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)
|
|
writeByte(bank, index + 1, 0x9f)
|
|
writeWord(bank, index + 2, param)
|
|
index + 4
|
|
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(0, _) =>
|
|
writeByte(bank, index + 1, 0x84 + ri)
|
|
index + 2
|
|
case NumericConstant(n, _) if !indirect && n >= -16 && n <= 15 =>
|
|
writeByte(bank, index + 1, ri + n.toInt.&(0x1f))
|
|
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(BRA, LongRelative, param) =>
|
|
writeByte(bank, index, 0x16)
|
|
writeWord(bank, index + 1, param - (index + 3))
|
|
index + 3
|
|
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 + " " + instr.productIterator.mkString("/"))
|
|
}
|
|
}
|
|
|
|
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, 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 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
|
|
inherentB(op) = value.+(0x10)
|
|
}
|
|
|
|
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._
|
|
|
|
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(SWI2, 0x3f)
|
|
inh(SWI3, 0x3f)
|
|
inh(SYNC, 0x13)
|
|
|
|
inab(ASL, 0x48)
|
|
inab(ASR, 0x47)
|
|
inab(CLR, 0x4f)
|
|
inab(COM, 0x43)
|
|
inab(DEC, 0x4a)
|
|
inab(INC, 0x4c)
|
|
inab(LSR, 0x44)
|
|
inab(NEG, 0x40)
|
|
inab(ROL, 0x49)
|
|
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)
|
|
|
|
|
|
}
|
|
|