1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-25 19:29:49 +00:00

6502: software BCD, increase default zpreg to 4

This commit is contained in:
Karol Stasiak 2018-08-03 13:06:23 +02:00
parent 30aa62ceaf
commit 388ceb8b3a
22 changed files with 698 additions and 117 deletions

View File

@ -74,12 +74,14 @@ Default: native if targeting 65816, no otherwise.
`.ini` equivalent: `prevent_jmp_indirect_bug`.
Default: no if targeting a 65C02-compatible architecture or a non-6502 architecture, yes otherwise.
* `-fzp-register`, `-fno-zp-register` Whether should reserve 2 bytes of zero page as a pseudoregister.
* `-fzp-register`, `-fno-zp-register` Whether should reserve 4 bytes of zero page as a pseudoregister.
Increases language features.
`.ini` equivalent: `zeropage_register`.
Default: yes if targeting a 6502-based architecture, no otherwise.
* `-fdecimal-mode`, `-fno-decimal-mode` Whether decimal mode should be available.
* `-fdecimal-mode`, `-fno-decimal-mode`
Whether hardware decimal mode should be used (6502 only).
If disabled, a sofware decimal mode will be used.
`.ini` equivalent: `decimal_mode`.
Default: no if targeting Ricoh, yes otherwise.

View File

@ -47,7 +47,8 @@ Default: the same as `encoding`.
* `emit_65816` which 65816 instructions should the compiler emit, either `no`, `emulation` or `native`
* `decimal_mode` whether the compiler should emit decimal instructions, default is `false` on `ricoh` and `strictricoh` and `true` elsewhere
* `decimal_mode` whether the compiler should emit decimal instructions, default is `false` on `ricoh` and `strictricoh` and `true` elsewhere;
if disabled, a software decimal mode will be used
* `ro_arrays` whether the compiler should warn upon array writes, default is `false`
@ -59,8 +60,9 @@ Default: the same as `encoding`.
* `lunix` generate relocatable code for LUnix/LNG, default is `false`
* `zeropage_register` reserve 2 bytes of zero page as a pseudoregister to increase language features.
Default: `true` if targeting a 6502-based architecture, `false` otherwise.
* `zeropage_register` reserve a certain amount of bytes of zero page as a pseudoregister to increase language features.
Default: `4` if targeting a 6502-based architecture, `0` otherwise.
`true` is a synonym of the current compiler default (currently: 4) and `false` is a synonym for 0.
* `inline` - inline functions automatically by default, default is `false`.

View File

@ -104,9 +104,7 @@ There are no division, remainder or modulo operators.
These operators work using the decimal arithmetic (packed BCD).
**Work in progress**:
These operations don't work on Ricoh-based targets (i.e. Famicom) yet.
The compiler issues a warning if these operators appear in the code.
On Ricoh-based targets (e.g. Famicom) they require the zeropage register to have size at least 4
* `+'`, `-'`: decimal addition/subtraction
`byte +' byte`

81
include/bcd_6502.mfk Normal file
View File

@ -0,0 +1,81 @@
#if not(ARCH_6502)
#warn bcd_6502 module should be only used on 6502-compatible targets
#endif
// subtracts __reg+3 from __reg+2 in a decimal way respecting carry
// returns the result in A and C
asm byte __sbc_decimal() {
LDA #$99
ADC #0
SEC
SBC __reg+3
STA __reg+3
CLC
? JMP __adc_decimal
}
// subtracts __reg+3 from __reg+2 in a decimal way ignoring carry
// returns the result in A and C
asm byte __sub_decimal() {
LDA #$9A
SEC
SBC __reg+3
STA __reg+3
CLC
? JMP __adc_decimal
}
// adds __reg+2 and __reg+3 in a decimal way
// returns the result in A and C
asm byte __adc_decimal() {
LDA __reg+2
TAX
AND #$F
STA __reg+2
LDA __reg+3
AND #$F
ADC __reg+2
STX __reg+2
CMP #$A
BCC __adc_decimal_lo_no_carry
// not needed: SEC
ADC #5
AND #$F
TAX // X contains the sum of low digits
LDA #$10
CLC
ADC __reg+2
STA __reg+2 // halfcarry pushed into R2
? JMP __adc_decimal_after_hi
__adc_decimal_lo_no_carry:
TAX
__adc_decimal_after_hi:
CLC
LDA __reg+2
AND #$F0
ADC __reg+3
AND #$F0
BCS __adc_decimal_hi_carry
STA __reg+2 // R2 contains the sum of high digits
TXA
CLC
ADC __reg+2
CMP #$A0
BCC __adc_decimal_hi_no_carry
// ; not needed: SEC
SBC #$A0
SEC
__adc_decimal_hi_no_carry:
RTS
__adc_decimal_hi_carry:
// ; not needed: SEC
SBC #$A0
STA __reg+2 // R2 contains the sum of high digits
TXA
CLC
ADC __reg+2
SEC
RTS
}

View File

@ -340,7 +340,7 @@ object Main {
}.description("Whether should prevent indirect JMP bug on page boundary.")
boolean("-fdecimal-mode", "-fno-decimal-mode").action { (c, v) =>
c.changeFlag(CompilationFlag.DecimalMode, v)
}.description("Whether decimal mode should be available.")
}.description("Whether hardware decimal mode should be used (6502 only).")
boolean("-fvariable-overlap", "-fno-variable-overlap").action { (c, v) =>
c.changeFlag(CompilationFlag.VariableOverlap, v)
}.description("Whether variables should overlap if their scopes do not intersect.")

View File

@ -93,8 +93,8 @@ object Platform {
}
val startingModules = cs.get(classOf[String], "modules", "").split("[, ]+").filter(_.nonEmpty).toList
val zpRegisterSize = cs.get(classOf[String], "zeropage_register", "").toLowerCase match {
case "" | null => if (CpuFamily.forType(cpu) == CpuFamily.M6502) 2 else 0
case "true" | "on" | "yes" => 2
case "" | null => if (CpuFamily.forType(cpu) == CpuFamily.M6502) 4 else 0
case "true" | "on" | "yes" => 4
case "false" | "off" | "no" | "0" => 0
case x => x.toInt
}

View File

