mirror of
https://github.com/KarolS/millfork.git
synced 2025-02-06 01:30:13 +00:00
6502: software BCD, increase default zpreg to 4
This commit is contained in:
parent
30aa62ceaf
commit
388ceb8b3a
@ -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.
|
||||
|
||||
|
@ -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`.
|
||||
|
||||
|
@ -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
81
include/bcd_6502.mfk
Normal 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
|
||||
|
||||
}
|
@ -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.")
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 _ => ()
|
||||
}
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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,
|
||||
|
@ -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 =>
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
@ -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()
|
||||
|
@ -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 () {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user