enforce type safety

This commit is contained in:
Mark Canlas 2020-08-14 16:54:29 -04:00
parent 174e6936cf
commit ebb9588e04
8 changed files with 99 additions and 83 deletions

View File

@ -1,29 +1,33 @@
package com.htmlism.mos6502.dsl
import cats.implicits._
sealed trait Color
object Color {
implicit val ev: EnumAsByte[Color] =
new EnumAsByte[Color] {
def toByte(x: Color): Int =
x match {
case Black => 0x0
case White => 0x1
case Red => 0x2
case Cyan => 0x3
case Purple => 0x4
case Green => 0x5
case Blue => 0x6
case Yellow => 0x7
case Orange => 0x8
case Brown => 0x9
case LightRed => 0xA
case DarkGrey => 0xB
case Grey => 0xC
case LightGreen => 0xD
case LightBlue => 0xE
case LightGrey => 0xF
}
implicit val ev: Operand[Color] =
Operand
.operandInt
.contramap(toByte)
def toByte(x: Color): Int =
x match {
case Black => 0x0
case White => 0x1
case Red => 0x2
case Cyan => 0x3
case Purple => 0x4
case Green => 0x5
case Blue => 0x6
case Yellow => 0x7
case Orange => 0x8
case Brown => 0x9
case LightRed => 0xA
case DarkGrey => 0xB
case Grey => 0xC
case LightGreen => 0xD
case LightBlue => 0xE
case LightGrey => 0xF
}
case object Black extends Color

View File

@ -1,6 +1,8 @@
package com.htmlism.mos6502.dsl
import scala.collection.mutable.ListBuffer
import cats.implicits._
import com.htmlism._
object DslDemo extends App {
val cpu =
@ -75,22 +77,13 @@ object registers {
sealed trait DestinationA
case object A extends Register {
def add(n: Int)(implicit ctx: AssemblyContext): Unit = {
ctx.describe(s"add $n to a")
ctx.pushAsm("ADC #" + hex(n))
}
def add(n: ZeroAddress)(implicit ctx: AssemblyContext): Unit = {
ctx.describe(s"add to A value from zero page $n")
ctx.pushAsm("ADC " + hex(n))
}
def add[A](x: A)(implicit ctx: AssemblyContext, ev: Operand[A]): Unit =
ctx.push(ADC, x, s"add ADDR | LITERAL to a")
}
case object X extends Register with DestinationA {
def incr(implicit ctx: AssemblyContext): Unit = {
ctx.describe("incr x")
ctx.pushAsm("INX")
}
def incr(implicit ctx: AssemblyContext): Unit =
ctx.push(INX, "incr x")
}
case object Y extends Register with DestinationA
@ -100,51 +93,47 @@ class CPU {
def A: registers.A.type =
registers.A
def A_=(n: Int)(implicit ctx: AssemblyContext): Unit = {
ctx.describe(s"set a to value $n")
ctx.pushAsm(f"LDA #$$$n%h")
}
def A_=[A : EnumAsByte](x: A)(implicit ctx: AssemblyContext): Unit = {
val n = implicitly[EnumAsByte[A]].toByte(x)
ctx.describe(s"set a to value $n")
ctx.pushAsm("LDA #" + hex(n))
}
def A_=[A : Operand](x: A)(implicit ctx: AssemblyContext, ev: Operand[A]): Unit =
ctx.push(LDA, "set A to value " + ev.toString(x))
def A_=(reg: registers.DestinationA)(implicit ctx: AssemblyContext): Unit =
ctx.describe(s"set a to register $reg")
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)(implicit ctx: AssemblyContext): Unit = {
ctx.describe(s"set x to register $reg")
ctx.pushAsm("TAX")
}
def X_=(reg: registers.A.type)(implicit 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)(implicit ctx: AssemblyContext): Unit =
ctx.describe(s"set y to register $reg")
ctx.push(TAY, s"set x to register $reg")
}
class AssemblyContext {
val xs: ListBuffer[String] =
val xs: ListBuffer[Statement] =
ListBuffer()
val asm: ListBuffer[String] =
ListBuffer()
def push(instruction: Instruction): Unit =
xs.append(UnaryInstruction(instruction, None))
def pushAsm(s: String): Unit =
asm.append(s)
def push(instruction: Instruction, s: String): Unit =
xs.append(UnaryInstruction(instruction, s.some))
def describe(s: String): Unit =
xs.append(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 printOut(): Unit = {
asm.foreach(println)
xs.foreach(println)
}
}
}

View File

@ -1,5 +0,0 @@
package com.htmlism.mos6502.dsl
trait EnumAsByte[A] {
def toByte(x: A): Int
}

View File

@ -1,11 +1,15 @@
package com.htmlism.mos6502.dsl
case class GlobalAddress(n: Int) {
def write[A : EnumAsByte](x: A)(implicit ctx: AssemblyContext): Unit = {
val b = implicitly[EnumAsByte[A]].toByte(x)
import com.htmlism._
ctx.describe(s"write value $b to address $n")
ctx.pushAsm("LDA #" + hex(b))
ctx.pushAsm("STA " + hex(this))
object GlobalAddress {
implicit val operandGlobal: Operand[GlobalAddress] =
(x: GlobalAddress) => String.format("$%04x", x.n)
}
case class GlobalAddress(n: Int) {
def write[A](x: A)(implicit ctx: AssemblyContext, ev: Operand[A]): Unit = {
ctx.push(LDA, x, s"2: write value ${ev.toString(x)} to address $n")
ctx.push(STA, this)
}
}

View File

@ -0,0 +1,22 @@
package com.htmlism.mos6502.dsl
import cats.Contravariant
trait Operand[A] {
def toString(x: A): String
}
object Operand {
implicit val operandInt: Operand[Int] =
(x: Int) => String.format("#$%02x", x)
implicit val contra: Contravariant[Operand] = new Contravariant[Operand] {
def contramap[A, B](fa: Operand[A])(f: B => A): Operand[B] =
new Operand[B] {
def toString(x: B): String =
fa.toString {
f(x)
}
}
}
}

View File

@ -0,0 +1,9 @@
package com.htmlism.mos6502.dsl
import com.htmlism._
sealed trait Statement
case class UnaryInstruction(instruction: Instruction, comment: Option[String]) extends Statement
case class InstructionWithOperand[A : Operand](instruction: Instruction, operand: A, comment: Option[String]) extends Statement

View File

@ -1,3 +1,8 @@
package com.htmlism.mos6502.dsl
object ZeroAddress {
implicit val operandZero: Operand[ZeroAddress] =
(x: ZeroAddress) => String.format("$%02x", x.n)
}
case class ZeroAddress(n: Int)

View File

@ -1,12 +0,0 @@
package com.htmlism.mos6502
package object dsl {
def hex(n: Int): String =
String.format("$%02x", n)
def hex(n: ZeroAddress): String =
String.format("$%02x", n.n)
def hex(n: GlobalAddress): String =
String.format("$%04x", n.n)
}