mirror of
https://github.com/KarolS/millfork.git
synced 2025-03-21 06:30:14 +00:00
Z80: Emit RES and SET instructions
This commit is contained in:
parent
43c044c80c
commit
e280aca08b
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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), _, _) =>
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user