Skip to content

Instantly share code, notes, and snippets.

@utaal
Created March 28, 2012 14:01
Show Gist options
  • Save utaal/2226428 to your computer and use it in GitHub Desktop.
Save utaal/2226428 to your computer and use it in GitHub Desktop.
m68k assembler parser and interpreter
project/target/
target/
a sketchy a m68k assembler parser and interpreter written in Scala to learn Scala
name := "m68k-emu"
scalaVersion := "2.9.1"
libraryDependencies ++= Seq(
"org.specs2" %% "specs2" % "1.8.2" % "test"
, "org.specs2" %% "specs2-scalaz-core" % "6.0.1"
)
// Read here for optional dependencies:
// http://etorreborre.github.com/specs2/guide/org.specs2.guide.Runners.html#Dependencies
resolvers ++= Seq(
"snapshots" at "http://oss.sonatype.org/content/repositories/snapshots"
)
testOptions := Seq(Tests.Filter(s =>
Seq("Spec", "Suite", "Unit", "all").exists(s.endsWith(_))
))
package com.github.utaal.m68k
import com.github.utaal.m68k.ast._
object RegisterState {
def apply(): RegisterState = RegisterState()
}
case class RegisterState(value: Long) {
require (value >= 0L && value <= 0xffffffffL)
def getMask(size: Size): Long = size match {
case Size.B => 0xffL
case Size.W => 0xffffL
case Size.L => 0xffffffffL
}
def mask(size: Size, value: Long): Long = value & getMask(size)
def reverseMask(size: Size, value: Long): Long = value & (0xffffffffL ^ getMask(size))
def get(size: Size): Long = mask(size, value)
def set(size: Size, newValue: Long) =
new RegisterState(reverseMask(size, value) | mask(size, newValue))
}
object ProgramCounter {
def apply(): ProgramCounter = ProgramCounter(0L)
}
case class ProgramCounter(value: Long) {
require (value >= 0L && value <= 0xffffffL)
}
object StatusRegister {
def apply(): StatusRegister = StatusRegister(false, false, false, false, false)
}
case class StatusRegister(C: Boolean, V: Boolean, Z: Boolean, N: Boolean, X: Boolean)
object CPUState {
val DataRegisterNumber = 8
val AddressRegisterNumber = 8
val FramePointerRegister = 6
val StackPointerRegister = 7
private def makeRegisterArray(num: Int): Vector[RegisterState] = {
val emptyRegState = RegisterState(0L)
Vector.fill[RegisterState](num)(emptyRegState)
}
def apply(): CPUState = CPUState(
CPUState.makeRegisterArray(CPUState.DataRegisterNumber),
CPUState.makeRegisterArray(CPUState.AddressRegisterNumber),
ProgramCounter(),
StatusRegister()
)
}
case class CPUState(
D: Vector[RegisterState],
A: Vector[RegisterState],
PC: ProgramCounter,
SR: StatusRegister
) {
private def updatedRegVector(vec: Vector[RegisterState], num: Int, size: Size, value: Long) =
vec.updated(num, vec(num).set(size, value))
def setD(number: Int, size: Size, value: Long) = {
require (number >= 0 && number < CPUState.DataRegisterNumber)
this.copy(D = updatedRegVector(D, number, size, value))
}
def setA(number: Int, size: Size, value: Long) = {
require (number >= 0 && number < CPUState.AddressRegisterNumber)
this.copy(A = updatedRegVector(A, number, size, value))
}
val FP = A(CPUState.FramePointerRegister)
def setFP(size: Size, value: Long) =
setA(CPUState.FramePointerRegister, size, value)
val SP = A(CPUState.StackPointerRegister)
def setSP(size: Size, value: Long) =
setA(CPUState.StackPointerRegister, size, value)
def setPC(value: Long) =
this.copy(PC = ProgramCounter(value))
def setSR(sr: StatusRegister) =
this.copy(SR = sr)
}
import com.github.utaal.m68k._
import com.github.utaal.m68k.ast._
import org.specs2._
class CPUStateSpec extends mutable.Specification {
"The immutable Register state class" should {
"correctly store values which fit in the emulated register" in {
val r0 = RegisterState(0xffffffffL)
r0.value must be_>(0L)
RegisterState(0x100000000L) must throwAn[IllegalArgumentException]
RegisterState(-1L) must throwAn[IllegalArgumentException]
}
"produce a new RegisterState when a value is set" in {
val r0 = RegisterState(0xdeadbeefL)
r0.set(Size.L, 0xfefefefeL).value must_== 0xfefefefeL
r0.set(Size.W, 0xfefeL).value must_== 0xdeadfefeL
r0.set(Size.B, 0xfeL).value must_== 0xdeadbefeL
r0.set(Size.B, 0xfefeL).value must_== 0xdeadbefeL
}
}
"The CPUState immutable class" should {
"correctly construct" in {
val st = CPUState()
val emptyStatusRegister = StatusRegister(false, false, false, false, false)
st must beLike {
case CPUState(dvec, avec, ProgramCounter(0L), emptyStatusRegister) => {
forall(dvec ++ avec) (_ must beLike { case RegisterState(0L) => ok })
dvec.length must_== CPUState.DataRegisterNumber
avec.length must_== CPUState.AddressRegisterNumber
}
}
}
"correctly produce new instances of itself on register change" in {
val st = CPUState()
val st1 = st.setD(3, Size.L, 0xdeadbeefL)
st1.D(3).value must_== 0xdeadbeefL
st1.setD(4, Size.L, 0xfefefefeL).D(3).value must_== 0xdeadbeefL
st.setA(CPUState.AddressRegisterNumber + 1, Size.L, 0) must throwAn[IllegalArgumentException]
st.setA(4, Size.W, 0xfefefefeL).A(4).value must_== 0xfefeL
st.setFP(Size.L, 0xafafafafL).FP.value must_== 0xafafafafL
st.setSP(Size.B, 0xaffa).SP.value must_== 0xfaL
}
}
}
import org.specs2._
import runner.SpecificationsFinder._
class index extends Specification { def is =
examplesLinks("M68k emulator specifications")
// see the SpecificationsFinder trait for the parameters of the 'specifications' method
def examplesLinks(t: String) = specifications().foldLeft(t.title) { (res, cur) => res ^ see(cur) }
}
package com.github.utaal.m68k
import com.github.utaal.m68k.ast._
case class Initialization(
val symbols: Map[String, Long],
val memAlloc: Map[Long, (Size, Long)]) {
def addSymbol(label: String, value: Long) = this.copy(symbols=symbols + (label -> value))
def addMemAlloc(addr: Long, size: Size, value: Long) =
this.copy(memAlloc=memAlloc + (addr -> (size, value)))
}
object Initialization {
def apply(): Initialization = Initialization(Map[String, Long](), Map[Long, (Size, Long)]())
private def withSymbol(init: Initialization, lbl: Option[String], pos: Long) =
(lbl map (l => init.addSymbol(l, pos))) getOrElse init
private def byteValue(literal: Literal, init: Initialization) = literal match {
case Literal.Int(litValue) => litValue.toLong
case Literal.Char(litChar) => litChar.toLong
case Literal.Label(lbl) => init.symbols get lbl getOrElse error("missing symbol")
}
private def lineFold(a: (Long, Initialization), line: Line) = {
val (pos, init) = a
import DirectiveLine._
line match {
case Equ(lbl, value) => (pos, init.addSymbol(lbl, value))
case DC(lbl, size, values) =>
((pos, withSymbol(init, lbl, pos)) /: values) { (b, value) =>
val (pos, init) = b
(pos + size.bytes, init.addMemAlloc(pos, size, byteValue(value, init)))
}
case DS(lbl, size, count) =>
(pos + (size.bytes * count), withSymbol(init, lbl, pos))
case End(_, _) => (pos, init)
case OpLine(_, _) => (pos, init)
}
}
def from(program: Program): Initialization =
(Initialization() /: program.sections) { case (init, Section(_, org, lines)) =>
((org.toLong, init) /: lines)(lineFold)._2
}
}
package com.github.utaal.log
import scala.annotation.elidable
abstract class Level(val value: Int) extends Ordered[Level] {
def compare(b: Level) = this.value compare b.value
val name = toString.toLowerCase
}
object Level {
val DEBUG = 100
case object Debug extends Level(DEBUG)
case object Finest extends Level(elidable.FINEST)
case object Finer extends Level(elidable.FINER)
case object Fine extends Level(elidable.FINE)
case object Config extends Level(elidable.CONFIG)
case object Info extends Level(elidable.INFO)
case object Warning extends Level(elidable.WARNING)
case object Severe extends Level(elidable.SEVERE)
}
object Logger {
var level = Level.Debug
private def curlyFormat(fmt: String, subs: List[Any]) =
(fmt /: subs.view.zipWithIndex) { (res, sub) =>
res.replaceAll("\\{%s\\}".format(sub._2), sub._1.toString)
}
def for_[Scope <: Singleton](scope: Scope) = {
val packageName = scope.asInstanceOf[AnyRef].getClass.getPackage match {
case null => null
case nonDefaultPackage => nonDefaultPackage.getName
}
new Logger(packageName, level)
}
}
class Logger(name: String, minLevel: Level) {
private def log(level: Level, fmt: String, subs: List[Any]) = {
if (level >= minLevel)
println("[" + level.name + "] " + name + ": " + Logger.curlyFormat(fmt, subs))
}
@elidable(Level.DEBUG)
/*def DEBUG(msg: String) = log(Level.Debug, msg)*/
def DEBUG(fmt: String, subs: Any*) = log(Level.Debug, fmt, subs.toList)
}
package com.github.utaal.m68k.ast
import com.github.utaal.m68k.Size
sealed trait Operand
sealed trait Register {
def number: Int
}
object Register {
case class Data(override val number: Int) extends Register
case class Address(override val number: Int) extends Register
}
sealed trait Literal
object Literal {
case class Int(value: scala.Int) extends Literal
case class Char(value: scala.Char) extends Literal
case class Label(name: String) extends Literal
}
sealed trait DataAddressing extends Operand
object DataAddressing {
case class Immediate(value: Literal) extends DataAddressing
sealed trait Direct extends DataAddressing {
def register: Register
}
object Direct {
case class Data(override val register: Register.Data) extends Direct
case class Address(override val register: Register.Address) extends Direct
}
case class Indirect(register: Register.Address) extends DataAddressing
case class IndirIncr(register: Register.Address) extends DataAddressing
case class IndirDecr(register: Register.Address) extends DataAddressing
case class IdxDispl(displ: Int, index: Register.Address) extends DataAddressing
case class IdxBaseDispl(
displ: Int, index: Register.Address, base: Register.Data, baseSize: Size.WL)
extends DataAddressing
sealed trait Absolute extends DataAddressing
object Absolute {
case class Address(addr: Int, size: Size.WL) extends Absolute
case class Label(label: String) extends Absolute
}
}
sealed trait InstructionAddressing extends Operand
object InstructionAddressing {
sealed trait Relative extends InstructionAddressing
object Relative {
case class Label(name: String) extends Relative
}
}
sealed trait Op {
def opcode: String
}
sealed trait SizedOp extends Op {
def size: Size
}
package DataOps {
class Binary(
val opcode: String, val size: Size, val src: DataAddressing, val dest: DataAddressing)
extends SizedOp {
override def toString =
"Binary(%s,%s,%s,%s)" format (opcode, size.toString, src.toString, dest.toString)
override def equals(other: Any): Boolean = other match {
case b:Binary => opcode == b.opcode && size == b.size && src == b.src && dest == b.dest
case _ => false
}
}
object Binary {
def apply(opcode: String, size: Size, src: DataAddressing, dest: DataAddressing) =
new Binary(opcode, size, src, dest)
}
case class MOVE(
override val size: Size,
override val src: DataAddressing,
override val dest: DataAddressing) extends Binary("MOVE", size, src, dest)
case class BinaryA(
override val opcode: String,
override val size: Size.WL,
override val src: DataAddressing,
override val dest: DataAddressing.Direct.Address) extends Binary(opcode, size, src, dest)
case class BinaryI(
override val opcode: String,
override val size: Size,
override val src: DataAddressing.Immediate,
override val dest: DataAddressing) extends Binary(opcode, size, src, dest)
}
package ControlOps {
case class Unary(
val opcode: String, val dest: InstructionAddressing) extends Op
}
sealed trait Line
case class Section(name: String, org: Int, lines: List[Line])
sealed trait DirectiveLine extends Line
object DirectiveLine {
case class Equ(label: String, value: Int) extends DirectiveLine
case class DC(label: Option[String], size: Size, values: List[Literal]) extends DirectiveLine
case class DS(label: Option[String], size: Size, count: Int) extends DirectiveLine
case class End(label: Option[String], entryPoint: String) extends DirectiveLine
}
case class OpLine(label: Option[String], op: Op) extends Line
case class Program(sections: List[Section])
package com.github.utaal.m68k
import com.github.utaal.m68k.ast._
import scala.util.parsing.combinator.RegexParsers
object M68kParser extends RegexParsers {
override val skipWhitespace = false
object basic {
def number = regex("""[+-]?[0-9]+""".r) ^^ { n => n.toInt }
def size = "." ~> """[BWL]""".r ^^ { s => s match {
case "B" => Size.B
case "W" => Size.W
case "L" => Size.L
} }
def wl = "." ~> """[WL]""".r ^^ { s => s match {
case "W" => Size.W
case "L" => Size.L
} }
def label = regex("""[a-zA-Z_][0-9a-zA-Z_]+""".r)
def eol = ws ~ rep(elem('\n'))
}
object literal extends Parser[Literal] {
def int = basic.number ^^ { n => Literal.Int(n) }
def char = elem('\'') ~> regex("""[A-Za-z]""".r) <~ elem('\'') ^^ {
c => Literal.Char(c.charAt(0))
}
def label = basic.label ^^ { lbl => Literal.Label(lbl) }
def apply(in: Input): ParseResult[Literal] = (int | char)(in)
}
def ws = rep(elem(' '))
object register {
def data = "D" ~> basic.number ^^ { n => Register.Data(n) }
def address = "A" ~> basic.number ^^ { n => Register.Address(n) }
}
object dataAddressing {
import DataAddressing._
def immediate = "#" ~> literal ^^ { case n => Immediate(n) }
def directData = register.data ^^ { s => Direct.Data(s) }
def directAddress = register.address ^^ { s => Direct.Address(s) }
def direct: Parser[Direct] = (directData | directAddress)
def indirect = "(" ~> register.address <~ ")" ^^ { s => Indirect(s) }
def indirIncr = "(" ~> register.address <~ ")+" ^^ { s => IndirIncr(s) }
def indirDecr = "-(" ~> register.address <~ ")" ^^ { s => IndirDecr(s) }
def idxDispl = basic.number ~ "(" ~ register.address ~ ")" ^^ {
case displ ~ _ ~ idx ~ _ => IdxDispl(displ, idx)
}
def idxBaseDispl =
basic.number ~ "(" ~ register.address ~ "," ~ ws ~ register.data ~ basic.wl ~ ")" ^^ {
case displ ~ _ ~ idx ~ _ ~ _ ~ reg ~ size ~ _ => IdxBaseDispl(displ, idx, reg, size)
}
def absoluteAddress = basic.number ~ basic.wl ^^ {
case addr ~ size => Absolute.Address(addr, size)
}
def absoluteLabel = basic.label ^^ { lbl => Absolute.Label(lbl) }
def absolute = absoluteAddress | absoluteLabel
}
object instructionAddressing {
import InstructionAddressing._
def relativeLabel = basic.label ^^ { lbl => Relative.Label(lbl) }
}
object dataOp extends Parser[SizedOp] {
import DataOps._
import dataAddressing._
private def opcode[S <: Size](opcodes: Parser[String], size: Parser[S]) = opcodes ~ size ^^ {
case opcode ~ size => (opcode, size)
}
private def nonImmediateData =
directData | indirect | indirIncr | indirDecr | idxDispl | idxBaseDispl | absolute
private def any =
indirDecr | indirIncr | immediate | direct | indirect | idxDispl | idxBaseDispl | absolute
private def twoParams[P1, P2](fst: Parser[P1], snd: Parser[P2]) =
ws ~ fst ~ elem(',') ~ ws ~ snd ^^ { case _ ~ fst ~ _ ~ _ ~ snd => (fst, snd) }
def move = opcode("MOVE", basic.size) ~ twoParams(any, nonImmediateData) ^^ {
case ((opcode, size)) ~ ((fst, snd)) => MOVE(size, fst, snd)
}
def binaryAOpcodes = "MOVEA" | "ADDA" | "SUBA" | "CMPA"
def binaryA =
opcode(binaryAOpcodes, basic.wl) ~ twoParams(any, directAddress) ^^ {
case ((opcode, size)) ~ ((fst, snd)) => BinaryA(opcode, size, fst, snd)
}
def binaryAluOpcodes = "ADD" | "SUB" | "AND" | "OR" | "CMP"
def binaryAluSrcData =
opcode(binaryAluOpcodes, basic.size) ~ twoParams(directData, nonImmediateData) ^^ {
case ((opcode, size)) ~ ((fst, snd)) => Binary(opcode, size, fst, snd)
}
def binaryAluDestData =
opcode(binaryAluOpcodes, basic.size) ~ twoParams(nonImmediateData, directData) ^^ {
case ((opcode, size)) ~ ((fst, snd)) => Binary(opcode, size, fst, snd)
}
def binaryIOpcodes = "ADDI" | "SUBI" | "CMPI" | "ANDI" | "ORI"
def binaryI =
opcode(binaryIOpcodes, basic.size) ~ twoParams(immediate, nonImmediateData) ^^ {
case ((opcode, size)) ~ ((fst, snd)) => BinaryI(opcode, size, fst, snd)
}
def apply(in: Input): ParseResult[SizedOp] =
(move | binaryA | binaryAluSrcData | binaryAluDestData | binaryI)(in)
}
object controlOp extends Parser[Op] {
import ControlOps._
import instructionAddressing._
def jumpBraOpcodes = "JMP" | "BRA" | "BEQ" | "BNE" | "BLT" | "BGT" | "BLE" | "BGE"
def jumpBra = jumpBraOpcodes ~ ws ~ relativeLabel ^^ {
case opcode ~ _ ~ lbl => Unary(opcode, lbl)
}
def apply(in: Input): ParseResult[Op] = (jumpBra)(in)
}
def op = dataOp | controlOp
def lineLabel = basic.label <~ ":"
def opLine = (lineLabel?) ~ ws ~ op ^^ { case lbl ~ _ ~ op => OpLine(lbl, op) }
object directiveLine extends Parser[DirectiveLine] {
import DirectiveLine._
def end = (lineLabel?) ~ ws ~ "END" ~ ws ~ basic.label ^^ {
case lineLbl ~ _ ~ _ ~ _ ~ lbl => End(lineLbl, lbl)
}
def equ = lineLabel ~ ws ~ "EQU" ~ ws ~ basic.number ^^ {
case lineLbl ~ _ ~ _ ~ _ ~ num => Equ(lineLbl, num)
}
private def dcValues = (literal ~ rep((ws ~ "," ~ ws) ~> literal)) ^^ { case l ~ ll => l :: ll }
def dc = (lineLabel?) ~ ws ~ "DC" ~ basic.size ~ ws ~ dcValues ^^ {
case lineLbl ~ _ ~ _ ~ size ~ _ ~ values => DC(lineLbl, size, values)
}
def ds = (lineLabel?) ~ ws ~ "DS" ~ basic.size ~ ws ~ basic.number ^^ {
case lineLbl ~ _ ~ _ ~ size ~ _ ~ num => DS(lineLbl, size, num)
}
def apply(in: Input): ParseResult[DirectiveLine] = (end | equ | dc | ds)(in)
}
def line = opLine | directiveLine
def sectionHeader = "SECTION" ~ ws ~ basic.label ~ basic.eol ^^ {
case _ ~ _ ~ name ~ _ => name
}
def orgHeader = "ORG" ~ ws ~ basic.number ~ basic.eol ^^ {
case _ ~ _ ~ num ~ _ => num
}
def section = sectionHeader ~ orgHeader ~ rep(line <~ basic.eol) ^^ {
case sectionName ~ org ~ lines => Section(sectionName, org, lines)
}
def program = rep(section) ^^ { s => Program(s) }
def parseProgram(in: String): Either[String, Program] = parseAll(program, in + "\n") match {
case Success(res, _) => Right(res)
case f:Failure => Left(f.toString())
}
}
import com.github.utaal.m68k._
import com.github.utaal.m68k.ast._
import org.specs2._
class M68kParserSpec extends mutable.Specification {
implicit def ParsableString(a: String) = new {
import com.github.utaal.m68k.{M68kParser => pp}
private def parseMustEqual[T](parser: pp.Parser[T])(exp: T) =
pp.parseAll(parser, a) must beLike {
case pp.Success(res, _) => res must_== exp
}
def parsesAs(opLine: OpLine) = parseMustEqual(pp.opLine)(opLine)
def parsesAs(dirLine: DirectiveLine) = parseMustEqual(pp.directiveLine)(dirLine)
def parsesAs(section: Section) = parseMustEqual(pp.section)(section)
}
"The M68kParser.opLine production" should {
"accept instructions with immediate values as operand" in {
"ADDI.W #3, D2" parsesAs OpLine(None, DataOps.BinaryI(
"ADDI",
Size.W,
DataAddressing.Immediate(Literal.Int(3)),
DataAddressing.Direct.Data(Register.Data(2))
))
}
"accept instr. with register addressing as operand(s)" in {
"MOVE.L (A0), D2" parsesAs OpLine(None, DataOps.MOVE(
Size.L,
DataAddressing.Indirect(Register.Address(0)),
DataAddressing.Direct.Data(Register.Data(2))
))
"MOVEA.W -(A0), A3" parsesAs OpLine(None, DataOps.BinaryA(
"MOVEA",
Size.W,
DataAddressing.IndirDecr(Register.Address(0)),
DataAddressing.Direct.Address(Register.Address(3))
))
"SUB.L 2(A3, D2.W), D3" parsesAs OpLine(None, DataOps.Binary(
"SUB",
Size.L,
DataAddressing.IdxBaseDispl(2, Register.Address(3), Register.Data(2), Size.W),
DataAddressing.Direct.Data(Register.Data(3))
))
}
"accept instructions with absolute addressing as operand(s)" in {
"MOVE.L ll1, D0" parsesAs OpLine(None, DataOps.MOVE(
Size.L,
DataAddressing.Absolute.Label("ll1"),
DataAddressing.Direct.Data(Register.Data(0))
))
}
"accept unary control ops" in {
"JMP label" parsesAs OpLine(None, ControlOps.Unary(
"JMP",
InstructionAddressing.Relative.Label("label")
))
}
"accept labeled lines" in {
"lbl: BGE abc" parsesAs OpLine(Some("lbl"), ControlOps.Unary(
"BGE",
InstructionAddressing.Relative.Label("abc")
))
}
}
"The M68kParser.directiveLine production" should {
"accept valid directives" in {
"end_while: END begin" parsesAs DirectiveLine.End(Some("end_while"), "begin")
"aa: EQU 4" parsesAs DirectiveLine.Equ("aa", 4)
"vec: DS.W 3" parsesAs DirectiveLine.DS(Some("vec"), Size.W, 3)
"hw: DC.B 'h', 'w'" parsesAs DirectiveLine.DC(
Some("hw"), Size.B, Literal.Char('h') :: Literal.Char('w') :: Nil
)
}
}
"The M68kParser.section production" should {
"accept valid program sections" in {
"""|SECTION CODE
|ORG 100
|begin: END begin
|""".stripMargin parsesAs(
Section("CODE", 100, DirectiveLine.End(Some("begin"), "begin") :: Nil)
)
}
}
"The M68k parser" should {
"accept an entire program" in {
val program =
"""|SECTION DATA
|ORG 100
|d1: DS.W 3
|SECTION CODE
|ORG 200
|begin: MOVE.W d1, D0
|END CODE""".stripMargin
M68kParser.parseProgram(program) should beLike {
case Right(p) => p must_== Program(
Section("DATA", 100,
DirectiveLine.DS(Some("d1"), Size.W, 3) ::
Nil
) ::
Section("CODE", 200,
OpLine(Some("begin"), DataOps.MOVE(Size.W, DataAddressing.Absolute.Label("d1"),
DataAddressing.Direct.Data(Register.Data(0)))) ::
DirectiveLine.End(None, "CODE") ::
Nil
) ::
Nil
)
}
}
}
}
package com.github.utaal.m68k
import com.github.utaal.m68k.ast._
trait Memory {
def memSize: Long
def set(size: Size, addr: Long, value: Long): Memory
def get(size: Size, addr: Long): Long
def dump: String
}
object LinearMemory {
val log = com.github.utaal.log.Logger.for_(this)
}
class LinearMemory private (override val memSize: Long, memory: Vector[Char]) extends Memory {
import LinearMemory.log._
require(memSize > 0 && memSize <= Int.MaxValue)
def this(memSize: Long) =
this(memSize, Vector.fill(memSize.toInt)(0x00))
private def getNumChars(addr: Int, num: Int) = {
val slice = memory.slice(addr, addr + num)
(slice.head.toLong /: slice.tail)((c, i) => (c << 8) | i)
}
def get(size: Size, addr: Long) = {
require(addr > 0 && addr <= memSize)
getNumChars(addr.toInt, size.bytes)
}
def set(size: Size, addr: Long, value: Long) = {
require(addr > 0 && addr <= memSize)
require(value > 0 && value <= 0xffffffffL)
val len = size.bytes
val vec = Vector.tabulate[Char](len)(i => ((value >> (8 * (len - 1 - i))) & 0xffL).toChar)
new LinearMemory(memSize, memory.patch(addr.toInt, vec, size.bytes))
}
def dump: String = {
def hexString(grp: Seq[Char]) = grp.map("%02X" format _.toInt).mkString(" ")
def printableString(grp: Seq[Char]) =
grp.map { x => if (x.isLetterOrDigit) x else "?" }.mkString("")
memory.view.grouped(8).zipWithIndex.filter(_._1.exists(_ != 0)).map {
case (x, i) => ("%8d" format (i * 8)) + " " + hexString(x) + " " + printableString(x)
} mkString "\n"
}
}
import com.github.utaal.m68k._
import com.github.utaal.m68k.ast._
import org.specs2._
class MemorySpec extends mutable.Specification {
"Memory" should {
"create new immutable instances on set and match" in {
val mem: Memory = new LinearMemory(1000L)
val m1 = mem.set(Size.B, 100L, 0xfeL)
m1.get(Size.B, 100L) should_== 0xfeL
m1.get(Size.B, 101L) should_== 0x00L
val m2 = mem.set(Size.L, 100L, 0xdeadbeefL)
m2.get(Size.L, 100L) should_== 0xdeadbeefL
m2.get(Size.W, 100L) should_== 0xdeadL
m2.get(Size.W, 102L) should_== 0xbeefL
m2.get(Size.B, 101L) should_== 0xadL
m2.get(Size.B, 105L) should_== 0x00L
}
}
}
package com.github.utaal.m68k
sealed trait Size {
def bytes: Int
}
object Size {
sealed trait WL extends Size
case object B extends Size with WL {
val bytes = 1
}
case object W extends Size with WL {
val bytes = 2
}
case object L extends Size with WL {
val bytes = 4
}
}
package com.github.utaal.m68k
object util {
def describe[T <: AnyRef](t: T)(implicit m: scala.reflect.Manifest[T]) =
println("t was " + t.toString + " of class " + t.getClass.getName() + ", erased from " + m.erasure)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment