mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-09 09:41:23 +00:00
Inserting raw byte sequences in assembly
This commit is contained in:
parent
d6995091cf
commit
0ddf2f31c8
@ -68,8 +68,12 @@ You can insert macros into assembly, by prefixing them with `+` and using the sa
|
||||
}
|
||||
}
|
||||
|
||||
Currently there is no way to insert raw bytes into inline assembly
|
||||
(required for certain optimizations and calling conventions).
|
||||
You can insert raw bytes into your assembly using the array syntax:
|
||||
|
||||
[ $EA, $EA ]
|
||||
"this is a string to print" apple2
|
||||
["this is a string to print but this time it's zero-terminated so it will actually work" apple2, 0]
|
||||
[for x,0,until,8 [x]]
|
||||
|
||||
## Assembly functions
|
||||
|
||||
|
@ -522,7 +522,7 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
|
||||
}
|
||||
|
||||
def sizeInBytes: Int = addrMode match {
|
||||
case Implied => 1
|
||||
case Implied | RawByte => 1
|
||||
case Relative | ZeroPageX | ZeroPage | ZeroPageY | IndexedZ | IndexedX | IndexedY | IndexedSY | Stack | LongIndexedY | LongIndexedZ | Immediate => 2
|
||||
case AbsoluteIndexedX | AbsoluteX | Absolute | AbsoluteY | Indirect | LongRelative | WordImmediate => 3
|
||||
case LongAbsolute | LongAbsoluteX | LongIndirect => 4
|
||||
@ -530,7 +530,7 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
|
||||
}
|
||||
|
||||
def cost: Int = addrMode match {
|
||||
case Implied => 1000
|
||||
case Implied | RawByte => 1000
|
||||
case Relative | Immediate => 2000
|
||||
case ZeroPage => 2001
|
||||
case Stack | ZeroPageX | ZeroPageY => 2002
|
||||
@ -552,6 +552,8 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
|
||||
override def toString: String =
|
||||
if (opcode == LABEL) {
|
||||
parameter.toString
|
||||
} else if (opcode == BYTE) {
|
||||
" !byte " + parameter.toString
|
||||
} else if (addrMode == DoesNotExist) {
|
||||
s" ; $opcode"
|
||||
} else {
|
||||
|
@ -121,7 +121,7 @@ object Opcode extends Enumeration {
|
||||
PHX_W, PHY_W, PLY_W, PLX_W,
|
||||
|
||||
DISCARD_AF, DISCARD_XF, DISCARD_YF,
|
||||
LABEL = Value
|
||||
BYTE, LABEL = Value
|
||||
|
||||
def widen(opcode: Opcode.Value): Option[Opcode.Value] = opcode match {
|
||||
case ORA => Some(ORA_W)
|
||||
@ -327,6 +327,7 @@ object AddrMode extends Enumeration {
|
||||
AbsoluteIndexedX,
|
||||
TripleAbsolute,
|
||||
Undecided,
|
||||
RawByte,
|
||||
DoesNotExist = Value
|
||||
|
||||
|
||||
|
@ -40,7 +40,7 @@ object CoarseFlowAnalyzer {
|
||||
case _ => None
|
||||
}).fold(emptyStatus)(_ ~ _)
|
||||
|
||||
case AssemblyLine(JSR, _, _, _) =>
|
||||
case AssemblyLine(JSR | BYTE, _, _, _) =>
|
||||
currentStatus = initialStatus
|
||||
|
||||
case AssemblyLine(op, Implied, _, _) if FlowAnalyzerForImplied.hasDefinition(op) =>
|
||||
|
@ -37,6 +37,7 @@ object ReverseFlowAnalyzerPerOpcode {
|
||||
COP -> (_ => finalImportance),
|
||||
RTS -> (_ => finalImportance),
|
||||
RTL -> (_ => finalImportance),
|
||||
BYTE -> (_ => finalImportance),
|
||||
|
||||
RTI -> (_ => CpuImportance(
|
||||
a = Unimportant, ah = Unimportant,
|
||||
|
@ -24,6 +24,7 @@ object MacroExpander {
|
||||
def gx[T <: ExecutableStatement](s:T) = replaceVariable(s, paramName, target).asInstanceOf[ExecutableStatement]
|
||||
def h(s:String) = if (s == paramName) target.asInstanceOf[VariableExpression].name else s
|
||||
stmt match {
|
||||
case RawBytesStatement(contents) => RawBytesStatement(contents.replaceVariable(paramName, target))
|
||||
case ExpressionStatement(e) => ExpressionStatement(e.replaceVariable(paramName, target))
|
||||
case ReturnStatement(e) => ReturnStatement(e.map(f))
|
||||
case ReturnDispatchStatement(i,ps, bs) => ReturnDispatchStatement(i.replaceVariable(paramName, target), ps.map(fx), bs.map{
|
||||
|
@ -133,6 +133,15 @@ object StatementCompiler {
|
||||
case _ => a
|
||||
}
|
||||
List(AssemblyLine(o, actualAddrMode, c, e))
|
||||
case RawBytesStatement(contents) =>
|
||||
env.extractArrayContents(contents).map { expr =>
|
||||
env.eval(expr) match {
|
||||
case Some(c) => AssemblyLine(BYTE, RawByte, c, elidable = false)
|
||||
case None =>
|
||||
ErrorReporting.error("Non-constant raw byte", position = statement.position)
|
||||
AssemblyLine(BYTE, RawByte, Constant.Zero, elidable = false)
|
||||
}
|
||||
}
|
||||
case Assignment(dest, source) =>
|
||||
ExpressionCompiler.compileAssignment(ctx, source, dest)
|
||||
case ExpressionStatement(e@FunctionCallExpression(name, params)) =>
|
||||
|
@ -106,18 +106,33 @@ case class VariableDeclarationStatement(name: String,
|
||||
|
||||
trait ArrayContents extends Node {
|
||||
def getAllExpressions: List[Expression]
|
||||
def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents
|
||||
}
|
||||
|
||||
case class LiteralContents(contents: List[Expression]) extends ArrayContents {
|
||||
override def getAllExpressions: List[Expression] = contents
|
||||
|
||||
override def replaceVariable(variable: String, expression: Expression): ArrayContents =
|
||||
LiteralContents(contents.map(_.replaceVariable(variable, expression)))
|
||||
}
|
||||
|
||||
case class ForLoopContents(variable: String, start: Expression, end: Expression, direction: ForDirection.Value, body: ArrayContents) extends ArrayContents {
|
||||
override def getAllExpressions: List[Expression] = start :: end :: body.getAllExpressions.map(_.replaceVariable(variable, LiteralExpression(0, 1)))
|
||||
|
||||
override def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents =
|
||||
if (variableToReplace == variable) this else ForLoopContents(
|
||||
variable,
|
||||
start.replaceVariable(variableToReplace, expression),
|
||||
end.replaceVariable(variableToReplace, expression),
|
||||
direction,
|
||||
body.replaceVariable(variableToReplace, expression))
|
||||
}
|
||||
|
||||
case class CombinedContents(contents: List[ArrayContents]) extends ArrayContents {
|
||||
override def getAllExpressions: List[Expression] = contents.flatMap(_.getAllExpressions)
|
||||
|
||||
override def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents =
|
||||
CombinedContents(contents.map(_.replaceVariable(variableToReplace, expression)))
|
||||
}
|
||||
|
||||
case class ArrayDeclarationStatement(name: String,
|
||||
@ -152,6 +167,10 @@ case class FunctionDeclarationStatement(name: String,
|
||||
|
||||
sealed trait ExecutableStatement extends Statement
|
||||
|
||||
case class RawBytesStatement(contents: ArrayContents) extends ExecutableStatement {
|
||||
override def getAllExpressions: List[Expression] = contents.getAllExpressions
|
||||
}
|
||||
|
||||
sealed trait CompoundStatement extends ExecutableStatement {
|
||||
def getChildStatements: Seq[Statement]
|
||||
}
|
||||
|
@ -418,6 +418,11 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
|
||||
assOut.append(instr.toString)
|
||||
}
|
||||
instr match {
|
||||
case AssemblyLine(BYTE, RawByte, c, _) =>
|
||||
writeByte(bank, index, c)
|
||||
index += 1
|
||||
case AssemblyLine(BYTE, _, _, _) => ???
|
||||
case AssemblyLine(_, RawByte, _, _) => ???
|
||||
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(labelName)), _) =>
|
||||
labelMap(labelName) = index
|
||||
case AssemblyLine(_, DoesNotExist, _, _) =>
|
||||
|
@ -87,7 +87,7 @@ object InliningCalculator {
|
||||
case _ => Nil
|
||||
}
|
||||
|
||||
private val badOpcodes = Set(RTI, RTS, JSR, BRK, RTL, BSR) ++ OpcodeClasses.ChangesStack
|
||||
private val badOpcodes = Set(RTI, RTS, JSR, BRK, RTL, BSR, BYTE) ++ OpcodeClasses.ChangesStack
|
||||
private val jumpingRelatedOpcodes = Set(LABEL, JMP) ++ OpcodeClasses.ShortBranching
|
||||
|
||||
def codeForInlining(fname: String, functionsAlreadyKnownToBeNonInlineable: Set[String], code: List[AssemblyLine]): Option[List[AssemblyLine]] = {
|
||||
|
@ -244,6 +244,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
|
||||
TextCodec.Ascii
|
||||
}
|
||||
|
||||
// TODO: should reserve the `file` identifier here?
|
||||
def arrayFileContents: P[ArrayContents] = for {
|
||||
p <- "file" ~ HWS ~/ "(" ~/ HWS ~/ position()
|
||||
filePath <- doubleQuotedString ~/ HWS
|
||||
@ -283,6 +284,8 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
|
||||
|
||||
def arrayContents: P[ArrayContents] = arrayListContents | arrayLoopContents | arrayFileContents | arrayStringContents
|
||||
|
||||
def arrayContentsForAsm: P[RawBytesStatement] = (arrayListContents | arrayStringContents).map(RawBytesStatement)
|
||||
|
||||
def arrayDefinition: P[Seq[ArrayDeclarationStatement]] = for {
|
||||
p <- position()
|
||||
bank <- bankDeclaration
|
||||
@ -440,7 +443,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
|
||||
|
||||
def asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall).map(ExpressionStatement)
|
||||
|
||||
def asmStatement: P[ExecutableStatement] = (position("assembly statement") ~ P(asmLabel | asmMacro | asmInstruction)).map { case (p, s) => s.pos(p) } // TODO: macros
|
||||
def asmStatement: P[ExecutableStatement] = (position("assembly statement") ~ P(asmLabel | asmMacro | arrayContentsForAsm | asmInstruction)).map { case (p, s) => s.pos(p) } // TODO: macros
|
||||
|
||||
def statement: P[Seq[Statement]] = (position() ~ P(keywordStatement | variableDefinition(false) | expressionStatement)).map { case (p, s) => s.map(_.pos(p)) }
|
||||
|
||||
|
@ -147,4 +147,20 @@ class AssemblySuite extends FunSuite with Matchers {
|
||||
| }
|
||||
""".stripMargin)(_.readByte(0xc000) should equal(10))
|
||||
}
|
||||
|
||||
test("Inline raw bytes") {
|
||||
EmuBenchmarkRun(
|
||||
"""
|
||||
| byte output @$c000
|
||||
| asm void main () {
|
||||
| ? LDA #10
|
||||
| [ for x,0,until,8 [$EA] ]
|
||||
| [ $8d, 0, $c0]
|
||||
| JMP cc
|
||||
| "stuff" ascii
|
||||
| cc:
|
||||
| RTS
|
||||
| }
|
||||
""".stripMargin)(_.readByte(0xc000) should equal(10))
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user