package com.htmlism.mos6502.dsl import scala.collection.immutable.ListSet import scala.collection.mutable.ListBuffer import cats.syntax.all.* import com.htmlism.mos6502.model.* object DslDemo extends App: val cpu = new CPU import registers.{A, X} // address demonstration withAssemblyContext { implicit ctx => val payloadLocation = 0x01.z cpu.A = 0x40 A.add(payloadLocation) } // a becomes others withAssemblyContext { implicit ctx => cpu.A = cpu.X cpu.A = cpu.Y } // demonstrate first example withAssemblyContext { implicit ctx => cpu.A = 0xc0 cpu.X = cpu.A X.incr A.add(0xc4) } def withAssemblyContext(f: AssemblyContext => Unit): Unit = val ctx: AssemblyContext = new AssemblyContext f(ctx) ctx.printOut() println() println() object registers: sealed trait Register sealed trait DestinationA sealed trait IndexRegister case object A extends Register: def add[A](x: A)(using ctx: AssemblyContext, ev: Operand[A]): Unit = ev.operandType match case ValueLiteral => ctx.push(ADC, x, s"add LITERAL to a") case MemoryLocation => ctx.push(ADC, x, s"add ADDR to a") case object X extends Register with DestinationA with IndexRegister: def incr(using ctx: AssemblyContext): Unit = ctx.push(INX, "incr x") def loop(s: String, spec: RangeSpec)(f: AssemblyContext => Unit)(using ctx: AssemblyContext): Unit = val (start, stop, instruction) = spec match case Incrementing(from, to) => (from, to, INX) case Decrementing(from, to) => (from, to, DEX) ctx.push(LDX, start) label(s) f(ctx) ctx.push(instruction) ctx.push(CPX, stop) ctx.branch(BNE, s) case object Y extends Register with DestinationA with IndexRegister class CPU: def A: registers.A.type = registers.A def A_=[A](x: A)(using ctx: AssemblyContext, ev: Operand[A]): Unit = ctx.push(LDA, x, "set A to " + ev.toShow(x)) def A_=(reg: registers.DestinationA)(using ctx: AssemblyContext): Unit = reg match case registers.X => ctx.push(TXA) case registers.Y => ctx.push(TYA) def X: registers.X.type = registers.X def X_=(reg: registers.A.type)(using ctx: AssemblyContext): Unit = ctx.push(TAX, s"set x to register $reg") def Y: registers.Y.type = registers.Y def Y_=(reg: registers.A.type)(using ctx: AssemblyContext): Unit = ctx.push(TAY, s"set x to register $reg") class AssemblyContext: private val xs: ListBuffer[Statement] = ListBuffer() private var jumps: ListSet[Subroutine] = ListSet() def push(instruction: Instruction): Unit = xs.append(UnaryInstruction(instruction, None)) def push(instruction: Instruction, s: String): Unit = xs.append(UnaryInstruction(instruction, s.some)) def label(s: String): Unit = xs.append(Label(s)) def push[A: Operand](instruction: Instruction, x: A): Unit = xs.append(InstructionWithOperand(instruction, x, None)) def push[A: Operand](instruction: Instruction, x: A, s: String): Unit = xs.append(InstructionWithOperand(instruction, x: A, s.some)) def branch(instruction: Instruction, label: String): Unit = xs.append(BranchingInstruction(instruction, label)) def addJump(subroutine: Subroutine): Unit = jumps = jumps + subroutine def printOut(): Unit = xs.map(_.toAsm) .foreach(println) def triplets: List[(String, Option[String], Option[String])] = xs.map(_.toTriplet).toList def toFragment: AsmFragment = AsmFragment(xs.toList) def getJumps: ListSet[Subroutine] = jumps