diff --git a/src/main/scala/millfork/assembly/z80/ZLine.scala b/src/main/scala/millfork/assembly/z80/ZLine.scala index c1b172ea..caaea743 100644 --- a/src/main/scala/millfork/assembly/z80/ZLine.scala +++ b/src/main/scala/millfork/assembly/z80/ZLine.scala @@ -233,6 +233,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta case BYTE => " !byte " + parameter.toString // TODO: format? case LABEL => parameter.toString + ":" case RST => s" RST $parameter" + case IM => s" IM $parameter" case EX_AF_AF => " EX AF,AF'" case EX_SP => registers match { case OneRegister(r) => s" EX (SP),${asAssemblyString(r)})" @@ -257,6 +258,24 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta case OneRegisterOffset(r, o) => s" ${asAssemblyString(r, o)}" }).stripPrefix(" ") s" $op A,$ps" + case op if ZOpcodeClasses.BIT(op) => + val ps = registers match { + case OneRegister(r) => s" ${asAssemblyString(r)}" + case OneRegisterOffset(r, o) => s" ${asAssemblyString(r, o)}" + } + s" BIT ${ZOpcodeClasses.BIT_seq.indexOf(op)},$ps" + case op if ZOpcodeClasses.SET(op) => + val ps = registers match { + case OneRegister(r) => s" ${asAssemblyString(r)}" + case OneRegisterOffset(r, o) => s" ${asAssemblyString(r, o)}" + } + s" SET ${ZOpcodeClasses.SET_seq.indexOf(op)},$ps" + case op if ZOpcodeClasses.RES(op) => + val ps = registers match { + case OneRegister(r) => s" ${asAssemblyString(r)}" + case OneRegisterOffset(r, o) => s" ${asAssemblyString(r, o)}" + } + s" RES ${ZOpcodeClasses.RES_seq.indexOf(op)},$ps" case op => val os = op.toString//.stripSuffix("_16") val ps = registers match { @@ -349,6 +368,14 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta case OneRegisterOffset(s, _) => r == s case _ => false } + case op if ZOpcodeClasses.AllSingleBit(op) => registers match { + case OneRegister(MEM_HL) => r == H || r == L + case OneRegister(MEM_IX_D) => r == IXH || r == IXL + case OneRegister(MEM_IY_D) => r == IYH || r == IYL + case OneRegister(s) => r == s + case OneRegisterOffset(s, _) => r == s + case _ => false + } case INC_16 | DEC_16 | PUSH => registers match { case OneRegister(HL) => r == H || r == L case OneRegister(BC) => r == B || r == C @@ -384,6 +411,10 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta case OneRegisterOffset(s, p) => r == s && o == p case _ => false } + case op if ZOpcodeClasses.RES_or_SET(op) => registers match { + case OneRegisterOffset(s, p) => r == s && o == p + case _ => false + } case POP | INC_16 | DEC_16 => registers match { case OneRegister(IX | IY) => true case _ => false @@ -413,6 +444,10 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta case OneRegisterOffset(s, p) => r == s && o == p case _ => false } + case op if ZOpcodeClasses.AllSingleBit(op) => registers match { + case OneRegisterOffset(s, p) => r == s && o == p + case _ => false + } case PUSH | INC_16 | DEC_16 => registers match { case OneRegister(IX | IY) => true case _ => false @@ -466,6 +501,14 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta case OneRegisterOffset(s, _) => r == s case _ => false } + case op if ZOpcodeClasses.RES_or_SET(op) => registers match { + case OneRegister(MEM_HL) => r == H || r == L + case OneRegister(MEM_IX_D) => r == IXH || r == IXL + case OneRegister(MEM_IY_D) => r == IYH || r == IYL + case OneRegister(s) => r == s + case OneRegisterOffset(s, _) => r == s + case _ => false + } case INC_16 | DEC_16 | POP => registers match { case OneRegister(HL) => r == H || r == L case OneRegister(BC) => r == B || r == C diff --git a/src/main/scala/millfork/assembly/z80/ZOpcode.scala b/src/main/scala/millfork/assembly/z80/ZOpcode.scala index 49e7d312..dfed791e 100644 --- a/src/main/scala/millfork/assembly/z80/ZOpcode.scala +++ b/src/main/scala/millfork/assembly/z80/ZOpcode.scala @@ -13,10 +13,12 @@ object ZOpcode extends Enumeration { LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR, SCF, CCF, DAA, CPL, NEG, + RES0, RES1, RES2, RES3, RES4, RES5, RES6, RES7, + BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7, + SET0, SET1, SET2, SET3, SET4, SET5, SET6, SET7, POP, PUSH, NOP, - RLC, RRC, RL, RR, SLA, SRA, SRL, SLL, RLD, - BIT, RES, SET, + RLC, RRC, RL, RR, SLA, SRA, SRL, SLL, RLD, RRD, EXX, EX_DE_HL, EX_AF_AF, EX_SP, RST, IM, EI, DI, DJNZ, JP, JR, CALL, RET, RETN, RETI, HALT, @@ -28,9 +30,25 @@ object ZOpcodeClasses { import ZOpcode._ - val EdInstructions = Set(SLA, SRA, SRL, SLL, BIT, RES, SET) - val CbInstructions = Set(SLA, SRA, SRL, SLL, BIT, RES, SET) + + val RES_seq = IndexedSeq(RES0, RES1, RES2, RES3, RES4, RES5, RES6, RES7) + val SET_seq = IndexedSeq(SET0, SET1, SET2, SET3, SET4, SET5, SET6, SET7) + val BIT_seq = IndexedSeq(BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7) + private val all_bit_seq = BIT_seq ++ RES_seq ++ SET_seq + + def singleBitOpcode(op:ZOpcode.Value): Int = 0x40 + all_bit_seq.indexOf(op) * 8 + + val RES: Set[ZOpcode.Value] = RES_seq.toSet + val SET: Set[ZOpcode.Value] = SET_seq.toSet + val BIT: Set[ZOpcode.Value] = BIT_seq.toSet + val AllSingleBit: Set[ZOpcode.Value] = RES ++ SET ++ BIT + val RES_or_SET: Set[ZOpcode.Value] = RES ++ SET + + val CbInstructions: Set[ZOpcode.Value] = Set(SLA, SRA, SRL, SLL) ++ BIT ++ RES ++ SET val CbInstructionsUnlessA = Set(RLC, RRC, RL, RR) + val EdInstructions: Set[ZOpcode.Value] = Set(NEG, RETN, RETI, IM, RRD, RLD, + INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR, + LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR) ++ BIT ++ RES ++ SET val NoopDiscards = Set(DISCARD_F, DISCARD_A, DISCARD_HL, DISCARD_BCDEIX) @@ -52,7 +70,7 @@ object ZOpcodeClasses { val ChangesDEAlways = Set( LDI, LDIR, LDD, LDDR, EXX, EX_DE_HL, CALL, JR, JP, LABEL) - val ChangesOnlyRegister = Set(INC, DEC, INC_16, DEC_16, POP, EX_SP, IN_C, IN_IMM, RL, RR, RLC, RRC, SLA, SRA, SRL, SLL, SET, RES) + val ChangesOnlyRegister: Set[ZOpcode.Value] = Set(INC, DEC, INC_16, DEC_16, POP, EX_SP, IN_C, IN_IMM, RL, RR, RLC, RRC, SLA, SRA, SRL, SLL) ++ SET ++ RES val ChangesFirstRegister = Set(LD, LD_16, ADD_16, SBC_16) val ChangesAAlways = Set(DAA, ADD, ADC, SUB, SBC, XOR, OR, AND) val NonLinear = Set(JP, JR, CALL, LABEL, BYTE, EXX, EX_DE_HL, EX_SP, EXX, RET, RETI, RETN, HALT) diff --git a/src/main/scala/millfork/assembly/z80/opt/CoarseFlowAnalyzer.scala b/src/main/scala/millfork/assembly/z80/opt/CoarseFlowAnalyzer.scala index 201db64a..062585ce 100644 --- a/src/main/scala/millfork/assembly/z80/opt/CoarseFlowAnalyzer.scala +++ b/src/main/scala/millfork/assembly/z80/opt/CoarseFlowAnalyzer.scala @@ -58,6 +58,19 @@ object CoarseFlowAnalyzer { currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m ^ n) & 0xff), cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus) + case ZLine(INC, OneRegister(r), _, _) => + currentStatus = currentStatus. + copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus). + setRegister(r, currentStatus.getRegister(r).map(i => i.+(1).&(0xff))) + case ZLine(DEC, OneRegister(r), _, _) => + currentStatus = currentStatus. + copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus). + setRegister(r, currentStatus.getRegister(r).map(i => i.-(1).&(0xff))) + case ZLine(op, OneRegister(r), _, _) if ZOpcodeClasses.SET(op) => + currentStatus = currentStatus.setRegister(r, currentStatus.getRegister(r).map(i => i | 1.<<(ZOpcodeClasses.SET_seq.indexOf(op)))) + case ZLine(op, OneRegister(r), _, _) if ZOpcodeClasses.RES(op) => + currentStatus = currentStatus.setRegister(r, currentStatus.getRegister(r).map(i => i & ~1.<<(ZOpcodeClasses.RES_seq.indexOf(op)))) + case ZLine(ADD, OneRegisterOffset(s, o), _, _) => currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m + n) & 0xff), cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus) diff --git a/src/main/scala/millfork/assembly/z80/opt/ReverseFlowAnalyzer.scala b/src/main/scala/millfork/assembly/z80/opt/ReverseFlowAnalyzer.scala index 7811a3c0..e4334f1d 100644 --- a/src/main/scala/millfork/assembly/z80/opt/ReverseFlowAnalyzer.scala +++ b/src/main/scala/millfork/assembly/z80/opt/ReverseFlowAnalyzer.scala @@ -318,6 +318,10 @@ object ReverseFlowAnalyzer { currentImportance = currentImportance.butReadsRegister(s) case ZLine(INC | DEC | INC_16 | DEC_16, OneRegisterOffset(s, o), _, _) => currentImportance = currentImportance.butReadsRegister(s, o) + case ZLine(op, OneRegister(s), _, _) if ZOpcodeClasses.AllSingleBit(op)=> + currentImportance = currentImportance.butReadsRegister(s) + case ZLine(op, OneRegisterOffset(s, o), _, _) if ZOpcodeClasses.AllSingleBit(op)=> + currentImportance = currentImportance.butReadsRegister(s, o) case ZLine(POP, OneRegister(r), _, _) => currentImportance = currentImportance.butWritesRegister(r) case ZLine(PUSH, OneRegister(r), _, _) => diff --git a/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala b/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala index 5ceed9af..32326e06 100644 --- a/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala +++ b/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala @@ -348,6 +348,8 @@ object ZBuiltIns { calculateAddress case Some(NumericConstant(0xff | -1, _)) => calculateAddress :+ ZLine.ldImm8(lv, 0xff) + case Some(NumericConstant(n, _)) if n.&(n - 1) == 0 && ctx.options.flag(CompilationFlag.EmitExtended80Opcodes) => + calculateAddress :+ ZLine.register(ZOpcodeClasses.SET_seq(Integer.numberOfTrailingZeros(n.toInt)), lv) case _ => setup ++ List( ZLine.register(OR, lv), @@ -359,6 +361,8 @@ object ZBuiltIns { calculateAddress :+ ZLine.ldImm8(lv, 0) case Some(NumericConstant(0xff | -1, _)) => calculateAddress + case Some(NumericConstant(n, _)) if n.^(0xff).&(n.^(0xff) - 1) == 0 && ctx.options.flag(CompilationFlag.EmitExtended80Opcodes) => + calculateAddress :+ ZLine.register(ZOpcodeClasses.RES_seq(Integer.numberOfTrailingZeros(n.^(0xff).toInt)), lv) case _ => setup ++ List( ZLine.register(AND, lv), diff --git a/src/main/scala/millfork/output/Z80Assembler.scala b/src/main/scala/millfork/output/Z80Assembler.scala index ad036f05..3eb1867d 100644 --- a/src/main/scala/millfork/output/Z80Assembler.scala +++ b/src/main/scala/millfork/output/Z80Assembler.scala @@ -62,6 +62,20 @@ class Z80Assembler(program: Program, } writeByte(bank, index, opcode) index + 1 + case ZLine(IM, NoRegisters, param, _) => + val opcode = param.quickSimplify match { + case NumericConstant(0, _) => 0x46 + case NumericConstant(1, _) => 0x56 + case NumericConstant(2, _) => 0x5e + case _ => ErrorReporting.error("Invalid param for IM"); 0xc7 + } + writeByte(bank, index, 0xED) + writeByte(bank, index + 1, opcode) + index + 2 + case ZLine(op, OneRegister(reg), _, _) if ZOpcodeClasses.AllSingleBit(op) => + writeByte(bank, index, 0xcb) + writeByte(bank, index + 1, ZOpcodeClasses.singleBitOpcode(op) + internalRegisterIndex(reg)) + index + 2 case ZLine(op, NoRegisters, _, _) if implieds.contains(op) => writeByte(bank, index, implieds(op)) index + 1 @@ -124,6 +138,12 @@ class Z80Assembler(program: Program, writeByte(bank, index, o) writeByte(bank, index + 1, param) index + 2 + case ZLine(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _, _) if ZOpcodeClasses.AllSingleBit(op) => + writeByte(bank, index, prefixByte(ix)) + writeByte(bank, index + 1, 0xcb) + writeByte(bank, index + 2, ZOpcodeClasses.singleBitOpcode(op) + internalRegisterIndex(ZRegister.MEM_HL)) + writeByte(bank, index + 3, offset) + index + 4 case ZLine(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _, _) if oneRegister.contains(op) => val o = oneRegister(op) writeByte(bank, index, prefixByte(ix)) @@ -406,6 +426,8 @@ object Z80Assembler { edImplieds(NEG) = 0x44 edImplieds(RETI) = 0x4d edImplieds(RETN) = 0x45 + edImplieds(RRD) = 0x67 + edImplieds(RLD) = 0x6f edImplieds(LDI) = 0xa0 edImplieds(LDIR) = 0xb0 edImplieds(CPI) = 0xa1 diff --git a/src/main/scala/millfork/parser/Z80Parser.scala b/src/main/scala/millfork/parser/Z80Parser.scala index f94f5e65..6657a0ca 100644 --- a/src/main/scala/millfork/parser/Z80Parser.scala +++ b/src/main/scala/millfork/parser/Z80Parser.scala @@ -195,6 +195,31 @@ case class Z80Parser(filename: String, input: String, currentDirectory: String, case "SRA" => one8Register(SRA) case "SRL" => one8Register(SRL) + case "BIT" => (param(false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(false)).map { + case (ZRegister.IMM_8, Some(LiteralExpression(n, _)), (r2, e2)) + if n >= 0 && n <= 7 && r2 != ZRegister.MEM_BC && r2 != ZRegister.MEM_DE => + (ZOpcodeClasses.BIT_seq(n.toInt), OneRegister(r2), e2, zero) + case _ => + ErrorReporting.error("Invalid parameters for BIT", Some(pos)) + (NOP, NoRegisters, None, zero) + } + case "SET" => (param(false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(false)).map { + case (ZRegister.IMM_8, Some(LiteralExpression(n, _)), (r2, e2)) + if n >= 0 && n <= 7 && r2 != ZRegister.MEM_BC && r2 != ZRegister.MEM_DE => + (ZOpcodeClasses.SET_seq(n.toInt), OneRegister(r2), e2, zero) + case _ => + ErrorReporting.error("Invalid parameters for SET", Some(pos)) + (NOP, NoRegisters, None, zero) + } + case "RES" => (param(false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(false)).map { + case (ZRegister.IMM_8, Some(LiteralExpression(n, _)), (r2, e2)) + if n >= 0 && n <= 7 && r2 != ZRegister.MEM_BC && r2 != ZRegister.MEM_DE => + (ZOpcodeClasses.RES_seq(n.toInt), OneRegister(r2), e2, zero) + case _ => + ErrorReporting.error("Invalid parameters for RES", Some(pos)) + (NOP, NoRegisters, None, zero) + } + case "SCF" => imm(SCF) case "CCF" => imm(CCF) case "CPL" => imm(CPL) diff --git a/src/test/scala/millfork/test/BitOpSuite.scala b/src/test/scala/millfork/test/BitOpSuite.scala index 6156483d..caca9edb 100644 --- a/src/test/scala/millfork/test/BitOpSuite.scala +++ b/src/test/scala/millfork/test/BitOpSuite.scala @@ -1,6 +1,7 @@ package millfork.test -import millfork.test.emu.EmuBenchmarkRun +import millfork.Cpu +import millfork.test.emu.EmuCrossPlatformBenchmarkRun import org.scalatest.{FunSuite, Matchers} /** @@ -9,7 +10,7 @@ import org.scalatest.{FunSuite, Matchers} class BitOpSuite extends FunSuite with Matchers { test("Word AND") { - EmuBenchmarkRun(""" + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(""" | word output @$c000 | void main () { | byte b @@ -20,7 +21,7 @@ class BitOpSuite extends FunSuite with Matchers { """.stripMargin)(_.readWord(0xc000) should equal(0x44)) } test("Long AND and EOR") { - EmuBenchmarkRun(""" + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(""" | long output @$c000 | void main () { | byte b @@ -33,4 +34,29 @@ class BitOpSuite extends FunSuite with Matchers { | } """.stripMargin)(_.readLong(0xc000) should equal(0x40)) } + + test("Smart bit set/reset") { + EmuCrossPlatformBenchmarkRun(Cpu.Cmos, Cpu.Z80)(""" + | byte output @$c000 + | void main () { + | output = 5 + | barrier() + | output &= 0xfe + | barrier() + | output |= 2 + | } + | noinline void barrier() { } + """.stripMargin)(_.readLong(0xc000) should equal(6)) + } + + test("Smart bit set/reset 2") { + EmuCrossPlatformBenchmarkRun(Cpu.Cmos, Cpu.Z80)(""" + | byte output @$c000 + | void main () { + | output = 5 + | output &= 0xfe + | output |= 2 + | } + """.stripMargin)(_.readLong(0xc000) should equal(6)) + } }