1
0
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:
Karol Stasiak 2018-04-02 21:40:54 +02:00
parent d6995091cf
commit 0ddf2f31c8
12 changed files with 69 additions and 8 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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) =>

View File

@ -37,6 +37,7 @@ object ReverseFlowAnalyzerPerOpcode {
COP -> (_ => finalImportance),
RTS -> (_ => finalImportance),
RTL -> (_ => finalImportance),
BYTE -> (_ => finalImportance),
RTI -> (_ => CpuImportance(
a = Unimportant, ah = Unimportant,

View File

@ -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{

View File

@ -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)) =>

View File

@ -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]
}

View File

@ -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, _, _) =>

View File

@ -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]] = {

View File

@ -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)) }

View File

@ -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))
}
}