mirror of
https://github.com/mcanlas/6502-opcodes.git
synced 2025-02-09 20:30:47 +00:00
enforce type safety
This commit is contained in:
parent
174e6936cf
commit
ebb9588e04
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
package com.htmlism.mos6502.dsl
|
||||
|
||||
trait EnumAsByte[A] {
|
||||
def toByte(x: A): Int
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
22
src/main/scala/com/htmlism/mos6502/dsl/Operand.scala
Normal file
22
src/main/scala/com/htmlism/mos6502/dsl/Operand.scala
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
src/main/scala/com/htmlism/mos6502/dsl/Statement.scala
Normal file
9
src/main/scala/com/htmlism/mos6502/dsl/Statement.scala
Normal 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
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user