@ -48,8 +48,10 @@ case class CpuImportance(a: Importance = UnknownImportance,
w: Importance = UnknownImportance,
r0: Importance = UnknownImportance,
r1: Importance = UnknownImportance,
r2: Importance = UnknownImportance,
r3: Importance = UnknownImportance,
) {
override def toString: String = s"A=$a,B=$ah,X=$x,Y=$y,Z=$iz; Z=$z,N=$n,C=$c,V=$v,D=$d,M=$m,X=$w; R0=$r0,R1=$r1"
override def toString: String = s"A=$a,B=$ah,X=$x,Y=$y,Z=$iz; Z=$z,N=$n,C=$c,V=$v,D=$d,M=$m,X=$w; R0=$r0,R1=$r1,R2=$r2,R3=$r3"
def ~(that: CpuImportance) = new CpuImportance(
a = this.a ~ that.a,
@ -65,6 +67,8 @@ case class CpuImportance(a: Importance = UnknownImportance,
w = this.w ~ that.w,
r0 = this.r0 ~ that.r0,
r1 = this.r1 ~ that.r1,
r2 = this.r2 ~ that.r2,
r3 = this.r3 ~ that.r3,
)
def isUnimportant(state: State.Value): Boolean = state match {
@ -86,12 +90,15 @@ case class CpuImportance(a: Importance = UnknownImportance,
def isPseudoregisterUnimportant(index: Int): Boolean = index match {
case 0 => r0 != Important
case 1 => r1 != Important
case 2 => r2 != Important
case 3 => r3 != Important
case _ => false
}
}
object ReverseFlowAnalyzer {
val functionsThatReadC = Set("__adc_decimal", "__sbc_decimal")
private val aluAdders = Set(Opcode.ADC, Opcode.SBC, Opcode.ISC, Opcode.DCP, Opcode.ADC_W, Opcode.SBC_W)
private val actuallyRead = Set(AddrMode.IndexedZ, AddrMode.IndexedSY, AddrMode.IndexedY, AddrMode.LongIndexedY, AddrMode.LongIndexedZ, AddrMode.IndexedX, AddrMode.Indirect, AddrMode.AbsoluteIndexedX)
private val absoluteLike = Set(AddrMode.ZeroPage, AddrMode.Absolute, AddrMode.LongAbsolute)
@ -109,13 +116,15 @@ object ReverseFlowAnalyzer {
m = Important,
w = Important,
r0 = Unimportant,
r1 = Unimportant)
r1 = Unimportant,
r2 = Unimportant,
r3 = Unimportant)
private val finalImportance: CpuImportance = CpuImportance(
a = Important, ah = Important,
x = Important, y = Important, iz = Important,
c = Important, v = Important, d = Important, z = Important, n = Important,
m = Important, w = Important,
r0 = Important, r1 = Important)
r0 = Important, r1 = Important, r2 = Important, r3 = Important)
//noinspection RedundantNewCaseClass
def analyze(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[CpuImportance] = {
@ -181,20 +190,32 @@ object ReverseFlowAnalyzer {
x = if (niceFunctionProperties(DoesntChangeX -> fun.name)) currentImportance.x ~ result.x else result.x,
y = if (niceFunctionProperties(DoesntChangeY -> fun.name)) currentImportance.y ~ result.y else result.y,
iz = if (niceFunctionProperties(DoesntChangeIZ -> fun.name)) currentImportance.iz ~ result.iz else result.iz,
c = if (niceFunctionProperties(DoesntChangeC -> fun.name)) currentImportance.c ~ result.c else result.c,
c = if (functionsThatReadC(fun.name)) Important else if (niceFunctionProperties(DoesntChangeC -> fun.name)) currentImportance.c ~ result.c else result.c,
d = if (niceFunctionProperties(DoesntConcernD -> fun.name)) currentImportance.d else result.d,
r0 =
if (ZeropageRegisterOptimizations.functionsThatUsePseudoregisterAsInput(fun.name))
if (ZeropageRegisterOptimizations.functionsThatUsePseudoregisterAsInput.getOrElse(fun.name, Set())(0))
Important
else if (niceFunctionProperties(DoesntChangeZpRegister -> fun.name))
currentImportance.r0 ~ result.r0
else result.r0,
r1 =
if (ZeropageRegisterOptimizations.functionsThatUsePseudoregisterAsInput(fun.name))
if (ZeropageRegisterOptimizations.functionsThatUsePseudoregisterAsInput.getOrElse(fun.name, Set())(1))
Important
else if (niceFunctionProperties(DoesntChangeZpRegister -> fun.name))
currentImportance.r1 ~ result.r1
else result.r1
else result.r1,
r2 =
if (ZeropageRegisterOptimizations.functionsThatUsePseudoregisterAsInput.getOrElse(fun.name, Set())(2))
Important
else if (niceFunctionProperties(DoesntChangeZpRegister -> fun.name))
currentImportance.r2 ~ result.r2
else result.r2,
r3 =
if (ZeropageRegisterOptimizations.functionsThatUsePseudoregisterAsInput.getOrElse(fun.name, Set())(3))
Important
else if (niceFunctionProperties(DoesntChangeZpRegister -> fun.name))
currentImportance.r3 ~ result.r3
else result.r3
)
case AssemblyLine(ANC, _, NumericConstant(0, _), _) =>
@ -283,6 +304,10 @@ object ReverseFlowAnalyzer {
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Unimportant)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(1, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r1 = Unimportant)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(2, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r2 = Unimportant)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(3, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r3 = Unimportant)
case _ => ()
}
}
@ -290,6 +315,10 @@ object ReverseFlowAnalyzer {
currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Unimportant, r1 = Unimportant)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(1, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r1 = Unimportant, r2 = Unimportant)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(2, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r2 = Unimportant, r3 = Unimportant)
case _ => ()
}
}
@ -298,6 +327,10 @@ object ReverseFlowAnalyzer {
currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Important, r1 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(1, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r1 = Important, r2 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(2, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r2 = Important, r3 = Important)
case _ => ()
}
} else if (OpcodeClasses.ReadsMemoryIfNotImpliedOrImmediate(currentLine.opcode)) {
@ -305,6 +338,10 @@ object ReverseFlowAnalyzer {
currentLine.parameter match {
case MemoryAddressConstant(th: Thing)
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Important, r1 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(1, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r1 = Important, r2 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(2, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r2 = Important, r3 = Important)
case _ => ()
}
} else {
@ -313,6 +350,10 @@ object ReverseFlowAnalyzer {
if th.name == "__reg" => currentImportance = currentImportance.copy(r0 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(1, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r1 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(2, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r2 = Important)
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(3, _))
if th.name == "__reg" => currentImportance = currentImportance.copy(r3 = Important)
case _ => ()
}
}

View File

@ -105,6 +105,8 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
def functionReadsD(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntConcernD -> name)
def functionReadsC(name: String): Boolean = ReverseFlowAnalyzer.functionsThatReadC(name)
def functionChangesMemory(name: String): Boolean = !niceFunctionProperties(NiceFunctionProperty.DoesntWriteMemory -> name)
def functionReadsMemory(name: String): Boolean = !niceFunctionProperties(NiceFunctionProperty.DoesntReadMemory -> name)
@ -861,7 +863,16 @@ case object ChangesY extends AssemblyLinePattern {
case object ReadsNOrZ extends HasOpcodeIn(OpcodeClasses.ReadsNOrZ)
case object ReadsC extends HasOpcodeIn(OpcodeClasses.ReadsC)
case object ReadsC extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import Opcode._
import AddrMode._
line match {
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => ctx.functionReadsC(th.name)
case _ => OpcodeClasses.ReadsC(line.opcode)
}
}
}
case object ReadsD extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {

View File

@ -11,7 +11,11 @@ import millfork.env.{CompoundConstant, Constant, MathOperator}
*/
object ZeropageRegisterOptimizations {
val functionsThatUsePseudoregisterAsInput = Set("__mul_u8u8u8")
val functionsThatUsePseudoregisterAsInput: Map[String, Set[Int]] = Map(
"__mul_u8u8u8" -> Set(0, 1),
"__adc_decimal" -> Set(2, 3),
"__sbc_decimal" -> Set(2, 3),
"__sub_decimal" -> Set(2, 3))
val ConstantMultiplication = new RuleBasedAssemblyOptimization("Constant multiplication",
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
@ -58,21 +62,85 @@ object ZeropageRegisterOptimizations {
},
)
val ConstantDecimalMath = new RuleBasedAssemblyOptimization("Constant decimal math",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(HasOpcode(STA) & RefersTo("__reg", 2) & MatchAddrMode(0) & MatchParameter(1) & MatchA(4)) ~
(Linear & Not(RefersToOrUses("__reg", 3)) & DoesntChangeMemoryAt(0, 1)).* ~
(HasOpcode(STA) & RefersTo("__reg", 3) & MatchA(5)) ~
(Elidable & HasOpcode(JSR) & RefersTo("__add_decimal", 0) & HasClear(State.C) & DoesntMatterWhatItDoesWith(State.C)) ~~> { (code, ctx) =>
val sum = asDecimal(ctx.get[Int](4)& 0xff, ctx.get[Int](5)& 0xff, _ + _)
code.init :+ AssemblyLine.immediate(LDA, sum & 0xff)
},
(HasOpcode(STA) & RefersTo("__reg", 2) & MatchAddrMode(0) & MatchParameter(1) & MatchA(4)) ~
(Linear & Not(RefersToOrUses("__reg", 3)) & DoesntChangeMemoryAt(0, 1)).* ~
(HasOpcode(STA) & RefersTo("__reg", 3) & MatchA(5)) ~
(Elidable & HasOpcode(JSR) & RefersTo("__add_decimal", 0) & HasClear(State.C)) ~~> { (code, ctx) =>
val sum = asDecimal(ctx.get[Int](4)& 0xff, ctx.get[Int](5)& 0xff, _ + _)
if (sum > 0xff) {
code.init ++ List(AssemblyLine.immediate(LDA, sum & 0xff), AssemblyLine.implied(SEC))
} else {
code.init ++ List(AssemblyLine.immediate(LDA, sum & 0xff), AssemblyLine.implied(CLC))
}
},
(HasOpcode(STA) & RefersTo("__reg", 2) & MatchAddrMode(0) & MatchParameter(1) & MatchA(4)) ~
(Linear & Not(RefersToOrUses("__reg", 3)) & DoesntChangeMemoryAt(0, 1)).* ~
(HasOpcode(STA) & RefersTo("__reg", 3) & MatchA(5)) ~
(Elidable & HasOpcode(JSR) & RefersTo("__add_decimal", 0) & HasSet(State.C) & DoesntMatterWhatItDoesWith(State.C)) ~~> { (code, ctx) =>
val sum = asDecimal(asDecimal(ctx.get[Int](4) & 0xff, ctx.get[Int](5) & 0xff, _ + _), 1, _ + _)
code.init :+ AssemblyLine.immediate(LDA, sum & 0xff)
},
(HasOpcode(STA) & RefersTo("__reg", 2) & MatchAddrMode(0) & MatchParameter(1) & MatchA(4)) ~
(Linear & Not(RefersToOrUses("__reg", 3)) & DoesntChangeMemoryAt(0, 1)).* ~
(HasOpcode(STA) & RefersTo("__reg", 3) & MatchA(5)) ~
(Elidable & HasOpcode(JSR) & RefersTo("__add_decimal", 0) & HasSet(State.C)) ~~> { (code, ctx) =>
val sum = asDecimal(asDecimal(ctx.get[Int](4) & 0xff, ctx.get[Int](5) & 0xff, _ + _), 1, _ + _)
if (sum > 0xff) {
code.init ++ List(AssemblyLine.immediate(LDA, sum & 0xff), AssemblyLine.implied(SEC))
} else {
code.init ++ List(AssemblyLine.immediate(LDA, sum & 0xff), AssemblyLine.implied(CLC))
}
},
(HasOpcode(STA) & RefersTo("__reg", 2) & MatchAddrMode(0) & MatchParameter(1) & MatchA(4)) ~
(Linear & Not(RefersToOrUses("__reg", 3)) & DoesntChangeMemoryAt(0, 1)).* ~
(HasOpcode(STA) & RefersTo("__reg", 3) & MatchA(5)) ~
Where(ctx => ctx.get[Int](4) > ctx.get[Int](5)) ~
(Elidable & HasOpcode(JSR) & RefersTo("__sub_decimal", 0) & HasClear(State.C) & DoesntMatterWhatItDoesWith(State.C)) ~~> { (code, ctx) =>
val diff = asDecimal(ctx.get[Int](4)& 0xff, ctx.get[Int](5)& 0xff, _ - _)
code.init :+ AssemblyLine.immediate(LDA, diff & 0xff)
},
)
// TODO: do this in a smarter way
val DeadRegStore = new RuleBasedAssemblyOptimization("Dead zeropage register store",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
(Elidable & HasOpcode(STA) & RefersTo("__reg", 0) & MatchAddrMode(0) & MatchParameter(1)) ~
(LinearOrLabel & DoesNotConcernMemoryAt(0, 1)).* ~
(HasOpcodeIn(Set(RTS, RTL)) | CallsAnyExcept(functionsThatUsePseudoregisterAsInput)) ~~> (_.tail),
(HasOpcodeIn(Set(RTS, RTL)) | CallsAnyExcept(functionsThatUsePseudoregisterAsInput.filter(_._2.contains(0)).keySet)) ~~> (_.tail),
(Elidable & HasOpcode(STA) & RefersTo("__reg", 1) & MatchAddrMode(0) & MatchParameter(1)) ~
(LinearOrLabel & DoesNotConcernMemoryAt(0, 1)).* ~
(HasOpcodeIn(Set(RTS, RTL)) | CallsAnyExcept(functionsThatUsePseudoregisterAsInput)) ~~> (_.tail),
(HasOpcodeIn(Set(RTS, RTL)) | CallsAnyExcept(functionsThatUsePseudoregisterAsInput.filter(_._2.contains(1)).keySet)) ~~> (_.tail),
(Elidable & HasOpcode(STA) & RefersTo("__reg", 2) & MatchAddrMode(0) & MatchParameter(1)) ~
(LinearOrLabel & DoesNotConcernMemoryAt(0, 1)).* ~
(HasOpcodeIn(Set(RTS, RTL)) | CallsAnyExcept(functionsThatUsePseudoregisterAsInput.filter(_._2.contains(2)).keySet)) ~~> (_.tail),
(Elidable & HasOpcode(STA) & RefersTo("__reg", 3) & MatchAddrMode(0) & MatchParameter(1)) ~
(LinearOrLabel & DoesNotConcernMemoryAt(0, 1)).* ~
(HasOpcodeIn(Set(RTS, RTL)) | CallsAnyExcept(functionsThatUsePseudoregisterAsInput.filter(_._2.contains(3)).keySet)) ~~> (_.tail),
)
val DeadRegStoreFromFlow = new RuleBasedAssemblyOptimization("Dead zeropage register store from flow",
needsFlowInfo = FlowInfoRequirement.BothFlows,
(Elidable & HasOpcode(STA) & RefersTo("__reg", 0) & DoesntMatterWhatItDoesWithReg(0)) ~~> (_.tail),
(Elidable & HasOpcode(STA) & RefersTo("__reg", 1) & DoesntMatterWhatItDoesWithReg(1)) ~~> (_.tail),
(Elidable & HasOpcode(STA) & RefersTo("__reg", 2) & DoesntMatterWhatItDoesWithReg(2)) ~~> (_.tail),
(Elidable & HasOpcode(STA) & RefersTo("__reg", 3) & DoesntMatterWhatItDoesWithReg(3)) ~~> (_.tail),
(Elidable & HasOpcode(LDY) & RefersTo("__reg", 0)) ~
(Linear & Not(ConcernsY) & Not(RefersToOrUses("__reg", 0))).*.capture(2) ~
@ -103,7 +171,38 @@ object ZeropageRegisterOptimizations {
})
)
private def parseNormalToDecimalValue(a: Long): Long = {
if (a < 0) -parseNormalToDecimalValue(-a)
var x = a
var result = 0L
var multiplier = 1L
while (x > 0) {
result += multiplier * (x % 16L)
x /= 16L
multiplier *= 10L
}
result
}
private def storeDecimalValueInNormalRespresentation(a: Long): Long = {
if (a < 0) -storeDecimalValueInNormalRespresentation(-a)
var x = a
var result = 0L
var multiplier = 1L
while (x > 0) {
result += multiplier * (x % 10L)
x /= 10L
multiplier *= 16L
}
result
}
private def asDecimal(a: Long, b: Long, f: (Long, Long) => Long): Long =
storeDecimalValueInNormalRespresentation(f(parseNormalToDecimalValue(a), parseNormalToDecimalValue(b)))
val All: List[AssemblyOptimization[AssemblyLine]] = List(
ConstantDecimalMath,
ConstantMultiplication,
DeadRegStore,
DeadRegStoreFromFlow,

View File

@ -118,8 +118,9 @@ object BuiltIns {
}
def compileAddition(ctx: CompilationContext, params: List[(Boolean, Expression)], decimal: Boolean): List[AssemblyLine] = {
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode)) {
ctx.log.warn("Unsupported decimal operation", params.head._2.position)
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode) && ctx.options.zpRegisterSize < 4) {
ctx.log.error("Unsupported decimal operation. Consider increasing the size of the zeropage register.", params.head._2.position)
return compileAddition(ctx, params, decimal = false)
}
// if (params.isEmpty) {
// return Nil
@ -142,10 +143,24 @@ object BuiltIns {
}
val remainingParamsCompiled = normalizedParams.tail.flatMap { p =>
if (p._1) {
insertBeforeLast(AssemblyLine.implied(SEC), simpleOperation(SBC, ctx, p._2, IndexChoice.PreferY, preserveA = true, commutative = false, decimal = decimal))
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode)) {
val reg = ctx.env.get[VariableInMemory]("__reg")
if (p._1) {
List(AssemblyLine.zeropage(STA, reg, 2)) ++
MosExpressionCompiler.preserveZpregIfNeededDestroyingAAndX(ctx, 2,
MosExpressionCompiler.compileToA(ctx, p._2)) ++
List(AssemblyLine.zeropage(STA, reg, 3), AssemblyLine.absolute(JSR, ctx.env.get[FunctionInMemory]("__sub_decimal")))
} else {
List(AssemblyLine.zeropage(STA, reg, 2), AssemblyLine.implied(CLC)) ++
MosExpressionCompiler.preserveZpregIfNeededDestroyingAAndX(ctx, 2, MosExpressionCompiler.compileToA(ctx, p._2)) ++
List(AssemblyLine.zeropage(STA, reg, 3), AssemblyLine.absolute(JSR, ctx.env.get[FunctionInMemory]("__adc_decimal")))
}
} else {
insertBeforeLast(AssemblyLine.implied(CLC), simpleOperation(ADC, ctx, p._2, IndexChoice.PreferY, preserveA = true, commutative = true, decimal = decimal))
if (p._1) {
insertBeforeLast(AssemblyLine.implied(SEC), simpleOperation(SBC, ctx, p._2, IndexChoice.PreferY, preserveA = true, commutative = false, decimal = decimal))
} else {
insertBeforeLast(AssemblyLine.implied(CLC), simpleOperation(ADC, ctx, p._2, IndexChoice.PreferY, preserveA = true, commutative = true, decimal = decimal))
}
}
}
firstParamCompiled ++ firstParamSignCompiled ++ remainingParamsCompiled
@ -748,14 +763,15 @@ object BuiltIns {
PseudoregisterBuiltIns.compileByteMultiplication(ctx, Some(variables(0)._1), variables(1)._1, storeInRegLo = false)
else
PseudoregisterBuiltIns.compileByteMultiplication(ctx, Some(variables(0)._1), variables(1)._1, storeInRegLo = true) ++
compileByteMultiplication(ctx, VariableExpression("__reg.lo"), constant)
compileByteMultiplication(ctx, VariableExpression("__reg.b0"), constant)
case _ => ??? // TODO
}
}
def compileInPlaceByteAddition(ctx: CompilationContext, v: LhsExpression, addend: Expression, subtract: Boolean, decimal: Boolean): List[AssemblyLine] = {
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode)) {
ctx.log.warn("Unsupported decimal operation", v.position)
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode) && ctx.options.zpRegisterSize < 4) {
ctx.log.error("Unsupported decimal operation. Consider increasing the size of the zeropage register.", v.position)
return compileInPlaceByteAddition(ctx, v, addend, subtract, decimal = false)
}
val env = ctx.env
val b = env.get[Type]("byte")
@ -783,7 +799,20 @@ object BuiltIns {
simpleOperation(DEC, ctx, v, IndexChoice.RequireX, preserveA = false, commutative = true)
}
case _ =>
if (!subtract && simplicity(env, v) > simplicity(env, addend)) {
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode)) {
val reg = ctx.env.get[MemoryVariable]("__reg")
val loadRhs = MosExpressionCompiler.compile(ctx, addend, Some(b -> ctx.env.genRelativeVariable(reg.toAddress + 3, b, zeropage = true)), NoBranching)
val loadLhs = MosExpressionCompiler.compileToA(ctx, v)
val storeLhs = MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
val subroutine = if (subtract) "__sub_decimal" else "__adc_decimal"
loadRhs ++ MosExpressionCompiler.preserveZpregIfNeededDestroyingAAndX(ctx, 3, loadLhs) ++ List(
AssemblyLine.zeropage(STA, reg, 2)) ++ (if (subtract) List(
AssemblyLine.absolute(JSR, ctx.env.get[ThingInMemory]("__sub_decimal"))
) else List(
AssemblyLine.implied(CLC),
AssemblyLine.absolute(JSR, ctx.env.get[ThingInMemory]("__adc_decimal"))
)) ++ storeLhs
} else if (!subtract && simplicity(env, v) > simplicity(env, addend)) {
val loadRhs = MosExpressionCompiler.compile(ctx, addend, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
val modifyAcc = insertBeforeLast(AssemblyLine.implied(CLC), simpleOperation(ADC, ctx, v, IndexChoice.PreferY, preserveA = true, commutative = true, decimal = decimal))
val storeLhs = MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
@ -806,8 +835,9 @@ object BuiltIns {
}
def compileInPlaceWordOrLongAddition(ctx: CompilationContext, lhs: LhsExpression, addend: Expression, subtract: Boolean, decimal: Boolean): List[AssemblyLine] = {
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode)) {
ctx.log.warn("Unsupported decimal operation", lhs.position)
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode) && ctx.options.zpRegisterSize < 4) {
ctx.log.error("Unsupported decimal operation. Consider increasing the size of the zeropage register.", lhs.position)
return compileInPlaceWordOrLongAddition(ctx, lhs, addend, subtract, decimal = false)
}
val env = ctx.env
val b = env.get[Type]("byte")
@ -941,7 +971,7 @@ object BuiltIns {
}
(base ++ List(AssemblyLine.implied(PHA))) -> List(List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, 0x101)))
} else {
Nil -> base.map(_ :: Nil)
Nil -> base.map(l => l.copy(opcode = LDA) :: Nil)
}
} else {
base -> List(Nil)
@ -955,7 +985,7 @@ object BuiltIns {
if (isRhsComplex(base)) {
???
} else {
Nil -> fixedBase
Nil -> fixedBase.map(l => l.copy(opcode = LDA) :: Nil)
???
}
} else {
@ -972,7 +1002,7 @@ object BuiltIns {
List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, 0x102)),
List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, 0x101)))
} else {
Nil -> base.map(_ :: Nil)
Nil -> base.map(l => l.copy(opcode = LDA) :: Nil)
}
} else {
if (lhsIsStack) {
@ -1046,7 +1076,30 @@ object BuiltIns {
val extendMultipleBytes = targetSize > addendSize + 1
val extendAtLeastOneByte = targetSize > addendSize
for (i <- 0 until targetSize) {
if (subtract) {
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode)) {
val reg = ctx.env.get[VariableInMemory]("__reg")
buffer ++= staTo(LDA, targetBytes(i))
if (targetBytes(i).isEmpty) {
buffer += AssemblyLine.immediate(LDA, 0)
}
buffer += AssemblyLine.zeropage(STA, reg, 2)
// TODO: AX?
buffer ++= MosExpressionCompiler.preserveZpregIfNeededDestroyingAAndX(ctx, 2, addendByteRead(i))
buffer += AssemblyLine.zeropage(STA, reg, 3)
if (subtract) {
if (i == 0) {
buffer += AssemblyLine.absolute(JSR, env.get[ThingInMemory]("__sub_decimal"))
} else {
buffer += AssemblyLine.absolute(JSR, env.get[ThingInMemory]("__sbc_decimal"))
}
} else {
if (i == 0) {
buffer += AssemblyLine.implied(CLC)
}
buffer += AssemblyLine.absolute(JSR, env.get[ThingInMemory]("__adc_decimal"))
}
buffer ++= targetBytes(i)
} else if (subtract) {
if (addendSize < targetSize && addendType.isSigned) {
// TODO: sign extension
???
@ -1208,7 +1261,7 @@ object BuiltIns {
}
private def getStorageForEachByte(ctx: CompilationContext, lhs: LhsExpression): List[List[AssemblyLine]] = {
def getStorageForEachByte(ctx: CompilationContext, lhs: LhsExpression): List[List[AssemblyLine]] = {
val env = ctx.env
lhs match {
case v: VariableExpression =>

View File

@ -15,7 +15,26 @@ import millfork.node.{Expression, MosRegister, _}
*/
object DecimalBuiltIns {
def compileByteShiftLeft(ctx: CompilationContext, l: Expression, r: Expression, rotate: Boolean): List[AssemblyLine] = {
ctx.env.eval(r) match {
if (ctx.options.zpRegisterSize >= 4 && !ctx.options.flag(CompilationFlag.DecimalMode)) {
val reg = ctx.env.get[VariableInMemory]("__reg")
val subroutine = ctx.env.get[ThingInMemory]("__adc_decimal")
ctx.env.eval(r) match {
case Some(NumericConstant(0, _)) =>
Nil
case Some(NumericConstant(v, _)) =>
val addition =
MosExpressionCompiler.compileToA(ctx, l) ++ List.fill(v.toInt)(List(
AssemblyLine.zeropage(STA, reg, 2),
AssemblyLine.zeropage(STA, reg, 3),
AssemblyLine.implied(CLC),
AssemblyLine.absolute(JSR, subroutine)
)).flatten
if (rotate) addition.filterNot(_.opcode == CLC) else addition
case _ =>
ctx.log.error("Cannot shift by a non-constant amount", r.position)
Nil
}
} else ctx.env.eval(r) match {
case Some(NumericConstant(0, _)) =>
Nil
case Some(NumericConstant(v, _)) =>
@ -92,7 +111,23 @@ object DecimalBuiltIns {
case Some(NumericConstant(0, _)) =>
Nil
case Some(NumericConstant(v, _)) =>
List.fill(v.toInt)(BuiltIns.compileInPlaceWordOrLongAddition(ctx, l, l, decimal = true, subtract = false)).flatten
if (!ctx.options.flag(CompilationFlag.DecimalMode) && ctx.options.zpRegisterSize >= 4) {
import BuiltIns.staTo
val targetBytes: List[List[AssemblyLine]] = BuiltIns.getStorageForEachByte(ctx, l)
val reg = ctx.env.get[VariableInMemory]("__reg")
val subroutine = ctx.env.get[FunctionInMemory]("__adc_decimal")
List.fill(v.toInt) {
targetBytes.zipWithIndex.flatMap { case (staByte, i) =>
staTo(LDA, staByte) ++
List(AssemblyLine.zeropage(STA, reg, 2), AssemblyLine.zeropage(STA, reg, 3)) ++
(if (i == 0) List(AssemblyLine.implied(CLC)) else Nil) ++
List(AssemblyLine.absolute(JSR, subroutine)) ++
(if (i == targetBytes.size - 1) staByte else MosExpressionCompiler.preserveCarryIfNeeded(ctx, staByte))
}
}.flatten
} else {
List.fill(v.toInt)(BuiltIns.compileInPlaceWordOrLongAddition(ctx, l, l, decimal = true, subtract = false)).flatten
}
case _ =>
ctx.log.error("Cannot shift by a non-constant amount", r.position)
Nil
@ -128,6 +163,9 @@ object DecimalBuiltIns {
}
def compileInPlaceByteMultiplication(ctx: CompilationContext, l: LhsExpression, r: Expression): List[AssemblyLine] = {
val ricoh = !ctx.options.flag(CompilationFlag.DecimalMode) && ctx.options.zpRegisterSize >= 4
val reg = ctx.env.maybeGet[VariableInMemory]("__reg")
val adcSubroutine = ctx.env.maybeGet[ThingInMemory]("__adc_decimal")
val multiplier = ctx.env.eval(r) match {
case Some(NumericConstant(v, _)) =>
if (v.&(0xf0) > 0x90 || v.&(0xf) > 9)
@ -141,16 +179,27 @@ object DecimalBuiltIns {
val sta = fullStorage.last
if (sta.opcode != STA) ???
val fullLoad = fullStorage.init :+ sta.copy(opcode = LDA)
val transferToStash = sta.addrMode match {
val transferToStash = if (ricoh) AssemblyLine.zeropage(STA, reg.get, 1) else sta.addrMode match {
case AbsoluteX | AbsoluteIndexedX | ZeroPageX | IndexedX => AssemblyLine.implied(TAY)
case _ => AssemblyLine.implied(TAX)
}
val transferToAccumulator = sta.addrMode match {
val transferToAccumulator = if (ricoh) AssemblyLine.zeropage(LDA, reg.get, 1) else sta.addrMode match {
case AbsoluteX | AbsoluteIndexedX | ZeroPageX | IndexedX => AssemblyLine.implied(TYA)
case _ => AssemblyLine.implied(TXA)
}
def add1 = List(transferToAccumulator, AssemblyLine.implied(CLC), sta.copy(opcode = ADC), sta)
def add1: List[AssemblyLine] = if (ricoh) {
List(
AssemblyLine.zeropage(LDA, reg.get),
AssemblyLine.zeropage(STA, reg.get, 2),
AssemblyLine.zeropage(LDA, reg.get, 1),
AssemblyLine.zeropage(STA, reg.get, 3),
AssemblyLine.implied(CLC),
AssemblyLine.absolute(JSR, adcSubroutine.get),
AssemblyLine.zeropage(STA, reg.get),
AssemblyLine.zeropage(STA, reg.get, 2)
)
} else List(transferToAccumulator, AssemblyLine.implied(CLC), sta.copy(opcode = ADC), sta)
def times7 = List(
AssemblyLine.implied(ASL), AssemblyLine.implied(ASL),
AssemblyLine.implied(ASL), AssemblyLine.implied(ASL),
@ -170,10 +219,12 @@ object DecimalBuiltIns {
sta)
val execute = multiplier match {
case 0 => List(AssemblyLine.immediate(LDA, 0), sta)
case 0 =>
if (ricoh) List(AssemblyLine.immediate(LDA, 0), AssemblyLine.zeropage(STA, reg.get))
else List(AssemblyLine.immediate(LDA, 0), sta)
case 1 => Nil
case x =>
val ways = sta.addrMode match {
val ways = if(ricoh) waysForRicoh else sta.addrMode match {
case Absolute | AbsoluteX | AbsoluteY | AbsoluteIndexedX | Indirect =>
waysForLongAddrModes
case _ =>
@ -181,15 +232,43 @@ object DecimalBuiltIns {
}
ways(x).flatMap {
case 1 => add1
case -7 => times7
case q if q < 9 => List.fill(q - 1)(List(AssemblyLine.implied(CLC), sta.copy(opcode = ADC))).flatten :+ sta
case 8 => times8
case 9 => times9
case q => List(AssemblyLine.implied(ASL), AssemblyLine.implied(ASL), AssemblyLine.implied(ASL), AssemblyLine.implied(ASL)) ++
case -7 if !ricoh => times7
case q if q < 9 && ricoh => List.fill(q - 1) {
List(
AssemblyLine.zeropage(LDA, reg.get),
AssemblyLine.zeropage(STA, reg.get, 3),
AssemblyLine.implied(CLC),
AssemblyLine.absolute(JSR, adcSubroutine.get),
AssemblyLine.zeropage(STA, reg.get, 2)
)
}.flatten :+ AssemblyLine.zeropage(STA, reg.get)
case q if q < 9 && !ricoh => List.fill(q - 1) (List(AssemblyLine.implied(CLC), sta.copy(opcode = ADC))).flatten :+ sta
case 8 if !ricoh => times8
case 9 if !ricoh => times9
case 10 if ricoh =>
List(
AssemblyLine.zeropage(LDA, reg.get, 2),
AssemblyLine.implied(ASL),
AssemblyLine.implied(ASL),
AssemblyLine.implied(ASL),
AssemblyLine.implied(ASL),
AssemblyLine.zeropage(STA, reg.get, 2),
AssemblyLine.zeropage(STA, reg.get)
)
case q if !ricoh => List(AssemblyLine.implied(ASL), AssemblyLine.implied(ASL), AssemblyLine.implied(ASL), AssemblyLine.implied(ASL)) ++
List.fill(q - 10)(List(AssemblyLine.implied(CLC), sta.copy(opcode = ADC))).flatten :+ sta
case _ => throw new IllegalStateException(ways.toString)
}
}
if (execute.contains(transferToAccumulator)) {
if (ricoh) {
fullLoad ++
List(
AssemblyLine.zeropage(STA, reg.get),
AssemblyLine.zeropage(STA, reg.get, 1),
AssemblyLine.zeropage(STA, reg.get, 2)) ++
execute ++
List(AssemblyLine.zeropage(LDA, reg.get)) ++ fullStorage
} else if (execute.contains(transferToAccumulator)) {
AssemblyLine.implied(SED) :: (fullLoad ++ List(transferToStash) ++ execute :+ AssemblyLine.implied(CLD))
} else {
AssemblyLine.implied(SED) :: (fullLoad ++ execute :+ AssemblyLine.implied(CLD))
@ -220,6 +299,18 @@ object DecimalBuiltIns {
81 -> List(9,9), 82 -> List(9,9,1), 83 -> List(9,9,1,1), 84 -> List(12,-7), 85 -> List(12,-7,1), 86 -> List(2,10,1,2,1,2), 87 -> List(3,9,1,1,3), 88 -> List(8,11), 89 -> List(8,11,1), 90 -> List(9,10),
91 -> List(9,10,1), 92 -> List(9,10,1,1), 93 -> List(3,10,1,3), 94 -> List(3,10,1,3,1), 95 -> List(2,9,1,5), 96 -> List(8,12), 97 -> List(8,12,1), 98 -> List(14,-7), 99 -> List(11,9),
)
private lazy val waysForRicoh: Map[Int, List[Int]] = Map (
2 -> List(2), 3 -> List(3), 4 -> List(2,2), 5 -> List(2,2,1), 6 -> List(3,2), 7 -> List(3,2,1), 8 -> List(2,2,2), 9 -> List(3,3), 10 -> List(10),
11 -> List(10,1), 12 -> List(10,1,1), 13 -> List(10,1,1,1), 14 -> List(10,1,1,1,1), 15 -> List(2,2,1,3), 16 -> List(2,2,2,2), 17 -> List(2,2,2,2,1), 18 -> List(3,3,2), 19 -> List(3,3,2,1), 20 -> List(2,10),
21 -> List(2,10,1), 22 -> List(10,1,2), 23 -> List(10,1,2,1), 24 -> List(10,1,1,2), 25 -> List(10,1,1,2,1), 26 -> List(10,1,1,1,2), 27 -> List(10,1,1,1,2,1), 28 -> List(10,1,1,1,1,2), 29 -> List(3,2,1,2,2,1), 30 -> List(3,10),
31 -> List(3,10,1), 32 -> List(3,10,1,1), 33 -> List(10,1,3), 34 -> List(10,1,3,1), 35 -> List(10,1,3,1,1), 36 -> List(10,1,1,3), 37 -> List(10,1,1,3,1), 38 -> List(10,1,1,3,1,1), 39 -> List(10,1,1,1,3), 40 -> List(2,2,10),
41 -> List(2,2,10,1), 42 -> List(2,10,1,2), 43 -> List(2,10,1,2,1), 44 -> List(10,1,2,2), 45 -> List(10,1,2,2,1), 46 -> List(10,1,2,1,2), 47 -> List(10,1,2,1,2,1), 48 -> List(10,1,1,2,2), 49 -> List(10,1,1,2,2,1), 50 -> List(2,2,1,10),
51 -> List(2,2,1,10,1), 52 -> List(2,2,1,10,1,1), 53 -> List(10,5,1,1,1), 54 -> List(3,3,3,2), 55 -> List(10,1,5), 56 -> List(10,1,5,1), 57 -> List(10,1,5,1,1), 58 -> List(10,1,5,1,1,1), 59 -> List(7,2,2,1,2,1), 60 -> List(3,2,10),
61 -> List(3,2,10,1), 62 -> List(3,10,1,2), 63 -> List(2,10,1,3), 64 -> List(3,10,1,1,2), 65 -> List(3,10,1,1,2,1), 66 -> List(10,1,3,2), 67 -> List(10,1,3,2,1), 68 -> List(10,1,3,1,2), 69 -> List(10,1,2,1,3), 70 -> List(3,2,1,10),
71 -> List(3,2,1,10,1), 72 -> List(10,1,1,3,2), 73 -> List(10,1,1,3,2,1), 74 -> List(10,1,1,3,1,2), 75 -> List(10,1,1,2,1,3), 76 -> List(3,3,2,1,2,2), 77 -> List(10,1,7), 78 -> List(10,1,1,1,3,2), 79 -> List(10,1,7,1,1), 80 -> List(2,2,2,10),
81 -> List(2,2,2,10,1), 82 -> List(2,2,10,1,2), 83 -> List(2,2,10,1,2,1), 84 -> List(2,10,1,2,2), 85 -> List(2,10,1,2,2,1), 86 -> List(2,10,1,2,1,2), 87 -> List(3,3,3,1,1,3), 88 -> List(10,1,2,2,2), 89 -> List(10,1,2,2,2,1), 90 -> List(3,3,10),
91 -> List(3,3,10,1), 92 -> List(10,1,2,1,2,2), 93 -> List(3,10,1,3), 94 -> List(3,10,1,3,1), 95 -> List(3,10,1,3,1,1), 96 -> List(10,1,1,2,2,2), 97 -> List(3,10,1,1,3,1), 98 -> List(10,1,1,1,1,7), 99 -> List(10,1,3,3),
)
// The following functions are used to generate the tables above:
@ -279,6 +370,7 @@ object DecimalBuiltIns {
def main(args: Array[String]): Unit = {
val shortCosts = multiplyCosts(ZeroPageY)
val longCosts = multiplyCosts(AbsoluteX)
val ricohCosts = Map(1 -> 1, 2 -> 1, 3 -> 2, 5 -> 4, 7 -> 6, 10 -> 0)
for (i <- 2 to 99) {
if (waysForLongAddrModes(i) != waysForShortAddrModes(i)) {
println(i)
@ -304,5 +396,11 @@ object DecimalBuiltIns {
print(s"$i -> List(${findWay(i, longCosts).mkString(",")}), ")
if (i % 10 == 0) println()
}
println()
println()
for (i <- 2 to 99) {
print(s"$i -> List(${findWay(i, ricohCosts).mkString(",")}), ")
if (i % 10 == 0) println()
}
}
}

View File

@ -4,6 +4,7 @@ import millfork.CompilationFlag
import millfork.assembly.mos.AddrMode._
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
import millfork.assembly.z80.ZLine
import millfork.compiler._
import millfork.env._
import millfork.error.ConsoleLogger
@ -79,6 +80,37 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
case Nil => Nil
}
def preserveZpregIfNeededDestroyingAAndX(ctx: CompilationContext, Offset: Int, code: List[AssemblyLine]): List[AssemblyLine] = {
if (code.exists{
case AssemblyLine(op,
AddrMode.ZeroPage | AddrMode.Absolute | AddrMode.LongAbsolute,
CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(Offset, _)),
_) if th.name =="__reg" && OpcodeClasses.ChangesMemoryAlways(op) || OpcodeClasses.ChangesMemoryIfNotImplied(op) => true
case AssemblyLine(op,
AddrMode.ZeroPage | AddrMode.Absolute | AddrMode.LongAbsolute,
MemoryAddressConstant(th),
_) if th.name =="__reg" && Offset == 0 && OpcodeClasses.ChangesMemoryAlways(op) || OpcodeClasses.ChangesMemoryIfNotImplied(op) => true
case AssemblyLine(JSR | BYTE | BSR, _, _, _) => true
case _ => false
}) {
List(AssemblyLine.zeropage(LDA, ctx.env.get[VariableInMemory]("__reg"), Offset), AssemblyLine.implied(PHA)) ++
code ++
List(
AssemblyLine.implied(TAX),
AssemblyLine.implied(PLA),
AssemblyLine.zeropage(STA, ctx.env.get[VariableInMemory]("__reg"), Offset),
AssemblyLine.implied(TXA))
} else code
}
def preserveCarryIfNeeded(ctx: CompilationContext, code: List[AssemblyLine]): List[AssemblyLine] = {
if (code.exists {
case AssemblyLine(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th), _) => true
case x => OpcodeClasses.ChangesC(x.opcode)
}) {
AssemblyLine.implied(PHP) +: fixTsx(code) :+ AssemblyLine.implied(PLP)
} else code
}
def preserveRegisterIfNeeded(ctx: CompilationContext, register: MosRegister.Value, code: List[AssemblyLine]): List[AssemblyLine] = {
val state = register match {
case MosRegister.A => State.A
@ -298,6 +330,12 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
val noop: List[AssemblyLine] = Nil
def compileToA(ctx: CompilationContext, expr: Expression): List[AssemblyLine] = {
val env = ctx.env
val b = env.get[Type]("byte")
compile(ctx, expr, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None)
}
def compile(ctx: CompilationContext, expr: Expression, exprTypeAndVariable: Option[(Type, Variable)], branches: BranchSpec): List[AssemblyLine] = {
val env = ctx.env
val b = env.get[Type]("byte")

View File

@ -61,48 +61,87 @@ object PseudoregisterBuiltIns {
ctx.log.error("Word addition or subtraction requires the zeropage pseudoregister", r.position)
return Nil
}
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode) && ctx.options.zpRegisterSize < 4) {
ctx.log.error("Unsupported decimal operation. Consider increasing the size of the zeropage register.", r.position)
return Nil
}
val b = ctx.env.get[Type]("byte")
val w = ctx.env.get[Type]("word")
val reg = ctx.env.get[VariableInMemory]("__reg")
// TODO: smarter on 65816
val compileRight = MosExpressionCompiler.compile(ctx, r, Some(MosExpressionCompiler.getExpressionType(ctx, r) -> reg), BranchSpec.None)
val op = if (subtract) SBC else ADC
val prepareCarry = AssemblyLine.implied(if (subtract) SEC else CLC)
compileRight match {
val rightType = MosExpressionCompiler.getExpressionType(ctx, r)
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode)) {
val compileRight = MosExpressionCompiler.compile(ctx, r, Some(rightType -> ctx.env.get[VariableInMemory]("__reg.b2b3")), BranchSpec.None)
compileRight match {
case List(
AssemblyLine(LDA, Immediate, NumericConstant(0, _), _),
AssemblyLine(STA, ZeroPage, _, _),
AssemblyLine(LDX | LDA, Immediate, NumericConstant(0, _), _),
AssemblyLine(STA | STX, ZeroPage, _, _)) => Nil
case _ =>
compileRight ++
List(
AssemblyLine.zeropage(LDX, reg, 3),
AssemblyLine.zeropage(LDA, reg, 2),
AssemblyLine.zeropage(STA, reg, 3),
AssemblyLine.implied(TXA),
AssemblyLine.implied(PHA),
AssemblyLine.zeropage(LDA, reg),
AssemblyLine.zeropage(STA, reg, 2)) ++ (
if (subtract) List(AssemblyLine.absolute(JSR, ctx.env.get[ThingInMemory]("__sub_decimal")))
else List(AssemblyLine.implied(CLC), AssemblyLine.absolute(JSR, ctx.env.get[ThingInMemory]("__adc_decimal")))
) ++ List(
AssemblyLine.zeropage(STA, reg),
AssemblyLine.implied(PLA),
AssemblyLine.zeropage(STA, reg, 3),
AssemblyLine.zeropage(LDA, reg, 1),
AssemblyLine.zeropage(STA, reg, 2),
AssemblyLine.absolute(JSR,
if (subtract) ctx.env.get[ThingInMemory]("__sbc_decimal")
else ctx.env.get[ThingInMemory]("__adc_decimal")),
AssemblyLine.zeropage(STA, reg, 1))
}
} else {
val compileRight = MosExpressionCompiler.compile(ctx, r, Some(rightType -> reg), BranchSpec.None)
compileRight match {
case List(
AssemblyLine(LDA, Immediate, NumericConstant(0, _), _),
AssemblyLine(STA, ZeroPage, _, _),
AssemblyLine(LDX | LDA, Immediate, NumericConstant(0, _), _),
AssemblyLine(STA | STX, ZeroPage, _, _)) => Nil
case List(
AssemblyLine(LDA, Immediate, NumericConstant(0, _), _),
AssemblyLine(STA, ZeroPage, _, _),
AssemblyLine(LDX | LDA, Immediate, NumericConstant(0, _), _),
AssemblyLine(STA | STX, ZeroPage, _, _)) => Nil
case List(
l@AssemblyLine(LDA, _, _, _),
AssemblyLine(STA, ZeroPage, _, _),
h@AssemblyLine(LDX | LDA, addrMode, _, _),
AssemblyLine(STA | STX, ZeroPage, _, _)) if addrMode != ZeroPageY => BuiltIns.wrapInSedCldIfNeeded(decimal,
List(prepareCarry,
AssemblyLine.zeropage(LDA, reg),
l.copy(opcode = op),
AssemblyLine.zeropage(STA, reg),
AssemblyLine.zeropage(LDA, reg, 1),
h.copy(opcode = op),
AssemblyLine.zeropage(STA, reg, 1),
AssemblyLine.zeropage(LDA, reg)))
case List(
l@AssemblyLine(LDA, _, _, _),
AssemblyLine(STA, ZeroPage, _, _),
h@AssemblyLine(LDX | LDA, addrMode, _, _),
AssemblyLine(STA | STX, ZeroPage, _, _)) if addrMode != ZeroPageY => BuiltIns.wrapInSedCldIfNeeded(decimal,
List(prepareCarry,
AssemblyLine.zeropage(LDA, reg),
l.copy(opcode = op),
AssemblyLine.zeropage(STA, reg),
AssemblyLine.zeropage(LDA, reg, 1),
h.copy(opcode = op),
AssemblyLine.zeropage(STA, reg, 1),
AssemblyLine.zeropage(LDA, reg)))
case _ => BuiltIns.wrapInSedCldIfNeeded(decimal,
List(
AssemblyLine.zeropage(LDA, reg, 1),
AssemblyLine.implied(PHA),
AssemblyLine.zeropage(LDA, reg),
AssemblyLine.implied(PHA)) ++ MosExpressionCompiler.fixTsx(MosExpressionCompiler.fixTsx(compileRight)) ++ List(
prepareCarry,
AssemblyLine.implied(PLA),
AssemblyLine.zeropage(op, reg),
AssemblyLine.zeropage(STA, reg),
AssemblyLine.implied(PLA),
AssemblyLine.zeropage(op, reg, 1),
AssemblyLine.zeropage(STA, reg, 1)))
case _ =>
List(
AssemblyLine.zeropage(LDA, reg, 1),
AssemblyLine.implied(PHA),
AssemblyLine.zeropage(LDA, reg),
AssemblyLine.implied(PHA)) ++ MosExpressionCompiler.fixTsx(MosExpressionCompiler.fixTsx(compileRight)) ++
BuiltIns.wrapInSedCldIfNeeded(decimal, List(
prepareCarry,
AssemblyLine.implied(PLA),
AssemblyLine.zeropage(op, reg),
AssemblyLine.zeropage(STA, reg),
AssemblyLine.implied(PLA),
AssemblyLine.zeropage(op, reg, 1),
AssemblyLine.zeropage(STA, reg, 1)))
}
}
}

View File

@ -608,7 +608,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
} else {
if (minus) MathOperator.Minus else MathOperator.Plus
}
Some(CompoundConstant(op, c, addend))
Some(CompoundConstant(op, c, addend).quickSimplify)
}
}
}
@ -1096,6 +1096,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
if (typ.name == "__reg$type") {
return (".lo", 0, b) ::
(".hi", 1, b) ::
(".b2b3", 2, w) ::
List.tabulate(typ.size) { i => (".b" + i, i, b) }
}
typ match {

View File

@ -11,7 +11,18 @@ import millfork.node._
object UnusedFunctions extends NodeOptimization {
private val operatorImplementations: List[(String, Int, String)] = List(
("*", 2, "__mul_u8u8u8")
("*", 2, "__mul_u8u8u8"),
("*=", 2, "__mul_u8u8u8"),
("+'", 4, "__adc_decimal"),
("+'=", 4, "__adc_decimal"),
("-'", 4, "__sub_decimal"),
("-'=", 4, "__sub_decimal"),
("-'", 4, "__sbc_decimal"),
("-'=", 4, "__sbc_decimal"),
("<<'", 4, "__adc_decimal"),
("<<'=", 4, "__adc_decimal"),
("*'", 4, "__adc_decimal"),
("*'=", 4, "__adc_decimal"),
)
override def optimize(nodes: List[Node], options: CompilationOptions): List[Node] = {
@ -77,7 +88,13 @@ object UnusedFunctions extends NodeOptimization {
s.name.stripSuffix(".addr.hi"))
case s: LiteralExpression => Nil
case HalfWordExpression(param, _) => getAllCalledFunctions(param :: Nil)
case SumExpression(xs, _) => getAllCalledFunctions(xs.map(_._2))
case SumExpression(xs, decimal) =>
var set = List[String]()
if (decimal) {
if (xs.tail.exists(_._1)) set ::= "-'"
if (xs.tail.exists(p => !p._1)) set ::= "+'"
}
set ++ getAllCalledFunctions(xs.map(_._2))
case FunctionCallExpression(name, xs) => name :: getAllCalledFunctions(xs)
case IndexedExpression(arr, index) => arr :: getAllCalledFunctions(List(index))
case SeparateBytesExpression(h, l) => getAllCalledFunctions(List(h, l))

View File

@ -1,6 +1,6 @@
package millfork.parser
import millfork.CompilationOptions
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly.mos.AssemblyLine
/**
@ -17,6 +17,9 @@ class MosSourceLoadingQueue(initialFilenames: List[String],
if (options.zpRegisterSize > 0) {
moduleQueue.enqueue(() => parseModule("zp_reg", includePath, Left(None)))
}
if (options.zpRegisterSize >= 4 && !options.flag(CompilationFlag.DecimalMode)) {
moduleQueue.enqueue(() => parseModule("bcd_6502", includePath, Left(None)))
}
}
override val supportedPragmas: Set[String] = Set()

View File

@ -10,7 +10,7 @@ import org.scalatest.{FunSuite, Matchers}
class ByteDecimalMathSuite extends FunSuite with Matchers {
test("Decimal byte addition") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| byte a
@ -22,7 +22,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("Decimal byte addition 2") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| byte a
@ -33,8 +33,36 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
""".stripMargin)(_.readByte(0xc000) should equal(0x70))
}
test("Decimal byte addition comprehensive suite for Ricoh") {
val pairs = List(
0 -> 0,
55 -> 55,
90 -> 90,
86 -> 84,
7 -> 8,
1 -> 9,
1 -> 99,
99 -> 1,
2 -> 8
)
for ((i,j) <- pairs) {
EmuUnoptimizedCrossPlatformRun(Cpu.Ricoh)(
"""
| byte output @$c000
| void main () {
| init()
| run()
| }
| void init() { output = $#i }
| void run () { output +'= $#j }
""".stripMargin.replace("#i", i.toString).replace("#j", j.toString)) { m =>
toDecimal(m.readByte(0xc000)) should equal((i + j) % 100)
}
}
}
test("Decimal byte subtraction") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| byte a
@ -46,7 +74,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("In-place decimal byte addition") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| array output[3] @$c000
| byte a
@ -60,7 +88,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("In-place decimal byte addition 2") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| array output[3] @$c000
| void main () {
@ -80,7 +108,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("In-place decimal byte subtraction") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| byte a
@ -91,8 +119,40 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
""".stripMargin)(_.readByte(0xc000) should equal(0x15))
}
test("Decimal word addition") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| word output @$c000
| void main () {
| output = f() +' g()
| }
| word f() {
| return $253
| }
| word g() {
| return $455
| }
""".stripMargin)(_.readWord(0xc000) should equal(0x708))
}
test("Decimal word subtraction") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| word output @$c000
| void main () {
| output = f() -' $264
| }
| word f() {
| return $1500 -' g()
| }
| word g() {
| return $455
| }
""".stripMargin)(_.readWord(0xc000) should equal(0x781))
}
test("In-place decimal word subtraction") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| word output @$c000
| word a
@ -106,7 +166,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("In-place decimal long subtraction") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| long output @$c000
| word a
@ -121,7 +181,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("Flag switching test") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| void main () {
@ -132,7 +192,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("Flag switching test 2") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| void main () {
@ -143,7 +203,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("Flag switching test 3") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| void main () {
@ -154,7 +214,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("Decimal left shift test") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| void main () {
@ -169,7 +229,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("Decimal left shift test 2") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| void main () {
@ -183,7 +243,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("Decimal left shift test 3") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| word output @$c000
| void main () {
@ -197,7 +257,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("Decimal left shift test 4") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| long output @$c000
| void main () {
@ -211,7 +271,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("Decimal right shift test") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| void main () {
@ -226,7 +286,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("Decimal right shift test 2") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| void main () {
@ -240,7 +300,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("Decimal right shift test 3") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| word output @$c000
| void main () {
@ -275,7 +335,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
test("Decimal word right-shift comprehensive suite") {
for (i <- List(0, 1, 10, 100, 1000, 2000, 500, 200, 280, 300, 5234, 7723, 7344, 9, 16, 605, 1111, 2222, 3333, 9999, 8888, 8100)) {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| word output @$c000
| void main () {
@ -291,7 +351,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
test("Decimal byte multiplication comprehensive suite") {
val numbers = List(0, 1, 2, 3, 6, 8, 10, 11, 12, 14, 15, 16, 20, 40, 73, 81, 82, 98, 99)
for (i <- numbers; j <- numbers) {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| void main () {
@ -309,7 +369,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
test("Decimal comparison") {
// CMP#0 shouldn't be elided after a decimal operation.
// Currently no emulator used for testing can catch that.
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Ricoh)(
"""
| byte output @$c000
| void main () {

View File

@ -90,6 +90,9 @@ object EmuCrossPlatformBenchmarkRun {
if (platforms.contains(millfork.Cpu.Mos)) {
EmuBenchmarkRun.apply(source)(verifier)
}
if (platforms.contains(millfork.Cpu.Ricoh)) {
verifier(EmuUndocumentedRun.apply(source))
}
if (platforms.contains(millfork.Cpu.Cmos)) {
EmuCmosBenchmarkRun.apply(source)(verifier)
}

View File

@ -22,7 +22,7 @@ object EmuPlatform {
Map("default" -> new VariableAllocator(
if (CpuFamily.forType(cpu) == CpuFamily.M6502) pointers else Nil,
new AfterCodeByteAllocator(0xff00))),
if (CpuFamily.forType(cpu) == CpuFamily.M6502) 2 else 0,
if (CpuFamily.forType(cpu) == CpuFamily.M6502) 4 else 0,
pointers,
".bin",
false,

View File

@ -12,8 +12,8 @@ import millfork.assembly.mos.AssemblyLine
import millfork.compiler.{CompilationContext, LabelGenerator}
import millfork.compiler.mos.MosCompiler
import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, NormalFunction}
import millfork.error.{ConsoleLogger, Logger}
import millfork.node.StandardCallGraph
import millfork.error.Logger
import millfork.node.{Program, StandardCallGraph}
import millfork.node.opt.NodeOptimization
import millfork.output.{MemoryBank, MosAssembler}
import millfork.parser.{MosParser, PreprocessingResult, Preprocessor}
@ -27,6 +27,28 @@ import scala.collection.JavaConverters._
*/
case class Timings(nmos: Long, cmos: Long)
object EmuRun {
private def preload(filename: String): Option[Program] = {
TestErrorReporting.log.info(s"Loading $filename")
val source = Files.readAllLines(Paths.get(filename), StandardCharsets.US_ASCII).asScala.mkString("\n")
val options = CompilationOptions(EmuPlatform.get(millfork.Cpu.Mos), Map(
CompilationFlag.LenientTextEncoding -> true
), None, 4, JobContext(TestErrorReporting.log, new LabelGenerator))
val PreprocessingResult(preprocessedSource, features, _) = Preprocessor.preprocessForTest(options, source)
TestErrorReporting.log.info(s"Parsing $filename")
MosParser("", preprocessedSource, "", options, features).toAst match {
case Success(x, _) => Some(x)
case _ => None
}
}
private lazy val cachedZpregO: Option[Program]= preload("include/zp_reg.mfk")
private lazy val cachedBcdO: Option[Program] = preload("include/bcd_6502.mfk")
def cachedZpreg: Program = synchronized { cachedZpregO.getOrElse(throw new IllegalStateException()) }
def cachedBcd: Program = synchronized { cachedBcdO.getOrElse(throw new IllegalStateException()) }
}
class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], assemblyOptimizations: List[AssemblyOptimization[AssemblyLine]]) extends Matchers {
def apply(source: String): MemoryBank = {
@ -101,6 +123,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
println(source)
val platform = EmuPlatform.get(cpu)
val options = CompilationOptions(platform, Map(
CompilationFlag.DecimalMode -> millfork.Cpu.defaultFlags(cpu).contains(CompilationFlag.DecimalMode),
CompilationFlag.LenientTextEncoding -> true,
CompilationFlag.EmitIllegals -> this.emitIllegals,
CompilationFlag.InlineFunctions -> this.inline,
@ -113,13 +136,11 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
CompilationFlag.OptimizeForSpeed -> blastProcessing,
CompilationFlag.OptimizeForSonicSpeed -> blastProcessing
// CompilationFlag.CheckIndexOutOfBounds -> true,
), None, 2, JobContext(log, new LabelGenerator))
), None, 4, JobContext(log, new LabelGenerator))
log.hasErrors = false
log.verbosity = 999
var effectiveSource = source
if (!source.contains("_panic")) effectiveSource += "\n void _panic(){while(true){}}"
if (source.contains("import zp_reg"))
effectiveSource += Files.readAllLines(Paths.get("include/zp_reg.mfk"), StandardCharsets.US_ASCII).asScala.mkString("\n", "\n", "")
log.setSource(Some(effectiveSource.lines.toIndexedSeq))
val PreprocessingResult(preprocessedSource, features, _) = Preprocessor.preprocessForTest(options, effectiveSource)
val parserF = MosParser("", preprocessedSource, "", options, features)
@ -127,9 +148,16 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
case Success(unoptimized, _) =>
log.assertNoErrors("Parse failed")
// prepare
val program = nodeOptimizations.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
val withLibraries = {
var tmp = unoptimized
if(source.contains("import zp_reg"))
tmp += EmuRun.cachedZpreg
if(!options.flag(CompilationFlag.DecimalMode) && (source.contains("+'") || source.contains("-'") || source.contains("<<'") || source.contains("*'")))
tmp += EmuRun.cachedBcd
tmp
}
val program = nodeOptimizations.foldLeft(withLibraries)((p, opt) => p.applyNodeOptimization(opt, options))
val callGraph = new StandardCallGraph(program, log)
val env = new Environment(None, "", CpuFamily.M6502, options.jobContext)
env.collectDeclarations(program, options)

View File

@ -10,6 +10,7 @@ object EmuUnoptimizedCrossPlatformRun {
def apply(platforms: Cpu.Value*)(source: String)(verifier: MemoryBank => Unit): Unit = {
val (_, mm) = if (platforms.contains(Cpu.Mos)) EmuUnoptimizedRun.apply2(source) else Timings(-1, -1) -> null
val (_, mc) = if (platforms.contains(Cpu.Cmos)) EmuUnoptimizedCmosRun.apply2(source) else Timings(-1, -1) -> null
val (_, mn) = if (platforms.contains(Cpu.Ricoh)) EmuUnoptimizedRicohRun.apply2(source) else Timings(-1, -1) -> null
val (_, mz) = if (platforms.contains(Cpu.Z80)) EmuUnoptimizedZ80Run.apply2(source) else Timings(-1, -1) -> null
val (_, mi) = if (platforms.contains(Cpu.Intel8080)) EmuUnoptimizedIntel8080Run.apply2(source) else Timings(-1, -1) -> null
val (_, ms) = if (platforms.contains(Cpu.Sharp)) EmuUnoptimizedSharpRun.apply2(source) else Timings(-1, -1) -> null
@ -17,6 +18,10 @@ object EmuUnoptimizedCrossPlatformRun {
println(f"Running 6502")
verifier(mm)
}
if (platforms.contains(millfork.Cpu.Ricoh)) {
println(f"Running Ricoh")
verifier(mn)
}
if (platforms.contains(Cpu.Cmos)) {
println(f"Running 65C02")
verifier(mc)

View File

@ -8,6 +8,8 @@ import millfork.Cpu
*/
object EmuUnoptimizedRun extends EmuRun(Cpu.StrictMos, Nil, Nil)
object EmuUnoptimizedRicohRun extends EmuRun(Cpu.Ricoh, Nil, Nil)
object EmuUnoptimizedCmosRun extends EmuRun(Cpu.Cmos, Nil, Nil)
object EmuUnoptimizedZ80Run extends EmuZ80Run(Cpu.Z80, Nil, Nil)