package millfork.output import millfork.CompilationFlag.EmitIllegals import millfork.assembly.OptimizationContext import millfork.assembly.opt.{SingleStatus, Status} import millfork.{CompilationFlag, CompilationOptions, Cpu, Platform} import millfork.assembly.z80.{ZOpcode, _} import millfork.assembly.z80.opt.{CoarseFlowAnalyzer, ConditionalInstructions, CpuStatus, JumpFollowing, JumpShortening} import millfork.compiler.z80.Z80Compiler import millfork.env._ import millfork.node.NiceFunctionProperty.DoesntWriteMemory import millfork.node.Z80NiceFunctionProperty.{DoesntChangeA, DoesntChangeBC, DoesntChangeCF, DoesntChangeDE, DoesntChangeHL, DoesntChangeIY, SetsATo} import millfork.node.{NiceFunctionProperty, Position, Program, ZRegister} import scala.annotation.tailrec import scala.collection.mutable /** * @author Karol Stasiak */ class Z80Assembler(program: Program, rootEnv: Environment, platform: Platform) extends AbstractAssembler[ZLine](program, rootEnv, platform, Z80InliningCalculator, Z80Compiler) { override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[ZLine]): List[ZLine] = { if (actuallyOptimize) { JumpShortening(f, ConditionalInstructions(options, JumpFollowing(options, code)), options) } else code } private def internalRegisterIndex(reg: ZRegister.Value): Int = reg match { case ZRegister.B => 0 case ZRegister.C => 1 case ZRegister.D => 2 case ZRegister.E => 3 case ZRegister.H => 4 case ZRegister.L => 5 case ZRegister.IXH => 4 case ZRegister.IXL => 5 case ZRegister.IYH => 4 case ZRegister.IYL => 5 case ZRegister.MEM_HL => 6 case ZRegister.MEM_IX_D => 6 case ZRegister.MEM_IY_D => 6 case ZRegister.A => 7 case ZRegister.BC => 0 case ZRegister.DE => 1 case ZRegister.HL => 2 case ZRegister.AF => 3 case ZRegister.SP => 3 } private def prefixByte(reg: ZRegister.Value): Int = reg match { case ZRegister.IX | ZRegister.MEM_IX_D => 0xdd case ZRegister.IY | ZRegister.MEM_IY_D => 0xfd case ZRegister.IXH | ZRegister.IXL => 0xdd case ZRegister.IYH | ZRegister.IYL => 0xfd } override def emitInstruction(bank: String, options: CompilationOptions, index: Int, instr: ZLine): Int = { import millfork.assembly.z80.ZOpcode._ import ZRegister._ import Z80Assembler._ import CompilationFlag._ def requireIntel8080(): Unit = if (!options.flag(EmitIntel8080Opcodes)) log.error("Unsupported instruction: " + instr) def requireZ80(): Unit = if (!options.flag(EmitZ80Opcodes)) log.error("Unsupported instruction: " + instr) def requireZ80Illegals(): Unit = if (!options.flag(EmitZ80Opcodes) || !options.flag(EmitIllegals)) log.error("Unsupported instruction: " + instr) def requireExtended80(): Unit = if (!options.flag(EmitExtended80Opcodes)) log.error("Unsupported instruction: " + instr) def requireSharp(): Unit = if (!options.flag(EmitSharpOpcodes)) log.error("Unsupported instruction: " + instr) def requireEZ80(): Unit = if (!options.flag(EmitEZ80Opcodes)) log.error("Unsupported instruction: " + instr) def requireIntel8085(): Unit = if (!options.flag(EmitIntel8085Opcodes)) log.error("Unsupported instruction: " + instr) def requireIntel8085Illegals(): Unit = if (!options.flag(EmitIntel8085Opcodes) || !options.flag(EmitIllegals)) log.error("Unsupported instruction: " + instr) def requireNext(): Unit = if (!options.flag(EmitZ80NextOpcodes)) log.error("Unsupported instruction: " + instr) def useSharpOpcodes():Boolean = { if (!options.flag(EmitSharpOpcodes) && !options.flag(EmitIntel8080Opcodes)) log.error("Cannot determine which variant to emit : " + instr) options.flag(EmitSharpOpcodes) } implicit val position = instr.source.map(sl => Position(sl.moduleName, sl.line, 0, 0)) try { instr match { case ZLine0(LABEL, NoRegisters, MemoryAddressConstant(Label(labelName))) => val bank0 = mem.banks(bank) labelMap(labelName) = bank0.index -> index index case ZLine0(BYTE, NoRegisters, param) => writeByte(bank, index, param) index + 1 case ZLine0(CHANGED_MEM, NoRegisters, MemoryAddressConstant(Label(l))) if l.contains("..brk") => breakpointSet += mem.banks(bank).index -> index index case ZLine0(DISCARD_F | DISCARD_HL | DISCARD_BC | DISCARD_DE | DISCARD_IX | DISCARD_IY | DISCARD_A | CHANGED_MEM, NoRegisters, _) => index case ZLine0(LABEL | BYTE | DISCARD_F | DISCARD_HL | DISCARD_BC | DISCARD_DE | DISCARD_IX | DISCARD_IY | DISCARD_A | CHANGED_MEM, _, _) => ??? case ZLine0(RIM, NoRegisters, _) => requireIntel8085() writeByte(bank, index, 0x20) index + 1 case ZLine0(SIM, NoRegisters, _) => requireIntel8085() writeByte(bank, index, 0x30) index + 1 case ZLine0(RST, NoRegisters, param) => val opcode = param.quickSimplify match { case NumericConstant(n, _) if n >=0 && n <= 0x38 && n % 8 == 0 => 0xc7 + n.toInt case _ => log.error("Invalid param for RST"); 0xc7 } writeByte(bank, index, opcode) index + 1 case ZLine0(IM, NoRegisters, param) => requireZ80() val opcode = param.quickSimplify match { case NumericConstant(0, _) => 0x46 case NumericConstant(1, _) => 0x56 case NumericConstant(2, _) => 0x5e case _ => log.error("Invalid param for IM"); 0xc7 } writeByte(bank, index, 0xED) writeByte(bank, index + 1, opcode) index + 2 case ZLine0(op, OneRegister(reg), _) if ZOpcodeClasses.AllSingleBit(op) => requireExtended80() writeByte(bank, index, 0xcb) writeByte(bank, index + 1, ZOpcodeClasses.singleBitOpcode(op) + internalRegisterIndex(reg)) index + 2 case ZLine0(op, NoRegisters, _) if implieds.contains(op) => writeByte(bank, index, implieds(op)) index + 1 case ZLine0(EXX, NoRegisters, _)=> requireZ80() writeByte(bank, index, 0xd9) index + 1 case ZLine0(EX_AF_AF, NoRegisters, _)=> requireZ80() writeByte(bank, index, 8) index + 1 case ZLine0(RETI, NoRegisters, _) => requireExtended80() if (useSharpOpcodes()) { writeByte(bank, index, 0xd9) index + 1 } else { writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x4d) index + 2 } case ZLine0(op, NoRegisters, _) if edImplieds.contains(op) => requireZ80() writeByte(bank, index, 0xed) writeByte(bank, index + 1, edImplieds(op)) index + 2 case ZLine0(op, NoRegisters, _) if nextEdImplieds.contains(op) => requireNext() writeByte(bank, index, 0xed) writeByte(bank, index + 1, nextEdImplieds(op)) index + 2 case ZLine0(ADD_16, TwoRegisters(r@(ZRegister.HL | ZRegister.BC | ZRegister.DE), ZRegister.A), _) => requireNext() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x33 - internalRegisterIndex(r)) index + 2 case ZLine0(ADD_16, TwoRegisters(r@(ZRegister.HL | ZRegister.BC | ZRegister.DE), ZRegister.IMM_16), nn) => requireNext() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x36 - internalRegisterIndex(r)) writeWord(bank, index + 2, nn) index + 4 case ZLine0(PUSH, OneRegister(ZRegister.IMM_16), nn) => requireNext() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x8A) writeByte(bank, index + 2, nn.hiByte) writeByte(bank, index + 3, nn.loByte) index + 4 case ZLine0(TEST, OneRegister(ZRegister.IMM_8), nn) => requireNext() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x27) writeByte(bank, index + 2, nn) index + 3 case ZLine0(NEXTREG, TwoRegisters(ZRegister.IMM_8, ZRegister.IMM_8), nn) => requireNext() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x91) writeWord(bank, index + 2, nn) index + 4 case ZLine0(NEXTREG, TwoRegisters(ZRegister.IMM_8, ZRegister.A), nn) => requireNext() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x92) writeByte(bank, index + 2, nn) index + 3 case ZLine0(ADD_16, TwoRegisters(ZRegister.HL, source), _) => writeByte(bank, index, 9 + 16 * internalRegisterIndex(source)) index + 1 case ZLine0(ADD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), source@(ZRegister.IX | ZRegister.IY)), _)=> requireZ80() if (ix == source) { writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, 9 + 16 * internalRegisterIndex(HL)) index + 2 } else { log.fatal("Cannot assemble " + instr) index } case ZLine0(ADD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), source), _) => requireZ80() writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, 9 + 16 * internalRegisterIndex(source)) index + 2 case ZLine0(ADC_16, TwoRegisters(ZRegister.HL, reg), _) => requireZ80() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x4a + 0x10 * internalRegisterIndex(reg)) index + 2 case ZLine0(SBC_16, TwoRegisters(ZRegister.HL, reg), _) => requireZ80() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x42 + 0x10 * internalRegisterIndex(reg)) index + 2 case ZLine0(LD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), ZRegister.IMM_16), param) => requireZ80() writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, 0x21) writeWord(bank, index + 2, param) index + 4 case ZLine0(LD_16, TwoRegisters(target, ZRegister.IMM_16), param) => writeByte(bank, index, 1 + 16 * internalRegisterIndex(target)) writeWord(bank, index + 1, param) index + 3 case ZLine0(LD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), ZRegister.MEM_ABS_16), param) => requireZ80() writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, 0x2a) writeWord(bank, index + 2, param) index + 4 case ZLine0(LD_16, TwoRegisters(ZRegister.MEM_ABS_16, ix@(ZRegister.IX | ZRegister.IY)), param) => requireZ80() writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, 0x22) writeWord(bank, index + 2, param) index + 4 case ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.MEM_ABS_16), param) => requireIntel8080() writeByte(bank, index, 0x2a) writeWord(bank, index + 1, param) index + 3 case ZLine0(LD_16, TwoRegisters(ZRegister.MEM_ABS_16, ZRegister.HL), param) => requireIntel8080() writeByte(bank, index, 0x22) writeWord(bank, index + 1, param) index + 3 case ZLine0(LD_16, TwoRegisters(reg@(ZRegister.BC | ZRegister.DE | ZRegister.SP), ZRegister.MEM_ABS_16), param) => requireZ80() writeByte(bank, index, 0xed) writeByte(bank, index+1, 0x4b + 0x10 * internalRegisterIndex(reg)) writeWord(bank, index + 2, param) index + 4 case ZLine0(LD_16, TwoRegisters(ZRegister.MEM_ABS_16, ZRegister.SP), param) => requireExtended80() if (useSharpOpcodes()) { writeByte(bank, index, 8) writeWord(bank, index + 1, param) index + 3 } else { writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x43 + 0x10 * internalRegisterIndex(SP)) writeWord(bank, index + 2, param) index + 4 } case ZLine0(LD_16, TwoRegisters(ZRegister.MEM_ABS_16, reg@(ZRegister.BC | ZRegister.DE)), param) => requireZ80() writeByte(bank, index, 0xed) writeByte(bank, index+1, 0x43 + 0x10 * internalRegisterIndex(reg)) writeWord(bank, index + 2, param) index + 4 case ZLine0(LD_16, TwoRegisters(ZRegister.SP, ZRegister.HL), _) => writeByte(bank, index, 0xF9) index + 1 case ZLine0(LD_16, TwoRegisters(ZRegister.SP, ix@(ZRegister.IX | ZRegister.IY)), _) => requireZ80() writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, 0xF9) index + 2 case ZLine0(SRA, OneRegister(HL), _) => requireIntel8085Illegals() writeByte(bank, index, 0x10) index + 1 case ZLine0(RL, OneRegister(DE), _) => requireIntel8085Illegals() writeByte(bank, index, 0x18) index + 1 case ZLine0(op, OneRegister(ZRegister.IMM_8), param) if immediates.contains(op) => val o = immediates(op) writeByte(bank, index, o) writeByte(bank, index + 1, param) index + 2 case ZLine0(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _) if ZOpcodeClasses.AllSingleBit(op) => requireZ80() writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, 0xcb) writeByte(bank, index + 2, offset) writeByte(bank, index + 3, ZOpcodeClasses.singleBitOpcode(op) + internalRegisterIndex(ZRegister.MEM_HL)) index + 4 case ZLine0(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _) if oneRegister.contains(op) => requireZ80() val o = oneRegister(op) writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.MEM_HL) * o.multiplier) writeByte(bank, index + 2, offset) index + 3 case ZLine0(op, OneRegister(ix@(ZRegister.IX | ZRegister.IY)), _) if oneRegister.contains(op) => requireZ80() val o = oneRegister(op) writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.HL) * o.multiplier) writeByte(bank, index + 2, instr.parameter) index + 3 case ZLine0(op, OneRegister(ix@(IXH | IYH | IXL | IYL)), _) if oneRegister.contains(op) => requireZ80Illegals() val o = oneRegister(op) writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ix) * o.multiplier) index + 2 case ZLine0(op, OneRegister(reg), _) if reg != ZRegister.IMM_8 && oneRegister.contains(op) => val o = oneRegister(op) writeByte(bank, index, o.opcode + internalRegisterIndex(reg) * o.multiplier) index + 1 case ZLine0(SLL, OneRegister(reg), _) => requireZ80Illegals() writeByte(bank, index, 0xcb) writeByte(bank, index + 1, 0x30 + internalRegisterIndex(reg)) index + 2 case ZLine0(SWAP, OneRegister(reg), _) => requireSharp() writeByte(bank, index, 0xcb) writeByte(bank, index + 1, 0x30 + internalRegisterIndex(reg)) index + 2 case ZLine0(SLL, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _) => requireZ80Illegals() writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, 0xcb) writeByte(bank, index + 2, offset) writeByte(bank, index + 3, 0x30 + internalRegisterIndex(MEM_HL)) index + 4 case ZLine0(op, OneRegister(reg), _) if cbOneRegister.contains(op) => requireExtended80() val o = cbOneRegister(op) writeByte(bank, index, 0xcb) writeByte(bank, index + 1, o.opcode + internalRegisterIndex(reg) * o.multiplier) index + 2 case ZLine0(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _) if cbOneRegister.contains(op) => requireZ80() val o = cbOneRegister(op) writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, 0xcb) writeByte(bank, index + 2, offset) writeByte(bank, index + 3, o.opcode + internalRegisterIndex(MEM_HL) * o.multiplier) index + 4 case ZLine0(LD, registers, _) => registers match { case TwoRegisters(I, A) => requireZ80() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x47) index + 2 case TwoRegisters(A, I) => requireZ80() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x57) index + 2 case TwoRegisters(R, A) => requireZ80() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x4f) index + 2 case TwoRegisters(A, R) => requireZ80() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x5f) index + 2 case TwoRegisters(I | R, _) | TwoRegisters(_, I | R) => log.fatal("Cannot assemble " + instr) index case TwoRegisters(reg, ZRegister.IMM_8) => writeByte(bank, index, 6 + 8 * internalRegisterIndex(reg)) writeByte(bank, index + 1, instr.parameter) index + 2 case TwoRegistersOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), ZRegister.IMM_8, offset) => requireZ80() writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, 0x36) writeByte(bank, index + 2, offset) writeByte(bank, index + 3, instr.parameter) index + 4 case TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8) => if (useSharpOpcodes()) { writeByte(bank, index, 0xfa) } else { writeByte(bank, index, 0x3a) } writeWord(bank, index + 1, instr.parameter) index + 3 case TwoRegisters(ZRegister.MEM_ABS_8, ZRegister.A) => if (useSharpOpcodes()) { writeByte(bank, index, 0xea) } else { writeByte(bank, index, 0x32) } writeWord(bank, index + 1, instr.parameter) index + 3 case TwoRegisters(ZRegister.MEM_BC, ZRegister.A) => writeByte(bank, index, 2) index + 1 case TwoRegisters(ZRegister.MEM_DE, ZRegister.A) => writeByte(bank, index, 0x12) index + 1 case TwoRegisters(ZRegister.A, ZRegister.MEM_BC) => writeByte(bank, index, 0x0a) index + 1 case TwoRegisters(ZRegister.A, ZRegister.MEM_DE) => writeByte(bank, index, 0x1a) index + 1 case TwoRegistersOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), source, offset) => requireZ80() writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(ZRegister.MEM_HL) * 8) writeByte(bank, index + 2, offset) index + 3 case TwoRegistersOffset(target, ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset) => requireZ80() writeByte(bank, index, prefixByte(ix)) writeByte(bank, index + 1, 0x40 + internalRegisterIndex(ZRegister.MEM_HL) + internalRegisterIndex(target) * 8) writeByte(bank, index + 2, offset) index + 3 case TwoRegisters(target@(IXH | IYH | IXL | IYL), source@(A | B | C | D | E)) => requireZ80Illegals() writeByte(bank, index, prefixByte(target)) writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8) index + 2 case TwoRegisters(target@(A | B | C | D | E), source@(IXH | IYH | IXL | IYL)) => requireZ80Illegals() writeByte(bank, index, prefixByte(source)) writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8) index + 2 case TwoRegisters(target@(IXH | IXL), source@(IXH | IXL)) => requireZ80Illegals() writeByte(bank, index, prefixByte(source)) writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8) index + 2 case TwoRegisters(target@(IYH | IYL), source@(IYH | IYL)) => requireZ80Illegals() writeByte(bank, index, prefixByte(source)) writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8) index + 2 case TwoRegisters(target@(H | L), source@(H | L)) => writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8) index + 1 case TwoRegisters(target@(IXH | IYH | IXL | IYL | H | L), source@(IXH | IYH | IXL | IYL | H | L)) => log.error("Cannot assemble " + instr) writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8) index + 1 case TwoRegisters(target, source) => writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8) index + 1 case TwoRegisters(target, source) => writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8) index + 1 } case ZLine0(IN_IMM, OneRegister(ZRegister.A), param) => requireIntel8080() writeByte(bank, index, 0xdb) writeByte(bank, index + 1, param) index + 2 case ZLine0(IN_C, OneRegister(reg), param) => requireZ80() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x40 + 8 * internalRegisterIndex(reg)) index + 2 case ZLine0(OUT_IMM, OneRegister(ZRegister.A), param) => requireIntel8080() writeByte(bank, index, 0xd3) writeByte(bank, index + 1, param) index + 2 case ZLine0(OUT_C, OneRegister(reg), param) => requireZ80() writeByte(bank, index, 0xed) writeByte(bank, index + 1, 0x41 + 8 * internalRegisterIndex(reg)) index + 2 case ZLine0(CALL, NoRegisters, param) => writeByte(bank, index, 0xcd) writeWord(bank, index + 1, param) index + 3 case ZLine0(CALL, IfFlagClear(ZFlag.Z), param) => writeByte(bank, index, 0xc4) writeWord(bank, index + 1, param) index + 3 case ZLine0(CALL, IfFlagClear(ZFlag.C), param) => writeByte(bank, index, 0xd4) writeWord(bank, index + 1, param) index + 3 case ZLine0(CALL, IfFlagClear(ZFlag.P), param) => requireIntel8080() writeByte(bank, index, 0xe4) writeWord(bank, index + 1, param) index + 3 case ZLine0(CALL, IfFlagClear(ZFlag.S), param) => requireIntel8080() writeByte(bank, index, 0xf4) writeWord(bank, index + 1, param) index + 3 case ZLine0(CALL, IfFlagSet(ZFlag.Z), param) => writeByte(bank, index, 0xcc) writeWord(bank, index + 1, param) index + 3 case ZLine0(CALL, IfFlagSet(ZFlag.C), param) => writeByte(bank, index, 0xdc) writeWord(bank, index + 1, param) index + 3 case ZLine0(CALL, IfFlagSet(ZFlag.P), param) => requireIntel8080() writeByte(bank, index, 0xec) writeWord(bank, index + 1, param) index + 3 case ZLine0(CALL, IfFlagSet(ZFlag.S), param) => requireIntel8080() writeByte(bank, index, 0xfc) writeWord(bank, index + 1, param) index + 3 case ZLine0(JP, NoRegisters, param) => writeByte(bank, index, 0xc3) writeWord(bank, index + 1, param) index + 3 case ZLine0(JP, IfFlagClear(ZFlag.Z), param) => writeByte(bank, index, 0xc2) writeWord(bank, index + 1, param) index + 3 case ZLine0(JP, IfFlagClear(ZFlag.C), param) => writeByte(bank, index, 0xd2) writeWord(bank, index + 1, param) index + 3 case ZLine0(JP, IfFlagClear(ZFlag.P), param) => requireIntel8080() writeByte(bank, index, 0xe2) writeWord(bank, index + 1, param) index + 3 case ZLine0(JP, IfFlagClear(ZFlag.S), param) => requireIntel8080() writeByte(bank, index, 0xf2) writeWord(bank, index + 1, param) index + 3 case ZLine0(JP, IfFlagClear(ZFlag.K), param) => requireIntel8085Illegals() writeByte(bank, index, 0xdd) writeWord(bank, index + 1, param) index + 3 case ZLine0(JP, IfFlagSet(ZFlag.Z), param) => writeByte(bank, index, 0xca) writeWord(bank, index + 1, param) index + 3 case ZLine0(JP, IfFlagSet(ZFlag.C), param) => writeByte(bank, index, 0xda) writeWord(bank, index + 1, param) index + 3 case ZLine0(JP, IfFlagSet(ZFlag.P), param) => requireIntel8080() writeByte(bank, index, 0xea) writeWord(bank, index + 1, param) index + 3 case ZLine0(JP, IfFlagSet(ZFlag.S), param) => requireIntel8080() writeByte(bank, index, 0xfa) writeWord(bank, index + 1, param) index + 3 case ZLine0(JP, IfFlagSet(ZFlag.K), param) => requireIntel8085Illegals() writeByte(bank, index, 0xfd) writeWord(bank, index + 1, param) index + 3 case ZLine0(JP, OneRegister(HL), _) => writeByte(bank, index, 0xe9) index + 1 case ZLine0(JP, OneRegister(IX), _) => requireZ80() writeByte(bank, index, 0xdd) writeByte(bank, index, 0xe9) index + 2 case ZLine0(JP, OneRegister(IY), _) => requireZ80() writeByte(bank, index, 0xfd) writeByte(bank, index, 0xe9) index + 2 case ZLine0(RET, IfFlagClear(ZFlag.Z), _) => writeByte(bank, index, 0xc0) index + 1 case ZLine0(RET, IfFlagClear(ZFlag.C), _) => writeByte(bank, index, 0xd0) index + 1 case ZLine0(RET, IfFlagClear(ZFlag.P), _) => requireIntel8080() writeByte(bank, index, 0xe0) index + 1 case ZLine0(RET, IfFlagClear(ZFlag.S), _) => requireIntel8080() writeByte(bank, index, 0xf0) index + 1 case ZLine0(RET, IfFlagSet(ZFlag.Z), _) => writeByte(bank, index, 0xc8) index + 1 case ZLine0(RET, IfFlagSet(ZFlag.C), _) => writeByte(bank, index, 0xd8) index + 1 case ZLine0(RET, IfFlagSet(ZFlag.P), _) => requireIntel8080() writeByte(bank, index, 0xe8) index + 1 case ZLine0(RET, IfFlagSet(ZFlag.S), _) => requireIntel8080() writeByte(bank, index, 0xf8) index + 1 case ZLine0(JR, IfFlagClear(ZFlag.Z), param) => requireExtended80() writeByte(bank, index, 0x20) writeByte(bank, index + 1, AssertByte(param - index - 2)) index + 2 case ZLine0(JR, IfFlagClear(ZFlag.C), param) => requireExtended80() writeByte(bank, index, 0x30) writeByte(bank, index + 1, AssertByte(param - index - 2)) index + 2 case ZLine0(JR, IfFlagSet(ZFlag.Z), param) => requireExtended80() writeByte(bank, index, 0x28) writeByte(bank, index + 1, AssertByte(param - index - 2)) index + 2 case ZLine0(JR, IfFlagSet(ZFlag.C), param) => requireExtended80() writeByte(bank, index, 0x38) writeByte(bank, index + 1, AssertByte(param - index - 2)) index + 2 case ZLine0(JR, NoRegisters, param) => requireExtended80() writeByte(bank, index, 0x18) writeByte(bank, index + 1, AssertByte(param - index - 2)) index + 2 case ZLine0(DJNZ, NoRegisters, param) => requireZ80() writeByte(bank, index, 0x10) writeByte(bank, index + 1, AssertByte(param - index - 2)) index + 2 case ZLine0(EX_SP, OneRegister(HL), _) => requireIntel8080() writeByte(bank, index, 0xe3) index + 1 case ZLine0(EX_SP, OneRegister(IX), _) => requireZ80() writeByte(bank, index, 0xdd) writeByte(bank, index + 1, 0xe3) index + 2 case ZLine0(EX_SP, OneRegister(IY), _) => requireZ80() writeByte(bank, index, 0xfd) writeByte(bank, index + 1, 0xe3) index + 2 case ZLine0(EX_DE_HL, _, _) => requireIntel8080() writeByte(bank, index, 0xeb) index + 1 case ZLine0(ADD_SP, _, param) => requireSharp() writeByte(bank, index, 0xe8) writeByte(bank, index + 1, param) index + 2 case ZLine0(LD_HLSP, _, param) => requireSharp() writeByte(bank, index, 0xf8) writeByte(bank, index + 1, param) index + 2 case ZLine0(LD_AHLI, _, _) => requireSharp() writeByte(bank, index, 0x2a) index + 1 case ZLine0(LD_AHLD, _, _) => requireSharp() writeByte(bank, index, 0x3a) index + 1 case ZLine0(LD_HLIA, _, _) => requireSharp() writeByte(bank, index, 0x22) index + 1 case ZLine0(LD_HLDA, _, _) => requireSharp() writeByte(bank, index, 0x32) index + 1 case ZLine0(LDH_AD, _, param) => requireSharp() writeByte(bank, index, 0xf0) writeByte(bank, index + 1, param.loByte) index + 2 case ZLine0(LDH_DA, _, param) => requireSharp() writeByte(bank, index, 0xe0) writeByte(bank, index + 1, param.loByte) index + 2 case ZLine0(LDH_AC, _, _) => requireSharp() writeByte(bank, index, 0xf2) index + 1 case ZLine0(LDH_CA, _, _) => requireSharp() writeByte(bank, index, 0xe2) index + 1 case ZLine0(STOP, _, _) => requireSharp() writeByte(bank, index, 0x10) index + 1 case ZLine0(DSUB, _, _) => requireIntel8085Illegals() writeByte(bank, index, 0x08) index + 1 case ZLine0(LD_DEHL, _, param) => requireIntel8085Illegals() writeByte(bank, index, 0x28) writeByte(bank, index + 1, param) index + 2 case ZLine0(LD_DESP, _, param) => requireIntel8085Illegals() writeByte(bank, index, 0x38) writeByte(bank, index + 1, param) index + 2 case ZLine0(RSTV, _, _) => requireIntel8085Illegals() writeByte(bank, index, 0xcb) index + 1 case ZLine0(SHLX, _, _) => requireIntel8085Illegals() writeByte(bank, index, 0xd9) index + 1 case ZLine0(LHLX, _, _) => requireIntel8085Illegals() writeByte(bank, index, 0xed) index + 1 case ZLine0(RLDE, _, _) => requireIntel8085Illegals() writeByte(bank, index, 0x18) index + 1 case ZLine0(RRHL, _, _) => requireIntel8085Illegals() writeByte(bank, index, 0x10) index + 1 case _ => log.fatal("Cannot assemble " + instr) index } } catch { case _ : MatchError => log.fatal("Cannot assemble " + instr) index } } override def injectLabels(labelMap: Map[String, (Int, Int)], code: List[ZLine]): List[ZLine] = code // TODO override def quickSimplify(code: List[ZLine]): List[ZLine] = code.map(a => a.copy(parameter = a.parameter.quickSimplify)) override def gatherNiceFunctionProperties(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: NormalFunction, code: List[ZLine]): Unit = { import ZOpcode._ import NiceFunctionProperty._ val functionName = function.name if (isNaughty(code)) return val localLabels = code.flatMap { case ZLine0(LABEL, _, MemoryAddressConstant(Label(l))) => Some(l) case _ => None }.toSet if (code.exists { case ZLine0(JP | JR, _, MemoryAddressConstant(Label(l))) => !localLabels(l) case ZLine0(JP | JR, _, _) => true case ZLine0(RST, _, _) => true case _ => false }) return val optimizationContext = OptimizationContext(options, Map(), None, Set()) val flow = CoarseFlowAnalyzer.analyze(function, code, optimizationContext) def retPropertyScan[T](extractor: CpuStatus => Status[T])(niceFunctionProperty: Status[T] => Option[NiceFunctionProperty]): Unit = { val statuses = code.zipWithIndex.flatMap{ case (ZLine0(RET | RETI | RETN, _, _), ix) => Some(extractor(flow(ix))) case _ => None }.toSet statuses.toSeq match { case Seq(only) => niceFunctionProperty(only).foreach { np => niceFunctionProperties += (np -> functionName) } case _ => } } def simpleRetPropertyScan[T](extractor: CpuStatus => Status[T])(niceFunctionProperty: T => NiceFunctionProperty): Unit = { retPropertyScan(extractor) { case SingleStatus(x) => Some(niceFunctionProperty(x)) case _ => None } } def genericPropertyScan(niceFunctionProperty: NiceFunctionProperty)(predicate: ZLine => Boolean): Unit = { val preserved = code.forall { case ZLine0(JP | JR, _, MemoryAddressConstant(Label(label))) => localLabels(label) case ZLine0(CALL | JP | JR, _, MemoryAddressConstant(th)) => niceFunctionProperties(niceFunctionProperty -> th.name) case ZLine0(CALL | JP | JR, _, _) => false case l => predicate(l) } if (preserved) { niceFunctionProperties += (niceFunctionProperty -> functionName) } } genericPropertyScan(DoesntChangeA)(l => !l.changesRegister(ZRegister.A)) genericPropertyScan(DoesntChangeHL)(l => !l.changesRegister(ZRegister.HL)) genericPropertyScan(DoesntChangeDE)(l => !l.changesRegister(ZRegister.DE)) genericPropertyScan(DoesntChangeBC)(l => !l.changesRegister(ZRegister.BC)) genericPropertyScan(DoesntChangeIY)(l => !l.changesRegister(ZRegister.IY)) simpleRetPropertyScan(_.a)(SetsATo) } override def gatherFunctionOptimizationHints(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: FunctionInMemory): Unit = { import NiceFunctionProperty._ val functionName = function.name if (function.optimizationHints("preserves_a")) niceFunctionProperties += DoesntChangeA -> functionName if (function.optimizationHints("preserves_bc")) niceFunctionProperties += DoesntChangeBC -> functionName if (function.optimizationHints("preserves_de")) niceFunctionProperties += DoesntChangeDE -> functionName if (function.optimizationHints("preserves_hl")) niceFunctionProperties += DoesntChangeHL -> functionName if (function.optimizationHints("preserves_cf")) niceFunctionProperties += DoesntChangeCF -> functionName if (function.optimizationHints("preserves_memory")) niceFunctionProperties += DoesntWriteMemory -> functionName if (function.optimizationHints("idempotent")) niceFunctionProperties += Idempotent -> functionName } @tailrec private def isNaughty(code: List[ZLine]): Boolean = { import ZOpcode._ code match { case ZLine0(JP, OneRegister(_), _) :: _ => true case ZLine0(PUSH, _, _) :: ZLine0(RET | RETI | RETN, _, _) :: _ => true case _ :: xs => isNaughty(xs) case Nil => false } } override def bytePseudoopcode: String = "DB" override def deduplicate(options: CompilationOptions, compiledFunctions: mutable.Map[String, CompiledFunction[ZLine]]): Unit = new Z80Deduplicate(rootEnv, options).apply(compiledFunctions) } object Z80Assembler { case class One(opcode: Int, multiplier: Int) val implieds: mutable.Map[ZOpcode.Value, Int] = mutable.Map[ZOpcode.Value, Int]() val immediates: mutable.Map[ZOpcode.Value, Int] = mutable.Map[ZOpcode.Value, Int]() val edImplieds: mutable.Map[ZOpcode.Value, Int] = mutable.Map[ZOpcode.Value, Int]() val oneRegister: mutable.Map[ZOpcode.Value, One] = mutable.Map[ZOpcode.Value, One]() val cbOneRegister: mutable.Map[ZOpcode.Value, One] = mutable.Map[ZOpcode.Value, One]() val nextEdImplieds: mutable.Map[ZOpcode.Value, Int] = mutable.Map[ZOpcode.Value, Int]() do { import ZOpcode._ implieds(NOP) = 0 implieds(DAA) = 0x27 implieds(SCF) = 0x37 implieds(CPL) = 0x2f implieds(CCF) = 0x3f implieds(RET) = 0xc9 implieds(EI) = 0xfb implieds(DI) = 0xf3 implieds(HALT) = 0x76 implieds(RLCA) = 7 implieds(RRCA) = 0xf implieds(RLA) = 0x17 implieds(RRA) = 0x1f immediates(ADD) = 0xc6 immediates(ADC) = 0xce immediates(SUB) = 0xd6 immediates(SBC) = 0xde immediates(AND) = 0xe6 immediates(XOR) = 0xee immediates(OR) = 0xf6 immediates(CP) = 0xfe edImplieds(NEG) = 0x44 edImplieds(RETN) = 0x45 edImplieds(RRD) = 0x67 edImplieds(RLD) = 0x6f edImplieds(LDI) = 0xa0 edImplieds(LDIR) = 0xb0 edImplieds(CPI) = 0xa1 edImplieds(CPIR) = 0xb1 edImplieds(INI) = 0xa2 edImplieds(INIR) = 0xb2 edImplieds(OUTI) = 0xa3 edImplieds(OUTIR) = 0xb3 edImplieds(LDD) = 0xa8 edImplieds(LDDR) = 0xb8 edImplieds(CPD) = 0xa9 edImplieds(CPDR) = 0xb9 edImplieds(IND) = 0xaa edImplieds(INDR) = 0xba edImplieds(OUTD) = 0xab edImplieds(OUTDR) = 0xbb oneRegister(ADD) = One(0x80, 1) oneRegister(ADC) = One(0x88, 1) oneRegister(SUB) = One(0x90, 1) oneRegister(SBC) = One(0x98, 1) oneRegister(AND) = One(0xa0, 1) oneRegister(XOR) = One(0xa8, 1) oneRegister(OR) = One(0xb0, 1) oneRegister(CP) = One(0xb8, 1) oneRegister(INC) = One(4, 8) oneRegister(DEC) = One(5, 8) oneRegister(INC_16) = One(3, 0x10) oneRegister(DEC_16) = One(0xb, 0x10) oneRegister(POP) = One(0xc1, 0x10) oneRegister(PUSH) = One(0xc5, 0x10) cbOneRegister(RLC) = One(0, 1) cbOneRegister(RRC) = One(8, 1) cbOneRegister(RL) = One(0x10, 1) cbOneRegister(RR) = One(0x18, 1) cbOneRegister(SLA) = One(0x20, 1) cbOneRegister(SRA) = One(0x28, 1) cbOneRegister(SRL) = One(0x38, 1) nextEdImplieds(LDIX) = 0xa4 nextEdImplieds(LDWS) = 0xa5 nextEdImplieds(LDIRX) = 0xb4 nextEdImplieds(LDDX) = 0xb5 nextEdImplieds(LDDRX) = 0xac nextEdImplieds(LDPIRX) = 0xbc nextEdImplieds(OUTINB) = 0x90 nextEdImplieds(MUL) = 0xa4 nextEdImplieds(SWAPNIB) = 0x23 nextEdImplieds(MIRROR) = 0x24 nextEdImplieds(PIXELDN) = 0x93 nextEdImplieds(PIXELAD) = 0x94 nextEdImplieds(SETAE) = 0x95 } while (false) }