mirror of
https://github.com/KarolS/millfork.git
synced 2025-03-24 10:33:53 +00:00
65816: various improvements
This commit is contained in:
parent
cd8697552c
commit
5a99dc7293
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,7 +16,6 @@ examples/lunix/
|
||||
*.jar
|
||||
*.class
|
||||
*.zip
|
||||
*.js
|
||||
|
||||
# compiled Millfork files
|
||||
*.prg
|
||||
|
@ -152,6 +152,13 @@ object Opcode extends Enumeration {
|
||||
case ASL => Some(ASL_W)
|
||||
case LSR => Some(LSR_W)
|
||||
|
||||
case PHA => Some(PHA_W)
|
||||
case PLA => Some(PLA_W)
|
||||
case PHX => Some(PHX_W)
|
||||
case PLX => Some(PLX_W)
|
||||
case PHY => Some(PHY_W)
|
||||
case PLY => Some(PLY_W)
|
||||
|
||||
case _ => None
|
||||
}
|
||||
|
||||
|
@ -14,20 +14,23 @@ object SixteenOptimizations {
|
||||
val AccumulatorSwapping = new RuleBasedAssemblyOptimization("Accumulator swapping",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
(Elidable & HasOpcode(PHA) & HasAccu8 & DoesntMatterWhatItDoesWith(State.AH, State.A, State.N, State.Z)) ~
|
||||
(Linear & Not(ConcernsStack)) ~
|
||||
(Linear & Not(ConcernsStack)).* ~
|
||||
(Elidable & HasOpcode(PLA) & DoesntMatterWhatItDoesWith(State.AH)) ~~> { code =>
|
||||
AssemblyLine.implied(XBA) :: (code.tail.init :+ AssemblyLine.implied(XBA))
|
||||
},
|
||||
(Elidable & HasOpcode(TAX) & HasAccu8 & HasIndex8 & DoesntMatterWhatItDoesWith(State.AH, State.A, State.N, State.Z)) ~
|
||||
(Linear & Not(ConcernsX)) ~
|
||||
(Linear & Not(ConcernsX)).* ~
|
||||
(Elidable & HasOpcode(TXA) & DoesntMatterWhatItDoesWith(State.AH, State.X)) ~~> { code =>
|
||||
AssemblyLine.implied(XBA) :: (code.tail.init :+ AssemblyLine.implied(XBA))
|
||||
},
|
||||
(Elidable & HasOpcode(TAY) & HasAccu8 & HasIndex8 & DoesntMatterWhatItDoesWith(State.AH, State.A, State.N, State.Z)) ~
|
||||
(Linear & Not(ConcernsY)) ~
|
||||
(Linear & Not(ConcernsY)).* ~
|
||||
(Elidable & HasOpcode(TYA) & DoesntMatterWhatItDoesWith(State.AH, State.Y)) ~~> { code =>
|
||||
AssemblyLine.implied(XBA) :: (code.tail.init :+ AssemblyLine.implied(XBA))
|
||||
},
|
||||
(Elidable & HasOpcode(XBA) & DoesntMatterWhatItDoesWith(State.A, State.AH, State.N, State.Z)) ~~> { code =>
|
||||
Nil
|
||||
},
|
||||
)
|
||||
|
||||
val RepSepWeakening = new RuleBasedAssemblyOptimization("REP/SEP weakening",
|
||||
@ -121,6 +124,7 @@ object SixteenOptimizations {
|
||||
|
||||
val PointlessLoadAfterLoadOrStore = new RuleBasedAssemblyOptimization("Pointless 16-bit load after load or store",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
// TODO: flags!
|
||||
|
||||
(HasOpcodeIn(LDA_W, STA_W) & HasAddrMode(WordImmediate) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesA) & Not(ChangesAH)).* ~
|
||||
|
@ -45,7 +45,9 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case 1 => List(
|
||||
AssemblyLine(LDA, Immediate, expr.loByte),
|
||||
AssemblyLine(STA, addrMode, addr))
|
||||
case 2 => List(
|
||||
case 2 => if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
AssemblyLine.accu16 :: AssemblyLine(LDA_W, WordImmediate, expr) ::(AssemblyLine.variable(ctx, STA_W, m) :+ AssemblyLine.accu8)
|
||||
} else List(
|
||||
AssemblyLine(LDA, Immediate, expr.loByte),
|
||||
AssemblyLine(STA, addrMode, addr),
|
||||
AssemblyLine(LDA, Immediate, expr.hiByte),
|
||||
@ -54,13 +56,15 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
AssemblyLine(LDA, Immediate, expr.subbyte(i)),
|
||||
AssemblyLine(STA, addrMode, addr + i))).flatten
|
||||
}
|
||||
case StackVariable(_, t, offset) =>
|
||||
case m@StackVariable(_, t, offset) =>
|
||||
t.size match {
|
||||
case 0 => Nil
|
||||
case 1 => AssemblyLine.tsx(ctx) ++ List(
|
||||
AssemblyLine.immediate(LDA, expr.loByte),
|
||||
AssemblyLine.dataStackX(ctx, STA, offset))
|
||||
case 2 => AssemblyLine.tsx(ctx) ++ List(
|
||||
case 2 => if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
AssemblyLine.accu16 :: AssemblyLine(LDA_W, WordImmediate, expr) :: (AssemblyLine.variable(ctx, STA_W, m) :+ AssemblyLine.accu8)
|
||||
} else AssemblyLine.tsx(ctx) ++ List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.immediate(LDA, expr.loByte),
|
||||
AssemblyLine.dataStackX(ctx, STA, offset),
|
||||
@ -371,8 +375,11 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
AssemblyLine.implied(PLA))
|
||||
} else List(AssemblyLine.immediate(LDX, 0), AssemblyLine.implied(XBA)) ++ AssemblyLine.variable(ctx, LDA, source) :+ AssemblyLine.immediate(LDX, 0)
|
||||
case 2 =>
|
||||
// TODO: use LDA_W
|
||||
AssemblyLine.variable(ctx, LDA, source, 1) ++ List(AssemblyLine.implied(XBA)) ++ AssemblyLine.variable(ctx, LDA, source)
|
||||
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
AssemblyLine.accu16 :: (AssemblyLine.variable(ctx, LDA_W, source) :+ AssemblyLine.accu8)
|
||||
} else {
|
||||
AssemblyLine.variable(ctx, LDA, source, 1) ++ List(AssemblyLine.implied(XBA)) ++ AssemblyLine.variable(ctx, LDA, source)
|
||||
}
|
||||
}
|
||||
case RegisterVariable(MosRegister.X, _) => AssemblyLine.variable(ctx, LDX, source)
|
||||
case RegisterVariable(MosRegister.Y, _) => AssemblyLine.variable(ctx, LDY, source)
|
||||
@ -422,6 +429,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
if (exprType.size > target.typ.size) {
|
||||
ctx.log.error(s"Variable `$target.name` is too small", expr.position)
|
||||
Nil
|
||||
} else if (exprType.size == 2 && target.typ.size == 2 && ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
AssemblyLine.accu16 :: (AssemblyLine.variable(ctx, LDA_W, source) ++ AssemblyLine.variable(ctx, STA_W, target) :+ AssemblyLine.accu8)
|
||||
} else {
|
||||
val copyFromLo = List.tabulate(exprType.size)(i => AssemblyLine.variable(ctx, LDA, source, i) ++ AssemblyLine.variable(ctx, STA, target, i))
|
||||
val copy = if (shouldCopyFromHiToLo(source.toAddress, target.toAddress)) copyFromLo.reverse else copyFromLo
|
||||
@ -437,6 +446,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
if (exprType.size > target.typ.size) {
|
||||
ctx.log.error(s"Variable `$target.name` is too small", expr.position)
|
||||
Nil
|
||||
} else if (exprType.size == 2 && target.typ.size == 2 && ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
AssemblyLine.accu16 :: (AssemblyLine.variable(ctx, LDA_W, source) ++ AssemblyLine.variable(ctx, STA_W, target) :+ AssemblyLine.accu8)
|
||||
} else {
|
||||
val copy = List.tabulate(exprType.size)(i => AssemblyLine.variable(ctx, LDA, source, i) :+ AssemblyLine.dataStackX(ctx, STA, target, i))
|
||||
val extend = if (exprType.size == target.typ.size) Nil else if (exprType.isSigned) {
|
||||
@ -507,6 +518,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
if (exprType.size > target.typ.size) {
|
||||
ctx.log.error(s"Variable `$target.name` is too small", expr.position)
|
||||
Nil
|
||||
} else if (exprType.size == 2 && target.typ.size == 2 && ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
AssemblyLine.accu16 :: (AssemblyLine.variable(ctx, LDA_W, source) ++ AssemblyLine.variable(ctx, STA_W, target) :+ AssemblyLine.accu8)
|
||||
} else {
|
||||
val copy = List.tabulate(exprType.size)(i => AssemblyLine.dataStackX(ctx, LDA, offset + i) :: AssemblyLine.variable(ctx, STA, target, i))
|
||||
val extend = if (exprType.size == target.typ.size) Nil else if (exprType.isSigned) {
|
||||
@ -521,6 +534,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
if (exprType.size > target.typ.size) {
|
||||
ctx.log.error(s"Variable `$target.name` is too small", expr.position)
|
||||
Nil
|
||||
} else if (exprType.size == 2 && target.typ.size == 2 && ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
AssemblyLine.accu16 :: (AssemblyLine.variable(ctx, LDA_W, source) ++ AssemblyLine.variable(ctx, STA_W, target) :+ AssemblyLine.accu8)
|
||||
} else {
|
||||
val copyFromLo = List.tabulate(exprType.size)(i => List(AssemblyLine.dataStackX(ctx, LDA, offset + i), AssemblyLine.dataStackX(ctx, STA, target, i)))
|
||||
val copy = if (shouldCopyFromHiToLo(NumericConstant(source.baseOffset, 2), NumericConstant(target.baseOffset, 2))) copyFromLo.reverse else copyFromLo
|
||||
@ -662,9 +677,15 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
calculate ++ store
|
||||
}
|
||||
case 2 =>
|
||||
val calculate = PseudoregisterBuiltIns.compileWordAdditionToAX(ctx, params, decimal = decimal)
|
||||
val store = expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
calculate ++ store
|
||||
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
val calculate = PseudoregisterBuiltIns.compileWordAdditionToAW(ctx, params, decimal = decimal)
|
||||
val store = expressionStorageFromAW(ctx, exprTypeAndVariable, expr.position)
|
||||
calculate ++ store
|
||||
} else {
|
||||
val calculate = PseudoregisterBuiltIns.compileWordAdditionToAX(ctx, params, decimal = decimal)
|
||||
val store = expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
calculate ++ store
|
||||
}
|
||||
}
|
||||
}
|
||||
case SeparateBytesExpression(h, l) =>
|
||||
@ -1302,6 +1323,85 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def expressionStorageFromAW(ctx: CompilationContext, exprTypeAndVariable: Option[(Type, Variable)], position: Option[Position]): List[AssemblyLine] = {
|
||||
exprTypeAndVariable.fold(noop) {
|
||||
case (VoidType, _) => ctx.log.fatal("Cannot assign word to void", position)
|
||||
case (_, RegisterVariable(MosRegister.A, _)) => noop
|
||||
case (_, RegisterVariable(MosRegister.AW, _)) => noop
|
||||
case (_, RegisterVariable(MosRegister.X, _)) => List(AssemblyLine.implied(TAX))
|
||||
case (_, RegisterVariable(MosRegister.Y, _)) => List(AssemblyLine.implied(TAY))
|
||||
case (_, RegisterVariable(MosRegister.AX, _)) =>
|
||||
// TODO: sign extension
|
||||
List(AssemblyLine.implied(XBA), AssemblyLine.implied(TAX), AssemblyLine.implied(XBA))
|
||||
case (_, RegisterVariable(MosRegister.XA, _)) =>
|
||||
// TODO: sign extension
|
||||
List(AssemblyLine.implied(TAX), AssemblyLine.implied(XBA))
|
||||
case (_, RegisterVariable(MosRegister.YA, _)) =>
|
||||
// TODO: sign extension
|
||||
List(AssemblyLine.implied(TAY), AssemblyLine.implied(XBA))
|
||||
case (_, RegisterVariable(MosRegister.AY, _)) =>
|
||||
// TODO: sign extension
|
||||
List(AssemblyLine.implied(XBA), AssemblyLine.implied(TAY), AssemblyLine.implied(XBA))
|
||||
case (t, v: VariableInMemory) => t.size match {
|
||||
case 1 => v.typ.size match {
|
||||
case 1 =>
|
||||
AssemblyLine.variable(ctx, STA, v)
|
||||
case s if s > 1 =>
|
||||
if (t.isSigned) {
|
||||
AssemblyLine.variable(ctx, STA, v) ++ signExtendA(ctx) ++ List.tabulate(s - 1)(i => AssemblyLine.variable(ctx, STA, v, i + 1)).flatten
|
||||
} else {
|
||||
AssemblyLine.variable(ctx, STA, v) ++ List(AssemblyLine.immediate(LDA, 0)) ++
|
||||
List.tabulate(s - 1)(i => AssemblyLine.variable(ctx, STA, v, i + 1)).flatten
|
||||
}
|
||||
}
|
||||
case 2 => v.typ.size match {
|
||||
case 1 =>
|
||||
ctx.log.error(s"Variable `${v.name}` cannot hold a word", position)
|
||||
Nil
|
||||
case 2 =>
|
||||
AssemblyLine.accu16 :: (AssemblyLine.variable(ctx, STA_W, v) :+ AssemblyLine.accu8)
|
||||
case s if s > 2 =>
|
||||
if (t.isSigned) {
|
||||
AssemblyLine.accu16 :: AssemblyLine.variable(ctx, STA_W, v) ++
|
||||
List(AssemblyLine.accu8, AssemblyLine.implied(XBA)) ++
|
||||
signExtendA(ctx) ++
|
||||
List.tabulate(s - 2)(i => AssemblyLine.variable(ctx, STA, v, i + 2)).flatten
|
||||
} else {
|
||||
AssemblyLine.accu16 :: AssemblyLine.variable(ctx, STA_W, v)
|
||||
List(AssemblyLine.accu8, AssemblyLine.immediate(LDA, 0)) ++
|
||||
List.tabulate(s - 2)(i => AssemblyLine.variable(ctx, STA, v, i + 2)).flatten
|
||||
}
|
||||
}
|
||||
}
|
||||
case (t, v: StackVariable) => t.size match {
|
||||
case 1 => v.typ.size match {
|
||||
case 1 =>
|
||||
AssemblyLine.tsx(ctx) :+ AssemblyLine.dataStackX(ctx, STA, v)
|
||||
case s if s > 1 =>
|
||||
AssemblyLine.tsx(ctx) ++ (if (t.isSigned) {
|
||||
List(
|
||||
AssemblyLine.dataStackX(ctx, STA, v.baseOffset)) ++
|
||||
signExtendA(ctx) ++
|
||||
List.tabulate(s - 1)(i => AssemblyLine.dataStackX(ctx, STA, v, i + 1))
|
||||
} else {
|
||||
List(
|
||||
AssemblyLine.dataStackX(ctx, STA, v.baseOffset),
|
||||
AssemblyLine.immediate(LDA, 0)) ++
|
||||
List.tabulate(s - 1)(i => AssemblyLine.dataStackX(ctx, STA, v, i + 1))
|
||||
})
|
||||
}
|
||||
case 2 => v.typ.size match {
|
||||
case 1 =>
|
||||
ctx.log.error(s"Variable `${v.name}` cannot hold a word", position)
|
||||
Nil
|
||||
case 2 =>
|
||||
AssemblyLine.tsx(ctx) ++ List(AssemblyLine.accu16, AssemblyLine.dataStackX(ctx, STA_W, v), AssemblyLine.accu8)
|
||||
case s if s > 2 => ???
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def compileAssignment(ctx: CompilationContext, source: Expression, target: LhsExpression): List[AssemblyLine] = {
|
||||
val env = ctx.env
|
||||
val sourceType = AbstractExpressionCompiler.checkAssignmentTypeAndGetSourceType(ctx, source, target)
|
||||
|
@ -45,7 +45,7 @@ object PseudoregisterBuiltIns {
|
||||
if (params.isEmpty) {
|
||||
return List(AssemblyLine.immediate(LDA, 0), AssemblyLine.immediate(LDX, 0))
|
||||
}
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg.loword")
|
||||
val head = params.head match {
|
||||
case (false, e) => MosExpressionCompiler.compile(ctx, e, Some(MosExpressionCompiler.getExpressionType(ctx, e) -> reg), BranchSpec.None)
|
||||
case (true, e) => ???
|
||||
@ -56,6 +56,37 @@ object PseudoregisterBuiltIns {
|
||||
)
|
||||
}
|
||||
|
||||
def compileWordAdditionToAW(ctx: CompilationContext, params: List[(Boolean, Expression)], decimal: Boolean): List[AssemblyLine] = {
|
||||
// assume native16 is enabled and accu16 mode is disabled
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
val w = ctx.env.get[Type]("word")
|
||||
if (!decimal) {
|
||||
val (variablePart, constPart) = ctx.env.evalVariableAndConstantSubParts(SumExpression(params, decimal = false))
|
||||
variablePart match {
|
||||
case None =>
|
||||
return MosExpressionCompiler.compileConstant(ctx, constPart, RegisterVariable(MosRegister.AW, w))
|
||||
case Some(v) =>
|
||||
}
|
||||
}
|
||||
if (ctx.options.zpRegisterSize < 2) {
|
||||
ctx.log.error("Word addition or subtraction requires the zeropage pseudoregister", params.headOption.flatMap(_._2.position))
|
||||
return Nil
|
||||
}
|
||||
if (params.isEmpty) {
|
||||
return List(AssemblyLine.accu16, AssemblyLine.immediate(LDA_W, 0), AssemblyLine.accu8)
|
||||
}
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg.loword")
|
||||
val head = params.head match {
|
||||
case (false, e) => MosExpressionCompiler.compile(ctx, e, Some(MosExpressionCompiler.getExpressionType(ctx, e) -> reg), BranchSpec.None)
|
||||
case (true, e) => ???
|
||||
}
|
||||
params.tail.foldLeft[List[AssemblyLine]](head){case (code, (sub, param)) => code ++ addToReg(ctx, param, sub, decimal)} ++ List(
|
||||
AssemblyLine.accu16,
|
||||
AssemblyLine.zeropage(LDA_W, reg),
|
||||
AssemblyLine.accu8
|
||||
)
|
||||
}
|
||||
|
||||
def addToReg(ctx: CompilationContext, r: Expression, subtract: Boolean, decimal: Boolean): List[AssemblyLine] = {
|
||||
if (ctx.options.zpRegisterSize < 2) {
|
||||
ctx.log.error("Word addition or subtraction requires the zeropage pseudoregister", r.position)
|
||||
@ -65,11 +96,13 @@ object PseudoregisterBuiltIns {
|
||||
ctx.log.error("Unsupported decimal operation. Consider increasing the size of the zeropage register.", r.position)
|
||||
return Nil
|
||||
}
|
||||
val native16 = ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
val w = ctx.env.get[Type]("word")
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg.loword")
|
||||
// TODO: smarter on 65816
|
||||
val op = if (subtract) SBC else ADC
|
||||
val op16 = if (subtract) SBC_W else ADC_W
|
||||
val prepareCarry = AssemblyLine.implied(if (subtract) SEC else CLC)
|
||||
val rightType = MosExpressionCompiler.getExpressionType(ctx, r)
|
||||
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode)) {
|
||||
@ -112,6 +145,11 @@ object PseudoregisterBuiltIns {
|
||||
AssemblyLine0(STA, ZeroPage, _),
|
||||
AssemblyLine0(LDX | LDA, Immediate, NumericConstant(0, _)),
|
||||
AssemblyLine0(STA | STX, ZeroPage, _)) => Nil
|
||||
case List(
|
||||
AssemblyLine0(REP, Immediate, NumericConstant(0x20, _)),
|
||||
AssemblyLine0(LDA_W, WordImmediate, NumericConstant(0, _)),
|
||||
AssemblyLine0(STA_W, ZeroPage, _),
|
||||
AssemblyLine0(SEP, Immediate, NumericConstant(0x20, _))) => Nil
|
||||
|
||||
case List(
|
||||
l@AssemblyLine0(LDA, _, _),
|
||||
@ -126,9 +164,25 @@ object PseudoregisterBuiltIns {
|
||||
h.copy(opcode = op),
|
||||
AssemblyLine.zeropage(STA, reg, 1),
|
||||
AssemblyLine.zeropage(LDA, reg)))
|
||||
case List(
|
||||
AssemblyLine0(REP, Immediate, NumericConstant(0x20, _)),
|
||||
l@AssemblyLine0(LDA_W, addrMode, _),
|
||||
AssemblyLine0(STA_W, ZeroPage, _),
|
||||
AssemblyLine0(SEP, Immediate, NumericConstant(0x20, _))) if addrMode != ZeroPageY =>
|
||||
BuiltIns.wrapInSedCldIfNeeded(decimal, List(
|
||||
AssemblyLine.accu16,
|
||||
prepareCarry,
|
||||
AssemblyLine.zeropage(LDA_W, reg),
|
||||
l.copy(opcode = op16),
|
||||
AssemblyLine.zeropage(STA_W, reg),
|
||||
AssemblyLine.accu8))
|
||||
|
||||
case _ =>
|
||||
List(
|
||||
if (native16) {
|
||||
List(AssemblyLine.accu16, AssemblyLine.zeropage(LDA_W, reg), AssemblyLine.implied(PHA_W), AssemblyLine.accu8) ++
|
||||
MosExpressionCompiler.fixTsx(MosExpressionCompiler.fixTsx(compileRight)) ++
|
||||
List(AssemblyLine.accu16, prepareCarry, AssemblyLine.implied(PLA_W), AssemblyLine.zeropage(op16, reg), AssemblyLine.zeropage(STA_W, reg), AssemblyLine.accu8)
|
||||
} else List(
|
||||
AssemblyLine.zeropage(LDA, reg, 1),
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.zeropage(LDA, reg),
|
||||
|
4097
src/test/resources/cpu.js
Normal file
4097
src/test/resources/cpu.js
Normal file
File diff suppressed because it is too large
Load Diff
4095
src/test/resources/orig.cpu.js
Normal file
4095
src/test/resources/orig.cpu.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,7 @@ import org.scalatest.{FunSuite, Matchers}
|
||||
class WordMathSuite extends FunSuite with Matchers {
|
||||
|
||||
test("Word addition") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| word a
|
||||
| void main () {
|
||||
@ -21,7 +21,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Cast word addition") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen/*, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp*/)("""
|
||||
| byte output @$c000
|
||||
| word a
|
||||
| void main () {
|
||||
@ -34,7 +34,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word subtraction") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| word a
|
||||
| void main () {
|
||||
@ -46,7 +46,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word subtraction 2") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Cmos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Cmos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| word a
|
||||
| void main () {
|
||||
@ -58,7 +58,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word subtraction 3") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Cmos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen/*, Cpu.Cmos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp*/)("""
|
||||
| word output @$c000
|
||||
| word a
|
||||
| void main () {
|
||||
@ -73,7 +73,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Byte-to-word addition") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| word pair
|
||||
| void main () {
|
||||
@ -86,7 +86,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Literal addition") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| output = 640
|
||||
@ -96,7 +96,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Array element addition") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| word pair
|
||||
| array b[2]
|
||||
@ -113,7 +113,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("nesdev.com example") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| byte output @$c000
|
||||
| byte tile @$C3A6
|
||||
| array map [256] @$c300
|
||||
@ -135,7 +135,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("nesdev.com example 2") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| byte output @$c000
|
||||
| byte tile @$C3A6
|
||||
| array map [256] @$c300
|
||||
@ -153,7 +153,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("hi()/lo()") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| array output [7] @$c000
|
||||
| void main () {
|
||||
| output[0] = lo(33)
|
||||
@ -185,7 +185,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word addition 2") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen/*, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp*/)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| word v
|
||||
@ -201,7 +201,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word addition 3") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| byte c
|
||||
@ -217,7 +217,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word addition 4") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| word v
|
||||
@ -237,7 +237,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word bit ops 2") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| word v
|
||||
@ -253,7 +253,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word shift") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| word v
|
||||
@ -271,7 +271,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word shift 2") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| output = five()
|
||||
@ -286,7 +286,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word shift 3") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| output = five()
|
||||
@ -301,7 +301,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word shift 4") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| output = five()
|
||||
@ -316,7 +316,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word shift 5") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| output = five()
|
||||
@ -332,7 +332,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word multiplication 5") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| output = alot()
|
||||
@ -369,7 +369,7 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
private def multiplyCase1(x: Int, y: Int): Unit = {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80)(
|
||||
s"""
|
||||
| import zp_reg
|
||||
| word output @$$c000
|
||||
|
@ -123,6 +123,9 @@ object EmuCrossPlatformBenchmarkRun {
|
||||
if (platforms.contains(millfork.Cpu.Cmos)) {
|
||||
EmuCmosBenchmarkRun.apply(source)(verifier)
|
||||
}
|
||||
if (platforms.contains(millfork.Cpu.Sixteen)) {
|
||||
EmuNative65816BenchmarkRun.apply(source)(verifier)
|
||||
}
|
||||
if (platforms.contains(millfork.Cpu.Z80)) {
|
||||
EmuZ80BenchmarkRun.apply(source)(verifier)
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import millfork.output.MemoryBank
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object EmuNative65816BenchmarkRun {
|
||||
def apply(source:String)(verifier: MemoryBank=>Unit): Unit = {
|
||||
println(f"Compiling for 65816 (unoptimized)")
|
||||
val (Timings(_, t0), m0) = EmuUnoptimizedNative65816Run.apply2(source)
|
||||
println(f"Compiling for 65816 (optimized)")
|
||||
val (Timings(_, t2), m2) = EmuOptimizedNative65816Run.apply2(source)
|
||||
println(f"Before optimization: $t0%7d")
|
||||
println(f"After optimization: $t2%7d")
|
||||
println(f"Gain: ${(100L*(t0-t2)/t0.toDouble).round}%7d%%")
|
||||
println(f"Running 65816 unoptimized")
|
||||
verifier(m0)
|
||||
println(f"Running 65816 optimized")
|
||||
verifier(m2)
|
||||
}
|
||||
}
|
@ -16,5 +16,17 @@ object EmuOptimized65816Run extends EmuRun(
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good)
|
||||
|
||||
object EmuOptimizedNative65816Run extends EmuRun(
|
||||
Cpu.Sixteen,
|
||||
OptimizationPresets.NodeOpt,
|
||||
OptimizationPresets.AssOpt ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ SixteenOptimizations.All ++ OptimizationPresets.Good) {
|
||||
override def native16: Boolean = true
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -65,6 +65,8 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
|
||||
def softwareStack = false
|
||||
|
||||
def native16 = false
|
||||
|
||||
private val timingNmos = Array[Int](
|
||||
7, 6, 0, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6,
|
||||
2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
|
||||
@ -136,6 +138,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
CompilationFlag.SoftwareStack -> softwareStack,
|
||||
CompilationFlag.EmitCmosOpcodes -> millfork.Cpu.CmosCompatible.contains(platform.cpu),
|
||||
CompilationFlag.EmitEmulation65816Opcodes -> (platform.cpu == millfork.Cpu.Sixteen),
|
||||
CompilationFlag.EmitNative65816Opcodes -> (platform.cpu == millfork.Cpu.Sixteen && native16),
|
||||
CompilationFlag.Emit65CE02Opcodes -> (platform.cpu == millfork.Cpu.CE02),
|
||||
CompilationFlag.EmitHudsonOpcodes -> (platform.cpu == millfork.Cpu.HuC6280),
|
||||
CompilationFlag.OptimizeForSize -> optimizeForSize,
|
||||
@ -145,8 +148,18 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
), None, 4, Map(), JobContext(log, new LabelGenerator))
|
||||
log.hasErrors = false
|
||||
log.verbosity = 999
|
||||
if (native16 && platform.cpu != millfork.Cpu.Sixteen) throw new IllegalStateException
|
||||
var effectiveSource = source
|
||||
if (!source.contains("_panic")) effectiveSource += "\n void _panic(){while(true){}}"
|
||||
if (native16) effectiveSource +=
|
||||
"""
|
||||
|
|
||||
|asm void __init_16bit() @$200 {
|
||||
| clc
|
||||
| xce
|
||||
| sep #$30
|
||||
|}
|
||||
""".stripMargin
|
||||
log.setSource(Some(effectiveSource.lines.toIndexedSeq))
|
||||
val PreprocessingResult(preprocessedSource, features, _) = Preprocessor.preprocessForTest(options, effectiveSource)
|
||||
val parserF = MosParser("", preprocessedSource, "", options, features)
|
||||
@ -216,6 +229,8 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
val timings = platform.cpu match {
|
||||
case millfork.Cpu.Cmos =>
|
||||
runViaSymon(log, memoryBank, platform.codeAllocators("default").startAt, CpuBehavior.CMOS_6502)
|
||||
case millfork.Cpu.Sixteen =>
|
||||
runViaJs(log, memoryBank, platform.codeAllocators("default").startAt)
|
||||
case millfork.Cpu.Ricoh =>
|
||||
runViaHalfnes(log, memoryBank, platform.codeAllocators("default").startAt)
|
||||
case millfork.Cpu.Mos =>
|
||||
@ -300,4 +315,9 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
Timings(countNmos, countCmos) -> memoryBank
|
||||
}
|
||||
|
||||
def runViaJs(log: Logger, memoryBank: MemoryBank, org: Int): (Timings, MemoryBank) = {
|
||||
val (cycles, newOutput) = NashornEmulator.run(memoryBank.output, 80, 0x200)
|
||||
System.arraycopy(newOutput, 0, memoryBank.output, 0, 1 << 16)
|
||||
Timings(cycles, cycles) -> memoryBank
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,10 @@ object EmuUnoptimizedRicohRun extends EmuRun(Cpu.Ricoh, Nil, Nil)
|
||||
|
||||
object EmuUnoptimizedCmosRun extends EmuRun(Cpu.Cmos, Nil, Nil)
|
||||
|
||||
object EmuUnoptimizedNative65816Run extends EmuRun(Cpu.Sixteen, Nil, Nil) {
|
||||
override def native16: Boolean = true
|
||||
}
|
||||
|
||||
object EmuUnoptimizedZ80Run extends EmuZ80Run(Cpu.Z80, Nil, Nil)
|
||||
|
||||
object EmuUnoptimizedIntel8080Run extends EmuZ80Run(Cpu.Intel8080, Nil, Nil)
|
||||
|
111
src/test/scala/millfork/test/emu/NashornEmulator.scala
Normal file
111
src/test/scala/millfork/test/emu/NashornEmulator.scala
Normal file
@ -0,0 +1,111 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import javax.script.{Bindings, ScriptEngine, ScriptEngineManager}
|
||||
import java.io.FileReader
|
||||
import java.nio.file.Paths
|
||||
|
||||
import jdk.nashorn.api.scripting.ScriptObjectMirror
|
||||
|
||||
import scala.language.dynamics
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object NashornEmulator {
|
||||
|
||||
lazy val engine = {
|
||||
val jsFile = Paths.get(classOf[Nothing].getResource("/cpu.js").toURI).toFile
|
||||
val engine: ScriptEngine = new ScriptEngineManager().getEngineByName("nashorn")
|
||||
engine.eval(new FileReader(jsFile))
|
||||
engine
|
||||
}
|
||||
|
||||
private def newCpu(): JsObject = {
|
||||
JsObject(engine.get("CPU_65816").asInstanceOf[ScriptObjectMirror]).construct()
|
||||
}
|
||||
|
||||
def run(memory: Array[Byte], steps: Long, start: Int): (Long, Array[Byte]) = {
|
||||
val hex = memory.map(b => f"${b&0xff}%02x").mkString("")
|
||||
val cpu = newCpu()
|
||||
cpu.reset()
|
||||
cpu.load_binary(memory.map(_ & 0xff), 0)
|
||||
val memory0 = cpu.mmu.memory.!("0")
|
||||
for (i <- 0 until 1 << 16) memory0.!(i.toString, memory(i) & 0xff)
|
||||
cpu.r.pc = start
|
||||
cpu.r.k = 0
|
||||
var count = 0L
|
||||
while (count < steps && cpu.r.s.*[Number].intValue().&(0xff) > 1) {
|
||||
cpu.step()
|
||||
count += 1
|
||||
}
|
||||
val newMemory = (0 until 1 << 16).map(i => memory0.!(i.toString).*[Number].byteValue()).toArray
|
||||
val cycles = cpu.cycle_count.*[Number].longValue()
|
||||
cycles -> newMemory
|
||||
}
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
val cpu = newCpu()
|
||||
cpu.reset()
|
||||
cpu.load_binary("00", 0x200)
|
||||
cpu.execute(0x200)
|
||||
println(cpu.r.pc)
|
||||
}
|
||||
}
|
||||
|
||||
case class JsObject(private val mirror: Any) extends Dynamic {
|
||||
|
||||
|
||||
def !(index: JsObject): JsObject =
|
||||
mirror match {
|
||||
case x: ScriptObjectMirror => JsObject(x.get(index.mirror))
|
||||
case _ => throw new IllegalArgumentException(s"Accessing field $index of $getUnderlyingClass")
|
||||
}
|
||||
|
||||
def !(index: Any): JsObject =
|
||||
mirror match {
|
||||
case x: ScriptObjectMirror => JsObject(x.get(index))
|
||||
case _ => throw new IllegalArgumentException(s"Accessing field $index of $getUnderlyingClass")
|
||||
}
|
||||
|
||||
def !(index: String, value:Any): Unit =
|
||||
mirror match {
|
||||
case x: ScriptObjectMirror => x.setMember(index, value)
|
||||
case _ => throw new IllegalArgumentException(s"Setting field $index of $getUnderlyingClass")
|
||||
}
|
||||
|
||||
def selectDynamic(name: String): JsObject =
|
||||
mirror match {
|
||||
case x: ScriptObjectMirror => JsObject(x.get(name).asInstanceOf[Any] match {
|
||||
case y: Double => if (y.isValidInt) y.toInt else y
|
||||
case y => y
|
||||
})
|
||||
case _ => throw new IllegalArgumentException(s"Accessing field $name of $getUnderlyingClass")
|
||||
}
|
||||
|
||||
private def getUnderlyingClass = {
|
||||
if (mirror.asInstanceOf[AnyRef] eq null) "null" else mirror.getClass.getSimpleName
|
||||
}
|
||||
|
||||
def updateDynamic(name: String)(value: Any): Unit =
|
||||
mirror match {
|
||||
case x: ScriptObjectMirror => x.setMember(name, value)
|
||||
case _ => throw new IllegalArgumentException(s"Setting field $name of $getUnderlyingClass")
|
||||
}
|
||||
|
||||
def applyDynamic(name: String)(params: Any*): JsObject =
|
||||
mirror match {
|
||||
case x: ScriptObjectMirror => JsObject(x.callMember(name, params.toArray))
|
||||
case _ => throw new IllegalArgumentException(s"Accessing field $name of $getUnderlyingClass")
|
||||
}
|
||||
|
||||
def construct(params: Any*): JsObject =
|
||||
mirror match {
|
||||
case x: ScriptObjectMirror => JsObject(x.newObject(params.toArray))
|
||||
case _ => throw new IllegalArgumentException(s"Using $getUnderlyingClass as a constructor")
|
||||
}
|
||||
|
||||
@inline
|
||||
def *[T]: T = mirror.asInstanceOf[T]
|
||||
|
||||
override def toString: String = String.valueOf(mirror)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user