1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-10-03 20:55:36 +00:00

Z80: Emit RES and SET instructions

This commit is contained in:
Karol Stasiak 2018-07-24 16:16:49 +02:00
parent 43c044c80c
commit e280aca08b
8 changed files with 163 additions and 8 deletions

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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), _, _) =>

View File

@ -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),

View File

@ -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

View File

@ -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)

View File

@ -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))
}
}