mirror of
https://github.com/KarolS/millfork.git
synced 2025-08-15 04:27:21 +00:00
Tons of Z80 improvements:
– long assignments – word and long comparisons – byte multiplication – correct sign extension – fixed flow analysis for LD A,(IX,0) and similar – unused variable store elimination – unused code elimination – unused label elimination – poinless stack stashing elimination – function inlining – other flow analysis improvements – other bugfixes – other stuff – more tests
This commit is contained in:
@@ -89,6 +89,14 @@ object ZLine {
|
|||||||
|
|
||||||
def jump(label: Label, condition: ZRegisters): ZLine = ZLine(JP, condition, label.toAddress)
|
def jump(label: Label, condition: ZRegisters): ZLine = ZLine(JP, condition, label.toAddress)
|
||||||
|
|
||||||
|
def jumpR(label: String): ZLine = ZLine(JR, NoRegisters, Label(label).toAddress)
|
||||||
|
|
||||||
|
def jumpR(label: Label): ZLine = ZLine(JR, NoRegisters, label.toAddress)
|
||||||
|
|
||||||
|
def jumpR(label: String, condition: ZRegisters): ZLine = ZLine(JR, condition, Label(label).toAddress)
|
||||||
|
|
||||||
|
def jumpR(label: Label, condition: ZRegisters): ZLine = ZLine(JR, condition, label.toAddress)
|
||||||
|
|
||||||
def djnz(label: String): ZLine = ZLine(DJNZ, NoRegisters, Label(label).toAddress)
|
def djnz(label: String): ZLine = ZLine(DJNZ, NoRegisters, Label(label).toAddress)
|
||||||
|
|
||||||
def djnz(label: Label): ZLine = ZLine(DJNZ, NoRegisters, label.toAddress)
|
def djnz(label: Label): ZLine = ZLine(DJNZ, NoRegisters, label.toAddress)
|
||||||
@@ -144,6 +152,10 @@ object ZLine {
|
|||||||
def ldViaIx(target: ZRegister.Value, sourceOffset: Int): ZLine = ZLine(LD, TwoRegistersOffset(target, ZRegister.MEM_IX_D, sourceOffset), Constant.Zero)
|
def ldViaIx(target: ZRegister.Value, sourceOffset: Int): ZLine = ZLine(LD, TwoRegistersOffset(target, ZRegister.MEM_IX_D, sourceOffset), Constant.Zero)
|
||||||
|
|
||||||
def ldViaIx(targetOffset: Int, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegistersOffset(ZRegister.MEM_IX_D, source, targetOffset), Constant.Zero)
|
def ldViaIx(targetOffset: Int, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegistersOffset(ZRegister.MEM_IX_D, source, targetOffset), Constant.Zero)
|
||||||
|
|
||||||
|
def ldViaIy(target: ZRegister.Value, sourceOffset: Int): ZLine = ZLine(LD, TwoRegistersOffset(target, ZRegister.MEM_IY_D, sourceOffset), Constant.Zero)
|
||||||
|
|
||||||
|
def ldViaIy(targetOffset: Int, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegistersOffset(ZRegister.MEM_IY_D, source, targetOffset), Constant.Zero)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Constant, elidable: Boolean = true) extends AbstractCode {
|
case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Constant, elidable: Boolean = true) extends AbstractCode {
|
||||||
@@ -228,10 +240,10 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||||||
case JP | JR | DJNZ | CALL =>
|
case JP | JR | DJNZ | CALL =>
|
||||||
val ps = registers match {
|
val ps = registers match {
|
||||||
case NoRegisters => s" $parameter"
|
case NoRegisters => s" $parameter"
|
||||||
case IfFlagSet(ZFlag.P) => " PO,$parameter"
|
case IfFlagSet(ZFlag.P) => s" PO,$parameter"
|
||||||
case IfFlagClear(ZFlag.P) => " PE,$parameter"
|
case IfFlagClear(ZFlag.P) => s" PE,$parameter"
|
||||||
case IfFlagSet(ZFlag.S) => " M,$parameter"
|
case IfFlagSet(ZFlag.S) => s" M,$parameter"
|
||||||
case IfFlagClear(ZFlag.S) => " P,$parameter"
|
case IfFlagClear(ZFlag.S) => s" P,$parameter"
|
||||||
case IfFlagSet(f) => s" $f,$parameter"
|
case IfFlagSet(f) => s" $f,$parameter"
|
||||||
case IfFlagClear(f) => s" N$f,$parameter"
|
case IfFlagClear(f) => s" N$f,$parameter"
|
||||||
case OneRegister(r) => s" (${asAssemblyString(r)})"
|
case OneRegister(r) => s" (${asAssemblyString(r)})"
|
||||||
@@ -277,6 +289,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||||||
case TwoRegisters(_, MEM_IY_D) => r == IYH || r == IYL
|
case TwoRegisters(_, MEM_IY_D) => r == IYH || r == IYL
|
||||||
case TwoRegisters(_, MEM_ABS_8 | MEM_ABS_16 | IMM_8 | IMM_16) => false
|
case TwoRegisters(_, MEM_ABS_8 | MEM_ABS_16 | IMM_8 | IMM_16) => false
|
||||||
case TwoRegisters(_, s) => r == s
|
case TwoRegisters(_, s) => r == s
|
||||||
|
case TwoRegistersOffset(_, s, _) => r == s
|
||||||
case _ => false
|
case _ => false
|
||||||
}) || (registers match {
|
}) || (registers match {
|
||||||
case TwoRegisters(MEM_HL, _) => r == H || r == L
|
case TwoRegisters(MEM_HL, _) => r == H || r == L
|
||||||
@@ -294,6 +307,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||||||
case TwoRegisters(_, MEM_IX_D | IX) => r == IXH || r == IXL
|
case TwoRegisters(_, MEM_IX_D | IX) => r == IXH || r == IXL
|
||||||
case TwoRegisters(_, MEM_IY_D | IY) => r == IYH || r == IYL
|
case TwoRegisters(_, MEM_IY_D | IY) => r == IYH || r == IYL
|
||||||
case TwoRegisters(_, s) => r == s
|
case TwoRegisters(_, s) => r == s
|
||||||
|
case TwoRegistersOffset(_, s, _) => r == s
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
case ADD | ADC | OR | XOR | CP | SUB | SBC => registers match {
|
case ADD | ADC | OR | XOR | CP | SUB | SBC => registers match {
|
||||||
@@ -304,6 +318,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||||||
case OneRegister(MEM_IY_D) => r == IYH || r == IYL || r == A
|
case OneRegister(MEM_IY_D) => r == IYH || r == IYL || r == A
|
||||||
case OneRegister(IMM_8 | IMM_16) => r == A
|
case OneRegister(IMM_8 | IMM_16) => r == A
|
||||||
case OneRegister(s) => r == s || r == A
|
case OneRegister(s) => r == s || r == A
|
||||||
|
case OneRegisterOffset(s, _) => r == s
|
||||||
case _ => r == A
|
case _ => r == A
|
||||||
}
|
}
|
||||||
case INC | DEC | RL | RLC | RR | RRC | SLA | SLL | SRA | SRL => registers match {
|
case INC | DEC | RL | RLC | RR | RRC | SLA | SLL | SRA | SRL => registers match {
|
||||||
@@ -313,6 +328,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||||||
case OneRegister(MEM_IX_D) => r == IXH || r == IXL
|
case OneRegister(MEM_IX_D) => r == IXH || r == IXL
|
||||||
case OneRegister(MEM_IY_D) => r == IYH || r == IYL
|
case OneRegister(MEM_IY_D) => r == IYH || r == IYL
|
||||||
case OneRegister(s) => r == s
|
case OneRegister(s) => r == s
|
||||||
|
case OneRegisterOffset(s, _) => r == s
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
case INC_16 | DEC_16 | PUSH => registers match {
|
case INC_16 | DEC_16 | PUSH => registers match {
|
||||||
@@ -322,13 +338,14 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||||||
case OneRegister(IX) => r == IXH || r == IXL
|
case OneRegister(IX) => r == IXH || r == IXL
|
||||||
case OneRegister(IY) => r == IYH || r == IYL
|
case OneRegister(IY) => r == IYH || r == IYL
|
||||||
case OneRegister(AF) => r == A
|
case OneRegister(AF) => r == A
|
||||||
|
case OneRegisterOffset(s, _) => r == s
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
case JP | JR | RET | RETI | RETN |
|
case JP | JR | RET | RETI | RETN |
|
||||||
POP |
|
POP |
|
||||||
DISCARD_A | DISCARD_BCDEIX | DISCARD_HL | DISCARD_F => false
|
DISCARD_A | DISCARD_BCDEIX | DISCARD_HL | DISCARD_F => false
|
||||||
case DJNZ => r == B
|
case DJNZ => r == B
|
||||||
case DAA => r == A
|
case DAA | NEG => r == A
|
||||||
case _ => true // TODO
|
case _ => true // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -348,6 +365,14 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||||||
case OneRegisterOffset(s, p) => r == s && o == p
|
case OneRegisterOffset(s, p) => r == s && o == p
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
|
case POP | INC_16 | DEC_16 => registers match {
|
||||||
|
case OneRegister(IX | IY) => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
case LD_16 | ADD_16 => registers match {
|
||||||
|
case TwoRegisters(IX | IY, _) => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
case _ => false // TODO
|
case _ => false // TODO
|
||||||
}
|
}
|
||||||
case _ => changesRegister(r)
|
case _ => changesRegister(r)
|
||||||
@@ -370,6 +395,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||||||
opcode match {
|
opcode match {
|
||||||
case LD => registers match {
|
case LD => registers match {
|
||||||
case TwoRegisters(s, _) => r == s
|
case TwoRegisters(s, _) => r == s
|
||||||
|
case TwoRegistersOffset(s, _, _) => r == s
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
case LD_16 | ADD_16 | SBC_16 | ADC_16 => registers match {
|
case LD_16 | ADD_16 | SBC_16 | ADC_16 => registers match {
|
||||||
@@ -379,10 +405,12 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||||||
case TwoRegisters(IX, _) => r == IXH || r == IXL
|
case TwoRegisters(IX, _) => r == IXH || r == IXL
|
||||||
case TwoRegisters(IY, _) => r == IYH || r == IYL
|
case TwoRegisters(IY, _) => r == IYH || r == IYL
|
||||||
case TwoRegisters(s, _) => r == s
|
case TwoRegisters(s, _) => r == s
|
||||||
|
case TwoRegistersOffset(s, _, _) => r == s
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
case INC | DEC | RL | RLC | RR | RRC | SLA | SLL | SRA | SRL => registers match {
|
case INC | DEC | RL | RLC | RR | RRC | SLA | SLL | SRA | SRL => registers match {
|
||||||
case OneRegister(s) => r == s
|
case OneRegister(s) => r == s
|
||||||
|
case OneRegisterOffset(s, _) => r == s
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
case INC_16 | DEC_16 | POP => registers match {
|
case INC_16 | DEC_16 | POP => registers match {
|
||||||
@@ -392,12 +420,13 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||||||
case OneRegister(IX) => r == IXH || r == IXL
|
case OneRegister(IX) => r == IXH || r == IXL
|
||||||
case OneRegister(IY) => r == IYH || r == IYL
|
case OneRegister(IY) => r == IYH || r == IYL
|
||||||
case OneRegister(AF) => r == A
|
case OneRegister(AF) => r == A
|
||||||
|
case OneRegisterOffset(s, _) => r == s
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
case JP | JR | RET | RETI | RETN |
|
case JP | JR | RET | RETI | RETN |
|
||||||
POP |
|
POP |
|
||||||
DISCARD_A | DISCARD_BCDEIX | DISCARD_HL | DISCARD_F => false
|
DISCARD_A | DISCARD_BCDEIX | DISCARD_HL | DISCARD_F => false
|
||||||
case ADD | ADC | OR | XOR | SUB | SBC | DAA => r == A
|
case ADD | ADC | OR | XOR | SUB | SBC | DAA | NEG => r == A
|
||||||
case CP => false
|
case CP => false
|
||||||
case DJNZ => r == B
|
case DJNZ => r == B
|
||||||
case _ => true // TODO
|
case _ => true // TODO
|
||||||
|
@@ -32,6 +32,15 @@ object ZOpcodeClasses {
|
|||||||
val CbInstructions = Set(SLA, SRA, SRL, SLL, BIT, RES, SET)
|
val CbInstructions = Set(SLA, SRA, SRL, SLL, BIT, RES, SET)
|
||||||
val CbInstructionsUnlessA = Set(RLC, RRC, RL, RR)
|
val CbInstructionsUnlessA = Set(RLC, RRC, RL, RR)
|
||||||
|
|
||||||
|
val NoopDiscards = Set(DISCARD_F, DISCARD_A, DISCARD_HL, DISCARD_BCDEIX)
|
||||||
|
|
||||||
|
val ChangesAFAlways = Set( // TODO: !
|
||||||
|
DAA, ADD, ADC, SUB, SBC, XOR, OR, AND, INC, DEC,
|
||||||
|
SCF, CCF, NEG,
|
||||||
|
ADD_16, ADC_16, SBC_16, INC_16, DEC_16,
|
||||||
|
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
||||||
|
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR,
|
||||||
|
EXX, CALL, JR, JP, LABEL, DJNZ)
|
||||||
val ChangesBCAlways = Set(
|
val ChangesBCAlways = Set(
|
||||||
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
||||||
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR,
|
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR,
|
||||||
|
@@ -3,7 +3,7 @@ package millfork.assembly.z80.opt
|
|||||||
import millfork.assembly.AssemblyOptimization
|
import millfork.assembly.AssemblyOptimization
|
||||||
import millfork.assembly.z80._
|
import millfork.assembly.z80._
|
||||||
import millfork.assembly.z80.ZOpcode._
|
import millfork.assembly.z80.ZOpcode._
|
||||||
import millfork.env.{Constant, NumericConstant}
|
import millfork.env.{CompoundConstant, Constant, MathOperator, NumericConstant}
|
||||||
import millfork.node.ZRegister
|
import millfork.node.ZRegister
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,7 +66,7 @@ object AlwaysGoodZ80Optimizations {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val ReloadingKnownValueFromMemory = new RuleBasedAssemblyOptimization("Reloading known value from memory",
|
val ReloadingKnownValueFromMemory = new RuleBasedAssemblyOptimization("Reloading known value from memory",
|
||||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||||
for7Registers(register =>
|
for7Registers(register =>
|
||||||
Is8BitLoad(ZRegister.MEM_HL, register) ~
|
Is8BitLoad(ZRegister.MEM_HL, register) ~
|
||||||
(Linear & Not(Changes(ZRegister.H)) & Not(Changes(ZRegister.L)) & Not(ChangesMemory) & Not(Changes(register)) & Not(IsRegular8BitLoadFrom(ZRegister.MEM_HL))).* ~
|
(Linear & Not(Changes(ZRegister.H)) & Not(Changes(ZRegister.L)) & Not(ChangesMemory) & Not(Changes(register)) & Not(IsRegular8BitLoadFrom(ZRegister.MEM_HL))).* ~
|
||||||
@@ -82,42 +82,74 @@ object AlwaysGoodZ80Optimizations {
|
|||||||
),
|
),
|
||||||
(Is8BitLoad(ZRegister.MEM_ABS_8, ZRegister.A) & MatchParameter(0)).captureLine(1) ~
|
(Is8BitLoad(ZRegister.MEM_ABS_8, ZRegister.A) & MatchParameter(0)).captureLine(1) ~
|
||||||
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
|
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
|
||||||
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)) ~~> { code => code.init }
|
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)) ~~> { code => code.init },
|
||||||
|
|
||||||
|
(Is8BitLoad(ZRegister.MEM_HL, ZRegister.A) & MatchConstantInHL(0)).captureLine(1) ~
|
||||||
|
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
|
||||||
|
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)) ~~> { code => code.init },
|
||||||
|
|
||||||
|
(Is8BitLoad(ZRegister.MEM_ABS_8, ZRegister.A) & MatchParameter(0)).captureLine(1) ~
|
||||||
|
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
|
||||||
|
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_HL) & MatchConstantInHL(0)) ~~> { code => code.init },
|
||||||
|
|
||||||
|
(Is8BitLoad(ZRegister.MEM_HL, ZRegister.A) & MatchConstantInHL(0)).captureLine(1) ~
|
||||||
|
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
|
||||||
|
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_HL) & MatchConstantInHL(0)) ~~> { code => code.init },
|
||||||
|
|
||||||
|
(Is16BitLoad(ZRegister.MEM_ABS_16, ZRegister.HL) & MatchParameter(0)).captureLine(1) ~
|
||||||
|
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.HL))).* ~
|
||||||
|
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.MEM_ABS_16) & MatchParameter(0)) ~~> { code => code.init },
|
||||||
|
|
||||||
|
(Is8BitLoad(ZRegister.MEM_ABS_8, ZRegister.A) & MatchParameter(0) & MatchRegister(ZRegister.A, 2)).captureLine(1) ~
|
||||||
|
(Linear & DoesntChangeMemoryAt(1) & Not(Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8))).* ~
|
||||||
|
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)) ~~> { (code, ctx) =>
|
||||||
|
code.init :+ ZLine.ldImm8(ZRegister.A, ctx.get[Int](2))
|
||||||
|
},
|
||||||
|
|
||||||
|
(Is16BitLoad(ZRegister.MEM_ABS_16, ZRegister.HL) & MatchParameter(0) & MatchConstantInHL(2)).captureLine(1) ~
|
||||||
|
(Linear & DoesntChangeMemoryAt(1) & Not(Is16BitLoad(ZRegister.HL, ZRegister.MEM_ABS_16))).* ~
|
||||||
|
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.MEM_ABS_16) & MatchParameter(0)) ~~> { (code, ctx) =>
|
||||||
|
code.init :+ ZLine.ldImm16(ZRegister.HL, ctx.get[Constant](2))
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
val PointlessLoad = new RuleBasedAssemblyOptimization("Pointless load",
|
val PointlessLoad = new RuleBasedAssemblyOptimization("Pointless load",
|
||||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||||
|
// 0-6
|
||||||
for7Registers(register =>
|
for7Registers(register =>
|
||||||
(Elidable & Is8BitLoadTo(register) & DoesntMatterWhatItDoesWith(register)) ~~> (_ => Nil)
|
(Elidable & Is8BitLoadTo(register) & DoesntMatterWhatItDoesWith(register)) ~~> (_ => Nil)
|
||||||
),
|
),
|
||||||
|
// 7-11
|
||||||
for5LargeRegisters(register =>
|
for5LargeRegisters(register =>
|
||||||
(Elidable & Is16BitLoadTo(register) & DoesntMatterWhatItDoesWith(register)) ~~> (_ => Nil)
|
(Elidable & Is16BitLoadTo(register) & DoesntMatterWhatItDoesWith(register)) ~~> (_ => Nil)
|
||||||
),
|
),
|
||||||
|
// 12-18
|
||||||
for7Registers(register =>
|
for7Registers(register =>
|
||||||
(Is8BitLoad(register, ZRegister.IMM_8) & MatchImmediate(0)) ~
|
(Is8BitLoad(register, ZRegister.IMM_8) & MatchImmediate(0)) ~
|
||||||
(Linear & Not(Changes(register))).* ~
|
(Linear & Not(Changes(register))).* ~
|
||||||
(Elidable & Is8BitLoad(register, ZRegister.IMM_8) & MatchImmediate(0)) ~~> (_.init)
|
(Elidable & Is8BitLoad(register, ZRegister.IMM_8) & MatchImmediate(0)) ~~> (_.init)
|
||||||
),
|
),
|
||||||
|
// 19-23
|
||||||
for5LargeRegisters(register =>
|
for5LargeRegisters(register =>
|
||||||
(Is16BitLoad(register, ZRegister.IMM_16) & MatchImmediate(0)) ~
|
(Is16BitLoad(register, ZRegister.IMM_16) & MatchImmediate(0)) ~
|
||||||
(Linear & Not(Changes(register))).* ~
|
(Linear & Not(Changes(register))).* ~
|
||||||
(Elidable & Is16BitLoad(register, ZRegister.IMM_16) & MatchImmediate(0)) ~~> (_.init)
|
(Elidable & Is16BitLoad(register, ZRegister.IMM_16) & MatchImmediate(0)) ~~> (_.init)
|
||||||
),
|
),
|
||||||
|
// 24
|
||||||
(Elidable & Is8BitLoadTo(ZRegister.MEM_HL)) ~
|
(Elidable & Is8BitLoadTo(ZRegister.MEM_HL)) ~
|
||||||
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.HL))).* ~
|
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.HL))).* ~
|
||||||
Is8BitLoadTo(ZRegister.MEM_HL) ~~> (_.tail),
|
Is8BitLoadTo(ZRegister.MEM_HL) ~~> (_.tail),
|
||||||
|
// 25
|
||||||
(Elidable & Is8BitLoadTo(ZRegister.MEM_DE)) ~
|
(Elidable & Is8BitLoadTo(ZRegister.MEM_DE)) ~
|
||||||
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.DE))).* ~
|
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.DE))).* ~
|
||||||
Is8BitLoadTo(ZRegister.MEM_DE) ~~> (_.tail),
|
Is8BitLoadTo(ZRegister.MEM_DE) ~~> (_.tail),
|
||||||
|
// 26
|
||||||
(Elidable & Is8BitLoadTo(ZRegister.MEM_BC)) ~
|
(Elidable & Is8BitLoadTo(ZRegister.MEM_BC)) ~
|
||||||
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.BC))).* ~
|
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.BC))).* ~
|
||||||
Is8BitLoadTo(ZRegister.MEM_BC) ~~> (_.tail),
|
Is8BitLoadTo(ZRegister.MEM_BC) ~~> (_.tail),
|
||||||
|
// 27
|
||||||
(Elidable & MatchTargetIxOffsetOf8BitLoad(0) & MatchUnimportantIxOffset(0)) ~~> (_ => Nil),
|
(Elidable & MatchTargetIxOffsetOf8BitLoad(0) & MatchUnimportantIxOffset(0)) ~~> (_ => Nil),
|
||||||
|
// 28-34
|
||||||
for7Registers(register =>
|
for7Registers(register =>
|
||||||
(Elidable & Is8BitLoadTo(register) & NoOffset & MatchSourceRegisterAndOffset(1)) ~
|
(Elidable & Is8BitLoadTo(register) & NoOffset & MatchSourceRegisterAndOffset(1)) ~
|
||||||
(Linear & Not(Concerns(register)) & DoesntChangeMatchedRegisterAndOffset(1)).* ~
|
(Linear & Not(Concerns(register)) & DoesntChangeMatchedRegisterAndOffset(1)).* ~
|
||||||
@@ -132,10 +164,29 @@ object AlwaysGoodZ80Optimizations {
|
|||||||
}, head.parameter)
|
}, head.parameter)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
// 35-41
|
||||||
for7Registers(register =>
|
for7Registers(register =>
|
||||||
(Elidable & Is8BitLoad(register, register)) ~~> (_ => Nil)
|
(Elidable & Is8BitLoad(register, register)) ~~> (_ => Nil)
|
||||||
),
|
),
|
||||||
|
// 42-48
|
||||||
|
for7Registers(register =>
|
||||||
|
(Elidable & Is8BitLoadTo(register) & MatchSourceRegisterAndOffset(0)) ~
|
||||||
|
(Linear & Not(Concerns(register)) & DoesntChangeMatchedRegisterAndOffset(0)).* ~
|
||||||
|
(Elidable & HasOpcodeIn(Set(ADD, ADC, XOR, OR, AND, CP, SUB, SBC)) & HasRegisters(OneRegister(register)) & DoesntMatterWhatItDoesWith(register)) ~~> ((code,ctx) =>
|
||||||
|
code.tail.init :+ code.last.copy(registers = ctx.get[RegisterAndOffset](0).toOneRegister)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
val PointlessStackStashing = new RuleBasedAssemblyOptimization("Pointless stack stashing",
|
||||||
|
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||||
|
// 0-4
|
||||||
|
for5LargeRegisters(register => {
|
||||||
|
(Elidable & HasOpcode(PUSH) & HasRegisterParam(register)) ~
|
||||||
|
(Linear & Not(HasOpcode(POP)) & Not(Changes(register))).* ~
|
||||||
|
(Elidable & HasOpcode(POP) & HasRegisterParam(register)) ~~> (_.tail.init)
|
||||||
|
}),
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -195,6 +246,22 @@ object AlwaysGoodZ80Optimizations {
|
|||||||
simplifiable16BitAddWithSplitTarget(ZRegister.IYH, ZRegister.IYL, ZRegister.IY, ZRegister.BC),
|
simplifiable16BitAddWithSplitTarget(ZRegister.IYH, ZRegister.IYL, ZRegister.IY, ZRegister.BC),
|
||||||
simplifiable16BitAddWithSplitTarget(ZRegister.IYH, ZRegister.IYL, ZRegister.IY, ZRegister.DE),
|
simplifiable16BitAddWithSplitTarget(ZRegister.IYH, ZRegister.IYL, ZRegister.IY, ZRegister.DE),
|
||||||
|
|
||||||
|
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) & MatchRegister(ZRegister.BC, 0) & MatchRegister(ZRegister.HL, 1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
|
||||||
|
List(ZLine.ldImm16(ZRegister.HL, ctx.get[Int](0) + ctx.get[Int](1)))
|
||||||
|
},
|
||||||
|
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & MatchRegister(ZRegister.DE, 0) & MatchRegister(ZRegister.HL, 1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
|
||||||
|
List(ZLine.ldImm16(ZRegister.HL, ctx.get[Int](0) + ctx.get[Int](1)))
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) & MatchRegister(ZRegister.BC, 0) & MatchConstantInHL(1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
|
||||||
|
List(ZLine.ldImm16(ZRegister.HL, (ctx.get[Constant](1) + ctx.get[Int](0)).quickSimplify))
|
||||||
|
},
|
||||||
|
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & MatchRegister(ZRegister.DE, 0) & MatchConstantInHL(1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
|
||||||
|
List(ZLine.ldImm16(ZRegister.HL, (ctx.get[Constant](1) + ctx.get[Int](0)).quickSimplify))
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
|
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
|
||||||
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
|
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
|
||||||
(Elidable & Is8BitLoadTo(ZRegister.L)) ~
|
(Elidable & Is8BitLoadTo(ZRegister.L)) ~
|
||||||
@@ -216,7 +283,58 @@ object AlwaysGoodZ80Optimizations {
|
|||||||
code.last)
|
code.last)
|
||||||
},
|
},
|
||||||
|
|
||||||
(Elidable & HasOpcodeIn(Set(ADD, OR, AND, XOR, SUB)) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
|
(Elidable & HasOpcodeIn(Set(ADD, OR, XOR, SUB)) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
|
||||||
|
(Elidable & HasOpcode(AND) & Has8BitImmediate(0xff) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
|
||||||
|
(Elidable & HasOpcode(AND) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => List(ZLine.ldImm8(ZRegister.A, 0))),
|
||||||
|
(Elidable & HasOpcode(AND) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => List(ZLine.ldImm8(ZRegister.A, 0))),
|
||||||
|
|
||||||
|
|
||||||
|
(Elidable & HasOpcode(OR) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0)) ~
|
||||||
|
(Elidable & HasOpcodeIn(Set(JP, JR)) & HasRegisters(IfFlagSet(ZFlag.S)) & DoesntMatterWhatItDoesWithFlags) ~
|
||||||
|
Where(ctx => ctx.get[Constant](1).isInstanceOf[NumericConstant]) ~~> { (code, ctx) =>
|
||||||
|
val value = (ctx.get[Int](0) | ctx.get[NumericConstant](1).value).toInt & 0xff
|
||||||
|
if (value.&(0x80) == 0) List(ZLine.ldImm8(ZRegister.A, value))
|
||||||
|
else List(ZLine.ldImm8(ZRegister.A, value), code.last.copy(registers = NoRegisters))
|
||||||
|
},
|
||||||
|
|
||||||
|
(Elidable & HasOpcode(OR) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0)) ~
|
||||||
|
(Elidable & HasOpcodeIn(Set(JP, JR)) & HasRegisters(IfFlagClear(ZFlag.S)) & DoesntMatterWhatItDoesWithFlags) ~
|
||||||
|
Where(ctx => ctx.get[Constant](1).isInstanceOf[NumericConstant]) ~~> { (code, ctx) =>
|
||||||
|
val value = (ctx.get[Int](0) | ctx.get[NumericConstant](1).value).toInt & 0xff
|
||||||
|
if (value.&(0x80) != 0) List(ZLine.ldImm8(ZRegister.A, value))
|
||||||
|
else List(ZLine.ldImm8(ZRegister.A, value), code.last.copy(registers = NoRegisters))
|
||||||
|
},
|
||||||
|
|
||||||
|
(Elidable & HasOpcode(ADD) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
|
||||||
|
List(ZLine.ldImm8(ZRegister.A, (ctx.get[Constant](1) + ctx.get[Int](0)).quickSimplify))
|
||||||
|
},
|
||||||
|
|
||||||
|
(Elidable & HasOpcode(SUB) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
|
||||||
|
List(ZLine.ldImm8(ZRegister.A, (NumericConstant(ctx.get[Int](0) & 0xff, 1) - ctx.get[Constant](1)).quickSimplify))
|
||||||
|
},
|
||||||
|
|
||||||
|
(Elidable & HasOpcode(OR) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
|
||||||
|
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.Or, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
|
||||||
|
},
|
||||||
|
|
||||||
|
(Elidable & HasOpcode(XOR) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
|
||||||
|
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.Exor, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
|
||||||
|
},
|
||||||
|
|
||||||
|
(Elidable & HasOpcode(AND) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
|
||||||
|
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.And, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
|
||||||
|
},
|
||||||
|
|
||||||
|
(Elidable & HasOpcode(ADD) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0)) ~
|
||||||
|
(Elidable & HasOpcode(DAA) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
|
||||||
|
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.DecimalPlus, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
|
||||||
|
},
|
||||||
|
|
||||||
|
(Elidable & HasOpcode(SUB) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0)) ~
|
||||||
|
(Elidable & HasOpcode(DAA) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
|
||||||
|
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.DecimalMinus, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
|
||||||
|
},
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val FreeHL = new RuleBasedAssemblyOptimization("Free HL",
|
val FreeHL = new RuleBasedAssemblyOptimization("Free HL",
|
||||||
@@ -252,16 +370,32 @@ object AlwaysGoodZ80Optimizations {
|
|||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val UnusedCodeRemoval = new RuleBasedAssemblyOptimization("Unreachable code removal",
|
||||||
|
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||||
|
(HasOpcodeIn(Set(JP, JR)) & HasRegisters(NoRegisters)) ~ (Not(HasOpcode(LABEL)) & Elidable).+ ~~> (c => c.head :: Nil)
|
||||||
|
)
|
||||||
|
|
||||||
val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal",
|
val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal",
|
||||||
needsFlowInfo = FlowInfoRequirement.JustLabels,
|
needsFlowInfo = FlowInfoRequirement.JustLabels,
|
||||||
(Elidable & HasOpcode(LABEL) & HasCallerCount(0)) ~~> (_ => Nil)
|
(Elidable & HasOpcode(LABEL) & HasCallerCount(0)) ~~> (_ => Nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val BranchInPlaceRemoval = new RuleBasedAssemblyOptimization("Branch in place",
|
||||||
|
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||||
|
(HasOpcodeIn(Set(JP, JR)) & MatchJumpTarget(0) & Elidable) ~
|
||||||
|
HasOpcodeIn(ZOpcodeClasses.NoopDiscards).* ~
|
||||||
|
(HasOpcode(LABEL) & MatchJumpTarget(0)) ~~> (c => c.last :: Nil),
|
||||||
|
)
|
||||||
|
|
||||||
val All: List[AssemblyOptimization[ZLine]] = List[AssemblyOptimization[ZLine]](
|
val All: List[AssemblyOptimization[ZLine]] = List[AssemblyOptimization[ZLine]](
|
||||||
|
BranchInPlaceRemoval,
|
||||||
|
EmptyMemoryStoreRemoval,
|
||||||
FreeHL,
|
FreeHL,
|
||||||
PointlessLoad,
|
PointlessLoad,
|
||||||
|
PointlessStackStashing,
|
||||||
ReloadingKnownValueFromMemory,
|
ReloadingKnownValueFromMemory,
|
||||||
SimplifiableMaths,
|
SimplifiableMaths,
|
||||||
|
UnusedCodeRemoval,
|
||||||
UnusedLabelRemoval,
|
UnusedLabelRemoval,
|
||||||
UsingKnownValueFromAnotherRegister,
|
UsingKnownValueFromAnotherRegister,
|
||||||
)
|
)
|
||||||
|
@@ -77,6 +77,10 @@ object CoarseFlowAnalyzer {
|
|||||||
case ZLine(CP, _, _, _) =>
|
case ZLine(CP, _, _, _) =>
|
||||||
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||||
|
|
||||||
|
case ZLine(LD_16, TwoRegisters(t, ZRegister.IMM_16), NumericConstant(value, _), _) =>
|
||||||
|
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt))
|
||||||
|
case ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), xx, _) =>
|
||||||
|
currentStatus = currentStatus.setHL(SingleStatus(xx))
|
||||||
case ZLine(LD, TwoRegisters(t, ZRegister.IMM_8), NumericConstant(value, _), _) =>
|
case ZLine(LD, TwoRegisters(t, ZRegister.IMM_8), NumericConstant(value, _), _) =>
|
||||||
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt))
|
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt))
|
||||||
case ZLine(LD, TwoRegistersOffset(t, ZRegister.IMM_8, o), NumericConstant(value, _), _) =>
|
case ZLine(LD, TwoRegistersOffset(t, ZRegister.IMM_8, o), NumericConstant(value, _), _) =>
|
||||||
@@ -88,6 +92,14 @@ object CoarseFlowAnalyzer {
|
|||||||
case ZLine(ADD_16, TwoRegisters(t, s), _, _) =>
|
case ZLine(ADD_16, TwoRegisters(t, s), _, _) =>
|
||||||
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||||
.setRegister(t, (currentStatus.getRegister(t) <*> currentStatus.getRegister(s)) ((m, n) => (m + n) & 0xffff))
|
.setRegister(t, (currentStatus.getRegister(t) <*> currentStatus.getRegister(s)) ((m, n) => (m + n) & 0xffff))
|
||||||
|
|
||||||
|
case ZLine(SLA, OneRegister(r), _, _) =>
|
||||||
|
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||||
|
.setRegister(r, currentStatus.getRegister(r).map(_.<<(1).&(0xff)))
|
||||||
|
case ZLine(SRL, OneRegister(r), _, _) =>
|
||||||
|
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||||
|
.setRegister(r, currentStatus.getRegister(r).map(_.>>(1).&(0x7f)))
|
||||||
|
|
||||||
case ZLine(opcode, registers, _, _) =>
|
case ZLine(opcode, registers, _, _) =>
|
||||||
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||||
if (ZOpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.copy(a = AnyStatus)
|
if (ZOpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.copy(a = AnyStatus)
|
||||||
|
@@ -3,6 +3,7 @@ package millfork.assembly.z80.opt
|
|||||||
|
|
||||||
import millfork.assembly.opt._
|
import millfork.assembly.opt._
|
||||||
import millfork.assembly.z80.ZFlag
|
import millfork.assembly.z80.ZFlag
|
||||||
|
import millfork.env.{Constant, NumericConstant}
|
||||||
import millfork.node.ZRegister
|
import millfork.node.ZRegister
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,6 +17,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
|||||||
e: Status[Int] = UnknownStatus,
|
e: Status[Int] = UnknownStatus,
|
||||||
h: Status[Int] = UnknownStatus,
|
h: Status[Int] = UnknownStatus,
|
||||||
l: Status[Int] = UnknownStatus,
|
l: Status[Int] = UnknownStatus,
|
||||||
|
hl: Status[Constant] = UnknownStatus,
|
||||||
ixh: Status[Int] = UnknownStatus,
|
ixh: Status[Int] = UnknownStatus,
|
||||||
ixl: Status[Int] = UnknownStatus,
|
ixl: Status[Int] = UnknownStatus,
|
||||||
iyh: Status[Int] = UnknownStatus,
|
iyh: Status[Int] = UnknownStatus,
|
||||||
@@ -36,8 +38,8 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
|||||||
case ZRegister.C => this.copy(c = value)
|
case ZRegister.C => this.copy(c = value)
|
||||||
case ZRegister.D => this.copy(d = value)
|
case ZRegister.D => this.copy(d = value)
|
||||||
case ZRegister.E => this.copy(e = value)
|
case ZRegister.E => this.copy(e = value)
|
||||||
case ZRegister.H => this.copy(h = value)
|
case ZRegister.H => this.copy(h = value, hl = AnyStatus)
|
||||||
case ZRegister.L => this.copy(l = value)
|
case ZRegister.L => this.copy(l = value, hl = AnyStatus)
|
||||||
case ZRegister.IXH => this.copy(ixh = value)
|
case ZRegister.IXH => this.copy(ixh = value)
|
||||||
case ZRegister.IXL => this.copy(ixl = value)
|
case ZRegister.IXL => this.copy(ixl = value)
|
||||||
case ZRegister.IYH => this.copy(iyh = value)
|
case ZRegister.IYH => this.copy(iyh = value)
|
||||||
@@ -54,7 +56,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
|||||||
case ZRegister.SP => this
|
case ZRegister.SP => this
|
||||||
case ZRegister.BC => this.copy(b = value.hi, c = value.lo)
|
case ZRegister.BC => this.copy(b = value.hi, c = value.lo)
|
||||||
case ZRegister.DE => this.copy(d = value.hi, e = value.lo)
|
case ZRegister.DE => this.copy(d = value.hi, e = value.lo)
|
||||||
case ZRegister.HL => this.copy(h = value.hi, l = value.lo)
|
case ZRegister.HL => this.copy(h = value.hi, l = value.lo, hl = value.map(NumericConstant(_, 2)))
|
||||||
case ZRegister.IX => this.copy(ixh = value.hi, ixl = value.lo)
|
case ZRegister.IX => this.copy(ixh = value.hi, ixl = value.lo)
|
||||||
case ZRegister.IY => this.copy(iyh = value.hi, iyl = value.lo)
|
case ZRegister.IY => this.copy(iyh = value.hi, iyl = value.lo)
|
||||||
case ZRegister.AF => this.copy(a = value.hi, cf = AnyStatus, zf = AnyStatus, hf = AnyStatus, pf = AnyStatus, sf = AnyStatus)
|
case ZRegister.AF => this.copy(a = value.hi, cf = AnyStatus, zf = AnyStatus, hf = AnyStatus, pf = AnyStatus, sf = AnyStatus)
|
||||||
@@ -123,6 +125,13 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
|||||||
hf = this.hf ~ that.hf,
|
hf = this.hf ~ that.hf,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def setHL(c: Status[Constant]): CpuStatus = c match {
|
||||||
|
case SingleStatus(NumericConstant(nn, _)) => this.copy(l = SingleStatus(nn.toInt.&(0xff)), h = SingleStatus(nn.toInt.&(0xff00).>>(8)), hl = c)
|
||||||
|
case SingleStatus(cc) => this.copy(l = AnyStatus, h = AnyStatus, hl = c)
|
||||||
|
case AnyStatus => this.copy(l = AnyStatus, h = AnyStatus, hl = AnyStatus)
|
||||||
|
case UnknownStatus => this.copy(l = UnknownStatus, h = UnknownStatus, hl = UnknownStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override def toString: String = {
|
override def toString: String = {
|
||||||
val memRepr = if (memIx.isEmpty) "" else (0 to memIx.keys.max).map(i => memIx.getOrElse(i, UnknownStatus)).mkString("")
|
val memRepr = if (memIx.isEmpty) "" else (0 to memIx.keys.max).map(i => memIx.getOrElse(i, UnknownStatus)).mkString("")
|
||||||
|
@@ -0,0 +1,101 @@
|
|||||||
|
package millfork.assembly.z80.opt
|
||||||
|
|
||||||
|
import millfork.assembly.opt.SingleStatus
|
||||||
|
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine}
|
||||||
|
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
|
||||||
|
import millfork.env._
|
||||||
|
import millfork.error.ErrorReporting
|
||||||
|
import millfork.node.ZRegister
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karol Stasiak
|
||||||
|
*/
|
||||||
|
object EmptyMemoryStoreRemoval extends AssemblyOptimization[ZLine] {
|
||||||
|
override def name = "Removing pointless stores to automatic variables"
|
||||||
|
|
||||||
|
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
|
||||||
|
val paramVariables = f.params match {
|
||||||
|
// case NormalParamSignature(List(MemoryVariable(_, typ, _))) if typ.size == 1 =>
|
||||||
|
// Set[String]()
|
||||||
|
case NormalParamSignature(ps) =>
|
||||||
|
ps.map(_.name).toSet
|
||||||
|
case _ =>
|
||||||
|
// assembly functions do not get this optimization
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
val flow = FlowAnalyzer.analyze(f, code, optimizationContext.options, FlowInfoRequirement.BothFlows)
|
||||||
|
import millfork.node.ZRegister._
|
||||||
|
val stillUsedVariables = code.flatMap {
|
||||||
|
case ZLine(_, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), MemoryAddressConstant(th), _) => Some(th.name)
|
||||||
|
case ZLine(_, TwoRegisters(_, MEM_ABS_8 | MEM_ABS_16), MemoryAddressConstant(th), _) => Some(th.name)
|
||||||
|
case ZLine(_, TwoRegisters(_, IMM_16), MemoryAddressConstant(th), _) => Some(th.name)
|
||||||
|
case ZLine(_, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), _) => Some(th.name)
|
||||||
|
case ZLine(_, TwoRegisters(_, MEM_ABS_8 | MEM_ABS_16), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), _) => Some(th.name)
|
||||||
|
case ZLine(_, TwoRegisters(_, IMM_16), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), _) => Some(th.name)
|
||||||
|
case _ => None
|
||||||
|
}.toSet
|
||||||
|
val variablesWithAddressesTaken = code.zipWithIndex.flatMap {
|
||||||
|
case (ZLine(_, _, SubbyteConstant(MemoryAddressConstant(th), _), _), _) =>
|
||||||
|
Some(th.name)
|
||||||
|
case (ZLine(_, _, SubbyteConstant(CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), _), _), _) =>
|
||||||
|
Some(th.name)
|
||||||
|
case (ZLine(_,
|
||||||
|
TwoRegisters(ZRegister.MEM_HL, _) | TwoRegisters(_, ZRegister.MEM_HL) | OneRegister(ZRegister.MEM_HL),
|
||||||
|
_, _), i) =>
|
||||||
|
flow(i)._1.statusBefore.hl match {
|
||||||
|
case SingleStatus(MemoryAddressConstant(th)) =>
|
||||||
|
if (flow(i)._1.importanceAfter.hlNumeric != Unimportant) Some(th.name)
|
||||||
|
else None
|
||||||
|
case SingleStatus(CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _))) =>
|
||||||
|
if (flow(i)._1.importanceAfter.hlNumeric != Unimportant) Some(th.name)
|
||||||
|
else None
|
||||||
|
case _ => None // TODO: ???
|
||||||
|
}
|
||||||
|
case _ => None
|
||||||
|
}.toSet
|
||||||
|
val allLocalVariables = f.environment.getAllLocalVariables
|
||||||
|
val localVariables = allLocalVariables.filter {
|
||||||
|
case MemoryVariable(name, typ, VariableAllocationMethod.Auto | VariableAllocationMethod.Zeropage) =>
|
||||||
|
typ.size > 0 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name)
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localVariables.isEmpty) {
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
val toRemove = mutable.Set[Int]()
|
||||||
|
val badVariables = mutable.Set[String]()
|
||||||
|
|
||||||
|
for(v <- localVariables) {
|
||||||
|
val lifetime = VariableLifetime.apply(v.name, flow)
|
||||||
|
val lastaccess = lifetime.last
|
||||||
|
if (lastaccess >= 0) {
|
||||||
|
val lastVariableAccess = code(lastaccess)
|
||||||
|
import millfork.assembly.z80.ZOpcode._
|
||||||
|
if (lastVariableAccess match {
|
||||||
|
case ZLine(LD, TwoRegisters(MEM_HL, _), _, true) => true
|
||||||
|
case ZLine(LD | LD_16, TwoRegisters(MEM_ABS_8 | MEM_ABS_16, _), _, true) => true
|
||||||
|
case ZLine(INC | DEC, OneRegister(MEM_HL), _, true) =>
|
||||||
|
val importances = flow(lastaccess)._1.importanceAfter
|
||||||
|
Seq(importances.sf, importances.zf).forall(_ == Unimportant)
|
||||||
|
case ZLine(SLA | SLL | SRA | SRL | RL | RR | RLC | RRC, OneRegister(MEM_HL), _, true) =>
|
||||||
|
val importances = flow(lastaccess)._1.importanceAfter
|
||||||
|
Seq(importances.sf, importances.zf, importances.cf).forall(_ == Unimportant)
|
||||||
|
case _ => false
|
||||||
|
}) {
|
||||||
|
badVariables += v.name
|
||||||
|
toRemove += lastaccess
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (toRemove.isEmpty) {
|
||||||
|
code
|
||||||
|
} else {
|
||||||
|
ErrorReporting.debug(s"Removing pointless store(s) to ${badVariables.mkString(", ")}")
|
||||||
|
code.zipWithIndex.filter(x => !toRemove(x._2)).map(_._1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -36,6 +36,7 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
|||||||
e: Importance = UnknownImportance,
|
e: Importance = UnknownImportance,
|
||||||
h: Importance = UnknownImportance,
|
h: Importance = UnknownImportance,
|
||||||
l: Importance = UnknownImportance,
|
l: Importance = UnknownImportance,
|
||||||
|
hlNumeric: Importance = UnknownImportance,
|
||||||
ixh: Importance = UnknownImportance,
|
ixh: Importance = UnknownImportance,
|
||||||
ixl: Importance = UnknownImportance,
|
ixl: Importance = UnknownImportance,
|
||||||
iyh: Importance = UnknownImportance,
|
iyh: Importance = UnknownImportance,
|
||||||
@@ -50,17 +51,18 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
|||||||
) {
|
) {
|
||||||
override def toString: String = {
|
override def toString: String = {
|
||||||
val memRepr = if (memIx.isEmpty) "" else (0 to memIx.keys.max).map(i => memIx.getOrElse(i, UnknownImportance)).mkString("")
|
val memRepr = if (memIx.isEmpty) "" else (0 to memIx.keys.max).map(i => memIx.getOrElse(i, UnknownImportance)).mkString("")
|
||||||
s"A=$a,B=$b,C=$c,D=$d,E=$e,H=$h,L=$l,IX=$ixh$ixl,Y=$iyh$iyl; Z=$zf,C=$cf,N=$nf,S=$sf,P=$pf,H=$hf; M=" ++ memRepr.padTo(4, ' ')
|
s"A=$a,B=$b,C=$c,D=$d,E=$e,H=$h,L=$l,IX=$ixh$ixl,Y=$iyh$iyl; Z=$zf,C=$cf,N=$nf,S=$sf,P=$pf,H=$hf; HL=$hlNumeric; M=" ++ memRepr.padTo(4, ' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
def ~(that: CpuImportance) = new CpuImportance(
|
def ~(that: CpuImportance) = new CpuImportance(
|
||||||
a = this.a ~ that.a,
|
a = this.a ~ that.a,
|
||||||
b = this.a ~ that.a,
|
b = this.b ~ that.b,
|
||||||
c = this.a ~ that.a,
|
c = this.c ~ that.c,
|
||||||
d = this.a ~ that.a,
|
d = this.d ~ that.d,
|
||||||
e = this.a ~ that.a,
|
e = this.e ~ that.e,
|
||||||
h = this.a ~ that.a,
|
h = this.h ~ that.h,
|
||||||
l = this.a ~ that.a,
|
l = this.l ~ that.l,
|
||||||
|
hlNumeric = this.hlNumeric ~ that.hlNumeric,
|
||||||
ixh = this.ixh ~ that.ixh,
|
ixh = this.ixh ~ that.ixh,
|
||||||
ixl = this.ixl ~ that.ixl,
|
ixl = this.ixl ~ that.ixl,
|
||||||
iyh = this.iyh ~ that.iyh,
|
iyh = this.iyh ~ that.iyh,
|
||||||
@@ -111,9 +113,10 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
|||||||
case ZRegister.D => this.copy(d = Important)
|
case ZRegister.D => this.copy(d = Important)
|
||||||
case ZRegister.E => this.copy(e = Important)
|
case ZRegister.E => this.copy(e = Important)
|
||||||
case ZRegister.DE | ZRegister.MEM_DE => this.copy(d = Important, e = Important)
|
case ZRegister.DE | ZRegister.MEM_DE => this.copy(d = Important, e = Important)
|
||||||
case ZRegister.H => this.copy(h = Important)
|
case ZRegister.H => this.copy(h = Important, hlNumeric = Important)
|
||||||
case ZRegister.L => this.copy(l = Important)
|
case ZRegister.L => this.copy(l = Important, hlNumeric = Important)
|
||||||
case ZRegister.HL | ZRegister.MEM_HL => this.copy(h = Important, l = Important)
|
case ZRegister.HL => this.copy(h = Important, l = Important, hlNumeric = Important)
|
||||||
|
case ZRegister.MEM_HL => this.copy(h = Important, l = Important)
|
||||||
case ZRegister.IXH => this.copy(ixh = Important)
|
case ZRegister.IXH => this.copy(ixh = Important)
|
||||||
case ZRegister.IXL => this.copy(ixl = Important)
|
case ZRegister.IXL => this.copy(ixl = Important)
|
||||||
case ZRegister.IYH => this.copy(iyh = Important)
|
case ZRegister.IYH => this.copy(iyh = Important)
|
||||||
@@ -137,7 +140,7 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
|||||||
case ZRegister.MEM_DE => this.copy(d = Important, e = Important)
|
case ZRegister.MEM_DE => this.copy(d = Important, e = Important)
|
||||||
case ZRegister.H => this.copy(h = Unimportant)
|
case ZRegister.H => this.copy(h = Unimportant)
|
||||||
case ZRegister.L => this.copy(l = Unimportant)
|
case ZRegister.L => this.copy(l = Unimportant)
|
||||||
case ZRegister.HL => this.copy(h = Unimportant, l = Unimportant)
|
case ZRegister.HL => this.copy(h = Unimportant, l = Unimportant, hlNumeric = Unimportant)
|
||||||
case ZRegister.MEM_HL => this.copy(h = Important, l = Important)
|
case ZRegister.MEM_HL => this.copy(h = Important, l = Important)
|
||||||
case ZRegister.IXH => this.copy(ixh = Unimportant)
|
case ZRegister.IXH => this.copy(ixh = Unimportant)
|
||||||
case ZRegister.IXL => this.copy(ixl = Unimportant)
|
case ZRegister.IXL => this.copy(ixl = Unimportant)
|
||||||
@@ -193,20 +196,16 @@ object ReverseFlowAnalyzer {
|
|||||||
}
|
}
|
||||||
val currentLine = codeArray(i)
|
val currentLine = codeArray(i)
|
||||||
currentLine match {
|
currentLine match {
|
||||||
|
case ZLine(LABEL, _, _, _) => ()
|
||||||
case ZLine(DJNZ, _, MemoryAddressConstant(Label(l)), _) =>
|
case ZLine(DJNZ, _, MemoryAddressConstant(Label(l)), _) =>
|
||||||
val L = l
|
val labelIndex = getLabelIndex(codeArray, l)
|
||||||
val labelIndex = codeArray.indexWhere {
|
|
||||||
case ZLine(LABEL, _, MemoryAddressConstant(Label(L)), _) => true
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
currentImportance = if (labelIndex < 0) finalImportance else (importanceArray(labelIndex) ~ currentImportance).butReadsRegister(ZRegister.B).butReadsFlag(ZFlag.Z)
|
currentImportance = if (labelIndex < 0) finalImportance else (importanceArray(labelIndex) ~ currentImportance).butReadsRegister(ZRegister.B).butReadsFlag(ZFlag.Z)
|
||||||
case ZLine(JP | JR, IfFlagSet(_) | IfFlagClear(_), MemoryAddressConstant(Label(l)), _) =>
|
case ZLine(JP | JR, IfFlagSet(flag), MemoryAddressConstant(Label(l)), _) =>
|
||||||
val L = l
|
val labelIndex = getLabelIndex(codeArray, l)
|
||||||
val labelIndex = codeArray.indexWhere {
|
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex) ~ currentImportance.butReadsFlag(flag)
|
||||||
case ZLine(LABEL, _, MemoryAddressConstant(Label(L)), _) => true
|
case ZLine(JP | JR, IfFlagClear(flag), MemoryAddressConstant(Label(l)), _) =>
|
||||||
case _ => false
|
val labelIndex = getLabelIndex(codeArray, l)
|
||||||
}
|
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex) ~ currentImportance.butReadsFlag(flag)
|
||||||
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex) ~ currentImportance
|
|
||||||
case ZLine(DISCARD_HL, _, _, _) =>
|
case ZLine(DISCARD_HL, _, _, _) =>
|
||||||
currentImportance = currentImportance.copy(h = Unimportant, l = Unimportant)
|
currentImportance = currentImportance.copy(h = Unimportant, l = Unimportant)
|
||||||
case ZLine(DISCARD_BCDEIX, _, _, _) =>
|
case ZLine(DISCARD_BCDEIX, _, _, _) =>
|
||||||
@@ -227,15 +226,65 @@ object ReverseFlowAnalyzer {
|
|||||||
case ZLine(OR | AND, OneRegister(ZRegister.A), _, _) =>
|
case ZLine(OR | AND, OneRegister(ZRegister.A), _, _) =>
|
||||||
currentImportance = currentImportance.butReadsRegister(ZRegister.A)
|
currentImportance = currentImportance.butReadsRegister(ZRegister.A)
|
||||||
|
|
||||||
case ZLine(AND | ADD | SUB | OR | XOR | CP, OneRegister(s), _, _) =>
|
case ZLine(ADD | SUB | CP, OneRegister(s), _, _) =>
|
||||||
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsRegister(s)
|
currentImportance = currentImportance.butReadsRegister(s).copy(
|
||||||
case ZLine(ADC | SBC, OneRegister(s), _, _) =>
|
a = Important,
|
||||||
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsRegister(s).butReadsFlag(ZFlag.C)
|
cf = Unimportant,
|
||||||
|
zf = Unimportant,
|
||||||
|
sf = Unimportant,
|
||||||
|
hf = Unimportant,
|
||||||
|
pf = Unimportant,
|
||||||
|
nf = Unimportant
|
||||||
|
)
|
||||||
|
case ZLine(ADD | SUB | CP, OneRegisterOffset(s, o), _, _) =>
|
||||||
|
currentImportance = currentImportance.butReadsRegister(s, o).copy(
|
||||||
|
a = Important,
|
||||||
|
cf = Unimportant,
|
||||||
|
zf = Unimportant,
|
||||||
|
sf = Unimportant,
|
||||||
|
hf = Unimportant,
|
||||||
|
pf = Unimportant,
|
||||||
|
nf = Unimportant
|
||||||
|
)
|
||||||
|
|
||||||
case ZLine(AND | ADD | SUB | OR | XOR | CP, OneRegisterOffset(s, o), _, _) =>
|
case ZLine(AND | OR | XOR, OneRegister(s), _, _) =>
|
||||||
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsRegister(s, o)
|
currentImportance = currentImportance.butReadsRegister(s).copy(
|
||||||
|
a = Important,
|
||||||
|
cf = Unimportant,
|
||||||
|
zf = Unimportant,
|
||||||
|
pf = Unimportant,
|
||||||
|
sf = Unimportant
|
||||||
|
)
|
||||||
|
|
||||||
|
case ZLine(AND | OR | XOR, OneRegisterOffset(s, o), _, _) =>
|
||||||
|
currentImportance = currentImportance.butReadsRegister(s, o).copy(
|
||||||
|
a = Important,
|
||||||
|
cf = Unimportant,
|
||||||
|
zf = Unimportant,
|
||||||
|
pf = Unimportant,
|
||||||
|
sf = Unimportant
|
||||||
|
)
|
||||||
|
case ZLine(ADC | SBC, OneRegister(s), _, _) =>
|
||||||
|
currentImportance = currentImportance.butReadsRegister(s).copy(
|
||||||
|
a = Important,
|
||||||
|
cf = Important,
|
||||||
|
zf = Unimportant,
|
||||||
|
sf = Unimportant,
|
||||||
|
hf = Unimportant,
|
||||||
|
pf = Unimportant,
|
||||||
|
nf = Unimportant
|
||||||
|
)
|
||||||
case ZLine(ADC | SBC, OneRegisterOffset(s, o), _, _) =>
|
case ZLine(ADC | SBC, OneRegisterOffset(s, o), _, _) =>
|
||||||
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsRegister(s, o).butReadsFlag(ZFlag.C)
|
currentImportance = currentImportance.butReadsRegister(s, o).copy(
|
||||||
|
a = Important,
|
||||||
|
cf = Important,
|
||||||
|
zf = Unimportant,
|
||||||
|
sf = Unimportant,
|
||||||
|
hf = Unimportant,
|
||||||
|
pf = Unimportant,
|
||||||
|
nf = Unimportant
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
case ZLine(INC | DEC | INC_16 | DEC_16, OneRegister(s), _, _) =>
|
case ZLine(INC | DEC | INC_16 | DEC_16, OneRegister(s), _, _) =>
|
||||||
currentImportance = currentImportance.butReadsRegister(s)
|
currentImportance = currentImportance.butReadsRegister(s)
|
||||||
@@ -247,6 +296,11 @@ object ReverseFlowAnalyzer {
|
|||||||
currentImportance = currentImportance.butReadsRegister(r)
|
currentImportance = currentImportance.butReadsRegister(r)
|
||||||
case ZLine(CALL, NoRegisters, _, _) =>
|
case ZLine(CALL, NoRegisters, _, _) =>
|
||||||
currentImportance = finalImportance.copy(memIx = currentImportance.memIx)
|
currentImportance = finalImportance.copy(memIx = currentImportance.memIx)
|
||||||
|
|
||||||
|
case ZLine(SLA | SRL, OneRegister(r), _, _) =>
|
||||||
|
currentImportance = currentImportance.butReadsRegister(r).butWritesFlag(ZFlag.C).butWritesFlag(ZFlag.Z)
|
||||||
|
case ZLine(RL | RR | RLC | RRC, OneRegister(r), _, _) =>
|
||||||
|
currentImportance = currentImportance.butReadsRegister(r).butReadsFlag(ZFlag.C).butWritesFlag(ZFlag.Z)
|
||||||
case _ =>
|
case _ =>
|
||||||
currentImportance = finalImportance // TODO
|
currentImportance = finalImportance // TODO
|
||||||
}
|
}
|
||||||
@@ -259,4 +313,11 @@ object ReverseFlowAnalyzer {
|
|||||||
|
|
||||||
importanceArray.toList
|
importanceArray.toList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def getLabelIndex(codeArray: Array[ZLine], L: String) = {
|
||||||
|
codeArray.indexWhere {
|
||||||
|
case ZLine(ZOpcode.LABEL, _, MemoryAddressConstant(Label(L)), _) => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -114,7 +114,7 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions) {
|
|||||||
if (i eq null) {
|
if (i eq null) {
|
||||||
ErrorReporting.fatal(s"Value at index $i is null")
|
ErrorReporting.fatal(s"Value at index $i is null")
|
||||||
} else {
|
} else {
|
||||||
ErrorReporting.fatal(s"Value at index $i is a ${t.getClass.getSimpleName}, not a ${clazz.getSimpleName}")
|
throw new IllegalStateException(s"Value at index $i is a ${t.getClass.getSimpleName}, not a ${clazz.getSimpleName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -469,7 +469,14 @@ case class MatchImmediate(i: Int) extends AssemblyLinePattern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class RegisterAndOffset(register: ZRegister.Value, offset: Int)
|
case class RegisterAndOffset(register: ZRegister.Value, offset: Int) {
|
||||||
|
def toOneRegister: ZRegisters = register match {
|
||||||
|
case ZRegister.MEM_IX_D | ZRegister.MEM_IY_D => OneRegisterOffset(register, offset)
|
||||||
|
case _ =>
|
||||||
|
if (offset != 0) ???
|
||||||
|
OneRegister(register)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case class MatchSourceRegisterAndOffset(i: Int) extends AssemblyLinePattern {
|
case class MatchSourceRegisterAndOffset(i: Int) extends AssemblyLinePattern {
|
||||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||||
@@ -495,7 +502,10 @@ case class DoesntChangeMatchedRegisterAndOffset(i: Int) extends AssemblyLinePatt
|
|||||||
import ZRegister._
|
import ZRegister._
|
||||||
ro.register match {
|
ro.register match {
|
||||||
case AF | SP => false // ?
|
case AF | SP => false // ?
|
||||||
case MEM_ABS_8 | MEM_ABS_16 | MEM_HL | MEM_DE | MEM_BC => !line.changesMemory
|
case MEM_ABS_8 | MEM_ABS_16 => !line.changesMemory
|
||||||
|
case MEM_HL => !line.changesMemory && !line.changesRegister(ZRegister.HL)
|
||||||
|
case MEM_BC => !line.changesMemory && !line.changesRegister(ZRegister.BC)
|
||||||
|
case MEM_DE => !line.changesMemory && !line.changesRegister(ZRegister.DE)
|
||||||
case _ => !line.changesRegisterAndOffset(ro.register, ro.offset)
|
case _ => !line.changesRegisterAndOffset(ro.register, ro.offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -511,6 +521,24 @@ case class MatchParameter(i: Int) extends AssemblyLinePattern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case class MatchJumpTarget(i: Int) extends AssemblyLinePattern {
|
||||||
|
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||||
|
line.registers match {
|
||||||
|
case NoRegisters | IfFlagClear(_) | IfFlagSet(_) => ctx.addObject(i, line.parameter.quickSimplify)
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class MatchConstantInHL(i: Int) extends AssemblyLinePattern {
|
||||||
|
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||||
|
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||||
|
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||||
|
flowInfo.statusBefore.hl match {
|
||||||
|
case SingleStatus(value) => ctx.addObject(i, value)
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case class MatchOpcode(i: Int) extends AssemblyLinePattern {
|
case class MatchOpcode(i: Int) extends AssemblyLinePattern {
|
||||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||||
ctx.addObject(i, line.opcode)
|
ctx.addObject(i, line.opcode)
|
||||||
@@ -829,6 +857,16 @@ case class Has8BitImmediate(i: Int) extends TrivialAssemblyLinePattern {
|
|||||||
override def toString: String = "#" + i
|
override def toString: String = "#" + i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case class Match8BitImmediate(i: Int) extends AssemblyLinePattern {
|
||||||
|
|
||||||
|
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = line.registers match {
|
||||||
|
case TwoRegisters(_, ZRegister.IMM_8) => ctx.addObject(i, line.parameter)
|
||||||
|
case OneRegister(ZRegister.IMM_8) => ctx.addObject(i, line.parameter)
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case class HasImmediateWhere(predicate: Int => Boolean) extends TrivialAssemblyLinePattern {
|
case class HasImmediateWhere(predicate: Int => Boolean) extends TrivialAssemblyLinePattern {
|
||||||
override def apply(line: ZLine): Boolean =
|
override def apply(line: ZLine): Boolean =
|
||||||
(line.registers match {
|
(line.registers match {
|
||||||
|
@@ -0,0 +1,65 @@
|
|||||||
|
package millfork.assembly.z80.opt
|
||||||
|
|
||||||
|
import millfork.assembly.opt.SingleStatus
|
||||||
|
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine}
|
||||||
|
import millfork.env._
|
||||||
|
import millfork.error.ErrorReporting
|
||||||
|
import millfork.node.ZRegister
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karol Stasiak
|
||||||
|
*/
|
||||||
|
object VariableLifetime {
|
||||||
|
|
||||||
|
// This only works for non-stack variables.
|
||||||
|
// TODO: this is also probably very wrong
|
||||||
|
def apply(variableName: String, codeWithFlow: List[(FlowInfo, ZLine)]): Range = {
|
||||||
|
val flags = codeWithFlow.map {
|
||||||
|
case (_, ZLine(_, _, MemoryAddressConstant(MemoryVariable(n, _, _)), _)) => n == variableName
|
||||||
|
case (_, ZLine(_, _, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(MemoryVariable(n, _, _)), NumericConstant(_, 1)), _)) => n == variableName
|
||||||
|
case (i, ZLine(_, TwoRegisters(ZRegister.MEM_HL, _) | TwoRegisters(_, ZRegister.MEM_HL) | OneRegister(ZRegister.MEM_HL), _, _)) =>
|
||||||
|
i.statusBefore.hl match {
|
||||||
|
case SingleStatus(MemoryAddressConstant(MemoryVariable(n, _, _))) => n == variableName
|
||||||
|
case SingleStatus(CompoundConstant(MathOperator.Plus, MemoryAddressConstant(MemoryVariable(n, _, _)), NumericConstant(_, 1))) => n == variableName
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
if (flags.forall(!_)) return Range(0, 0)
|
||||||
|
var min = flags.indexOf(true)
|
||||||
|
var max = flags.lastIndexOf(true) + 1
|
||||||
|
var changed = true
|
||||||
|
val labelMap = codeWithFlow.zipWithIndex.flatMap(a => a._1._2.parameter match {
|
||||||
|
case MemoryAddressConstant(Label(l)) => List(l -> a._2)
|
||||||
|
case _ => Nil
|
||||||
|
}).groupBy(_._1).mapValues(_.map(_._2).toSet)
|
||||||
|
|
||||||
|
while (changed) {
|
||||||
|
changed = false
|
||||||
|
for ((label, indices) <- labelMap) {
|
||||||
|
if (indices.exists(i => i >= min && i < max)) {
|
||||||
|
indices.foreach { i =>
|
||||||
|
val before = max - min
|
||||||
|
min = min min i
|
||||||
|
max = max max (i + 1)
|
||||||
|
if (max - min != before) {
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorReporting.trace("Lifetime for " + variableName)
|
||||||
|
// codeWithFlow.zipWithIndex.foreach {
|
||||||
|
// case ((_, line), index) =>
|
||||||
|
// if (index >= min && index < max) {
|
||||||
|
// ErrorReporting.trace(f"$line%-30s <")
|
||||||
|
// } else {
|
||||||
|
// ErrorReporting.trace(line.toString)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
Range(min, max)
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
package millfork.compiler.z80
|
package millfork.compiler.z80
|
||||||
|
|
||||||
import millfork.assembly.z80._
|
import millfork.assembly.z80._
|
||||||
import millfork.compiler._
|
import millfork.compiler.{ComparisonType, _}
|
||||||
import millfork.env.NumericConstant
|
import millfork.env.NumericConstant
|
||||||
import millfork.node.{Expression, ZRegister}
|
import millfork.node.{Expression, ZRegister}
|
||||||
|
|
||||||
@@ -13,23 +13,7 @@ object Z80Comparisons {
|
|||||||
import ComparisonType._
|
import ComparisonType._
|
||||||
|
|
||||||
def compile8BitComparison(ctx: CompilationContext, compType: ComparisonType.Value, l: Expression, r: Expression, branches: BranchSpec): List[ZLine] = {
|
def compile8BitComparison(ctx: CompilationContext, compType: ComparisonType.Value, l: Expression, r: Expression, branches: BranchSpec): List[ZLine] = {
|
||||||
(ctx.env.eval(l), ctx.env.eval(r)) match {
|
handleConstantComparison(ctx, compType, l, r, branches).foreach(return _)
|
||||||
case (Some(NumericConstant(lc, _)), Some(NumericConstant(rc, _))) =>
|
|
||||||
val constantCondition = compType match {
|
|
||||||
case Equal => lc == rc
|
|
||||||
case NotEqual => lc != rc
|
|
||||||
case GreaterSigned | GreaterUnsigned => lc > rc
|
|
||||||
case LessOrEqualSigned | LessOrEqualUnsigned => lc <= rc
|
|
||||||
case GreaterOrEqualSigned | GreaterOrEqualUnsigned=> lc >= rc
|
|
||||||
case LessSigned | LessUnsigned => lc < rc
|
|
||||||
}
|
|
||||||
return branches match {
|
|
||||||
case BranchIfFalse(b) => if (!constantCondition) List(ZLine.jump(b)) else Nil
|
|
||||||
case BranchIfTrue(b) => if (constantCondition) List(ZLine.jump(b)) else Nil
|
|
||||||
case _ => Nil
|
|
||||||
}
|
|
||||||
case _ =>
|
|
||||||
}
|
|
||||||
compType match {
|
compType match {
|
||||||
case GreaterUnsigned | LessOrEqualUnsigned | GreaterSigned | LessOrEqualSigned =>
|
case GreaterUnsigned | LessOrEqualUnsigned | GreaterSigned | LessOrEqualSigned =>
|
||||||
return compile8BitComparison(ctx, ComparisonType.flip(compType), r, l, branches)
|
return compile8BitComparison(ctx, ComparisonType.flip(compType), r, l, branches)
|
||||||
@@ -54,4 +38,182 @@ object Z80Comparisons {
|
|||||||
}
|
}
|
||||||
calculateFlags :+ jump
|
calculateFlags :+ jump
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def handleConstantComparison(ctx: CompilationContext, compType: ComparisonType.Value, l: Expression, r: Expression, branches: BranchSpec): Option[List[ZLine]] = {
|
||||||
|
(ctx.env.eval(l), ctx.env.eval(r)) match {
|
||||||
|
case (Some(NumericConstant(lc, _)), Some(NumericConstant(rc, _))) =>
|
||||||
|
val constantCondition = compType match {
|
||||||
|
case Equal => lc == rc
|
||||||
|
case NotEqual => lc != rc
|
||||||
|
case GreaterSigned | GreaterUnsigned => lc > rc
|
||||||
|
case LessOrEqualSigned | LessOrEqualUnsigned => lc <= rc
|
||||||
|
case GreaterOrEqualSigned | GreaterOrEqualUnsigned => lc >= rc
|
||||||
|
case LessSigned | LessUnsigned => lc < rc
|
||||||
|
}
|
||||||
|
return Some(branches match {
|
||||||
|
case BranchIfFalse(b) => if (!constantCondition) List(ZLine.jump(b)) else Nil
|
||||||
|
case BranchIfTrue(b) => if (constantCondition) List(ZLine.jump(b)) else Nil
|
||||||
|
case _ => Nil
|
||||||
|
})
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
def compile16BitComparison(ctx: CompilationContext, compType: ComparisonType.Value, l: Expression, r: Expression, branches: BranchSpec): List[ZLine] = {
|
||||||
|
handleConstantComparison(ctx, compType, l, r, branches).foreach(return _)
|
||||||
|
compType match {
|
||||||
|
case GreaterUnsigned | LessOrEqualUnsigned | GreaterSigned | LessOrEqualSigned =>
|
||||||
|
return compile16BitComparison(ctx, ComparisonType.flip(compType), r, l, branches)
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
val calculateLeft = Z80ExpressionCompiler.compileToHL(ctx, l)
|
||||||
|
val calculateRight = Z80ExpressionCompiler.compileToHL(ctx, r)
|
||||||
|
val (calculated, useBC) = if (calculateLeft.exists(Z80ExpressionCompiler.changesBC)) {
|
||||||
|
if (calculateLeft.exists(Z80ExpressionCompiler.changesDE)) {
|
||||||
|
calculateRight ++ List(ZLine.register(ZOpcode.PUSH, ZRegister.HL)) ++ calculateLeft ++ List(ZLine.register(ZOpcode.POP, ZRegister.BC)) -> false
|
||||||
|
} else {
|
||||||
|
calculateRight ++ List(ZLine.ld8(ZRegister.D, ZRegister.H), ZLine.ld8(ZRegister.E, ZRegister.L)) ++ calculateLeft -> false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
calculateRight ++ List(ZLine.ld8(ZRegister.B, ZRegister.H), ZLine.ld8(ZRegister.C, ZRegister.L)) ++ calculateLeft -> true
|
||||||
|
}
|
||||||
|
val calculateFlags = calculated ++ List(
|
||||||
|
ZLine.register(ZOpcode.OR, ZRegister.A),
|
||||||
|
ZLine.registers(ZOpcode.SBC_16, ZRegister.HL, if (useBC) ZRegister.BC else ZRegister.DE))
|
||||||
|
if (branches == NoBranching) return calculateFlags
|
||||||
|
val jump = (compType, branches) match {
|
||||||
|
case (Equal, BranchIfTrue(label)) => ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||||
|
case (Equal, BranchIfFalse(label)) => ZLine.jump(label, IfFlagClear(ZFlag.Z))
|
||||||
|
case (NotEqual, BranchIfTrue(label)) => ZLine.jump(label, IfFlagClear(ZFlag.Z))
|
||||||
|
case (NotEqual, BranchIfFalse(label)) => ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||||
|
case (LessUnsigned, BranchIfTrue(label)) => ZLine.jump(label, IfFlagSet(ZFlag.C))
|
||||||
|
case (LessUnsigned, BranchIfFalse(label)) => ZLine.jump(label, IfFlagClear(ZFlag.C))
|
||||||
|
case (GreaterOrEqualUnsigned, BranchIfTrue(label)) => ZLine.jump(label, IfFlagClear(ZFlag.C))
|
||||||
|
case (GreaterOrEqualUnsigned, BranchIfFalse(label)) => ZLine.jump(label, IfFlagSet(ZFlag.C))
|
||||||
|
case _ => ???
|
||||||
|
}
|
||||||
|
calculateFlags :+ jump
|
||||||
|
}
|
||||||
|
|
||||||
|
def compileLongRelativeComparison(ctx: CompilationContext, compType: ComparisonType.Value, l: Expression, r: Expression, branches: BranchSpec): List[ZLine] = {
|
||||||
|
handleConstantComparison(ctx, compType, l, r, branches).foreach(return _)
|
||||||
|
compType match {
|
||||||
|
case Equal | NotEqual => throw new IllegalArgumentException
|
||||||
|
case GreaterUnsigned | LessOrEqualUnsigned | GreaterSigned | LessOrEqualSigned =>
|
||||||
|
return compileLongRelativeComparison(ctx, ComparisonType.flip(compType), r, l, branches)
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
val lt = Z80ExpressionCompiler.getExpressionType(ctx, l)
|
||||||
|
val rt = Z80ExpressionCompiler.getExpressionType(ctx, r)
|
||||||
|
val size = lt.size max rt.size
|
||||||
|
val calculateLeft = Z80ExpressionCompiler.compileByteReads(ctx, l, size, ZExpressionTarget.HL)
|
||||||
|
val calculateRight = Z80ExpressionCompiler.compileByteReads(ctx, r, size, ZExpressionTarget.BC)
|
||||||
|
val preserveHl = isBytesFromHL(calculateLeft)
|
||||||
|
val preserveBc = isBytesFromBC(calculateRight)
|
||||||
|
val calculateFlags = calculateLeft.zip(calculateRight).zipWithIndex.flatMap { case ((lb, rb), i) =>
|
||||||
|
import ZOpcode._
|
||||||
|
import ZRegister._
|
||||||
|
val sub = if (i == 0) SUB else SBC
|
||||||
|
var compareBytes = (lb, rb) match {
|
||||||
|
case (List(ZLine(LD, TwoRegisters(A, _), _, _)),
|
||||||
|
List(ZLine(LD, TwoRegisters(A, IMM_8), param, _))) =>
|
||||||
|
lb :+ ZLine.imm8(sub, param)
|
||||||
|
case (List(ZLine(LD, TwoRegisters(A, _), _, _)),
|
||||||
|
List(ZLine(LD, TwoRegisters(A, reg), _, _))) if reg != MEM_ABS_8 =>
|
||||||
|
lb :+ ZLine.register(sub, reg)
|
||||||
|
case (List(ZLine(LD, TwoRegisters(A, _), _, _)), _) =>
|
||||||
|
Z80ExpressionCompiler.stashAFIfChangedF(rb :+ ZLine.ld8(E, A)) ++ lb :+ ZLine.register(sub, E)
|
||||||
|
case _ =>
|
||||||
|
if (preserveBc || preserveHl) ??? // TODO: preserve HL/BC for the next round of comparisons
|
||||||
|
var compileArgs = rb ++ List(ZLine.ld8(E, A)) ++ Z80ExpressionCompiler.stashDEIfChanged(lb) ++ List(ZLine.ld8(D, A))
|
||||||
|
if (i > 0) compileArgs = Z80ExpressionCompiler.stashAFIfChangedF(compileArgs)
|
||||||
|
compileArgs ++ List(ZLine.ld8(A, D), ZLine.register(sub, E))
|
||||||
|
}
|
||||||
|
if (i > 0 && preserveBc) compareBytes = Z80ExpressionCompiler.stashBCIfChanged(compareBytes)
|
||||||
|
if (i > 0 && preserveHl) compareBytes = Z80ExpressionCompiler.stashHLIfChanged(compareBytes)
|
||||||
|
compareBytes
|
||||||
|
}
|
||||||
|
if (branches == NoBranching) return calculateFlags
|
||||||
|
val jump = (compType, branches) match {
|
||||||
|
case (Equal, BranchIfTrue(label)) => ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||||
|
case (Equal, BranchIfFalse(label)) => ZLine.jump(label, IfFlagClear(ZFlag.Z))
|
||||||
|
case (NotEqual, BranchIfTrue(label)) => ZLine.jump(label, IfFlagClear(ZFlag.Z))
|
||||||
|
case (NotEqual, BranchIfFalse(label)) => ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||||
|
case (LessUnsigned, BranchIfTrue(label)) => ZLine.jump(label, IfFlagSet(ZFlag.C))
|
||||||
|
case (LessUnsigned, BranchIfFalse(label)) => ZLine.jump(label, IfFlagClear(ZFlag.C))
|
||||||
|
case (GreaterOrEqualUnsigned, BranchIfTrue(label)) => ZLine.jump(label, IfFlagClear(ZFlag.C))
|
||||||
|
case (GreaterOrEqualUnsigned, BranchIfFalse(label)) => ZLine.jump(label, IfFlagSet(ZFlag.C))
|
||||||
|
case _ => ???
|
||||||
|
}
|
||||||
|
calculateFlags :+ jump
|
||||||
|
}
|
||||||
|
|
||||||
|
def compileLongEqualityComparison(ctx: CompilationContext, compType: ComparisonType.Value, l: Expression, r: Expression, branches: BranchSpec): List[ZLine] = {
|
||||||
|
handleConstantComparison(ctx, compType, l, r, branches).foreach(return _)
|
||||||
|
val lt = Z80ExpressionCompiler.getExpressionType(ctx, l)
|
||||||
|
val rt = Z80ExpressionCompiler.getExpressionType(ctx, r)
|
||||||
|
val size = lt.size max rt.size
|
||||||
|
val calculateLeft = Z80ExpressionCompiler.compileByteReads(ctx, l, size, ZExpressionTarget.HL)
|
||||||
|
val calculateRight = Z80ExpressionCompiler.compileByteReads(ctx, r, size, ZExpressionTarget.BC)
|
||||||
|
val preserveHl = isBytesFromHL(calculateLeft)
|
||||||
|
val preserveBc = isBytesFromBC(calculateRight)
|
||||||
|
val innerLabel = Z80Compiler.nextLabel("cp")
|
||||||
|
val (jump, epilogue) = (compType, branches) match {
|
||||||
|
case (Equal, BranchIfTrue(label)) =>
|
||||||
|
ZLine.jump(innerLabel, IfFlagClear(ZFlag.Z)) -> ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||||
|
case (NotEqual, BranchIfFalse(label)) =>
|
||||||
|
ZLine.jump(innerLabel, IfFlagClear(ZFlag.Z)) -> ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||||
|
case (Equal, BranchIfFalse(label)) =>
|
||||||
|
ZLine.jump(innerLabel, IfFlagSet(ZFlag.Z)) -> ZLine.jump(label, IfFlagClear(ZFlag.Z))
|
||||||
|
case (NotEqual, BranchIfTrue(label)) =>
|
||||||
|
ZLine.jump(innerLabel, IfFlagSet(ZFlag.Z)) -> ZLine.jump(label, IfFlagClear(ZFlag.Z))
|
||||||
|
case (_, NoBranching) => ZLine.implied(ZOpcode.NOP) -> ZLine.implied(ZOpcode.NOP)
|
||||||
|
case _ => throw new IllegalArgumentException
|
||||||
|
}
|
||||||
|
val calculateFlags = calculateLeft.zip(calculateRight).zipWithIndex.flatMap { case ((lb, rb), i) =>
|
||||||
|
var compareBytes = {
|
||||||
|
import ZOpcode._
|
||||||
|
import ZRegister._
|
||||||
|
(lb, rb) match {
|
||||||
|
case (_, List(ZLine(LD, TwoRegisters(A, IMM_8), param, _))) =>
|
||||||
|
lb :+ ZLine.imm8(CP, param)
|
||||||
|
case (List(ZLine(LD, TwoRegisters(A, IMM_8), param, _)), _) =>
|
||||||
|
rb :+ ZLine.imm8(CP, param)
|
||||||
|
case (List(ZLine(LD, TwoRegisters(A, _), _, _)),
|
||||||
|
List(ZLine(LD, TwoRegisters(A, reg), _, _))) if reg != MEM_ABS_8 =>
|
||||||
|
lb :+ ZLine.register(CP, reg)
|
||||||
|
case (List(ZLine(LD, TwoRegisters(A, reg), _, _)),
|
||||||
|
List(ZLine(LD, TwoRegisters(A, _), _, _))) if reg != MEM_ABS_8 =>
|
||||||
|
rb :+ ZLine.register(CP, reg)
|
||||||
|
case (List(ZLine(LD, TwoRegisters(A, _), _, _)), _) =>
|
||||||
|
(rb :+ ZLine.ld8(E, A)) ++ lb :+ ZLine.register(CP, E)
|
||||||
|
case _ =>
|
||||||
|
var actualLb = lb
|
||||||
|
if (i == 0 && preserveBc) actualLb = Z80ExpressionCompiler.stashBCIfChanged(actualLb)
|
||||||
|
actualLb ++ List(ZLine.ld8(E, A)) ++ Z80ExpressionCompiler.stashDEIfChanged(rb) :+ ZLine.register(CP, E)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i > 0 && preserveBc) compareBytes = Z80ExpressionCompiler.stashBCIfChanged(compareBytes)
|
||||||
|
if (i > 0 && preserveHl) compareBytes = Z80ExpressionCompiler.stashHLIfChanged(compareBytes)
|
||||||
|
if (i != size - 1 && branches != NoBranching) compareBytes :+ jump else compareBytes
|
||||||
|
}
|
||||||
|
if (branches == NoBranching) calculateFlags
|
||||||
|
else calculateFlags ++ List(epilogue, ZLine.label(innerLabel))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private def isBytesFromHL(calculateLeft: List[List[ZLine]]) = {
|
||||||
|
calculateLeft(1) match {
|
||||||
|
case List(ZLine(ZOpcode.LD, TwoRegisters(ZRegister.A, ZRegister.H), _, _)) => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def isBytesFromBC(calculateLeft: List[List[ZLine]]) = {
|
||||||
|
calculateLeft(1) match {
|
||||||
|
case List(ZLine(ZOpcode.LD, TwoRegisters(ZRegister.A, ZRegister.B), _, _)) => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,7 @@ import millfork.error.ErrorReporting
|
|||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
object ZExpressionTarget extends Enumeration {
|
object ZExpressionTarget extends Enumeration {
|
||||||
val A, HL, NOTHING = Value
|
val A, HL, BC, DE, NOTHING = Value
|
||||||
}
|
}
|
||||||
|
|
||||||
object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||||
@@ -21,6 +21,10 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
|
|
||||||
def compileToHL(ctx: CompilationContext, expression: Expression): List[ZLine] = compile(ctx, expression, ZExpressionTarget.HL)
|
def compileToHL(ctx: CompilationContext, expression: Expression): List[ZLine] = compile(ctx, expression, ZExpressionTarget.HL)
|
||||||
|
|
||||||
|
def compileToBC(ctx: CompilationContext, expression: Expression): List[ZLine] = compile(ctx, expression, ZExpressionTarget.BC)
|
||||||
|
|
||||||
|
def compileToDE(ctx: CompilationContext, expression: Expression): List[ZLine] = compile(ctx, expression, ZExpressionTarget.DE)
|
||||||
|
|
||||||
def changesBC(line: ZLine): Boolean = {
|
def changesBC(line: ZLine): Boolean = {
|
||||||
import ZRegister._
|
import ZRegister._
|
||||||
if (ZOpcodeClasses.ChangesBCAlways(line.opcode)) return true
|
if (ZOpcodeClasses.ChangesBCAlways(line.opcode)) return true
|
||||||
@@ -35,6 +39,25 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def changesAF(line: ZLine): Boolean = {
|
||||||
|
import ZRegister._
|
||||||
|
if (ZOpcodeClasses.ChangesAFAlways(line.opcode)) return true
|
||||||
|
if (ZOpcodeClasses.ChangesFirstRegister(line.opcode)) return line.registers match {
|
||||||
|
case TwoRegisters(A, _) => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
if (ZOpcodeClasses.ChangesOnlyRegister(line.opcode)) return line.registers match {
|
||||||
|
case OneRegister(A) => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
def changesF(line: ZLine): Boolean = {
|
||||||
|
// TODO
|
||||||
|
ZOpcodeClasses.ChangesAFAlways(line.opcode)
|
||||||
|
}
|
||||||
|
|
||||||
def changesDE(line: ZLine): Boolean = {
|
def changesDE(line: ZLine): Boolean = {
|
||||||
import ZRegister._
|
import ZRegister._
|
||||||
if (ZOpcodeClasses.ChangesDEAlways(line.opcode)) return true
|
if (ZOpcodeClasses.ChangesDEAlways(line.opcode)) return true
|
||||||
@@ -78,6 +101,12 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def stashAFIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesAF))
|
||||||
|
ZLine.register(PUSH, ZRegister.AF) :: (lines :+ ZLine.register(POP, ZRegister.AF)) else lines
|
||||||
|
|
||||||
|
def stashAFIfChangedF(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesF))
|
||||||
|
ZLine.register(PUSH, ZRegister.AF) :: (lines :+ ZLine.register(POP, ZRegister.AF)) else lines
|
||||||
|
|
||||||
def stashBCIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesBC))
|
def stashBCIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesBC))
|
||||||
ZLine.register(PUSH, ZRegister.BC) :: (lines :+ ZLine.register(POP, ZRegister.BC)) else lines
|
ZLine.register(PUSH, ZRegister.BC) :: (lines :+ ZLine.register(POP, ZRegister.BC)) else lines
|
||||||
|
|
||||||
@@ -87,27 +116,36 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
def stashHLIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesHL))
|
def stashHLIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesHL))
|
||||||
ZLine.register(PUSH, ZRegister.HL) :: (lines :+ ZLine.register(POP, ZRegister.HL)) else lines
|
ZLine.register(PUSH, ZRegister.HL) :: (lines :+ ZLine.register(POP, ZRegister.HL)) else lines
|
||||||
|
|
||||||
def targetifyA(target: ZExpressionTarget.Value, lines: List[ZLine], isSigned: Boolean): List[ZLine] = target match {
|
def targetifyA(target: ZExpressionTarget.Value, lines: List[ZLine], isSigned: Boolean): List[ZLine] = {
|
||||||
case ZExpressionTarget.NOTHING | ZExpressionTarget.A => lines
|
def toWord(h:ZRegister.Value, l: ZRegister.Value) ={
|
||||||
case ZExpressionTarget.HL => lines ++ (if (isSigned) {
|
lines ++ (if (isSigned) {
|
||||||
val label = Z80Compiler.nextLabel("sx")
|
val label = Z80Compiler.nextLabel("sx")
|
||||||
List(
|
List(
|
||||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
ZLine.ld8(l, ZRegister.A),
|
||||||
ZLine.ldImm8(ZRegister.H, 0xff),
|
ZLine.ldImm8(h, 0xff),
|
||||||
ZLine.imm8(OR, 0x7f),
|
ZLine.imm8(OR, 0x7f),
|
||||||
ZLine.jump(label, IfFlagSet(ZFlag.S)), // TODO: gameboy has no S flag
|
ZLine.jump(label, IfFlagSet(ZFlag.S)), // TODO: gameboy has no S flag
|
||||||
ZLine.ldImm8(ZRegister.H, 0),
|
ZLine.ldImm8(h, 0),
|
||||||
ZLine.label(label))
|
ZLine.label(label))
|
||||||
} else {
|
} else {
|
||||||
List(
|
List(
|
||||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
ZLine.ld8(l, ZRegister.A),
|
||||||
ZLine.ldImm8(ZRegister.H, 0))
|
ZLine.ldImm8(h, 0))
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
target match {
|
||||||
|
case ZExpressionTarget.NOTHING | ZExpressionTarget.A => lines
|
||||||
|
case ZExpressionTarget.HL => toWord(ZRegister.H, ZRegister.L)
|
||||||
|
case ZExpressionTarget.BC => toWord(ZRegister.B, ZRegister.C)
|
||||||
|
case ZExpressionTarget.DE => toWord(ZRegister.D, ZRegister.E)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def targetifyHL(target: ZExpressionTarget.Value, lines: List[ZLine]): List[ZLine] = target match {
|
def targetifyHL(target: ZExpressionTarget.Value, lines: List[ZLine]): List[ZLine] = target match {
|
||||||
case ZExpressionTarget.NOTHING | ZExpressionTarget.HL => lines
|
case ZExpressionTarget.NOTHING | ZExpressionTarget.HL => lines
|
||||||
case ZExpressionTarget.A => lines :+ ZLine.ld8(ZRegister.A, ZRegister.L)
|
case ZExpressionTarget.A => lines :+ ZLine.ld8(ZRegister.A, ZRegister.L)
|
||||||
|
case ZExpressionTarget.BC => lines ++ List(ZLine.ld8(ZRegister.C, ZRegister.L), ZLine.ld8(ZRegister.B, ZRegister.H))
|
||||||
|
case ZExpressionTarget.DE => lines ++ List(ZLine.ld8(ZRegister.E, ZRegister.L), ZLine.ld8(ZRegister.D, ZRegister.H))
|
||||||
}
|
}
|
||||||
|
|
||||||
def compile(ctx: CompilationContext, expression: Expression, target: ZExpressionTarget.Value, branches: BranchSpec = BranchSpec.None): List[ZLine] = {
|
def compile(ctx: CompilationContext, expression: Expression, target: ZExpressionTarget.Value, branches: BranchSpec = BranchSpec.None): List[ZLine] = {
|
||||||
@@ -121,6 +159,10 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
List(ZLine.ldImm8(ZRegister.A, const))
|
List(ZLine.ldImm8(ZRegister.A, const))
|
||||||
case ZExpressionTarget.HL =>
|
case ZExpressionTarget.HL =>
|
||||||
List(ZLine.ldImm16(ZRegister.HL, const))
|
List(ZLine.ldImm16(ZRegister.HL, const))
|
||||||
|
case ZExpressionTarget.BC =>
|
||||||
|
List(ZLine.ldImm16(ZRegister.BC, const))
|
||||||
|
case ZExpressionTarget.DE =>
|
||||||
|
List(ZLine.ldImm16(ZRegister.DE, const))
|
||||||
case ZExpressionTarget.NOTHING =>
|
case ZExpressionTarget.NOTHING =>
|
||||||
Nil // TODO
|
Nil // TODO
|
||||||
}
|
}
|
||||||
@@ -135,8 +177,9 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
case 1 => loadByte(v.toAddress, target)
|
case 1 => loadByte(v.toAddress, target)
|
||||||
case 2 => target match {
|
case 2 => target match {
|
||||||
case ZExpressionTarget.NOTHING => Nil
|
case ZExpressionTarget.NOTHING => Nil
|
||||||
case ZExpressionTarget.HL =>
|
case ZExpressionTarget.HL => List(ZLine.ldAbs16(ZRegister.HL, v))
|
||||||
List(ZLine.ldAbs16(ZRegister.HL, v))
|
case ZExpressionTarget.BC => List(ZLine.ldAbs16(ZRegister.BC, v))
|
||||||
|
case ZExpressionTarget.DE => List(ZLine.ldAbs16(ZRegister.DE, v))
|
||||||
}
|
}
|
||||||
case _ => ???
|
case _ => ???
|
||||||
}
|
}
|
||||||
@@ -148,6 +191,10 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
case ZExpressionTarget.NOTHING => Nil
|
case ZExpressionTarget.NOTHING => Nil
|
||||||
case ZExpressionTarget.HL =>
|
case ZExpressionTarget.HL =>
|
||||||
List(ZLine.ldViaIx(ZRegister.L, v.baseOffset), ZLine.ldViaIx(ZRegister.H, v.baseOffset + 1))
|
List(ZLine.ldViaIx(ZRegister.L, v.baseOffset), ZLine.ldViaIx(ZRegister.H, v.baseOffset + 1))
|
||||||
|
case ZExpressionTarget.BC =>
|
||||||
|
List(ZLine.ldViaIx(ZRegister.C, v.baseOffset), ZLine.ldViaIx(ZRegister.B, v.baseOffset + 1))
|
||||||
|
case ZExpressionTarget.DE =>
|
||||||
|
List(ZLine.ldViaIx(ZRegister.E, v.baseOffset), ZLine.ldViaIx(ZRegister.D, v.baseOffset + 1))
|
||||||
}
|
}
|
||||||
case _ => ???
|
case _ => ???
|
||||||
}
|
}
|
||||||
@@ -176,6 +223,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
case ZExpressionTarget.NOTHING => Nil
|
case ZExpressionTarget.NOTHING => Nil
|
||||||
case ZExpressionTarget.A=> List(ZLine.ld8(ZRegister.A, ZRegister.H))
|
case ZExpressionTarget.A=> List(ZLine.ld8(ZRegister.A, ZRegister.H))
|
||||||
case ZExpressionTarget.HL=> List(ZLine.ld8(ZRegister.L, ZRegister.H), ZLine.ldImm8(ZRegister.H, 0))
|
case ZExpressionTarget.HL=> List(ZLine.ld8(ZRegister.L, ZRegister.H), ZLine.ldImm8(ZRegister.H, 0))
|
||||||
|
case ZExpressionTarget.BC=> List(ZLine.ld8(ZRegister.C, ZRegister.H), ZLine.ldImm8(ZRegister.B, 0))
|
||||||
|
case ZExpressionTarget.DE=> List(ZLine.ld8(ZRegister.E, ZRegister.H), ZLine.ldImm8(ZRegister.D, 0))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case "lo" =>
|
case "lo" =>
|
||||||
@@ -187,6 +236,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
case ZExpressionTarget.NOTHING => Nil
|
case ZExpressionTarget.NOTHING => Nil
|
||||||
case ZExpressionTarget.A => List(ZLine.ld8(ZRegister.A, ZRegister.L))
|
case ZExpressionTarget.A => List(ZLine.ld8(ZRegister.A, ZRegister.L))
|
||||||
case ZExpressionTarget.HL => List(ZLine.ldImm8(ZRegister.H, 0))
|
case ZExpressionTarget.HL => List(ZLine.ldImm8(ZRegister.H, 0))
|
||||||
|
case ZExpressionTarget.BC => List(ZLine.ld8(ZRegister.C, ZRegister.L), ZLine.ldImm8(ZRegister.B, 0))
|
||||||
|
case ZExpressionTarget.DE => List(ZLine.ld8(ZRegister.E, ZRegister.L), ZLine.ldImm8(ZRegister.D, 0))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case "nonet" =>
|
case "nonet" =>
|
||||||
@@ -206,6 +257,24 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
} else {
|
} else {
|
||||||
???
|
???
|
||||||
}
|
}
|
||||||
|
case ZExpressionTarget.BC =>
|
||||||
|
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||||
|
List(
|
||||||
|
ZLine.ld8(ZRegister.C, ZRegister.A),
|
||||||
|
ZLine.ldImm8(ZRegister.B, 0),
|
||||||
|
ZLine.register(RL, ZRegister.B))
|
||||||
|
} else {
|
||||||
|
???
|
||||||
|
}
|
||||||
|
case ZExpressionTarget.DE =>
|
||||||
|
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||||
|
List(
|
||||||
|
ZLine.ld8(ZRegister.C, ZRegister.A),
|
||||||
|
ZLine.ldImm8(ZRegister.B, 0),
|
||||||
|
ZLine.register(RL, ZRegister.B))
|
||||||
|
} else {
|
||||||
|
???
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case "&&" =>
|
case "&&" =>
|
||||||
@@ -237,7 +306,9 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, AND, params), isSigned = false)
|
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, AND, params), isSigned = false)
|
||||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitOperation(ctx, AND, params))
|
case 2 => targetifyHL(target, ZBuiltIns.compile16BitOperation(ctx, AND, params))
|
||||||
}
|
}
|
||||||
case "*" => ???
|
case "*" =>
|
||||||
|
assertAllBytes("Long multiplication not supported", ctx, params)
|
||||||
|
targetifyA(target, Z80Multiply.compile8BitMultiply(ctx, params), isSigned = false)
|
||||||
case "|" =>
|
case "|" =>
|
||||||
getParamMaxSize(ctx, params) match {
|
getParamMaxSize(ctx, params) match {
|
||||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, OR, params), isSigned = false)
|
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, OR, params), isSigned = false)
|
||||||
@@ -278,7 +349,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
compileTransitiveRelation(ctx, "<", params, target, branches) { (l, r) =>
|
compileTransitiveRelation(ctx, "<", params, target, branches) { (l, r) =>
|
||||||
size match {
|
size match {
|
||||||
case 1 => Z80Comparisons.compile8BitComparison(ctx, if (signed) ComparisonType.LessSigned else ComparisonType.LessUnsigned, l, r, branches)
|
case 1 => Z80Comparisons.compile8BitComparison(ctx, if (signed) ComparisonType.LessSigned else ComparisonType.LessUnsigned, l, r, branches)
|
||||||
case _ => ???
|
case 2 => Z80Comparisons.compile16BitComparison(ctx, if (signed) ComparisonType.LessSigned else ComparisonType.LessUnsigned, l, r, branches)
|
||||||
|
case _ => Z80Comparisons.compileLongRelativeComparison(ctx, if (signed) ComparisonType.LessSigned else ComparisonType.LessUnsigned, l, r, branches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case ">=" =>
|
case ">=" =>
|
||||||
@@ -286,7 +358,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
compileTransitiveRelation(ctx, ">=", params, target, branches) { (l, r) =>
|
compileTransitiveRelation(ctx, ">=", params, target, branches) { (l, r) =>
|
||||||
size match {
|
size match {
|
||||||
case 1 => Z80Comparisons.compile8BitComparison(ctx, if (signed) ComparisonType.GreaterOrEqualSigned else ComparisonType.GreaterOrEqualUnsigned, l, r, branches)
|
case 1 => Z80Comparisons.compile8BitComparison(ctx, if (signed) ComparisonType.GreaterOrEqualSigned else ComparisonType.GreaterOrEqualUnsigned, l, r, branches)
|
||||||
case _ => ???
|
case 2 => Z80Comparisons.compile16BitComparison(ctx, if (signed) ComparisonType.GreaterOrEqualSigned else ComparisonType.GreaterOrEqualUnsigned, l, r, branches)
|
||||||
|
case _ => Z80Comparisons.compileLongRelativeComparison(ctx, if (signed) ComparisonType.GreaterOrEqualSigned else ComparisonType.GreaterOrEqualUnsigned, l, r, branches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case ">" =>
|
case ">" =>
|
||||||
@@ -294,7 +367,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
compileTransitiveRelation(ctx, ">", params, target, branches) { (l, r) =>
|
compileTransitiveRelation(ctx, ">", params, target, branches) { (l, r) =>
|
||||||
size match {
|
size match {
|
||||||
case 1 => Z80Comparisons.compile8BitComparison(ctx, if (signed) ComparisonType.GreaterSigned else ComparisonType.GreaterUnsigned, l, r, branches)
|
case 1 => Z80Comparisons.compile8BitComparison(ctx, if (signed) ComparisonType.GreaterSigned else ComparisonType.GreaterUnsigned, l, r, branches)
|
||||||
case _ => ???
|
case 2 => Z80Comparisons.compile16BitComparison(ctx, if (signed) ComparisonType.GreaterSigned else ComparisonType.GreaterUnsigned, l, r, branches)
|
||||||
|
case _ => Z80Comparisons.compileLongRelativeComparison(ctx, if (signed) ComparisonType.GreaterSigned else ComparisonType.GreaterUnsigned, l, r, branches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "<=" =>
|
case "<=" =>
|
||||||
@@ -302,7 +376,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
compileTransitiveRelation(ctx, "<=", params, target, branches) { (l, r) =>
|
compileTransitiveRelation(ctx, "<=", params, target, branches) { (l, r) =>
|
||||||
size match {
|
size match {
|
||||||
case 1 => Z80Comparisons.compile8BitComparison(ctx, if (signed) ComparisonType.LessOrEqualSigned else ComparisonType.LessOrEqualUnsigned, l, r, branches)
|
case 1 => Z80Comparisons.compile8BitComparison(ctx, if (signed) ComparisonType.LessOrEqualSigned else ComparisonType.LessOrEqualUnsigned, l, r, branches)
|
||||||
case _ => ???
|
case 2 => Z80Comparisons.compile16BitComparison(ctx, if (signed) ComparisonType.LessOrEqualSigned else ComparisonType.LessOrEqualUnsigned, l, r, branches)
|
||||||
|
case _ => Z80Comparisons.compileLongRelativeComparison(ctx, if (signed) ComparisonType.LessOrEqualSigned else ComparisonType.LessOrEqualUnsigned, l, r, branches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "==" =>
|
case "==" =>
|
||||||
@@ -310,7 +385,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
compileTransitiveRelation(ctx, "==", params, target, branches) { (l, r) =>
|
compileTransitiveRelation(ctx, "==", params, target, branches) { (l, r) =>
|
||||||
size match {
|
size match {
|
||||||
case 1 => Z80Comparisons.compile8BitComparison(ctx, ComparisonType.Equal, l, r, branches)
|
case 1 => Z80Comparisons.compile8BitComparison(ctx, ComparisonType.Equal, l, r, branches)
|
||||||
case _ => ???
|
case 2 => Z80Comparisons.compile16BitComparison(ctx, ComparisonType.Equal, l, r, branches)
|
||||||
|
case _ => Z80Comparisons.compileLongEqualityComparison(ctx, ComparisonType.Equal, l, r, branches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "!=" =>
|
case "!=" =>
|
||||||
@@ -318,7 +394,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
compileTransitiveRelation(ctx, "!=", params, target, branches) { (l, r) =>
|
compileTransitiveRelation(ctx, "!=", params, target, branches) { (l, r) =>
|
||||||
size match {
|
size match {
|
||||||
case 1 => Z80Comparisons.compile8BitComparison(ctx, ComparisonType.NotEqual, l, r, branches)
|
case 1 => Z80Comparisons.compile8BitComparison(ctx, ComparisonType.NotEqual, l, r, branches)
|
||||||
case _ => ???
|
case 2 => Z80Comparisons.compile16BitComparison(ctx, ComparisonType.NotEqual, l, r, branches)
|
||||||
|
case _ => Z80Comparisons.compileLongEqualityComparison(ctx, ComparisonType.NotEqual, l, r, branches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "+=" =>
|
case "+=" =>
|
||||||
@@ -369,7 +446,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
case "*=" =>
|
case "*=" =>
|
||||||
assertAllBytes("Long multiplication not supported", ctx, params)
|
assertAllBytes("Long multiplication not supported", ctx, params)
|
||||||
val (l, r, 1) = assertAssignmentLike(ctx, params)
|
val (l, r, 1) = assertAssignmentLike(ctx, params)
|
||||||
???
|
Z80Multiply.compile8BitInPlaceMultiply(ctx, l, r)
|
||||||
case "*'=" =>
|
case "*'=" =>
|
||||||
assertAllBytes("Long multiplication not supported", ctx, params)
|
assertAllBytes("Long multiplication not supported", ctx, params)
|
||||||
val (l, r, 1) = assertAssignmentLike(ctx, params)
|
val (l, r, 1) = assertAssignmentLike(ctx, params)
|
||||||
@@ -471,6 +548,24 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def calculateLoadAndStoreForByte(ctx: CompilationContext, expr: LhsExpression): (List[ZLine], List[ZLine]) = {
|
||||||
|
Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, expr) match {
|
||||||
|
case Some((LocalVariableAddressViaHL, calculate)) =>
|
||||||
|
(calculate :+ ZLine.ld8(ZRegister.A, ZRegister.MEM_HL)) -> List(ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||||
|
case Some((LocalVariableAddressViaIX(offset), calculate)) =>
|
||||||
|
(calculate :+ ZLine.ldViaIx(ZRegister.A, offset)) -> List(ZLine.ldViaIx(offset, ZRegister.A))
|
||||||
|
case Some((LocalVariableAddressViaIY(offset), calculate)) =>
|
||||||
|
(calculate :+ ZLine.ldViaIy(ZRegister.A, offset)) -> List(ZLine.ldViaIy(offset, ZRegister.A))
|
||||||
|
case None => expr match {
|
||||||
|
case SeparateBytesExpression(h: LhsExpression, l: LhsExpression) =>
|
||||||
|
val lo = calculateLoadAndStoreForByte(ctx, l)
|
||||||
|
val (_, hiStore) = calculateLoadAndStoreForByte(ctx, h)
|
||||||
|
lo._1 -> (lo._2 ++ List(ZLine.ldImm8(ZRegister.A, 0)) ++ hiStore)
|
||||||
|
case _ => ???
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def calculateAddressToAppropriatePointer(ctx: CompilationContext, expr: LhsExpression): Option[(LocalVariableAddressOperand, List[ZLine])] = {
|
def calculateAddressToAppropriatePointer(ctx: CompilationContext, expr: LhsExpression): Option[(LocalVariableAddressOperand, List[ZLine])] = {
|
||||||
val env = ctx.env
|
val env = ctx.env
|
||||||
expr match {
|
expr match {
|
||||||
@@ -515,6 +610,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
case ZExpressionTarget.NOTHING => Nil
|
case ZExpressionTarget.NOTHING => Nil
|
||||||
case ZExpressionTarget.A => List(ZLine.ldAbs8(ZRegister.A, sourceAddr))
|
case ZExpressionTarget.A => List(ZLine.ldAbs8(ZRegister.A, sourceAddr))
|
||||||
case ZExpressionTarget.HL => List(ZLine.ldAbs8(ZRegister.A, sourceAddr), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0))
|
case ZExpressionTarget.HL => List(ZLine.ldAbs8(ZRegister.A, sourceAddr), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0))
|
||||||
|
case ZExpressionTarget.BC => List(ZLine.ldAbs8(ZRegister.A, sourceAddr), ZLine.ld8(ZRegister.C, ZRegister.A), ZLine.ldImm8(ZRegister.B, 0))
|
||||||
|
case ZExpressionTarget.DE => List(ZLine.ldAbs8(ZRegister.A, sourceAddr), ZLine.ld8(ZRegister.E, ZRegister.A), ZLine.ldImm8(ZRegister.D, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +619,9 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
target match {
|
target match {
|
||||||
case ZExpressionTarget.NOTHING => Nil
|
case ZExpressionTarget.NOTHING => Nil
|
||||||
case ZExpressionTarget.A => List(ZLine.ldViaIx(ZRegister.A, offset))
|
case ZExpressionTarget.A => List(ZLine.ldViaIx(ZRegister.A, offset))
|
||||||
case ZExpressionTarget.HL => List(ZLine.ldViaIx(ZRegister.A, offset), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0))
|
case ZExpressionTarget.HL => List(ZLine.ldViaIx(ZRegister.L, offset), ZLine.ldImm8(ZRegister.H, 0))
|
||||||
|
case ZExpressionTarget.BC => List(ZLine.ldViaIx(ZRegister.C, offset), ZLine.ldImm8(ZRegister.B, 0))
|
||||||
|
case ZExpressionTarget.DE => List(ZLine.ldViaIx(ZRegister.E, offset), ZLine.ldImm8(ZRegister.D, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,20 +629,16 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
target match {
|
target match {
|
||||||
case ZExpressionTarget.NOTHING => Nil
|
case ZExpressionTarget.NOTHING => Nil
|
||||||
case ZExpressionTarget.A => List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL))
|
case ZExpressionTarget.A => List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL))
|
||||||
case ZExpressionTarget.HL => List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0))
|
case ZExpressionTarget.HL => List(ZLine.ld8(ZRegister.L, ZRegister.MEM_HL), ZLine.ldImm8(ZRegister.H, 0))
|
||||||
|
case ZExpressionTarget.BC => List(ZLine.ld8(ZRegister.C, ZRegister.MEM_HL), ZLine.ldImm8(ZRegister.B, 0))
|
||||||
|
case ZExpressionTarget.DE => List(ZLine.ld8(ZRegister.E, ZRegister.MEM_HL), ZLine.ldImm8(ZRegister.D, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def signExtend(targetAddr: Constant, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
def signExtend(targetAddr: Constant, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||||
if (bytes == 0) return Nil
|
if (bytes == 0) return Nil
|
||||||
val prepareA = if (signedSource) {
|
val prepareA = if (signedSource) {
|
||||||
val prefix = if (hiRegister == ZRegister.A) Nil else List(ZLine.ld8(ZRegister.A, hiRegister))
|
signExtendHighestByte(hiRegister)
|
||||||
val label = Z80Compiler.nextLabel("sx")
|
|
||||||
prefix ++ List(
|
|
||||||
ZLine.imm8(OR, 0x7f),
|
|
||||||
ZLine.jump(label, IfFlagSet(ZFlag.S)),
|
|
||||||
ZLine.ldImm8(ZRegister.A, 0),
|
|
||||||
ZLine.label(label))
|
|
||||||
} else {
|
} else {
|
||||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||||
}
|
}
|
||||||
@@ -551,16 +646,20 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
prepareA ++ fillUpperBytes
|
prepareA ++ fillUpperBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def signExtendHighestByte(hiRegister: ZRegister.Value) = {
|
||||||
|
val prefix = if (hiRegister == ZRegister.A) Nil else List(ZLine.ld8(ZRegister.A, hiRegister))
|
||||||
|
val label = Z80Compiler.nextLabel("sx")
|
||||||
|
prefix ++ List(
|
||||||
|
ZLine.imm8(OR, 0x7f),
|
||||||
|
ZLine.jump(label, IfFlagSet(ZFlag.S)),
|
||||||
|
ZLine.ldImm8(ZRegister.A, 0),
|
||||||
|
ZLine.label(label))
|
||||||
|
}
|
||||||
|
|
||||||
def signExtendViaIX(targetOffset: Int, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
def signExtendViaIX(targetOffset: Int, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||||
if (bytes == 0) return Nil
|
if (bytes == 0) return Nil
|
||||||
val prepareA = if (signedSource) {
|
val prepareA = if (signedSource) {
|
||||||
val prefix = if (hiRegister == ZRegister.A) Nil else List(ZLine.ld8(ZRegister.A, hiRegister))
|
signExtendHighestByte(hiRegister)
|
||||||
val label = Z80Compiler.nextLabel("sx")
|
|
||||||
prefix ++ List(
|
|
||||||
ZLine.imm8(OR, 0x7f),
|
|
||||||
ZLine.jump(label, IfFlagSet(ZFlag.S)),
|
|
||||||
ZLine.ldImm8(ZRegister.A, 0),
|
|
||||||
ZLine.label(label))
|
|
||||||
} else {
|
} else {
|
||||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||||
}
|
}
|
||||||
@@ -639,8 +738,25 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
case (None, offset) => ZLine.ld8(ZRegister.A, ZRegister.L) :: storeA((p.value + offset).quickSimplify, 1, signedSource)
|
case (None, offset) => ZLine.ld8(ZRegister.A, ZRegister.L) :: storeA((p.value + offset).quickSimplify, 1, signedSource)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TODO
|
case SeparateBytesExpression(hi: LhsExpression, lo: LhsExpression) =>
|
||||||
case SeparateBytesExpression(hi, lo) => ???
|
Z80ExpressionCompiler.stashHLIfChanged(ZLine.ld8(ZRegister.A, ZRegister.L) :: storeA(ctx, lo, signedSource)) ++
|
||||||
|
(ZLine.ld8(ZRegister.A, ZRegister.H) :: storeA(ctx, hi, signedSource))
|
||||||
|
case _: SeparateBytesExpression =>
|
||||||
|
ErrorReporting.error("Invalid `:`", target.position)
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def storeLarge(ctx: CompilationContext, target: LhsExpression, source: Expression): List[ZLine] = {
|
||||||
|
val env = ctx.env
|
||||||
|
target match {
|
||||||
|
case VariableExpression(vname) =>
|
||||||
|
env.get[Variable](vname) match {
|
||||||
|
case v: Variable =>
|
||||||
|
val size = v.typ.size
|
||||||
|
compileByteReads(ctx, source, size, ZExpressionTarget.HL).zip(compileByteStores(ctx, target, size)).flatMap(t => t._1 ++ t._2)
|
||||||
|
}
|
||||||
|
case _ => ???
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -679,7 +795,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def compileByteReads(ctx: CompilationContext, rhs: Expression, size: Int): List[List[ZLine]] = {
|
def compileByteReads(ctx: CompilationContext, rhs: Expression, size: Int, temporaryTarget: ZExpressionTarget.Value): List[List[ZLine]] = {
|
||||||
if (size == 1) throw new IllegalArgumentException
|
if (size == 1) throw new IllegalArgumentException
|
||||||
val env = ctx.env
|
val env = ctx.env
|
||||||
env.eval(rhs) match {
|
env.eval(rhs) match {
|
||||||
@@ -691,20 +807,20 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
env.get[Variable](vname) match {
|
env.get[Variable](vname) match {
|
||||||
case v: VariableInMemory =>
|
case v: VariableInMemory =>
|
||||||
List.tabulate(size) { i =>
|
List.tabulate(size) { i =>
|
||||||
if (i < size) {
|
if (i < v.typ.size) {
|
||||||
List(ZLine.ldAbs8(ZRegister.A, v.toAddress + i))
|
List(ZLine.ldAbs8(ZRegister.A, v.toAddress + i))
|
||||||
} else if (v.typ.isSigned) {
|
} else if (v.typ.isSigned) {
|
||||||
???
|
ZLine.ldAbs8(ZRegister.A, v.toAddress + v.typ.size - 1) :: signExtendHighestByte(ZRegister.A)
|
||||||
} else {
|
} else {
|
||||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case v: StackVariable =>
|
case v: StackVariable =>
|
||||||
List.tabulate(size) { i =>
|
List.tabulate(size) { i =>
|
||||||
if (i < size) {
|
if (i < v.typ.size) {
|
||||||
List(ZLine.ldViaIx(ZRegister.A, v.baseOffset + i))
|
List(ZLine.ldViaIx(ZRegister.A, v.baseOffset + i))
|
||||||
} else if (v.typ.isSigned) {
|
} else if (v.typ.isSigned) {
|
||||||
???
|
ZLine.ldViaIx(ZRegister.A, v.baseOffset + v.typ.size - 1) :: signExtendHighestByte(ZRegister.A)
|
||||||
} else {
|
} else {
|
||||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||||
}
|
}
|
||||||
@@ -721,15 +837,42 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
List.tabulate(size) { i =>
|
val (h, l) = temporaryTarget match {
|
||||||
if (i == 0) {
|
case ZExpressionTarget.HL => ZRegister.H -> ZRegister.L
|
||||||
compileToHL(ctx, rhs) :+ ZLine.ld8(ZRegister.A, ZRegister.L)
|
case ZExpressionTarget.BC => ZRegister.B -> ZRegister.C
|
||||||
} else if (i == 1) {
|
case ZExpressionTarget.DE => ZRegister.D -> ZRegister.E
|
||||||
List(ZLine.ld8(ZRegister.A, ZRegister.H))
|
case _ => throw new IllegalArgumentException("temporaryTarget")
|
||||||
} else {
|
}
|
||||||
// TODO: signed words?
|
val typ = getExpressionType(ctx, rhs)
|
||||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
typ.size match {
|
||||||
}
|
case 1 =>
|
||||||
|
List.tabulate(size) { i =>
|
||||||
|
if (i == 0) {
|
||||||
|
if (typ.isSigned) {
|
||||||
|
(compileToA(ctx, rhs) :+ ZLine.ld8(l, ZRegister.A)) ++
|
||||||
|
signExtendHighestByte(ZRegister.A) ++ List(ZLine.ld8(h, ZRegister.A), ZLine.ld8(ZRegister.A, l))
|
||||||
|
} else {
|
||||||
|
compileToA(ctx, rhs)
|
||||||
|
}
|
||||||
|
} else if (typ.isSigned) {
|
||||||
|
List(ZLine.ld8(ZRegister.A, h))
|
||||||
|
} else {
|
||||||
|
// TODO: signed words?
|
||||||
|
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2 =>
|
||||||
|
List.tabulate(size) { i =>
|
||||||
|
if (i == 0) {
|
||||||
|
compile(ctx, rhs, temporaryTarget, BranchSpec.None) :+ ZLine.ld8(ZRegister.A, l)
|
||||||
|
} else if (i == 1) {
|
||||||
|
List(ZLine.ld8(ZRegister.A, h))
|
||||||
|
} else {
|
||||||
|
// TODO: signed words?
|
||||||
|
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ => ???
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
128
src/main/scala/millfork/compiler/z80/Z80Multiply.scala
Normal file
128
src/main/scala/millfork/compiler/z80/Z80Multiply.scala
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package millfork.compiler.z80
|
||||||
|
|
||||||
|
import millfork.assembly.z80._
|
||||||
|
import millfork.compiler.{BranchSpec, CompilationContext}
|
||||||
|
import millfork.env.{CompoundConstant, Constant, MathOperator, NumericConstant}
|
||||||
|
import millfork.node.{ConstantArrayElementExpression, Expression, LhsExpression, ZRegister}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karol Stasiak
|
||||||
|
*/
|
||||||
|
object Z80Multiply {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles A = A * D
|
||||||
|
*/
|
||||||
|
private def multiplication(): List[ZLine] = {
|
||||||
|
import millfork.assembly.z80.ZOpcode._
|
||||||
|
import ZRegister._
|
||||||
|
import ZLine._
|
||||||
|
val lblAdd = Z80Compiler.nextLabel("mu")
|
||||||
|
val lblLoop = Z80Compiler.nextLabel("mu")
|
||||||
|
val lblStart = Z80Compiler.nextLabel("mu")
|
||||||
|
List(
|
||||||
|
ld8(E, A),
|
||||||
|
ldImm8(A, 0),
|
||||||
|
jumpR(lblStart),
|
||||||
|
label(lblAdd),
|
||||||
|
register(ADD, E),
|
||||||
|
label(lblLoop),
|
||||||
|
register(SLA, E),
|
||||||
|
label(lblStart),
|
||||||
|
register(SRL, D),
|
||||||
|
jumpR(lblAdd, IfFlagSet(ZFlag.C)),
|
||||||
|
jumpR(lblLoop, IfFlagClear(ZFlag.Z)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate A = l * r
|
||||||
|
*/
|
||||||
|
def compile8BitMultiply(ctx: CompilationContext, params: List[Expression]): List[ZLine] = {
|
||||||
|
var numericConst = 1L
|
||||||
|
var otherConst: Constant = NumericConstant(1, 1)
|
||||||
|
val filteredParams = params.filter { expr =>
|
||||||
|
ctx.env.eval(expr) match {
|
||||||
|
case None =>
|
||||||
|
true
|
||||||
|
case Some(NumericConstant(n, _)) =>
|
||||||
|
numericConst *= n
|
||||||
|
false
|
||||||
|
case Some(c) =>
|
||||||
|
otherConst = CompoundConstant(MathOperator.Times, otherConst, c).loByte.quickSimplify
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val productOfConstants = CompoundConstant(MathOperator.Times, otherConst, NumericConstant(numericConst & 0xff, 1)).quickSimplify
|
||||||
|
(filteredParams, otherConst) match {
|
||||||
|
case (Nil, NumericConstant(n, _)) => List(ZLine.ldImm8(ZRegister.A, (numericConst * n).toInt))
|
||||||
|
case (Nil, _) => List(ZLine.ldImm8(ZRegister.A, productOfConstants))
|
||||||
|
case (List(a), NumericConstant(n, _)) => Z80ExpressionCompiler.compileToA(ctx, a) ++ compile8BitMultiply((numericConst * n).toInt)
|
||||||
|
case (List(a), _) =>
|
||||||
|
compile8BitMultiply(ctx, a, ConstantArrayElementExpression(productOfConstants))
|
||||||
|
case (List(a, b), NumericConstant(n, _)) =>
|
||||||
|
compile8BitMultiply(ctx, a, b) ++ compile8BitMultiply((numericConst * n).toInt)
|
||||||
|
case _ => ???
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate A = l * r
|
||||||
|
*/
|
||||||
|
def compile8BitMultiply(ctx: CompilationContext, l: Expression, r: Expression): List[ZLine] = {
|
||||||
|
(ctx.env.eval(l), ctx.env.eval(r)) match {
|
||||||
|
case (Some(a), Some(b)) => List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.Times, a, b).loByte.quickSimplify))
|
||||||
|
case (Some(NumericConstant(count, _)), None) => Z80ExpressionCompiler.compileToA(ctx, r) ++ compile8BitMultiply(count.toInt)
|
||||||
|
case (None, Some(NumericConstant(count, _))) => Z80ExpressionCompiler.compileToA(ctx, l) ++ compile8BitMultiply(count.toInt)
|
||||||
|
case _ =>
|
||||||
|
val lb = Z80ExpressionCompiler.compileToA(ctx, l)
|
||||||
|
val rb = Z80ExpressionCompiler.compileToA(ctx, r)
|
||||||
|
val load = if (lb.exists(Z80ExpressionCompiler.changesDE)) {
|
||||||
|
lb ++ List(ZLine.ld8(ZRegister.D, ZRegister.A)) ++ Z80ExpressionCompiler.stashDEIfChanged(rb)
|
||||||
|
} else {
|
||||||
|
rb ++ List(ZLine.ld8(ZRegister.D, ZRegister.A)) ++ lb
|
||||||
|
}
|
||||||
|
load ++ multiplication()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate A = l * r
|
||||||
|
*/
|
||||||
|
def compile8BitInPlaceMultiply(ctx: CompilationContext, l: LhsExpression, r: Expression): List[ZLine] = {
|
||||||
|
ctx.env.eval(r) match {
|
||||||
|
case Some(NumericConstant(count, _)) =>
|
||||||
|
val (load, store) = Z80ExpressionCompiler.calculateLoadAndStoreForByte(ctx, l)
|
||||||
|
load ++ compile8BitMultiply(count.toInt) ++ store
|
||||||
|
case Some(c) =>
|
||||||
|
val (load, store) = Z80ExpressionCompiler.calculateLoadAndStoreForByte(ctx, l)
|
||||||
|
load ++ List(ZLine.ldImm8(ZRegister.D, c)) ++ multiplication() ++ store
|
||||||
|
case _ =>
|
||||||
|
val (load, store) = Z80ExpressionCompiler.calculateLoadAndStoreForByte(ctx, l)
|
||||||
|
val rb = Z80ExpressionCompiler.compileToA(ctx, r)
|
||||||
|
val loadRegisters = if (load.exists(Z80ExpressionCompiler.changesDE)) {
|
||||||
|
load ++ List(ZLine.ld8(ZRegister.D, ZRegister.A)) ++ Z80ExpressionCompiler.stashDEIfChanged(rb)
|
||||||
|
} else {
|
||||||
|
rb ++ List(ZLine.ld8(ZRegister.D, ZRegister.A)) ++ load
|
||||||
|
}
|
||||||
|
loadRegisters ++ multiplication() ++ store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate A = count * x
|
||||||
|
*/
|
||||||
|
def compile8BitMultiply(count: Int): List[ZLine] = {
|
||||||
|
import millfork.assembly.z80.ZOpcode._
|
||||||
|
import ZRegister._
|
||||||
|
count match {
|
||||||
|
case 0 => List(ZLine.ldImm8(A, 0))
|
||||||
|
case 1 => Nil
|
||||||
|
case n if n > 0 && n.-(1).&(n).==(0) => List.fill(Integer.numberOfTrailingZeros(n))(ZLine.register(SLA, A))
|
||||||
|
case _ =>
|
||||||
|
ZLine.ld8(E,A) :: Integer.toString(count & 0xff, 2).tail.flatMap{
|
||||||
|
case '0' => List(ZLine.register(SLA, A))
|
||||||
|
case '1' => List(ZLine.register(SLA, A), ZLine.register(ADD, E))
|
||||||
|
}.toList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -68,7 +68,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
|||||||
case 0 => ???
|
case 0 => ???
|
||||||
case 1 => Z80ExpressionCompiler.compileToA(ctx, source) ++ Z80ExpressionCompiler.storeA(ctx, destination, sourceType.isSigned)
|
case 1 => Z80ExpressionCompiler.compileToA(ctx, source) ++ Z80ExpressionCompiler.storeA(ctx, destination, sourceType.isSigned)
|
||||||
case 2 => Z80ExpressionCompiler.compileToHL(ctx, source) ++ Z80ExpressionCompiler.storeHL(ctx, destination, sourceType.isSigned)
|
case 2 => Z80ExpressionCompiler.compileToHL(ctx, source) ++ Z80ExpressionCompiler.storeHL(ctx, destination, sourceType.isSigned)
|
||||||
case _ => ??? // large object copy
|
case s => Z80ExpressionCompiler.storeLarge(ctx, destination, source)
|
||||||
}
|
}
|
||||||
case s: IfStatement =>
|
case s: IfStatement =>
|
||||||
compileIfStatement(ctx, s)
|
compileIfStatement(ctx, s)
|
||||||
|
@@ -368,11 +368,12 @@ object ZBuiltIns {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val store = Z80ExpressionCompiler.compileByteStores(ctx, lhs, size)
|
val store = Z80ExpressionCompiler.compileByteStores(ctx, lhs, size)
|
||||||
val loadLeft = Z80ExpressionCompiler.compileByteReads(ctx, lhs, size)
|
val loadLeft = Z80ExpressionCompiler.compileByteReads(ctx, lhs, size, ZExpressionTarget.HL)
|
||||||
val loadRight = Z80ExpressionCompiler.compileByteReads(ctx, rhs, size)
|
val loadRight = Z80ExpressionCompiler.compileByteReads(ctx, rhs, size, ZExpressionTarget.BC)
|
||||||
List.tabulate(size) {i =>
|
List.tabulate(size) {i =>
|
||||||
// TODO: stash things correctly?
|
// TODO: stash things correctly?
|
||||||
val firstPhase = loadRight(i) ++ List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ (loadLeft(i) :+ ZLine.register(if (i==0) opcodeFirst else opcodeLater, ZRegister.E))
|
val firstPhase = loadRight(i) ++ List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++
|
||||||
|
(Z80ExpressionCompiler.stashBCIfChanged(loadLeft(i)) :+ ZLine.register(if (i==0) opcodeFirst else opcodeLater, ZRegister.E))
|
||||||
val secondPhase = if (decimal) firstPhase :+ ZLine.implied(ZOpcode.DAA) else firstPhase
|
val secondPhase = if (decimal) firstPhase :+ ZLine.implied(ZOpcode.DAA) else firstPhase
|
||||||
secondPhase ++ store(i)
|
secondPhase ++ store(i)
|
||||||
}.flatten
|
}.flatten
|
||||||
|
@@ -101,6 +101,13 @@ object MosNiceFunctionProperty {
|
|||||||
case object DoesntChangeZpRegister extends NiceFunctionProperty("reg")
|
case object DoesntChangeZpRegister extends NiceFunctionProperty("reg")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Z80NiceFunctionProperty {
|
||||||
|
case object DoesntChangeBC extends NiceFunctionProperty("BC")
|
||||||
|
case object DoesntChangeDE extends NiceFunctionProperty("DE")
|
||||||
|
case object DoesntChangeHL extends NiceFunctionProperty("HL")
|
||||||
|
case object DoesntChangeIY extends NiceFunctionProperty("IY")
|
||||||
|
}
|
||||||
|
|
||||||
object MosRegister extends Enumeration {
|
object MosRegister extends Enumeration {
|
||||||
val A, X, Y, AX, AY, YA, XA, XY, YX, AW = Value
|
val A, X, Y, AX, AY, YA, XA, XY, YX, AW = Value
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,76 @@
|
|||||||
package millfork.output
|
package millfork.output
|
||||||
|
|
||||||
import millfork.assembly.z80.ZLine
|
import millfork.assembly.z80._
|
||||||
import millfork.compiler.AbstractCompiler
|
import millfork.compiler.AbstractCompiler
|
||||||
|
import millfork.env.{ExternFunction, Label, MemoryAddressConstant, NormalFunction}
|
||||||
|
|
||||||
|
import scala.collection.GenTraversableOnce
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
object Z80InliningCalculator extends AbstractInliningCalculator[ZLine] {
|
object Z80InliningCalculator extends AbstractInliningCalculator[ZLine] {
|
||||||
|
|
||||||
// TODO
|
import ZOpcode._
|
||||||
|
|
||||||
override def codeForInlining(fname: String, functionsAlreadyKnownToBeNonInlineable: Set[String], code: List[ZLine]): Option[List[ZLine]] = None
|
private val badOpcodes = Set(RET, RETI, RETN, CALL, BYTE, POP, PUSH)
|
||||||
|
private val jumpingRelatedOpcodes = Set(LABEL, JP, JR)
|
||||||
|
|
||||||
override def inline(code: List[ZLine], inlinedFunctions: Map[String, List[ZLine]], compiler: AbstractCompiler[ZLine]): List[ZLine] = code
|
override def codeForInlining(fname: String, functionsAlreadyKnownToBeNonInlineable: Set[String], code: List[ZLine]): Option[List[ZLine]] = {
|
||||||
|
if (code.isEmpty) return None
|
||||||
|
code.last match {
|
||||||
|
case ZLine(RET, NoRegisters, _, _) =>
|
||||||
|
case _ => return None
|
||||||
|
}
|
||||||
|
var result = code.init
|
||||||
|
while (result.nonEmpty && ZOpcodeClasses.NoopDiscards(result.last.opcode)) {
|
||||||
|
result = result.init
|
||||||
|
}
|
||||||
|
if (result.head.opcode == LABEL && result.head.parameter == Label(fname).toAddress) result = result.tail
|
||||||
|
if (result.exists {
|
||||||
|
case ZLine(op, _, MemoryAddressConstant(Label(l)), _) if jumpingRelatedOpcodes(op) =>
|
||||||
|
!l.startsWith(".")
|
||||||
|
case ZLine(CALL, _, MemoryAddressConstant(th: ExternFunction), _) => false
|
||||||
|
case ZLine(CALL, _, MemoryAddressConstant(th: NormalFunction), _) =>
|
||||||
|
!functionsAlreadyKnownToBeNonInlineable(th.name)
|
||||||
|
case ZLine(op, _, _, _) if jumpingRelatedOpcodes(op) || badOpcodes(op) => true
|
||||||
|
case _ => false
|
||||||
|
}) return None
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
def wrap(registers: ZRegisters, compiler: AbstractCompiler[ZLine], lines: List[ZLine]): List[ZLine] = registers match {
|
||||||
|
case NoRegisters => lines
|
||||||
|
case IfFlagClear(flag) =>
|
||||||
|
val label = compiler.nextLabel("ai")
|
||||||
|
ZLine.jump(label, IfFlagSet(flag)) :: (lines :+ ZLine.label(label))
|
||||||
|
case IfFlagSet(flag) =>
|
||||||
|
val label = compiler.nextLabel("ai")
|
||||||
|
ZLine.jump(label, IfFlagClear(flag)) :: (lines :+ ZLine.label(label))
|
||||||
|
case _ => throw new IllegalArgumentException("registers")
|
||||||
|
}
|
||||||
|
|
||||||
|
override def inline(code: List[ZLine], inlinedFunctions: Map[String, List[ZLine]], compiler: AbstractCompiler[ZLine]): List[ZLine] = {
|
||||||
|
code.flatMap {
|
||||||
|
case ZLine(CALL, registers, p, true) if inlinedFunctions.contains(p.toString) =>
|
||||||
|
val labelPrefix = compiler.nextLabel("ai")
|
||||||
|
wrap(registers, compiler,
|
||||||
|
inlinedFunctions(p.toString).map {
|
||||||
|
case line@ZLine(_, _, MemoryAddressConstant(Label(label)), _) =>
|
||||||
|
val newLabel = MemoryAddressConstant(Label(labelPrefix + label))
|
||||||
|
line.copy(parameter = newLabel)
|
||||||
|
case l => l
|
||||||
|
})
|
||||||
|
case ZLine(JP | JR, registers, p, true) if inlinedFunctions.contains(p.toString) =>
|
||||||
|
val labelPrefix = compiler.nextLabel("ai")
|
||||||
|
wrap(registers, compiler,
|
||||||
|
inlinedFunctions(p.toString).map {
|
||||||
|
case line@ZLine(_, _, MemoryAddressConstant(Label(label)), _) =>
|
||||||
|
val newLabel = MemoryAddressConstant(Label(labelPrefix + label))
|
||||||
|
line.copy(parameter = newLabel)
|
||||||
|
case l => l
|
||||||
|
} :+ ZLine.implied(RET))
|
||||||
|
case x => List(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -67,7 +67,7 @@ class ArraySuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("Array assignment through a pointer") {
|
test("Array assignment through a pointer") {
|
||||||
val m = EmuUnoptimizedRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| array output [3] @$c000
|
| array output [3] @$c000
|
||||||
| pointer p
|
| pointer p
|
||||||
@@ -80,13 +80,14 @@ class ArraySuite extends FunSuite with Matchers {
|
|||||||
| w = $105
|
| w = $105
|
||||||
| p[i]:ignored = w
|
| p[i]:ignored = w
|
||||||
| }
|
| }
|
||||||
""".stripMargin)
|
""".stripMargin) { m =>
|
||||||
m.readByte(0xc001) should equal(1)
|
m.readByte(0xc001) should equal(1)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("Array in place math") {
|
test("Array in place math") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| array output [4] @$c000
|
| array output [4] @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
|
@@ -10,7 +10,7 @@ import org.scalatest.{FunSuite, Matchers}
|
|||||||
class ByteMathSuite extends FunSuite with Matchers {
|
class ByteMathSuite extends FunSuite with Matchers {
|
||||||
|
|
||||||
test("Complex expression") {
|
test("Complex expression") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| byte output @$c000
|
| byte output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -23,7 +23,7 @@ class ByteMathSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("Byte addition") {
|
test("Byte addition") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| byte output @$c000
|
| byte output @$c000
|
||||||
| byte a
|
| byte a
|
||||||
@@ -35,7 +35,7 @@ class ByteMathSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("Byte addition 2") {
|
test("Byte addition 2") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| byte output @$c000
|
| byte output @$c000
|
||||||
| byte a
|
| byte a
|
||||||
@@ -47,7 +47,7 @@ class ByteMathSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("In-place byte addition") {
|
test("In-place byte addition") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| array output[3] @$c000
|
| array output[3] @$c000
|
||||||
| byte a
|
| byte a
|
||||||
@@ -101,7 +101,7 @@ class ByteMathSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("In-place byte addition 2") {
|
test("In-place byte addition 2") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| array output[3] @$c000
|
| array output[3] @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -137,7 +137,7 @@ class ByteMathSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def multiplyCase1(x: Int, y: Int): Unit = {
|
private def multiplyCase1(x: Int, y: Int): Unit = {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
s"""
|
s"""
|
||||||
| byte output @$$c000
|
| byte output @$$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -165,7 +165,7 @@ class ByteMathSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def multiplyCase2(x: Int, y: Int): Unit = {
|
private def multiplyCase2(x: Int, y: Int): Unit = {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
s"""
|
s"""
|
||||||
| byte output @$$c000
|
| byte output @$$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -178,7 +178,7 @@ class ByteMathSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("Byte multiplication 2") {
|
test("Byte multiplication 2") {
|
||||||
EmuUltraBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| import zp_reg
|
| import zp_reg
|
||||||
| byte output1 @$c001
|
| byte output1 @$c001
|
||||||
@@ -212,8 +212,15 @@ class ByteMathSuite extends FunSuite with Matchers {
|
|||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
| noinline void crash_if_bad() {
|
| noinline void crash_if_bad() {
|
||||||
|
| #if ARCH_6502
|
||||||
| if output1 != 20 { asm { lda $bfff }}
|
| if output1 != 20 { asm { lda $bfff }}
|
||||||
| if output2 != 27 { asm { lda $bfff }}
|
| if output2 != 27 { asm { lda $bfff }}
|
||||||
|
| #elseif ARCH_Z80
|
||||||
|
| if output1 != 20 { asm { ld a,($bfff) }}
|
||||||
|
| if output2 != 27 { asm { ld a,($bfff) }}
|
||||||
|
| #else
|
||||||
|
| #error unsupported architecture
|
||||||
|
| #endif
|
||||||
| }
|
| }
|
||||||
""".stripMargin){m =>
|
""".stripMargin){m =>
|
||||||
m.readByte(0xc002) should equal(27)
|
m.readByte(0xc002) should equal(27)
|
||||||
@@ -238,7 +245,7 @@ class ByteMathSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def multiplyCase3(x: Int, y: Int): Unit = {
|
private def multiplyCase3(x: Int, y: Int): Unit = {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
s"""
|
s"""
|
||||||
| import zp_reg
|
| import zp_reg
|
||||||
| byte output @$$c000
|
| byte output @$$c000
|
||||||
|
@@ -76,7 +76,7 @@ class ComparisonSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("Does it even work") {
|
test("Does it even work") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| word output @$c000
|
| word output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -99,7 +99,7 @@ class ComparisonSuite extends FunSuite with Matchers {
|
|||||||
| if 2222 == 3333 { output -= 1 }
|
| if 2222 == 3333 { output -= 1 }
|
||||||
| }
|
| }
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
EmuBenchmarkRun(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
||||||
}
|
}
|
||||||
|
|
||||||
test("Word comparison == and !=") {
|
test("Word comparison == and !=") {
|
||||||
@@ -122,7 +122,7 @@ class ComparisonSuite extends FunSuite with Matchers {
|
|||||||
| if a != 0 { output += 1 }
|
| if a != 0 { output += 1 }
|
||||||
| }
|
| }
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
EmuBenchmarkRun(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
||||||
}
|
}
|
||||||
|
|
||||||
test("Word comparison <=") {
|
test("Word comparison <=") {
|
||||||
@@ -143,7 +143,7 @@ class ComparisonSuite extends FunSuite with Matchers {
|
|||||||
| if a <= c { output += 1 }
|
| if a <= c { output += 1 }
|
||||||
| }
|
| }
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
EmuBenchmarkRun(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
||||||
}
|
}
|
||||||
test("Word comparison <") {
|
test("Word comparison <") {
|
||||||
val src =
|
val src =
|
||||||
@@ -162,7 +162,7 @@ class ComparisonSuite extends FunSuite with Matchers {
|
|||||||
| if a < 257 { output += 1 }
|
| if a < 257 { output += 1 }
|
||||||
| }
|
| }
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
EmuBenchmarkRun(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ class ComparisonSuite extends FunSuite with Matchers {
|
|||||||
| if c > 0 { output += 1 }
|
| if c > 0 { output += 1 }
|
||||||
| }
|
| }
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
EmuBenchmarkRun(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
||||||
}
|
}
|
||||||
|
|
||||||
test("Word comparison >=") {
|
test("Word comparison >=") {
|
||||||
@@ -206,7 +206,7 @@ class ComparisonSuite extends FunSuite with Matchers {
|
|||||||
| if a >= 0 { output += 1 }
|
| if a >= 0 { output += 1 }
|
||||||
| }
|
| }
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
EmuBenchmarkRun(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
||||||
}
|
}
|
||||||
|
|
||||||
test("Signed comparison >=") {
|
test("Signed comparison >=") {
|
||||||
@@ -265,7 +265,7 @@ class ComparisonSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("Multiple params for equality") {
|
test("Multiple params for equality") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| byte output @$c000
|
| byte output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -281,7 +281,7 @@ class ComparisonSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("Multiple params for inequality") {
|
test("Multiple params for inequality") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| byte output @$c000
|
| byte output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -297,7 +297,7 @@ class ComparisonSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("Warnings") {
|
test("Warnings") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| byte output @$c000
|
| byte output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -339,7 +339,7 @@ class ComparisonSuite extends FunSuite with Matchers {
|
|||||||
| if c > 335444 { output += 1 }
|
| if c > 335444 { output += 1 }
|
||||||
| }
|
| }
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
EmuBenchmarkRun(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(src)(_.readByte(0xc000) should equal(src.count(_ == '+')))
|
||||||
}
|
}
|
||||||
|
|
||||||
test("Mixed type comparison") {
|
test("Mixed type comparison") {
|
||||||
@@ -357,6 +357,6 @@ class ComparisonSuite extends FunSuite with Matchers {
|
|||||||
| if x < z { output += 1 }
|
| if x < z { output += 1 }
|
||||||
| }
|
| }
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
EmuBenchmarkRun(src)(_.readByte(0xc000) should equal(1))
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(src)(_.readByte(0xc000) should equal(1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package millfork.test
|
package millfork.test
|
||||||
|
|
||||||
import millfork.test.emu.EmuBenchmarkRun
|
import millfork.Cpu
|
||||||
|
import millfork.test.emu.{EmuBenchmarkRun, EmuCrossPlatformBenchmarkRun}
|
||||||
import org.scalatest.{FunSuite, Matchers}
|
import org.scalatest.{FunSuite, Matchers}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,7 +10,7 @@ import org.scalatest.{FunSuite, Matchers}
|
|||||||
class LongTest extends FunSuite with Matchers {
|
class LongTest extends FunSuite with Matchers {
|
||||||
|
|
||||||
test("Long assignment") {
|
test("Long assignment") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| long output4 @$c000
|
| long output4 @$c000
|
||||||
| long output2 @$c004
|
| long output2 @$c004
|
||||||
@@ -28,7 +29,7 @@ class LongTest extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
test("Long assignment 2") {
|
test("Long assignment 2") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| long output4 @$c000
|
| long output4 @$c000
|
||||||
| long output2 @$c004
|
| long output2 @$c004
|
||||||
@@ -51,7 +52,7 @@ class LongTest extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
test("Long addition") {
|
test("Long addition") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| long output @$c000
|
| long output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -71,7 +72,7 @@ class LongTest extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
test("Long addition 2") {
|
test("Long addition 2") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| long output @$c000
|
| long output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -85,7 +86,7 @@ class LongTest extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
test("Long subtraction") {
|
test("Long subtraction") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| long output @$c000
|
| long output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -105,7 +106,7 @@ class LongTest extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
test("Long subtraction 2") {
|
test("Long subtraction 2") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| long output @$c000
|
| long output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -119,7 +120,7 @@ class LongTest extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
test("Long subtraction 3") {
|
test("Long subtraction 3") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| long output @$c000
|
| long output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -139,7 +140,7 @@ class LongTest extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("Long AND") {
|
test("Long AND") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| long output @$c000
|
| long output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
@@ -159,7 +160,7 @@ class LongTest extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("Long INC/DEC") {
|
test("Long INC/DEC") {
|
||||||
EmuBenchmarkRun(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
"""
|
"""
|
||||||
| long output0 @$c000
|
| long output0 @$c000
|
||||||
| long output1 @$c004
|
| long output1 @$c004
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package millfork.test
|
package millfork.test
|
||||||
|
|
||||||
import millfork.Cpu
|
import millfork.Cpu
|
||||||
import millfork.test.emu.{EmuBenchmarkRun, EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedRun}
|
import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun}
|
||||||
import org.scalatest.{FunSuite, Matchers}
|
import org.scalatest.{FunSuite, Matchers}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10,7 +10,7 @@ import org.scalatest.{FunSuite, Matchers}
|
|||||||
class SignExtensionSuite extends FunSuite with Matchers {
|
class SignExtensionSuite extends FunSuite with Matchers {
|
||||||
|
|
||||||
test("Sbyte to Word") {
|
test("Sbyte to Word") {
|
||||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80)("""
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
|
||||||
| word output @$c000
|
| word output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
| sbyte b
|
| sbyte b
|
||||||
@@ -22,7 +22,7 @@ class SignExtensionSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
test("Sbyte to Word 2") {
|
test("Sbyte to Word 2") {
|
||||||
EmuUnoptimizedRun("""
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
|
||||||
| word output @$c000
|
| word output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
| output = b()
|
| output = b()
|
||||||
@@ -30,10 +30,10 @@ class SignExtensionSuite extends FunSuite with Matchers {
|
|||||||
| sbyte b() {
|
| sbyte b() {
|
||||||
| return -1
|
| return -1
|
||||||
| }
|
| }
|
||||||
""".stripMargin).readWord(0xc000) should equal(0xffff)
|
""".stripMargin){m => m.readWord(0xc000) should equal(0xffff)}
|
||||||
}
|
}
|
||||||
test("Sbyte to Long") {
|
test("Sbyte to Long") {
|
||||||
EmuUnoptimizedRun("""
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
|
||||||
| long output @$c000
|
| long output @$c000
|
||||||
| void main () {
|
| void main () {
|
||||||
| output = 421
|
| output = 421
|
||||||
@@ -42,11 +42,11 @@ class SignExtensionSuite extends FunSuite with Matchers {
|
|||||||
| sbyte b() {
|
| sbyte b() {
|
||||||
| return -1
|
| return -1
|
||||||
| }
|
| }
|
||||||
""".stripMargin).readLong(0xc000) should equal(420)
|
""".stripMargin){m => m.readLong(0xc000) should equal(420)}
|
||||||
}
|
}
|
||||||
|
|
||||||
test("Optimize pointless sign extension") {
|
test("Optimize pointless sign extension") {
|
||||||
EmuBenchmarkRun("""
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
|
||||||
| array output [10] @$c000
|
| array output [10] @$c000
|
||||||
| word w
|
| word w
|
||||||
| void main () {
|
| void main () {
|
||||||
|
@@ -7,7 +7,7 @@ import millfork.output.MemoryBank
|
|||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
object EmuBenchmarkRun {
|
object EmuBenchmarkRun {
|
||||||
def apply(source: String)(verifier: MemoryBank => Unit) = {
|
def apply(source: String)(verifier: MemoryBank => Unit): Unit = {
|
||||||
val (Timings(t0, _), m0) = EmuUnoptimizedRun.apply2(source)
|
val (Timings(t0, _), m0) = EmuUnoptimizedRun.apply2(source)
|
||||||
val (Timings(t1, _), m1) = EmuOptimizedRun.apply2(source)
|
val (Timings(t1, _), m1) = EmuOptimizedRun.apply2(source)
|
||||||
val (Timings(t2, _), m2) = EmuOptimizedInlinedRun.apply2(source)
|
val (Timings(t2, _), m2) = EmuOptimizedInlinedRun.apply2(source)
|
||||||
@@ -26,16 +26,21 @@ object EmuBenchmarkRun {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object EmuZ80BenchmarkRun {
|
object EmuZ80BenchmarkRun {
|
||||||
def apply(source: String)(verifier: MemoryBank => Unit) = {
|
def apply(source: String)(verifier: MemoryBank => Unit): Unit = {
|
||||||
val (Timings(t0, _), m0) = EmuUnoptimizedZ80Run.apply2(source)
|
val (Timings(t0, _), m0) = EmuUnoptimizedZ80Run.apply2(source)
|
||||||
val (Timings(t1, _), m1) = EmuOptimizedZ80Run.apply2(source)
|
val (Timings(t1, _), m1) = EmuOptimizedZ80Run.apply2(source)
|
||||||
|
val (Timings(t2, _), m2) = EmuOptimizedInlinedZ80Run.apply2(source)
|
||||||
println(f"Before optimization: $t0%7d")
|
println(f"Before optimization: $t0%7d")
|
||||||
println(f"After optimization: $t1%7d")
|
println(f"After optimization: $t1%7d")
|
||||||
|
println(f"After inlining: $t2%7d")
|
||||||
println(f"Gain: ${(100L * (t0 - t1) / t0.toDouble).round}%7d%%")
|
println(f"Gain: ${(100L * (t0 - t1) / t0.toDouble).round}%7d%%")
|
||||||
|
println(f"Gain with inlining: ${(100L * (t0 - t2) / t0.toDouble).round}%7d%%")
|
||||||
println(f"Running unoptimized")
|
println(f"Running unoptimized")
|
||||||
verifier(m0)
|
verifier(m0)
|
||||||
println(f"Running optimized")
|
println(f"Running optimized")
|
||||||
verifier(m1)
|
verifier(m1)
|
||||||
|
println(f"Running optimized inlined")
|
||||||
|
verifier(m2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package millfork.test.emu
|
package millfork.test.emu
|
||||||
|
|
||||||
import millfork.assembly.mos.opt.{LaterOptimizations, ZeropageRegisterOptimizations}
|
import millfork.assembly.mos.opt.{LaterOptimizations, ZeropageRegisterOptimizations}
|
||||||
|
import millfork.assembly.z80.opt.Z80OptimizationPresets
|
||||||
import millfork.{Cpu, OptimizationPresets}
|
import millfork.{Cpu, OptimizationPresets}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,4 +23,8 @@ object EmuOptimizedInlinedRun extends EmuRun(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
object EmuOptimizedInlinedZ80Run extends EmuZ80Run(Cpu.Z80, OptimizationPresets.NodeOpt, Z80OptimizationPresets.Good) {
|
||||||
|
override def inline: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@ import org.scalatest.Matchers
|
|||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], assemblyOptimizations: List[AssemblyOptimization[ZLine]]) extends Matchers {
|
class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], assemblyOptimizations: List[AssemblyOptimization[ZLine]]) extends Matchers {
|
||||||
|
def inline: Boolean = false
|
||||||
|
|
||||||
private val TooManyCycles: Long = 1000000
|
private val TooManyCycles: Long = 1000000
|
||||||
|
|
||||||
@@ -27,7 +28,9 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
|||||||
Console.err.flush()
|
Console.err.flush()
|
||||||
println(source)
|
println(source)
|
||||||
val platform = EmuPlatform.get(cpu)
|
val platform = EmuPlatform.get(cpu)
|
||||||
val extraFlags = Map(CompilationFlag.LenientTextEncoding -> true)
|
val extraFlags = Map(
|
||||||
|
CompilationFlag.InlineFunctions -> this.inline,
|
||||||
|
CompilationFlag.LenientTextEncoding -> true)
|
||||||
val options = CompilationOptions(platform, millfork.Cpu.defaultFlags(cpu).map(_ -> true).toMap ++ extraFlags, None, 0)
|
val options = CompilationOptions(platform, millfork.Cpu.defaultFlags(cpu).map(_ -> true).toMap ++ extraFlags, None, 0)
|
||||||
ErrorReporting.hasErrors = false
|
ErrorReporting.hasErrors = false
|
||||||
ErrorReporting.verbosity = 999
|
ErrorReporting.verbosity = 999
|
||||||
@@ -68,7 +71,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
|||||||
val assembler = new Z80Assembler(program, env2, platform)
|
val assembler = new Z80Assembler(program, env2, platform)
|
||||||
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
|
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
|
||||||
println(";;; compiled: -----------------")
|
println(";;; compiled: -----------------")
|
||||||
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
|
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("////; DISCARD_")).foreach(println)
|
||||||
println(";;; ---------------------------")
|
println(";;; ---------------------------")
|
||||||
assembler.labelMap.foreach { case (l, addr) => println(f"$l%-15s $$$addr%04x") }
|
assembler.labelMap.foreach { case (l, addr) => println(f"$l%-15s $$$addr%04x") }
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user