This commit is contained in:
Mark Canlas 2022-02-14 18:11:45 -05:00
parent 7452871e71
commit 08960dc94c
37 changed files with 138 additions and 273 deletions

View File

@ -2,7 +2,6 @@ package com.htmlism.nescant
trait ByteSource[A]
object ByteSource {
object ByteSource:
implicit val sourceForInt: ByteSource[Int] =
new ByteSource[Int] {}
}

View File

@ -1,11 +1,10 @@
package com.htmlism.nescant
object GlobalAddress {
object GlobalAddress:
implicit val sourceForGlobalAddress: ByteSource[GlobalAddress] =
new ByteSource[GlobalAddress] {}
implicit val sinkForGlobalAddress: ByteSink[GlobalAddress] =
new ByteSink[GlobalAddress] {}
}
case class GlobalAddress(n: Int)

View File

@ -1,10 +1,8 @@
package com.htmlism.nescant
trait Operand[A] {
trait Operand[A]:
def encode(x: A): String
}
object Operand {
object Operand:
implicit val operandForInt: Operand[Int] =
_.toString
}

View File

@ -9,7 +9,7 @@ package com.htmlism.nescant
*/
case class ReadWriteLocation[A](name: String, address: ZeroPageAddress)
object ReadWriteLocation {
object ReadWriteLocation:
implicit def sourceForReadWriteLocation[A]: ByteSource[ReadWriteLocation[A]] =
new ByteSource[ReadWriteLocation[A]] {}
@ -20,4 +20,3 @@ object ReadWriteLocation {
new Operand[ReadWriteLocation[A]] {
def encode(x: ReadWriteLocation[A]): String = ""
}
}

View File

@ -13,7 +13,7 @@ package com.htmlism.nescant
*/
case class VolatileDevice[A](name: String, address: ZeroPageAddress)
object VolatileDevice {
object VolatileDevice:
implicit def sourceForVolatileDevice[A]: ByteSource[VolatileDevice[A]] =
new ByteSource[VolatileDevice[A]] {}
@ -21,4 +21,3 @@ object VolatileDevice {
new Operand[VolatileDevice[A]] {
def encode(x: VolatileDevice[A]): String = ""
}
}

View File

@ -1,11 +1,10 @@
package com.htmlism.nescant
object ZeroPageAddress {
object ZeroPageAddress:
implicit val sourceForZeroPageAddress: ByteSource[ZeroPageAddress] =
new ByteSource[ZeroPageAddress] {}
implicit val sinkForZeroPageAddress: ByteSink[ZeroPageAddress] =
new ByteSink[ZeroPageAddress] {}
}
case class ZeroPageAddress(n: Int)

View File

@ -1,17 +1,13 @@
package com.htmlism.nescant
package object dsl {
implicit class AddressOps(n: Int) {
package object dsl:
implicit class AddressOps(n: Int):
def z: ZeroPageAddress =
ZeroPageAddress(n)
def g: GlobalAddress =
GlobalAddress(n)
}
implicit class SinkOps[A: ByteSink](x: A) {
def write[B](src: B): Unit = {
implicit class SinkOps[A: ByteSink](x: A):
def write[B](src: B): Unit =
val _ = src
}
}
}

View File

@ -4,7 +4,7 @@ package dsl
import org.scalatest.flatspec._
import org.scalatest.matchers._
class ByteSinkSpec extends AnyFlatSpec with should.Matchers {
class ByteSinkSpec extends AnyFlatSpec with should.Matchers:
"A zero page address" should "be a byte-wide sync" in {
123.z.write(456)
}
@ -18,4 +18,3 @@ class ByteSinkSpec extends AnyFlatSpec with should.Matchers {
sink.write(456)
}
}

View File

@ -4,7 +4,7 @@ package dsl
import org.scalatest.flatspec._
import org.scalatest.matchers._
class ByteSourceSpec extends AnyFlatSpec with should.Matchers {
class ByteSourceSpec extends AnyFlatSpec with should.Matchers:
private val sink =
123.z
@ -31,4 +31,3 @@ class ByteSourceSpec extends AnyFlatSpec with should.Matchers {
sink.write(src)
}
}

View File

@ -4,7 +4,7 @@ package dsl
import org.scalatest.flatspec._
import org.scalatest.matchers._
class PostFixOpsSpec extends AnyFlatSpec with should.Matchers {
class PostFixOpsSpec extends AnyFlatSpec with should.Matchers:
"Numbers" should "support zero page ops" in {
123.z shouldBe ZeroPageAddress(123)
}
@ -12,4 +12,3 @@ class PostFixOpsSpec extends AnyFlatSpec with should.Matchers {
it should "support global address ops" in {
123.g shouldBe GlobalAddress(123)
}
}

View File

@ -12,6 +12,7 @@ object Scala3Plugin extends AutoPlugin {
override def trigger: PluginTrigger = AllRequirements
override val buildSettings: Seq[Setting[_]] = Seq(
scalaVersion := "2.13.8",
scalaVersion := "3.1.0",
scalacOptions ++= Seq("-indent", "-rewrite")
)
}

View File

@ -6,7 +6,7 @@ import cats.implicits._
import com.htmlism.mos6502.model._
object MatchOpcodes {
object MatchOpcodes:
def paddedBinary(n: Int, width: Int) =
String.format(s"%${width}s", Integer.toBinaryString(n)).replace(" ", "0")
@ -76,7 +76,7 @@ object MatchOpcodes {
).view.mapValues(x => x -> Implied).toMap
// format: on
def doStuff(out: PrintWriter): Unit = {
def doStuff(out: PrintWriter): Unit =
val lookup =
generatedOpcodes ++
injectedOpcodesImplied ++
@ -100,16 +100,16 @@ object MatchOpcodes {
out.print("</tr>")
for (r <- Seq(0x00, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0)) {
for (r <- Seq(0x00, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0))
out.print("<tr>")
// left header
out.print(s"<th>${paddedBinary(r, 3)}</th>")
for (f <- fancyColumns) {
for (f <- fancyColumns)
val fullInt = r + f
lookup.get(fullInt) match {
lookup.get(fullInt) match
case Some((ints, mode)) =>
val hex = f"$fullInt%2X"
out.print(
@ -118,11 +118,8 @@ object MatchOpcodes {
case None =>
out.print(s"<td>UNDEF</td>")
}
}
out.print("</tr>")
}
out.print("</table>")
@ -137,16 +134,16 @@ object MatchOpcodes {
out.print("</tr>")
for (r <- wideRows) {
for (r <- wideRows)
out.print("<tr>")
// left header
out.print(s"<th>${paddedBinary(r, 3)}</th>")
for (c <- wideColumns) {
for (c <- wideColumns)
val fullInt = (r << 5) + c
lookup.get(fullInt) match {
lookup.get(fullInt) match
case Some((ints, mode)) =>
val hex = f"$fullInt%2X"
out.print(
@ -155,11 +152,8 @@ object MatchOpcodes {
case None =>
out.print(s"<td>UNDEF</td>")
}
}
out.print("</tr>")
}
out.print("</table>")
@ -167,9 +161,8 @@ object MatchOpcodes {
quartile(out, 1, lookup)
quartile(out, 2, lookup)
quartile(out, 3, lookup)
}
def quartile(out: PrintWriter, n: Int, lookup: Map[Int, (Instruction, AddressingMode)]) = {
def quartile(out: PrintWriter, n: Int, lookup: Map[Int, (Instruction, AddressingMode)]) =
out.print(s"<h2>${paddedBinary(n, 2)}</h2>")
out.print("<table>")
@ -193,16 +186,16 @@ object MatchOpcodes {
out.print("</tr>")
for (r <- rows) {
for (r <- rows)
out.print("<tr>")
// left header
out.print(s"<th>${paddedBinary(r, 3)}</th>")
for (c <- columns) {
for (c <- columns)
val fullInt = (r << (3 + 2)) + (c << 2) + n
lookup.get(fullInt) match {
lookup.get(fullInt) match
case Some((ints, mode)) =>
val hex = f"$fullInt%2X"
out.print(
@ -211,15 +204,11 @@ object MatchOpcodes {
case None =>
out.print(s"<td>UNDEF</td>")
}
}
out.print("</tr>")
}
out.print("</table>")
}
def wideColumns: Seq[Int] =
for {
@ -231,44 +220,38 @@ object MatchOpcodes {
def wideRows: Seq[Int] =
0 to 7
private def write(file: String)(f: PrintWriter => Unit) = {
private def write(file: String)(f: PrintWriter => Unit) =
val out = new PrintWriter(file)
f(out)
out.close()
}
def toOpcode(n: Int): Option[(Instruction, AddressingMode)] = {
def toOpcode(n: Int): Option[(Instruction, AddressingMode)] =
val BitPattern = ThreeBits >> ThreeBits >> TwoBits
n match {
n match
case BitPattern((aaa, bbb), cc) =>
cc match {
cc match
case 0 => c00(aaa, bbb)
case 1 => c01(aaa, bbb)
case 2 => c10(aaa, bbb)
case 3 => None
}
case _ => throw new IllegalStateException("an int should always have bits to find")
}
}
def c01(aaa: Int, bbb: Int): Option[(Instruction, AddressingMode)] = {
def c01(aaa: Int, bbb: Int): Option[(Instruction, AddressingMode)] =
val instruction =
Seq(ORA, AND, EOR, ADC, STA, LDA, CMP, SBC)(aaa)
val addressingMode =
Seq(IndirectX, ZeroPage, Immediate, Absolute, IndirectY, ZeroPageX, AbsoluteY, AbsoluteX)(bbb)
(instruction, addressingMode) match {
(instruction, addressingMode) match
case (STA, Immediate) =>
None
case _ =>
(instruction -> addressingMode).some
}
}
def c10(aaa: Int, bbb: Int): Option[(Instruction, AddressingMode)] = {
def c10(aaa: Int, bbb: Int): Option[(Instruction, AddressingMode)] =
val instruction =
Seq(ASL, ROL, LSR, ROR, STX, LDX, DEC, INC)(aaa)
@ -278,7 +261,7 @@ object MatchOpcodes {
val useLookup =
(instruction -> addressingMode).some
(instruction, addressingMode) match {
(instruction, addressingMode) match
case (ASL | ROL | LSR | ROR, ZeroPage | Accumulator | Absolute | ZeroPageX | AbsoluteX) =>
useLookup
@ -293,10 +276,8 @@ object MatchOpcodes {
case _ =>
None
}
}
def c00(aaa: Int, bbb: Int): Option[(Instruction, AddressingMode)] = {
def c00(aaa: Int, bbb: Int): Option[(Instruction, AddressingMode)] =
val instruction =
Seq(NoInstruction, BIT, NoInstruction, NoInstruction, STY, LDY, CPY, CPX)(aaa)
@ -306,7 +287,7 @@ object MatchOpcodes {
val useLookup =
(instruction -> addressingMode).some
(instruction, addressingMode) match {
(instruction, addressingMode) match
case (BIT, ZeroPage | Absolute) =>
useLookup
@ -321,6 +302,3 @@ object MatchOpcodes {
case _ =>
None
}
}
}

View File

@ -5,7 +5,7 @@ import scala.annotation.tailrec
/**
* Given an `Int`, destructure it into smaller `Int`s that use less bits
*/
trait BitExtractor[A] {
trait BitExtractor[A]:
self =>
def length: Int
@ -26,24 +26,21 @@ trait BitExtractor[A] {
a <- self.unapply(shifted)
} yield (a, b)
}
}
object AtomExtractor {
object AtomExtractor:
@tailrec
def pow(ex: Int, acc: Int = 1): Int =
if (ex == 0)
acc
else
pow(ex - 1, acc * 2)
}
abstract class PrimitiveBitExtractor(val length: Int) extends BitExtractor[Int] {
abstract class PrimitiveBitExtractor(val length: Int) extends BitExtractor[Int]:
private lazy val mask =
AtomExtractor.pow(length) - 1
def unapply(n: Int): Option[Int] =
Some(n & mask)
}
object OneBit extends PrimitiveBitExtractor(1)
object TwoBits extends PrimitiveBitExtractor(2)

View File

@ -1,10 +1,9 @@
package com.htmlism.mos6502.dsl
sealed trait Address {
sealed trait Address:
def n: Int
}
object ZeroAddress {
object ZeroAddress:
implicit val operandZero: Operand[ZeroAddress] =
new Operand[ZeroAddress] {
val operandType: OperandType =
@ -19,11 +18,10 @@ object ZeroAddress {
implicit val definitionValueForZero: DefinitionValue[ZeroAddress] =
operandZero.toAddressLiteral(_)
}
case class ZeroAddress(n: Int) extends Address
object GlobalAddress {
object GlobalAddress:
implicit val operandGlobal: Operand[GlobalAddress] =
new Operand[GlobalAddress] {
val operandType: OperandType =
@ -38,6 +36,5 @@ object GlobalAddress {
implicit val definitionValueForGlobal: DefinitionValue[GlobalAddress] =
operandGlobal.toAddressLiteral(_)
}
case class GlobalAddress(n: Int) extends Address

View File

@ -5,14 +5,13 @@ import scala.collection.mutable.ListBuffer
import cats.implicits._
case class AsmDocument(xs: List[TopLevelAsmDocumentFragment]) {
case class AsmDocument(xs: List[TopLevelAsmDocumentFragment]):
def toAsm: String =
xs
.map(_.toAsm)
.mkString("\n\n")
}
class AsmDocumentContext {
class AsmDocumentContext:
private val xs: ListBuffer[TopLevelAsmDocumentFragment] =
ListBuffer()
@ -25,52 +24,44 @@ class AsmDocumentContext {
def addJumpRegistry(ys: ListSet[Subroutine]): Unit =
jumps = jumps ++ ys
def toDoc: AsmDocument = {
def toDoc: AsmDocument =
val asmFragmentsAndSubroutines =
xs.toList ::: jumps.toList ++ jumps.flatMap(_.jumpRegistry).toList
AsmDocument(asmFragmentsAndSubroutines)
}
}
sealed trait TopLevelAsmDocumentFragment {
sealed trait TopLevelAsmDocumentFragment:
def toAsm: String
}
case class AsmFragment(xs: List[Statement]) extends TopLevelAsmDocumentFragment {
case class AsmFragment(xs: List[Statement]) extends TopLevelAsmDocumentFragment:
def toAsm: String =
xs.map(_.toAsm).mkString("\n")
}
case class Subroutine(name: String, fragment: AsmFragment, jumpRegistry: ListSet[Subroutine])
extends TopLevelAsmDocumentFragment {
extends TopLevelAsmDocumentFragment:
def toAsm: String =
name + ":" + "\n" + fragment.toAsm
}
case class DefinitionGroup(comment: String, xs: List[Definition[_]]) extends TopLevelAsmDocumentFragment {
def toAsm: String = {
case class DefinitionGroup(comment: String, xs: List[Definition[_]]) extends TopLevelAsmDocumentFragment:
def toAsm: String =
val groupCommentLine =
"; " + comment
val definitionLines =
xs
.map { d =>
d.comment match {
d.comment match
case Some(c) =>
f"define ${d.name}%-20s${d.value} ; $c"
case None =>
f"define ${d.name}%-20s${d.value}"
}
}
(groupCommentLine :: definitionLines)
.mkString("\n")
}
}
class DefinitionGroupContext {
class DefinitionGroupContext:
private val xs: ListBuffer[Definition[_]] =
ListBuffer()
@ -81,19 +72,17 @@ class DefinitionGroupContext {
def toGroup(s: String): DefinitionGroup =
DefinitionGroup(s, xs.toList)
}
/**
* @param comment
* Typically used by resources to describe their type safety
*/
case class Definition[A](name: String, x: A, comment: Option[String])(implicit ev: DefinitionValue[A]) {
case class Definition[A](name: String, x: A, comment: Option[String])(implicit ev: DefinitionValue[A]):
lazy val value: String =
ev
.value(x)
}
object Definition {
object Definition:
implicit def namedResourceForDefinition[A]: NamedResource[Definition[A]] =
new NamedResource[Definition[A]] {
def toDefinitions(x: Definition[A]): List[Definition[_]] =
@ -105,12 +94,10 @@ object Definition {
def apply[A: DefinitionValue](name: String, x: A, comment: String): Definition[A] =
Definition(name, x, comment.some)
}
class AsmBlockContext {
class AsmBlockContext:
private val xs: ListBuffer[Statement] =
ListBuffer()
def push(x: Statement): Unit =
xs.append(x)
}

View File

@ -2,7 +2,7 @@ package com.htmlism.mos6502.dsl
import cats.data.NonEmptyList
trait BitField[A] {
trait BitField[A]:
def definitionGroupComment: String
/**
@ -14,4 +14,3 @@ trait BitField[A] {
* ASM-safe label
*/
def label(x: A): String
}

View File

@ -4,7 +4,7 @@ import cats.data.NonEmptyList
sealed trait Color
object Color {
object Color:
implicit val colorOperand: Operand[Color] =
new Operand[Color] {
def toAddressLiteral(x: Color): String =
@ -65,4 +65,3 @@ object Color {
case object LightGreen extends Color
case object LightBlue extends Color
case object LightGrey extends Color
}

View File

@ -1,14 +1,12 @@
package com.htmlism.mos6502.dsl
trait DefinitionValue[A] {
trait DefinitionValue[A]:
/**
* The value as presented in a `define` declaration (i.e. where no alias is possible)
*/
def value(x: A): String
}
object DefinitionValue {
object DefinitionValue:
implicit val definitionValueForInt: DefinitionValue[Int] =
(x: Int) => String.format("$%02x", x)
}

View File

@ -7,7 +7,7 @@ import cats.implicits._
import com.htmlism.mos6502.model._
object DslDemo extends App {
object DslDemo extends App:
val cpu =
new CPU
@ -40,7 +40,7 @@ object DslDemo extends App {
A.add(0xc4)
}
def withAssemblyContext(f: AssemblyContext => Unit): Unit = {
def withAssemblyContext(f: AssemblyContext => Unit): Unit =
val ctx: AssemblyContext =
new AssemblyContext
@ -49,40 +49,35 @@ object DslDemo extends App {
ctx.printOut()
println()
println()
}
}
object registers {
object registers:
sealed trait Register
sealed trait DestinationA
sealed trait IndexRegister
case object A extends Register {
case object A extends Register:
def add[A](x: A)(implicit ctx: AssemblyContext, ev: Operand[A]): Unit =
ev.operandType match {
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 {
case object X extends Register with DestinationA with IndexRegister:
def incr(implicit ctx: AssemblyContext): Unit =
ctx.push(INX, "incr x")
def loop(s: String, spec: RangeSpec)(f: AssemblyContext => Unit)(implicit ctx: AssemblyContext): Unit = {
def loop(s: String, spec: RangeSpec)(f: AssemblyContext => Unit)(implicit ctx: AssemblyContext): Unit =
val (start, stop, instruction) =
spec match {
spec match
case Incrementing(from, to) =>
(from, to, INX)
case Decrementing(from, to) =>
(from, to, DEX)
}
ctx.push(LDX, start)
@ -93,13 +88,10 @@ object registers {
ctx.push(instruction)
ctx.push(CPX, stop)
ctx.branch(BNE, s)
}
}
case object Y extends Register with DestinationA with IndexRegister
}
class CPU {
class CPU:
def A: registers.A.type =
registers.A
@ -107,12 +99,11 @@ class CPU {
ctx.push(LDA, x, "set A to " + ev.toShow(x))
def A_=(reg: registers.DestinationA)(implicit ctx: AssemblyContext): Unit =
reg match {
reg match
case registers.X =>
ctx.push(TXA)
case registers.Y =>
ctx.push(TYA)
}
def X: registers.X.type =
registers.X
@ -125,9 +116,8 @@ class CPU {
def Y_=(reg: registers.A.type)(implicit ctx: AssemblyContext): Unit =
ctx.push(TAY, s"set x to register $reg")
}
class AssemblyContext {
class AssemblyContext:
private val xs: ListBuffer[Statement] =
ListBuffer()
@ -155,10 +145,9 @@ class AssemblyContext {
def addJump(subroutine: Subroutine): Unit =
jumps = jumps + subroutine
def printOut(): Unit = {
def printOut(): Unit =
xs.map(_.toAsm)
.foreach(println)
}
def triplets: List[(String, Option[String], Option[String])] =
xs.map(_.toTriplet).toList
@ -168,4 +157,3 @@ class AssemblyContext {
def getJumps: ListSet[Subroutine] =
jumps
}

View File

@ -2,7 +2,7 @@ package com.htmlism.mos6502.dsl
import cats.data.NonEmptyList
trait EnumAsm[A] {
trait EnumAsm[A]:
def comment: String
/**
@ -19,4 +19,3 @@ trait EnumAsm[A] {
* Comment string
*/
def comment(x: A): String
}

View File

@ -5,12 +5,10 @@ import com.htmlism.mos6502.model._
/**
* A typed collection, like memory mapped to screen pixels. Access by index instead of by address.
*/
case class IndexedAddressCollection[A](baseAddress: Int, name: String)(implicit ev: Operand[A]) {
case class IndexedAddressCollection[A](baseAddress: Int, name: String)(implicit ev: Operand[A]):
def apply(n: Int): GlobalAddress =
GlobalAddress(baseAddress + n)
def write(n: Int, x: A)(implicit ctx: AssemblyContext): Unit = {
def write(n: Int, x: A)(implicit ctx: AssemblyContext): Unit =
ctx.push(LDA, x, s"write ${ev.toShow(x)} to $name ($n)")
ctx.push(STA, GlobalAddress(baseAddress + n), "")
}
}

View File

@ -5,7 +5,7 @@ import cats.data.NonEmptyList
/**
* Like an enum, but values are specified
*/
trait Mapping[A] {
trait Mapping[A]:
def definitionGroupComment: String
/**
@ -24,9 +24,8 @@ trait Mapping[A] {
* Comment string
*/
def comment(x: A): String
}
object Mapping {
object Mapping:
implicit def mappingForBitField[A](implicit ev: BitField[A]): Mapping[A] =
new Mapping[A] {
private lazy val valueMap =
@ -72,4 +71,3 @@ object Mapping {
def comment(x: A): String =
ev.comment(x)
}
}

View File

@ -1,10 +1,9 @@
package com.htmlism.mos6502.dsl
trait NamedResource[A] {
trait NamedResource[A]:
/**
* A `Definable` can emit multiple definitions. Usually in the case of `word`s being split across two
* byte-definitions
*/
def toDefinitions(x: A): List[Definition[_]]
}

View File

@ -1,6 +1,6 @@
package com.htmlism.mos6502.dsl
trait Operand[A] {
trait Operand[A]:
self =>
def toAddressLiteral(x: A): String
@ -11,9 +11,8 @@ trait Operand[A] {
def toShow(x: A): String
def operandType: OperandType
}
object Operand {
object Operand:
implicit val operandInt: Operand[Int] =
new Operand[Int] {
val operandType: OperandType =
@ -37,4 +36,3 @@ object Operand {
def operandType: OperandType =
ValueLiteral
}
}

View File

@ -1,10 +1,9 @@
package com.htmlism.mos6502.dsl
sealed trait RangeSpec {
sealed trait RangeSpec:
def from: Int
def to: Int
}
case class Incrementing(from: Int, to: Int) extends RangeSpec

View File

@ -9,19 +9,16 @@ import com.htmlism.mos6502.model._
* @tparam A
* The input type of the write and the output type of the read
*/
case class ReadWriteLocation[A: Operand](name: String, address: ZeroAddress) {
def read(implicit ctx: AssemblyContext): Unit = {
case class ReadWriteLocation[A: Operand](name: String, address: ZeroAddress):
def read(implicit ctx: AssemblyContext): Unit =
val _ = ctx
// ctx.push(LDA, ev, s"write ${ev.toShow(x)} to $name ($n)")
}
def write(x: A)(implicit ctx: AssemblyContext): Unit = {
def write(x: A)(implicit ctx: AssemblyContext): Unit =
ctx.push(LDA, x)
ctx.push(STA, this)
}
}
object ReadWriteLocation {
object ReadWriteLocation:
implicit def operandForReadWriteLocation[A]: Operand[ReadWriteLocation[A]] =
new Operand[ReadWriteLocation[A]] {
def toAddressLiteral(x: ReadWriteLocation[A]): String =
@ -41,4 +38,3 @@ object ReadWriteLocation {
Definition(x.name, x.address, "Read/write location for A values")
}
}
}

View File

@ -4,68 +4,58 @@ import cats.implicits._
import com.htmlism.mos6502.model.Instruction
sealed trait Statement {
sealed trait Statement:
def toAsm: String
def toTriplet: (String, Option[String], Option[String])
}
object Statement {
object Statement:
val indent: String =
" "
}
case class UnaryInstruction(instruction: Instruction, comment: Option[String]) extends Statement {
def toAsm: String = {
case class UnaryInstruction(instruction: Instruction, comment: Option[String]) extends Statement:
def toAsm: String =
val left =
instruction.toString
comment match {
comment match
case Some(c) =>
Statement.indent + f"$left%-16s ; " + c
case None =>
Statement.indent + left
}
}
def toTriplet: (String, Option[String], Option[String]) =
(instruction.toString, None, comment)
}
case class InstructionWithOperand[A](instruction: Instruction, operand: A, comment: Option[String])(implicit
ev: Operand[A]
) extends Statement {
def toAsm: String = {
) extends Statement:
def toAsm: String =
val left =
instruction.toString
val operandStr =
ev.toAddressLiteral(operand)
comment match {
comment match
case Some(c) =>
Statement.indent + f"$left%-5s $operandStr%-11s; " + c
case None =>
Statement.indent + f"$left%-5s $operandStr"
}
}
def toTriplet: (String, Option[String], Option[String]) =
(instruction.toString, ev.toAddressLiteral(operand).some, comment)
}
case class Label(s: String) extends Statement {
case class Label(s: String) extends Statement:
def toAsm: String =
s + ":"
def toTriplet: (String, Option[String], Option[String]) =
(s, None, None)
}
case class BranchingInstruction(instruction: Instruction, label: String) extends Statement {
case class BranchingInstruction(instruction: Instruction, label: String) extends Statement:
def toAsm: String =
Statement.indent + instruction.toString + " " + label
def toTriplet: (String, Option[String], Option[String]) =
(instruction.toString, label.some, None)
}

View File

@ -11,14 +11,12 @@ package com.htmlism.mos6502.dsl
* @tparam A
* The return type of the read
*/
case class VolatileDevice[A](name: String, address: ZeroAddress) {
def read(implicit ctx: AssemblyContext): Unit = {
case class VolatileDevice[A](name: String, address: ZeroAddress):
def read(implicit ctx: AssemblyContext): Unit =
val _ = ctx
// ctx.push(LDA, ev, s"write ${ev.toShow(x)} to $name ($n)")
}
}
object VolatileDevice {
object VolatileDevice:
implicit def namedResourceForVolatileDevice[A]: NamedResource[VolatileDevice[A]] =
new NamedResource[VolatileDevice[A]] {
def toDefinitions(x: VolatileDevice[A]): List[Definition[ZeroAddress]] =
@ -26,4 +24,3 @@ object VolatileDevice {
Definition(x.name, x.address, "Volatile generator for A values")
}
}
}

View File

@ -1,28 +1,24 @@
package com.htmlism.mos6502
package object dsl extends syntax.DefinitionGroupSyntax with syntax.AsmDocSyntax with syntax.AsmSyntax {
def asmDoc(f: AsmDocumentContext => Unit): AsmDocument = {
package object dsl extends syntax.DefinitionGroupSyntax with syntax.AsmDocSyntax with syntax.AsmSyntax:
def asmDoc(f: AsmDocumentContext => Unit): AsmDocument =
val ctx: AsmDocumentContext =
new AsmDocumentContext
f(ctx)
ctx.toDoc
}
implicit class AddressOps(n: Int) {
implicit class AddressOps(n: Int):
def z: ZeroAddress =
ZeroAddress(n)
def addr: GlobalAddress =
GlobalAddress(n)
}
implicit class RangeOps(from: Int) {
implicit class RangeOps(from: Int):
def upTo(to: Int): Incrementing =
Incrementing(from, to)
def downTo(to: Int): Decrementing =
Decrementing(from, to)
}
}

View File

@ -1,8 +1,8 @@
package com.htmlism.mos6502.dsl
package syntax
trait AsmDocSyntax {
def asm(f: AssemblyContext => Unit)(implicit ctx: AsmDocumentContext): Unit = {
trait AsmDocSyntax:
def asm(f: AssemblyContext => Unit)(implicit ctx: AsmDocumentContext): Unit =
val asmCtx: AssemblyContext =
new AssemblyContext
@ -13,9 +13,8 @@ trait AsmDocSyntax {
ctx
.push(asmCtx.toFragment)
}
def group[A](s: String)(f: DefinitionGroupContext => A)(implicit ctx: AsmDocumentContext): Unit = {
def group[A](s: String)(f: DefinitionGroupContext => A)(implicit ctx: AsmDocumentContext): Unit =
val g: DefinitionGroupContext =
new DefinitionGroupContext
@ -23,21 +22,18 @@ trait AsmDocSyntax {
ctx
.push(g.toGroup(s))
}
def enumAsm[A: EnumAsm: Mapping](implicit ctx: AsmDocumentContext): Unit = {
def enumAsm[A: EnumAsm: Mapping](implicit ctx: AsmDocumentContext): Unit =
val _ = implicitly[EnumAsm[A]] // TODO unused
mapping
}
def bitField[A: BitField: Mapping](implicit ctx: AsmDocumentContext): Unit = {
def bitField[A: BitField: Mapping](implicit ctx: AsmDocumentContext): Unit =
val _ = implicitly[BitField[A]] // TODO unused
mapping
}
def mapping[A](implicit ctx: AsmDocumentContext, ev: Mapping[A]): Unit = {
def mapping[A](implicit ctx: AsmDocumentContext, ev: Mapping[A]): Unit =
val xs =
ev.all
.map(x => ev.label(x) -> ev.value(x))
@ -54,5 +50,3 @@ trait AsmDocSyntax {
ctx
.push(grp)
}
}

View File

@ -3,12 +3,12 @@ package syntax
import com.htmlism.mos6502.model._
trait AsmSyntax {
trait AsmSyntax:
def label(s: String)(implicit ctx: AssemblyContext): Unit =
ctx
.label(s)
def sub(s: String)(f: AssemblyContext => Unit): Subroutine = {
def sub(s: String)(f: AssemblyContext => Unit): Subroutine =
val ctx: AssemblyContext =
new AssemblyContext
@ -17,13 +17,10 @@ trait AsmSyntax {
ctx.push(RTS)
Subroutine(s, ctx.toFragment, ctx.getJumps)
}
def jump(s: Subroutine)(implicit ctx: AssemblyContext): Unit = {
def jump(s: Subroutine)(implicit ctx: AssemblyContext): Unit =
ctx
.addJump(s)
ctx
.branch(JSR, s.name)
}
}

View File

@ -1,8 +1,8 @@
package com.htmlism.mos6502.dsl
package syntax
trait DefinitionGroupSyntax {
def define[A <: Address: DefinitionValue](name: String, x: A)(implicit ctx: DefinitionGroupContext): Definition[A] = {
trait DefinitionGroupSyntax:
def define[A <: Address: DefinitionValue](name: String, x: A)(implicit ctx: DefinitionGroupContext): Definition[A] =
val definition =
Definition(name, x)
@ -10,9 +10,8 @@ trait DefinitionGroupSyntax {
.push(Definition(name, x))
definition
}
def constant(name: String, x: Int)(implicit ctx: DefinitionGroupContext): Definition[Int] = {
def constant(name: String, x: Int)(implicit ctx: DefinitionGroupContext): Definition[Int] =
val definition =
Definition(name, x)
@ -20,5 +19,3 @@ trait DefinitionGroupSyntax {
.push(Definition(name, x))
definition
}
}

View File

@ -2,22 +2,19 @@ package com.htmlism.mos6502.model
sealed trait Instruction { def theme: String; def color: String }
case object NoInstruction extends Instruction {
case object NoInstruction extends Instruction:
def theme: String = "noop"; def color: String = "white"
}
sealed trait Logical extends Instruction {
sealed trait Logical extends Instruction:
def theme: String = "logical"; def color: String = "OliveDrab"
}
case object AND extends Logical
case object EOR extends Logical
case object ORA extends Logical
case object BIT extends Logical
sealed trait Arithmetic extends Instruction {
sealed trait Arithmetic extends Instruction:
def theme: String = "arithmetic"; def color: String = "CadetBlue"
}
case object ADC extends Arithmetic
case object SBC extends Arithmetic
@ -25,56 +22,49 @@ case object CMP extends Arithmetic
case object CPX extends Arithmetic
case object CPY extends Arithmetic
sealed trait Load extends Instruction {
sealed trait Load extends Instruction:
def theme: String = "load"; def color: String = "BurlyWood"
}
case object LDA extends Load
case object LDX extends Load
case object LDY extends Load
sealed trait Store extends Instruction {
sealed trait Store extends Instruction:
def theme: String = "store"; def color: String = "Bisque"
}
case object STA extends Store
case object STX extends Store
case object STY extends Store
sealed trait Shift extends Instruction {
sealed trait Shift extends Instruction:
def theme: String = "shift"; def color: String = "SlateBlue"
}
case object ASL extends Shift
case object LSR extends Shift
case object ROL extends Shift
case object ROR extends Shift
sealed trait Increment extends Instruction {
sealed trait Increment extends Instruction:
def theme: String = "increment"; def color: String = "LightPink"
}
case object INC extends Increment
case object INX extends Increment
case object INY extends Increment
sealed trait Decrement extends Instruction {
sealed trait Decrement extends Instruction:
def theme: String = "decrement"; def color: String = "Khaki"
}
case object DEC extends Decrement
case object DEX extends Decrement
case object DEY extends Decrement
sealed trait Jump extends Instruction {
sealed trait Jump extends Instruction:
def theme: String = "jump"; def color: String = "Salmon"
}
case object JMP extends Jump
case object JSR extends Jump
case object RTS extends Jump
sealed trait Branch extends Instruction {
sealed trait Branch extends Instruction:
def theme: String = "branch"; def color: String = "DodgerBlue"
}
case object BCC extends Branch
case object BCS extends Branch
case object BEQ extends Branch
@ -84,16 +74,14 @@ case object BPL extends Branch
case object BVC extends Branch
case object BVS extends Branch
sealed trait System extends Instruction {
sealed trait System extends Instruction:
def theme: String = "system"; def color: String = "Peru"
}
case object BRK extends System
case object NOP extends System
case object RTI extends System
sealed trait Stack extends Instruction {
sealed trait Stack extends Instruction:
def theme: String = "stack"; def color: String = "Wheat"
}
case object TSX extends Stack
case object TXS extends Stack
case object PHA extends Stack
@ -101,25 +89,22 @@ case object PHP extends Stack
case object PLA extends Stack
case object PLP extends Stack
sealed trait Transfer extends Instruction {
sealed trait Transfer extends Instruction:
def theme: String = "transfer"; def color: String = "Teal"
}
case object TAX extends Transfer
case object TAY extends Transfer
case object TXA extends Transfer
case object TYA extends Transfer
sealed trait Clear extends Instruction {
sealed trait Clear extends Instruction:
def theme: String = "clear"; def color: String = "LightSteelBlue"
}
case object CLC extends Clear
case object CLD extends Clear
case object CLI extends Clear
case object CLV extends Clear
sealed trait SetFlag extends Instruction {
sealed trait SetFlag extends Instruction:
def theme: String = "set"; def color: String = "Thistle"
}
case object SEC extends SetFlag
case object SED extends SetFlag
case object SEI extends SetFlag

View File

@ -4,7 +4,7 @@ import cats.data.NonEmptyList
import org.scalatest.flatspec._
import org.scalatest.matchers._
class DslSpec extends AnyFlatSpec with should.Matchers {
class DslSpec extends AnyFlatSpec with should.Matchers:
"the dsl" should "compile" in {
val doc =
@ -86,7 +86,6 @@ class DslSpec extends AnyFlatSpec with should.Matchers {
)
)
}
}
sealed trait Triforce
@ -94,7 +93,7 @@ case object Courage extends Triforce
case object Wisdom extends Triforce
case object Power extends Triforce
object Triforce {
object Triforce:
implicit val enumTriforce: EnumAsm[Triforce] =
new EnumAsm[Triforce] {
def comment: String =
@ -109,7 +108,6 @@ object Triforce {
def comment(x: Triforce): String =
x.toString
}
}
sealed trait TestDirection
@ -118,7 +116,7 @@ case object Down extends TestDirection
case object Left extends TestDirection
case object Right extends TestDirection
object TestDirection {
object TestDirection:
implicit val bitFieldDirection: BitField[TestDirection] =
new BitField[TestDirection] {
def definitionGroupComment: String =
@ -130,4 +128,3 @@ object TestDirection {
def label(x: TestDirection): String =
x.toString.toLowerCase
}
}

View File

@ -3,7 +3,7 @@ package snake
import cats.data.NonEmptyList
object AsciiValue {
object AsciiValue:
implicit val asciiValueMapping: Mapping[AsciiValue] =
new Mapping[AsciiValue] {
def definitionGroupComment: String =
@ -13,12 +13,11 @@ object AsciiValue {
NonEmptyList.of(AsciiW, AsciiA, AsciiS, AsciiD)
def value(x: AsciiValue): Int =
x match {
x match
case AsciiW => 0x77
case AsciiA => 0x61
case AsciiS => 0x73
case AsciiD => 0x64
}
def label(x: AsciiValue): String =
x.toString.toLowerCase
@ -26,7 +25,6 @@ object AsciiValue {
def comment(x: AsciiValue): String =
x.toString
}
}
sealed trait AsciiValue

View File

@ -3,7 +3,7 @@ package snake
import cats.data.NonEmptyList
object Direction {
object Direction:
implicit val directionBitField: BitField[Direction] =
new BitField[Direction] {
def definitionGroupComment: String =
@ -15,7 +15,6 @@ object Direction {
def label(x: Direction): String =
"moving" + x.toString
}
}
sealed trait Direction

View File

@ -7,7 +7,7 @@ import org.scalatest.matchers._
import com.htmlism.mos6502.model._
class Easy6502Spec extends AnyFlatSpec with should.Matchers {
class Easy6502Spec extends AnyFlatSpec with should.Matchers:
"the three pixel demo" should "have the right instructions" in {
val doc =
@ -163,12 +163,10 @@ class Easy6502Spec extends AnyFlatSpec with should.Matchers {
)
}
def withAssemblyContext(f: AssemblyContext => Unit): AssemblyContext = {
def withAssemblyContext(f: AssemblyContext => Unit): AssemblyContext =
val ctx: AssemblyContext =
new AssemblyContext
f(ctx)
ctx
}
}