mirror of
https://github.com/KarolS/millfork.git
synced 2024-05-31 18:41:30 +00:00
869 lines
30 KiB
Scala
869 lines
30 KiB
Scala
package millfork.output
|
|
|
|
import millfork.assembly.mos.opt.{CoarseFlowAnalyzer, CpuStatus, HudsonOptimizations, JumpFixing, JumpFollowing, JumpShortening, SourceOfNZ}
|
|
import millfork.assembly._
|
|
import millfork.env._
|
|
import millfork.error.{ConsoleLogger, FatalErrorReporting}
|
|
import millfork.node.{FunctionCallExpression, MosNiceFunctionProperty, NiceFunctionProperty, Position, Program}
|
|
import millfork._
|
|
import millfork.assembly.mos._
|
|
import millfork.assembly.opt.{SingleStatus, Status}
|
|
import millfork.compiler.mos.MosCompiler
|
|
|
|
import scala.annotation.tailrec
|
|
import scala.collection.mutable
|
|
|
|
/**
|
|
* @author Karol Stasiak
|
|
*/
|
|
class MosAssembler(program: Program,
|
|
rootEnv: Environment,
|
|
platform: Platform) extends AbstractAssembler[AssemblyLine](program, rootEnv, platform, MosInliningCalculator, MosCompiler) {
|
|
|
|
|
|
override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[AssemblyLine]):List[AssemblyLine] = {
|
|
val optimizationContext = OptimizationContext(options, Map(), f.environment.maybeGet[ThingInMemory]("__reg"), f.environment.identityPage, Set())
|
|
if (actuallyOptimize) {
|
|
val finalCode = if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.removeLoadZero(f, code, optimizationContext) else code
|
|
JumpShortening(f, JumpShortening(f, JumpFixing(f, JumpFollowing(options, finalCode), options), optimizationContext), optimizationContext)
|
|
}
|
|
else JumpFixing(f, code, options)
|
|
}
|
|
|
|
override def emitInstruction(bank: String, options: CompilationOptions, index: Int, instr: AssemblyLine): Int = {
|
|
import millfork.assembly.mos.AddrMode._
|
|
import millfork.assembly.mos.Opcode._
|
|
implicit val position = instr.source.map(sl => Position(sl.moduleName, sl.line, 0, 0))
|
|
instr match {
|
|
case AssemblyLine0(BYTE, RawByte, c) =>
|
|
writeByte(bank, index, c)
|
|
index + 1
|
|
case AssemblyLine0(BYTE, _, _) => log.fatal("BYTE opcode failure")
|
|
case AssemblyLine0(_, RawByte, _) => log.fatal("BYTE opcode failure")
|
|
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(labelName))) =>
|
|
val bank0 = mem.banks(bank)
|
|
labelMap(labelName) = bank -> index
|
|
index
|
|
case AssemblyLine0(CHANGED_MEM, DoesNotExist, MemoryAddressConstant(Label(l))) if l.contains("..brk") =>
|
|
breakpointSet += mem.banks(bank).index -> index
|
|
index
|
|
case AssemblyLine0(_, DoesNotExist, _) =>
|
|
index
|
|
case AssemblyLine0(op, Implied, _) =>
|
|
writeByte(bank, index, MosAssembler.opcodeFor(op, Implied, options))
|
|
index + 1
|
|
case AssemblyLine0(op, Relative, param) =>
|
|
writeByte(bank, index, MosAssembler.opcodeFor(op, Relative, options))
|
|
writeByte(bank, index + 1, AssertByte(param - (index + 2)))
|
|
index + 2
|
|
case AssemblyLine0(op, am@(Immediate | ZeroPage | ZeroPageX | ZeroPageY | IndexedY | IndexedX | IndexedZ | IndexedSY | LongIndexedY | LongIndexedZ | Stack), param) =>
|
|
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
|
|
writeByte(bank, index + 1, param)
|
|
index + 2
|
|
case AssemblyLine0(op, am@(WordImmediate | Absolute | AbsoluteY | AbsoluteX | Indirect | AbsoluteIndexedX), param) =>
|
|
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
|
|
writeWord(bank, index + 1, param)
|
|
index + 3
|
|
case AssemblyLine0(op, am@LongRelative, param) =>
|
|
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
|
|
// TODO:
|
|
writeWord(bank, index + 1, param - (index + 3))
|
|
index + 3
|
|
case AssemblyLine0(op, am@(LongAbsolute | LongAbsoluteX | LongIndirect), param) =>
|
|
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
|
|
writeWord(bank, index + 1, param)
|
|
writeByte(bank, index + 3, extractBank(param, options))
|
|
index + 4
|
|
case AssemblyLine0(op, am@(TripleAbsolute), StructureConstant(_, List(a, b, c))) =>
|
|
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
|
|
writeWord(bank, index + 1, a)
|
|
writeWord(bank, index + 3, b)
|
|
writeWord(bank, index + 5, c)
|
|
index + 7
|
|
case AssemblyLine0(op, am@(ZeroPageWithRelative), StructureConstant(_, List(a, b))) =>
|
|
writeByte(bank, index, MosAssembler.opcodeFor(op, am, options))
|
|
writeByte(bank, index + 1, a)
|
|
writeByte(bank, index + 2, AssertByte(b - (index + 3)))
|
|
index + 3
|
|
case AssemblyLine0(TST, ImmediateWithZeroPage, StructureConstant(_, List(a, b))) =>
|
|
MosAssembler.requireHudson(options)
|
|
writeByte(bank, index, 0x83)
|
|
writeByte(bank, index + 1, a)
|
|
writeByte(bank, index + 2, b)
|
|
index + 3
|
|
case AssemblyLine0(TST, ImmediateWithZeroPageX, StructureConstant(_, List(a, b))) =>
|
|
MosAssembler.requireHudson(options)
|
|
writeByte(bank, index, 0xA3)
|
|
writeByte(bank, index + 1, a)
|
|
writeByte(bank, index + 2, b)
|
|
index + 3
|
|
case AssemblyLine0(TST, ImmediateWithAbsolute, StructureConstant(_, List(a, b))) =>
|
|
MosAssembler.requireHudson(options)
|
|
writeByte(bank, index, 0x93)
|
|
writeByte(bank, index + 1, a)
|
|
writeWord(bank, index + 2, b)
|
|
index + 4
|
|
case AssemblyLine0(TST, ImmediateWithAbsoluteX, StructureConstant(_, List(a, b))) =>
|
|
MosAssembler.requireHudson(options)
|
|
writeByte(bank, index, 0xB3)
|
|
writeByte(bank, index + 1, a)
|
|
writeWord(bank, index + 2, b)
|
|
index + 4
|
|
}
|
|
}
|
|
|
|
override def injectLabels(labelMap: Map[String, (String, Int)], code: List[AssemblyLine]): List[AssemblyLine] = {
|
|
import Opcode._
|
|
code.map {
|
|
case l@AssemblyLine(LDA | STA | CMP |
|
|
LDX | STX | CPX |
|
|
LDY | STY | CPY |
|
|
LDZ | STZ | CPZ |
|
|
BIT |
|
|
ADC | SBC | AND | ORA | EOR |
|
|
INC | DEC | ROL | ROR | ASL | LSR |
|
|
ISC | DCP | LAX | SAX | RLA | RRA | SLO | SRE, AddrMode.Absolute, p, Elidability.Elidable, _) =>
|
|
p match {
|
|
case NumericConstant(n, _) => if (n <= 255) l.copy(addrMode = AddrMode.ZeroPage) else l
|
|
case MemoryAddressConstant(th) => if (labelMap.getOrElse(th.name, 0 -> 0x800)._2 < 0x100) l.copy(addrMode = AddrMode.ZeroPage) else l
|
|
case CompoundConstant(MathOperator.Plus,
|
|
MemoryAddressConstant(th),
|
|
NumericConstant(n, _)) => if (labelMap.getOrElse(th.name, 0 -> 0x800)._2 < 0x100) l.copy(addrMode = AddrMode.ZeroPage) else l
|
|
case _ => l
|
|
}
|
|
|
|
case l => l
|
|
}
|
|
}
|
|
|
|
@tailrec
|
|
private def isNaughty(code: List[AssemblyLine]): Boolean = {
|
|
import Opcode._
|
|
import AddrMode._
|
|
code match {
|
|
case AssemblyLine0(JMP | JSR | BSR, Indirect | LongIndirect | AbsoluteIndexedX, _) :: _ => true
|
|
case AssemblyLine0(PHA, _, _) :: AssemblyLine0(RTS | RTL, _, _) :: _ => true
|
|
case _ :: xs => isNaughty(xs)
|
|
case Nil => false
|
|
}
|
|
}
|
|
|
|
override def quickSimplify(code: List[AssemblyLine]): List[AssemblyLine] = code.map(a => a.copy(parameter = a.parameter.quickSimplify))
|
|
|
|
override def gatherNiceFunctionProperties(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: NormalFunction, code: List[AssemblyLine]): Unit = {
|
|
import Opcode._
|
|
import AddrMode._
|
|
import MosNiceFunctionProperty._
|
|
import NiceFunctionProperty._
|
|
val functionName = function.name
|
|
if (isNaughty(code)) return
|
|
val localLabels = code.flatMap {
|
|
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(l))) => Some(l)
|
|
case _ => None
|
|
}.toSet
|
|
if (code.exists {
|
|
case AssemblyLine0(op, _, MemoryAddressConstant(Label(l))) if OpcodeClasses.AllDirectJumps(op) => !localLabels(l)
|
|
case AssemblyLine0(op, _, _) if OpcodeClasses.AllDirectJumps(op) => true
|
|
case AssemblyLine0(BRK | RTI, _, _) => true
|
|
case _ => false
|
|
}) return
|
|
val optimizationContext = OptimizationContext(options, Map(), function.environment.maybeGet[ThingInMemory]("__reg"), function.environment.identityPage, Set())
|
|
val flow = CoarseFlowAnalyzer.analyze(function, code, optimizationContext)
|
|
def rtsPropertyScan[T](extractor: CpuStatus => Status[T])(niceFunctionProperty: Status[T] => Option[NiceFunctionProperty]): Unit = {
|
|
val statuses = code.zipWithIndex.flatMap{
|
|
case (AssemblyLine0(RTS, _, _), ix) => Some(extractor(flow(ix)))
|
|
case _ => None
|
|
}.toSet
|
|
statuses.toSeq match {
|
|
case Seq(only) =>
|
|
niceFunctionProperty(only).foreach { np =>
|
|
niceFunctionProperties += (np -> functionName)
|
|
}
|
|
case _ =>
|
|
}
|
|
}
|
|
def simpleRtsPropertyScan[T](extractor: CpuStatus => Status[T])(niceFunctionProperty: T => NiceFunctionProperty): Unit = {
|
|
rtsPropertyScan(extractor) {
|
|
case SingleStatus(x) => Some(niceFunctionProperty(x))
|
|
case _ => None
|
|
}
|
|
}
|
|
def genericPropertyScan(niceFunctionProperty: NiceFunctionProperty)(predicate: AssemblyLine => Boolean): Unit = {
|
|
val preserved = code.forall {
|
|
case AssemblyLine0(JSR | BSR | JMP, Absolute | LongAbsolute, MemoryAddressConstant(th)) => niceFunctionProperties(niceFunctionProperty -> th.name)
|
|
case AssemblyLine0(JSR | BSR, _, _) => false
|
|
case AssemblyLine0(op, _, MemoryAddressConstant(Label(label))) if OpcodeClasses.AllDirectJumps(op) => localLabels(label)
|
|
case AssemblyLine0(op, _, _) if OpcodeClasses.AllDirectJumps(op) => false
|
|
case l => predicate(l)
|
|
}
|
|
if (preserved) {
|
|
niceFunctionProperties += (niceFunctionProperty -> functionName)
|
|
}
|
|
}
|
|
genericPropertyScan(DoesntChangeX) {
|
|
case AssemblyLine0(op, _, _) => !OpcodeClasses.ChangesX(op)
|
|
}
|
|
genericPropertyScan(DoesntChangeY) {
|
|
case AssemblyLine0(op, _, _) => !OpcodeClasses.ChangesY(op)
|
|
}
|
|
genericPropertyScan(DoesntChangeA) {
|
|
case AssemblyLine0(op, _, _) if OpcodeClasses.ChangesAAlways(op) => false
|
|
case AssemblyLine0(op, _, Implied) if OpcodeClasses.ChangesAIfImplied(op) => false
|
|
case _ => true
|
|
}
|
|
genericPropertyScan(DoesntChangeIZ) {
|
|
case AssemblyLine0(op, _, _) => !OpcodeClasses.ChangesIZ(op)
|
|
}
|
|
genericPropertyScan(DoesntChangeAH) {
|
|
case AssemblyLine0(op, _, _) if OpcodeClasses.ChangesAHAlways(op) => false
|
|
case AssemblyLine0(op, _, Implied) if OpcodeClasses.ChangesAHIfImplied(op) => false
|
|
case _ => true
|
|
}
|
|
genericPropertyScan(DoesntChangeC) {
|
|
case AssemblyLine0(SEP | REP, Immediate, NumericConstant(imm, _)) => (imm & 1) == 0
|
|
case AssemblyLine0(SEP | REP, _, _) => false
|
|
case AssemblyLine0(op, _, _) => !OpcodeClasses.ChangesC(op)
|
|
}
|
|
genericPropertyScan(DoesntConcernD) {
|
|
case AssemblyLine0(SEP | REP, Immediate, NumericConstant(imm, _)) => (imm & 8) == 0
|
|
case AssemblyLine0(SEP | REP, _, _) => false
|
|
case AssemblyLine0(op, _, _) => !OpcodeClasses.ReadsD(op) && !OpcodeClasses.OverwritesD(op)
|
|
}
|
|
genericPropertyScan(DoesntReadMemory) {
|
|
case AssemblyLine0(op, Implied | Immediate | WordImmediate, _) => true
|
|
case AssemblyLine0(op, _, _) if OpcodeClasses.ReadsMemoryIfNotImpliedOrImmediate(op) => false
|
|
case _ => true
|
|
}
|
|
genericPropertyScan(DoesntWriteMemory) {
|
|
case AssemblyLine0(op, Implied | Immediate | WordImmediate, _) => true
|
|
case AssemblyLine0(op, _, _) if OpcodeClasses.ChangesMemoryIfNotImplied(op) || OpcodeClasses.ChangesMemoryAlways(op) => false
|
|
case _ => true
|
|
}
|
|
genericPropertyScan(IsLeaf) {
|
|
case AssemblyLine0(JSR | BSR, Implied | Immediate | WordImmediate, _) => false
|
|
case AssemblyLine0(JMP, Absolute, th:Thing) => th.name.startsWith(".")
|
|
case _ => true
|
|
}
|
|
simpleRtsPropertyScan(_.src)(SetsSourceOfNZ)
|
|
simpleRtsPropertyScan(_.a0)(Bit0OfA)
|
|
simpleRtsPropertyScan(_.a7)(Bit7OfA)
|
|
simpleRtsPropertyScan(_.a)(SetsATo)
|
|
simpleRtsPropertyScan(_.x)(SetsXTo)
|
|
simpleRtsPropertyScan(_.y)(SetsYTo)
|
|
}
|
|
|
|
override def gatherFunctionOptimizationHints(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: FunctionInMemory): Unit = {
|
|
import MosNiceFunctionProperty._
|
|
import NiceFunctionProperty._
|
|
val functionName = function.name
|
|
if (function.optimizationHints("even")) niceFunctionProperties += Bit0OfA(false) -> functionName
|
|
if (function.optimizationHints("odd")) niceFunctionProperties += Bit0OfA(true) -> functionName
|
|
if (function.optimizationHints("preserves_a")) niceFunctionProperties += DoesntChangeA -> functionName
|
|
if (function.optimizationHints("preserves_x")) niceFunctionProperties += DoesntChangeX -> functionName
|
|
if (function.optimizationHints("preserves_y")) niceFunctionProperties += DoesntChangeY -> functionName
|
|
if (function.optimizationHints("preserves_c")) niceFunctionProperties += DoesntChangeC -> functionName
|
|
if (function.optimizationHints("preserves_memory")) niceFunctionProperties += DoesntWriteMemory -> functionName
|
|
if (function.optimizationHints("idempotent")) niceFunctionProperties += Idempotent -> functionName
|
|
}
|
|
|
|
override def bytePseudoopcode: String = "!byte"
|
|
|
|
override def deduplicate(options: CompilationOptions, compiledFunctions: mutable.Map[String, CompiledFunction[AssemblyLine]]): Unit =
|
|
new MosDeduplicate(rootEnv, options).apply(compiledFunctions)
|
|
}
|
|
|
|
|
|
object MosAssembler {
|
|
private val opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
|
private val illegalOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
|
private val cmosOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
|
private val sc02Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
|
private val rockwellOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
|
private val singleBitBranches = mutable.Map[Opcode.Value, Byte]()
|
|
private val wdcOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
|
private val cmosNopOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
|
private val ce02Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
|
private val hudsonOpcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
|
private val hudsonTransferOpcodes = mutable.Map[Opcode.Value, Byte]()
|
|
private val emulation65816Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
|
private val native65816Opcodes = mutable.Map[(Opcode.Value, AddrMode.Value), Byte]()
|
|
|
|
def requireHudson(options: CompilationOptions): Unit = {
|
|
if (!options.flag(CompilationFlag.EmitHudsonOpcodes)){
|
|
options.log.fatal("Cannot assemble an unknown opcode TST")
|
|
}
|
|
}
|
|
|
|
def opcodeFor(opcode: Opcode.Value, addrMode: AddrMode.Value, options: CompilationOptions): Byte = {
|
|
val key = opcode -> addrMode
|
|
opcodes.get(key).foreach(return _)
|
|
if (options.flag(CompilationFlag.EmitIllegals)) illegalOpcodes.get(key).foreach(return _)
|
|
if (options.flag(CompilationFlag.EmitCmosOpcodes)) cmosOpcodes.get(key).foreach(return _)
|
|
if (options.flag(CompilationFlag.EmitSC02Opcodes)) sc02Opcodes.get(key).foreach(return _)
|
|
if (options.flag(CompilationFlag.EmitRockwellOpcodes)) rockwellOpcodes.get(key).foreach(return _)
|
|
if (options.flag(CompilationFlag.EmitRockwellOpcodes) && addrMode == AddrMode.ZeroPageWithRelative) singleBitBranches.get(key._1).foreach(return _)
|
|
if (options.flag(CompilationFlag.EmitWdcOpcodes)) wdcOpcodes.get(key).foreach(return _)
|
|
if (options.flag(CompilationFlag.EmitCmosNopOpcodes)) cmosNopOpcodes.get(key).foreach(return _)
|
|
if (options.flag(CompilationFlag.Emit65CE02Opcodes)) ce02Opcodes.get(key).foreach(return _)
|
|
if (options.flag(CompilationFlag.EmitHudsonOpcodes)) hudsonOpcodes.get(key).foreach(return _)
|
|
if (options.flag(CompilationFlag.EmitHudsonOpcodes) && addrMode == AddrMode.TripleAbsolute) hudsonTransferOpcodes.get(key._1).foreach(return _)
|
|
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) emulation65816Opcodes.get(key).foreach(return _)
|
|
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) native65816Opcodes.get(key).foreach(return _)
|
|
options.log.fatal("Cannot assemble an unknown opcode " + key)
|
|
}
|
|
|
|
private def op(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op -> am))
|
|
opcodes(op -> am) = x.toByte
|
|
if (am == AddrMode.Relative) opcodes(op -> AddrMode.Immediate) = x.toByte
|
|
}
|
|
|
|
private def cm(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op -> am))
|
|
cmosOpcodes(op -> am) = x.toByte
|
|
}
|
|
|
|
private def sc(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op -> am))
|
|
sc02Opcodes(op -> am) = x.toByte
|
|
}
|
|
|
|
private def rw(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op -> am))
|
|
rockwellOpcodes(op -> am) = x.toByte
|
|
}
|
|
|
|
private def wd(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op -> am))
|
|
wdcOpcodes(op -> am) = x.toByte
|
|
}
|
|
|
|
private def sb(op: Opcode.Value, x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op))
|
|
singleBitBranches(op) = x.toByte
|
|
}
|
|
|
|
private def cn(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op -> am))
|
|
cmosNopOpcodes(op -> am) = x.toByte
|
|
}
|
|
|
|
private def il(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op -> am))
|
|
illegalOpcodes(op -> am) = x.toByte
|
|
}
|
|
|
|
private def hu(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op -> am))
|
|
hudsonOpcodes(op -> am) = x.toByte
|
|
}
|
|
|
|
private def ht(op: Opcode.Value,x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op))
|
|
hudsonTransferOpcodes(op) = x.toByte
|
|
}
|
|
|
|
private def ce(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op -> am))
|
|
ce02Opcodes(op -> am) = x.toByte
|
|
}
|
|
|
|
private def em(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op -> am))
|
|
emulation65816Opcodes(op -> am) = x.toByte
|
|
}
|
|
|
|
private def na(op: Opcode.Value, am: AddrMode.Value, x: Int): Unit = {
|
|
if (x < 0 || x > 0xff) FatalErrorReporting.reportFlyingPig("Invalid code for" + (op -> am))
|
|
native65816Opcodes(op -> am) = x.toByte
|
|
}
|
|
|
|
def getStandardLegalOpcodes: Set[Int] = opcodes.values.map(_ & 0xff).toSet
|
|
|
|
import AddrMode._
|
|
import Opcode._
|
|
|
|
op(ADC, Immediate, 0x69)
|
|
op(ADC, ZeroPage, 0x65)
|
|
op(ADC, ZeroPageX, 0x75)
|
|
op(ADC, Absolute, 0x6D)
|
|
op(ADC, AbsoluteX, 0x7D)
|
|
op(ADC, AbsoluteY, 0x79)
|
|
op(ADC, IndexedX, 0x61)
|
|
op(ADC, IndexedY, 0x71)
|
|
|
|
op(AND, Immediate, 0x29)
|
|
op(AND, ZeroPage, 0x25)
|
|
op(AND, ZeroPageX, 0x35)
|
|
op(AND, Absolute, 0x2D)
|
|
op(AND, AbsoluteX, 0x3D)
|
|
op(AND, AbsoluteY, 0x39)
|
|
op(AND, IndexedX, 0x21)
|
|
op(AND, IndexedY, 0x31)
|
|
|
|
op(ASL, Implied, 0x0A)
|
|
op(ASL, ZeroPage, 0x06)
|
|
op(ASL, ZeroPageX, 0x16)
|
|
op(ASL, Absolute, 0x0E)
|
|
op(ASL, AbsoluteX, 0x1E)
|
|
|
|
op(BIT, ZeroPage, 0x24)
|
|
op(BIT, Absolute, 0x2C)
|
|
|
|
op(BPL, Relative, 0x10)
|
|
op(BMI, Relative, 0x30)
|
|
op(BVC, Relative, 0x50)
|
|
op(BVS, Relative, 0x70)
|
|
op(BCC, Relative, 0x90)
|
|
op(BCS, Relative, 0xB0)
|
|
op(BNE, Relative, 0xD0)
|
|
op(BEQ, Relative, 0xF0)
|
|
|
|
op(BRK, Implied, 0)
|
|
op(BRK, Immediate, 0)
|
|
|
|
op(CMP, Immediate, 0xC9)
|
|
op(CMP, ZeroPage, 0xC5)
|
|
op(CMP, ZeroPageX, 0xD5)
|
|
op(CMP, Absolute, 0xCD)
|
|
op(CMP, AbsoluteX, 0xDD)
|
|
op(CMP, AbsoluteY, 0xD9)
|
|
op(CMP, IndexedX, 0xC1)
|
|
op(CMP, IndexedY, 0xD1)
|
|
|
|
op(CPX, Immediate, 0xE0)
|
|
op(CPX, ZeroPage, 0xE4)
|
|
op(CPX, Absolute, 0xEC)
|
|
|
|
op(CPY, Immediate, 0xC0)
|
|
op(CPY, ZeroPage, 0xC4)
|
|
op(CPY, Absolute, 0xCC)
|
|
|
|
op(DEC, ZeroPage, 0xC6)
|
|
op(DEC, ZeroPageX, 0xD6)
|
|
op(DEC, Absolute, 0xCE)
|
|
op(DEC, AbsoluteX, 0xDE)
|
|
|
|
op(EOR, Immediate, 0x49)
|
|
op(EOR, ZeroPage, 0x45)
|
|
op(EOR, ZeroPageX, 0x55)
|
|
op(EOR, Absolute, 0x4D)
|
|
op(EOR, AbsoluteX, 0x5D)
|
|
op(EOR, AbsoluteY, 0x59)
|
|
op(EOR, IndexedX, 0x41)
|
|
op(EOR, IndexedY, 0x51)
|
|
|
|
op(INC, ZeroPage, 0xE6)
|
|
op(INC, ZeroPageX, 0xF6)
|
|
op(INC, Absolute, 0xEE)
|
|
op(INC, AbsoluteX, 0xFE)
|
|
|
|
op(CLC, Implied, 0x18)
|
|
op(SEC, Implied, 0x38)
|
|
op(CLI, Implied, 0x58)
|
|
op(SEI, Implied, 0x78)
|
|
op(CLV, Implied, 0xB8)
|
|
op(CLD, Implied, 0xD8)
|
|
op(SED, Implied, 0xF8)
|
|
|
|
op(JMP, Absolute, 0x4C)
|
|
op(JMP, Indirect, 0x6C)
|
|
|
|
op(JSR, Absolute, 0x20)
|
|
|
|
op(LDA, Immediate, 0xA9)
|
|
op(LDA, ZeroPage, 0xA5)
|
|
op(LDA, ZeroPageX, 0xB5)
|
|
op(LDA, Absolute, 0xAD)
|
|
op(LDA, AbsoluteX, 0xBD)
|
|
op(LDA, AbsoluteY, 0xB9)
|
|
op(LDA, IndexedX, 0xA1)
|
|
op(LDA, IndexedY, 0xB1)
|
|
|
|
op(LDX, Immediate, 0xA2)
|
|
op(LDX, ZeroPage, 0xA6)
|
|
op(LDX, ZeroPageY, 0xB6)
|
|
op(LDX, Absolute, 0xAE)
|
|
op(LDX, AbsoluteY, 0xBE)
|
|
|
|
op(LDY, Immediate, 0xA0)
|
|
op(LDY, ZeroPage, 0xA4)
|
|
op(LDY, ZeroPageX, 0xB4)
|
|
op(LDY, Absolute, 0xAC)
|
|
op(LDY, AbsoluteX, 0xBC)
|
|
|
|
op(LSR, Implied, 0x4A)
|
|
op(LSR, ZeroPage, 0x46)
|
|
op(LSR, ZeroPageX, 0x56)
|
|
op(LSR, Absolute, 0x4E)
|
|
op(LSR, AbsoluteX, 0x5E)
|
|
|
|
op(NOP, Implied, 0xEA)
|
|
|
|
op(ORA, Immediate, 0x09)
|
|
op(ORA, ZeroPage, 0x05)
|
|
op(ORA, ZeroPageX, 0x15)
|
|
op(ORA, Absolute, 0x0D)
|
|
op(ORA, AbsoluteX, 0x1D)
|
|
op(ORA, AbsoluteY, 0x19)
|
|
op(ORA, IndexedX, 0x01)
|
|
op(ORA, IndexedY, 0x11)
|
|
|
|
op(TAX, Implied, 0xAA)
|
|
op(TXA, Implied, 0x8A)
|
|
op(DEX, Implied, 0xCA)
|
|
op(INX, Implied, 0xE8)
|
|
op(TAY, Implied, 0xA8)
|
|
op(TYA, Implied, 0x98)
|
|
op(DEY, Implied, 0x88)
|
|
op(INY, Implied, 0xC8)
|
|
|
|
op(ROL, Implied, 0x2A)
|
|
op(ROL, ZeroPage, 0x26)
|
|
op(ROL, ZeroPageX, 0x36)
|
|
op(ROL, Absolute, 0x2E)
|
|
op(ROL, AbsoluteX, 0x3E)
|
|
|
|
op(ROR, Implied, 0x6A)
|
|
op(ROR, ZeroPage, 0x66)
|
|
op(ROR, ZeroPageX, 0x76)
|
|
op(ROR, Absolute, 0x6E)
|
|
op(ROR, AbsoluteX, 0x7E)
|
|
|
|
op(RTI, Implied, 0x40)
|
|
op(RTS, Implied, 0x60)
|
|
|
|
op(SBC, Immediate, 0xE9)
|
|
op(SBC, ZeroPage, 0xE5)
|
|
op(SBC, ZeroPageX, 0xF5)
|
|
op(SBC, Absolute, 0xED)
|
|
op(SBC, AbsoluteX, 0xFD)
|
|
op(SBC, AbsoluteY, 0xF9)
|
|
op(SBC, IndexedX, 0xE1)
|
|
op(SBC, IndexedY, 0xF1)
|
|
|
|
op(STA, ZeroPage, 0x85)
|
|
op(STA, ZeroPageX, 0x95)
|
|
op(STA, Absolute, 0x8D)
|
|
op(STA, AbsoluteX, 0x9D)
|
|
op(STA, AbsoluteY, 0x99)
|
|
op(STA, IndexedX, 0x81)
|
|
op(STA, IndexedY, 0x91)
|
|
|
|
op(TXS, Implied, 0x9A)
|
|
op(TSX, Implied, 0xBA)
|
|
op(PHA, Implied, 0x48)
|
|
op(PLA, Implied, 0x68)
|
|
op(PHP, Implied, 0x08)
|
|
op(PLP, Implied, 0x28)
|
|
|
|
op(STX, ZeroPage, 0x86)
|
|
op(STX, ZeroPageY, 0x96)
|
|
op(STX, Absolute, 0x8E)
|
|
|
|
op(STY, ZeroPage, 0x84)
|
|
op(STY, ZeroPageX, 0x94)
|
|
op(STY, Absolute, 0x8C)
|
|
|
|
il(KIL, Implied, 0x02) // there are multiple candidates and some others could be a better choice, but whatever, 02 is fine
|
|
|
|
il(LAX, ZeroPage, 0xA7)
|
|
il(LAX, ZeroPageY, 0xB7)
|
|
il(LAX, Absolute, 0xAF)
|
|
il(LAX, AbsoluteY, 0xBF)
|
|
il(LAX, IndexedX, 0xA3)
|
|
il(LAX, IndexedY, 0xB3)
|
|
|
|
il(SAX, ZeroPage, 0x87)
|
|
il(SAX, ZeroPageY, 0x97)
|
|
il(SAX, Absolute, 0x8F)
|
|
il(TAS, AbsoluteY, 0x9B)
|
|
il(AHX, AbsoluteY, 0x9F)
|
|
il(SAX, IndexedX, 0x83)
|
|
il(AHX, IndexedY, 0x93)
|
|
il(SHY, AbsoluteX, 0x9C)
|
|
il(SHX, AbsoluteY, 0x9E)
|
|
il(LAS, AbsoluteY, 0xBB)
|
|
|
|
il(ANC, Immediate, 0x0B)
|
|
il(ALR, Immediate, 0x4B)
|
|
il(ARR, Immediate, 0x6B)
|
|
il(XAA, Immediate, 0x8B)
|
|
il(LXA, Immediate, 0xAB)
|
|
il(SBX, Immediate, 0xCB)
|
|
|
|
il(SLO, ZeroPage, 0x07)
|
|
il(SLO, ZeroPageX, 0x17)
|
|
il(SLO, IndexedX, 0x03)
|
|
il(SLO, IndexedY, 0x13)
|
|
il(SLO, Absolute, 0x0F)
|
|
il(SLO, AbsoluteX, 0x1F)
|
|
il(SLO, AbsoluteY, 0x1B)
|
|
|
|
il(RLA, ZeroPage, 0x27)
|
|
il(RLA, ZeroPageX, 0x37)
|
|
il(RLA, IndexedX, 0x23)
|
|
il(RLA, IndexedY, 0x33)
|
|
il(RLA, Absolute, 0x2F)
|
|
il(RLA, AbsoluteX, 0x3F)
|
|
il(RLA, AbsoluteY, 0x3B)
|
|
|
|
il(SRE, ZeroPage, 0x47)
|
|
il(SRE, ZeroPageX, 0x57)
|
|
il(SRE, IndexedX, 0x43)
|
|
il(SRE, IndexedY, 0x53)
|
|
il(SRE, Absolute, 0x4F)
|
|
il(SRE, AbsoluteX, 0x5F)
|
|
il(SRE, AbsoluteY, 0x5B)
|
|
|
|
il(RRA, ZeroPage, 0x67)
|
|
il(RRA, ZeroPageX, 0x77)
|
|
il(RRA, IndexedX, 0x63)
|
|
il(RRA, IndexedY, 0x73)
|
|
il(RRA, Absolute, 0x6F)
|
|
il(RRA, AbsoluteX, 0x7F)
|
|
il(RRA, AbsoluteY, 0x7B)
|
|
|
|
il(DCP, ZeroPage, 0xC7)
|
|
il(DCP, ZeroPageX, 0xD7)
|
|
il(DCP, IndexedX, 0xC3)
|
|
il(DCP, IndexedY, 0xD3)
|
|
il(DCP, Absolute, 0xCF)
|
|
il(DCP, AbsoluteX, 0xDF)
|
|
il(DCP, AbsoluteY, 0xDB)
|
|
|
|
il(ISC, ZeroPage, 0xE7)
|
|
il(ISC, ZeroPageX, 0xF7)
|
|
il(ISC, IndexedX, 0xE3)
|
|
il(ISC, IndexedY, 0xF3)
|
|
il(ISC, Absolute, 0xEF)
|
|
il(ISC, AbsoluteX, 0xFF)
|
|
il(ISC, AbsoluteY, 0xFB)
|
|
|
|
il(NOP, Immediate, 0x80)
|
|
il(NOP, ZeroPage, 0x44)
|
|
il(NOP, ZeroPageX, 0x54)
|
|
il(NOP, Absolute, 0x5C)
|
|
il(NOP, AbsoluteX, 0x1C)
|
|
|
|
cn(NOP, Immediate, 0x02)
|
|
cn(NOP, ZeroPage, 0x44)
|
|
cn(NOP, ZeroPageX, 0x54)
|
|
cn(NOP, Absolute, 0x5C)
|
|
|
|
cm(STZ, ZeroPage, 0x64)
|
|
cm(STZ, ZeroPageX, 0x74)
|
|
cm(STZ, Absolute, 0x9C)
|
|
cm(STZ, AbsoluteX, 0x9E)
|
|
|
|
cm(PHX, Implied, 0xDA)
|
|
cm(PHY, Implied, 0x5A)
|
|
cm(PLX, Implied, 0xFA)
|
|
cm(PLY, Implied, 0x7A)
|
|
|
|
cm(ORA, IndexedZ, 0x12)
|
|
cm(AND, IndexedZ, 0x32)
|
|
cm(EOR, IndexedZ, 0x52)
|
|
cm(ADC, IndexedZ, 0x72)
|
|
cm(STA, IndexedZ, 0x92)
|
|
cm(LDA, IndexedZ, 0xB2)
|
|
cm(CMP, IndexedZ, 0xD2)
|
|
cm(SBC, IndexedZ, 0xF2)
|
|
|
|
sc(TSB, ZeroPage, 0x04)
|
|
sc(TSB, Absolute, 0x0C)
|
|
sc(TRB, ZeroPage, 0x14)
|
|
sc(TRB, Absolute, 0x1C)
|
|
|
|
cm(BRA, Relative, 0x80)
|
|
cm(BIT, ZeroPageX, 0x34)
|
|
cm(BIT, AbsoluteX, 0x3C)
|
|
cm(INC, Implied, 0x1A)
|
|
cm(DEC, Implied, 0x3A)
|
|
cm(JMP, AbsoluteIndexedX, 0x7C)
|
|
wd(WAI, Implied, 0xCB)
|
|
wd(STP, Implied, 0xDB)
|
|
|
|
rw(RMB0, ZeroPage, 0x07)
|
|
rw(RMB1, ZeroPage, 0x17)
|
|
rw(RMB2, ZeroPage, 0x27)
|
|
rw(RMB3, ZeroPage, 0x37)
|
|
rw(RMB4, ZeroPage, 0x47)
|
|
rw(RMB5, ZeroPage, 0x57)
|
|
rw(RMB6, ZeroPage, 0x67)
|
|
rw(RMB7, ZeroPage, 0x77)
|
|
rw(SMB0, ZeroPage, 0x87)
|
|
rw(SMB1, ZeroPage, 0x97)
|
|
rw(SMB2, ZeroPage, 0xa7)
|
|
rw(SMB3, ZeroPage, 0xb7)
|
|
rw(SMB4, ZeroPage, 0xc7)
|
|
rw(SMB5, ZeroPage, 0xd7)
|
|
rw(SMB6, ZeroPage, 0xe7)
|
|
rw(SMB7, ZeroPage, 0xf7)
|
|
sb(BBR0, 0x0f)
|
|
sb(BBR1, 0x1f)
|
|
sb(BBR2, 0x2f)
|
|
sb(BBR3, 0x3f)
|
|
sb(BBR4, 0x4f)
|
|
sb(BBR5, 0x5f)
|
|
sb(BBR6, 0x6f)
|
|
sb(BBR7, 0x7f)
|
|
sb(BBS0, 0x8f)
|
|
sb(BBS1, 0x9f)
|
|
sb(BBS2, 0xaf)
|
|
sb(BBS3, 0xbf)
|
|
sb(BBS4, 0xcf)
|
|
sb(BBS5, 0xdf)
|
|
sb(BBS6, 0xef)
|
|
sb(BBS7, 0xff)
|
|
|
|
ce(CPZ, Immediate, 0xC2)
|
|
ce(CPZ, ZeroPage, 0xD4)
|
|
ce(CPZ, Absolute, 0xDC)
|
|
ce(DEZ, Implied, 0x3B)
|
|
ce(INZ, Implied,0x1B )
|
|
ce(DEC_W, ZeroPage, 0xC3)
|
|
ce(INC_W, ZeroPage, 0xE3)
|
|
ce(ASL_W, Absolute, 0xCB)
|
|
ce(ROL_W, Absolute, 0xEB)
|
|
ce(ASR, Implied, 0x43)
|
|
ce(ASR, ZeroPage, 0x44)
|
|
ce(ASR, ZeroPageX, 0x54)
|
|
ce(LDZ, Immediate, 0xA3)
|
|
ce(LDZ, Absolute, 0xAB)
|
|
ce(LDZ, AbsoluteX, 0xBB)
|
|
ce(TAB, Implied, 0x5B)
|
|
ce(TBA, Implied, 0x7B)
|
|
ce(TAZ, Implied, 0x4B)
|
|
ce(TZA, Implied, 0x6B)
|
|
ce(TSY, Implied, 0x0B)
|
|
ce(TYS, Implied, 0x2B)
|
|
ce(PHW, WordImmediate, 0xF4)
|
|
ce(PHW, Absolute, 0xFC)
|
|
ce(PHZ, Implied, 0xDB)
|
|
ce(PLZ, Implied, 0xFB)
|
|
ce(JSR, Indirect, 0x22)
|
|
ce(JSR, AbsoluteIndexedX, 0x23)
|
|
ce(CLE, Implied, 0x02)
|
|
ce(SEE, Implied, 0x03)
|
|
ce(NEG, Implied, 0x42)
|
|
ce(MAP, Implied, 0x5C)
|
|
ce(LDA, IndexedSY, 0xE2)
|
|
ce(STA, IndexedSY, 0x82)
|
|
ce(BSR, LongRelative, 0x63)
|
|
ce(BRA, LongRelative, 0x83)
|
|
ce(BPL, LongRelative, 0x13)
|
|
ce(BMI, LongRelative, 0x33)
|
|
ce(BVC, LongRelative, 0x53)
|
|
ce(BVS, LongRelative, 0x73)
|
|
ce(BCC, LongRelative, 0x93)
|
|
ce(BCS, LongRelative, 0xb3)
|
|
ce(BNE, LongRelative, 0xd3)
|
|
ce(BEQ, LongRelative, 0xf3)
|
|
|
|
hu(BSR, Relative, 0x44)
|
|
hu(CLY, Implied, 0xC2)
|
|
hu(CLX, Implied, 0x82)
|
|
hu(CLA, Implied, 0x62)
|
|
hu(CSH, Implied, 0xD4)
|
|
hu(CSL, Implied, 0x54)
|
|
hu(SET, Implied, 0xF4)
|
|
hu(HuSAX, Implied, 0x22)
|
|
hu(SAY, Implied, 0x42)
|
|
hu(SXY, Implied, 0x02)
|
|
hu(TAM, Immediate, 0x53)
|
|
hu(TMA, Immediate, 0x43)
|
|
hu(ST0, Immediate, 0x03)
|
|
hu(ST1, Immediate, 0x13)
|
|
hu(ST2, Immediate, 0x23)
|
|
hu(STP, Implied, 0xDB)
|
|
ht(TAI, 0xf3)
|
|
ht(TIA, 0xe3)
|
|
ht(TII, 0x73)
|
|
ht(TIN, 0xd3)
|
|
ht(TDD, 0xc3)
|
|
|
|
em(ORA, Stack, 0x03)
|
|
em(ORA, IndexedSY, 0x13)
|
|
na(ORA, LongIndexedZ, 0x07)
|
|
na(ORA, LongIndexedY, 0x17)
|
|
na(ORA, LongAbsolute, 0x0F)
|
|
na(ORA, LongAbsoluteX, 0x1F)
|
|
em(AND, Stack, 0x23)
|
|
em(AND, IndexedSY, 0x33)
|
|
na(AND, LongIndexedZ, 0x27)
|
|
na(AND, LongIndexedY, 0x37)
|
|
na(AND, LongAbsolute, 0x2F)
|
|
na(AND, LongAbsoluteX, 0x3F)
|
|
em(EOR, Stack, 0x43)
|
|
em(EOR, IndexedSY, 0x53)
|
|
na(EOR, LongIndexedZ, 0x47)
|
|
na(EOR, LongIndexedY, 0x57)
|
|
na(EOR, LongAbsolute, 0x4F)
|
|
na(EOR, LongAbsoluteX, 0x5F)
|
|
em(ADC, Stack, 0x63)
|
|
em(ADC, IndexedSY, 0x73)
|
|
na(ADC, LongIndexedZ, 0x67)
|
|
na(ADC, LongIndexedY, 0x77)
|
|
na(ADC, LongAbsolute, 0x6F)
|
|
na(ADC, LongAbsoluteX, 0x7F)
|
|
em(STA, Stack, 0x83)
|
|
em(STA, IndexedSY, 0x93)
|
|
na(STA, LongIndexedZ, 0x87)
|
|
na(STA, LongIndexedY, 0x97)
|
|
na(STA, LongAbsolute, 0x8F)
|
|
na(STA, LongAbsoluteX, 0x9F)
|
|
em(LDA, Stack, 0xA3)
|
|
em(LDA, IndexedSY, 0xB3)
|
|
na(LDA, LongIndexedZ, 0xA7)
|
|
na(LDA, LongIndexedY, 0xB7)
|
|
na(LDA, LongAbsolute, 0xAF)
|
|
na(LDA, LongAbsoluteX, 0xBF)
|
|
em(CMP, Stack, 0xC3)
|
|
em(CMP, IndexedSY, 0xD3)
|
|
na(CMP, LongIndexedZ, 0xC7)
|
|
na(CMP, LongIndexedY, 0xD7)
|
|
na(CMP, LongAbsolute, 0xCF)
|
|
na(CMP, LongAbsoluteX, 0xDF)
|
|
em(SBC, Stack, 0xE3)
|
|
em(SBC, IndexedSY, 0xF3)
|
|
na(SBC, LongIndexedZ, 0xE7)
|
|
na(SBC, LongIndexedY, 0xF7)
|
|
na(SBC, LongAbsolute, 0xEF)
|
|
na(SBC, LongAbsoluteX, 0xFF)
|
|
|
|
em(COP, Immediate, 0x02)
|
|
em(XBA, Implied, 0xEB)
|
|
em(TXY, Implied, 0x9B)
|
|
em(TYX, Implied, 0xBB)
|
|
em(JSR, LongAbsolute, 0x22)
|
|
em(JSR, AbsoluteIndexedX, 0xFC)
|
|
|
|
|
|
na(RTL, Implied, 0x6B)
|
|
na(JMP, LongAbsolute, 0x5C)
|
|
na(JMP, LongIndirect, 0x7C)
|
|
na(BRL, LongRelative, 0x82)
|
|
|
|
em(PHD, Implied, 0x0B)
|
|
em(PLD, Implied, 0x2B)
|
|
em(PHB, Implied, 0x8B)
|
|
em(PLB, Implied, 0xAB)
|
|
em(PHK, Implied, 0x4B)
|
|
|
|
na(REP, Immediate, 0xC2)
|
|
na(SEP, Immediate, 0xE2)
|
|
|
|
na(XCE, Implied, 0xFB)
|
|
na(TCD, Implied, 0x5B)
|
|
na(TDC, Implied, 0x7B)
|
|
na(TSC, Implied, 0x3B)
|
|
na(TCS, Implied, 0x1B)
|
|
|
|
for {
|
|
((narrow, am), code) <- emulation65816Opcodes ++ opcodes ++ cmosOpcodes ++ native65816Opcodes
|
|
wide <- Opcode.widen(narrow)
|
|
} na(wide, if (am == Immediate) WordImmediate else am, code & 0xff)
|
|
|
|
}
|