160 lines
3.6 KiB
Scala
160 lines
3.6 KiB
Scala
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
|