1
0
mirror of https://github.com/KarolS/millfork.git synced 2025-02-21 21:29:00 +00:00

Allow refering to labels from other functions in assembly (fixes #101)

This commit is contained in:
Karol Stasiak 2021-02-24 02:32:00 +01:00
parent ff6106a838
commit 196ad6542f
7 changed files with 123 additions and 22 deletions

View File

@ -52,6 +52,17 @@ Global label names have to start with a letter and can contain digits, underscor
Local label names (available since Millfork 0.3.22) start with a period and are visible only in the given function.
Anonymous labels designated with `+` or `-` are also not supported.
Referring to a global label with an offset requires wrapping it in `label(…)`:
STA .local_opcode // ok
STA label(.local_opcode) // ok
STA .local_opcode + 1 // ok
STA label(.local_opcode) + 1 // ok
STA global_opcode // ok
STA label(global_opcode) // ok
STA global_opcode + 1 // NOT OK
sta label(global_opcode) + 1 // ok
Assembly can refer to variables and constants defined in Millfork,
but you need to be careful with using absolute vs immediate addressing:

View File

@ -27,6 +27,17 @@ Global label names have to start with a letter and can contain digits, underscor
Local label names (available since Millfork 0.3.22) start with a period and are visible only in the given function.
Anonymous labels designated with `+` or `-` are also not supported.
Referring to a global label with an offset requires wrapping it in `label(…)`:
STA .local_opcode // ok
STA label(.local_opcode) // ok
STA .local_opcode + 1 // ok
STA label(.local_opcode) + 1 // ok
STA global_opcode // ok
STA label(global_opcode) // ok
STA global_opcode + 1 // NOT OK
STA label(global_opcode) + 1 // ok
Assembly can refer to variables and constants defined in Millfork,
but you need to be careful with using absolute vs immediate addressing:

View File

@ -52,6 +52,17 @@ Global label names have to start with a letter and can contain digits, underscor
Local label names (available since Millfork 0.3.22) start with a period and are visible only in the given function.
Anonymous labels designated with `+` or `-` are also not supported.
Referring to a global label with an offset requires wrapping it in `label(…)`:
LD (.local_opcode),A // ok
LD (label(.local_opcode)),A // ok
LD (.local_opcode + 1),A // ok
LD (label(.local_opcode) + 1),A // ok
LD (global_opcode),A // ok
LD (label(global_opcode)),A // ok
LD (global_opcode + 1),A // NOT OK
LD (label(global_opcode) + 1),A // ok
Assembly can refer to variables and constants defined in Millfork,
but you need to be careful with using absolute vs immediate addressing:

View File

@ -6,7 +6,7 @@ import millfork.assembly.m6809.{Inherent, MLine, MOpcode, NonExistent}
import millfork.compiler.{AbstractCompiler, AbstractExpressionCompiler, AbstractStatementCompiler, BranchSpec, CompilationContext}
import millfork.node.{Assignment, BlackHoleExpression, BreakStatement, ContinueStatement, DoWhileStatement, EmptyStatement, ExecutableStatement, Expression, ExpressionStatement, ForEachStatement, ForStatement, FunctionCallExpression, GotoStatement, IfStatement, LabelStatement, LiteralExpression, M6809AssemblyStatement, MemsetStatement, ReturnDispatchStatement, ReturnStatement, VariableExpression, WhileStatement}
import millfork.assembly.m6809.MOpcode._
import millfork.env.{BooleanType, ConstantBooleanType, FatBooleanType, Label, MemoryAddressConstant, StructureConstant, ThingInMemory}
import millfork.env.{BooleanType, Constant, ConstantBooleanType, FatBooleanType, Label, MemoryAddressConstant, StructureConstant, ThingInMemory}
/**
* @author Karol Stasiak
@ -58,12 +58,21 @@ object M6809StatementCompiler extends AbstractStatementCompiler[MLine] {
}
(eval ++ epilogue ++ rts) -> Nil
case M6809AssemblyStatement(opcode, addrMode, expression, elidability) =>
ctx.env.evalForAsm(expression, opcode) match {
case Some(e) => List(MLine(opcode, addrMode, e, elidability)) -> Nil
val e = ctx.env.evalForAsm(expression) match {
case Some(e) => e
case None =>
println(statement)
???
expression match {
case VariableExpression(name) =>
env.maybeGet[ThingInMemory](name).map(_.toAddress).getOrElse {
val fqName = if (name.startsWith(".")) env.prefix + name else name
MemoryAddressConstant(Label(fqName))
}
case _ =>
ctx.log.error("Invalid parameter", statement.position)
Constant.Zero
}
}
List(MLine(opcode, addrMode, e, elidability)) -> Nil
case Assignment(destination, source) =>
if (destination == BlackHoleExpression) return M6809ExpressionCompiler.compile(ctx, source, MExpressionTarget.NOTHING, BranchSpec.None) -> Nil
val destinationType = AbstractExpressionCompiler.getExpressionType(ctx, destination)
@ -107,13 +116,22 @@ object M6809StatementCompiler extends AbstractStatementCompiler[MLine] {
case s:ContinueStatement =>
compileContinueStatement(ctx, s) -> Nil
case M6809AssemblyStatement(opcode, addrMode, expression, elidability) =>
ctx.env.evalForAsm(expression, opcode) match {
case Some(param) =>
List(MLine(opcode, addrMode, param, elidability)) -> Nil
val silent = MOpcode.Branching(opcode) || opcode == LABEL || opcode == CHANGED_MEM
val param: Constant = ctx.env.evalForAsm(expression, silent = silent) match {
case Some(param) => param
case None =>
ctx.log.error("Invalid parameter", expression.position)
Nil -> Nil
expression match {
case VariableExpression(name) =>
env.maybeGet[ThingInMemory](name).map(_.toAddress).getOrElse{
val fqName = if (name.startsWith(".")) env.prefix + name else name
MemoryAddressConstant(Label(fqName))
}
case _ =>
ctx.log.error("Invalid parameter", expression.position)
Constant.Zero
}
}
List(MLine(opcode, addrMode, param, elidability)) -> Nil
case s:LabelStatement =>
List(MLine.label(env.prefix + s.name)) -> Nil
case s: GotoStatement =>

View File

@ -342,11 +342,10 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
x match {
// TODO: hmmm
case VariableExpression(name) =>
if (OpcodeClasses.ShortBranching(o) || o == JMP || o == LABEL || o == CHANGED_MEM || OpcodeClasses.HudsonTransfer(o)) {
val silent = OpcodeClasses.ShortBranching(o) || o == JMP || o == LABEL || o == CHANGED_MEM || OpcodeClasses.HudsonTransfer(o)
env.evalForAsm(x, silent = silent).orElse(env.maybeGet[ThingInMemory](name).map(_.toAddress)).getOrElse{
val fqName = if (name.startsWith(".")) env.prefix + name else name
MemoryAddressConstant(Label(fqName))
} else {
env.evalForAsm(x).getOrElse(env.get[ThingInMemory](name, x.position).toAddress)
}
case FunctionCallExpression("byte_and_pointer$", List(z, b@VariableExpression(name))) =>
StructureConstant(env.get[StructType]("byte_and_pointer$"), List(

View File

@ -973,15 +973,15 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
}
}
def evalForAsm(e: Expression, op: MOpcode.Value = MOpcode.NOP, silent: Boolean = false): Option[Constant] = {
def evalForAsm(e: Expression, silent: Boolean = false): Option[Constant] = {
e match {
// TODO: hmmm
case VariableExpression(name) =>
import MOpcode._
if (MOpcode.Branching(op) || op == LABEL || op == CHANGED_MEM) {
val fqName = if (name.startsWith(".")) prefix + name else name
return Some(MemoryAddressConstant(Label(fqName)))
}
case FunctionCallExpression("label", List(VariableExpression(name))) if (!name.contains(".")) =>
return Some(Label(name).toAddress)
case FunctionCallExpression("label", List(VariableExpression(name))) if (name.startsWith(".")) =>
return Some(Label(prefix + name).toAddress)
case FunctionCallExpression("label", _) =>
log.error("Invalid label reference", e.position)
return Some(Constant.Zero)
case _ =>
}
e match {
@ -994,7 +994,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
case None =>
if (name.startsWith(".")) Some(Label(prefix + name).toAddress)
else {
if (!silent) log.warn(s"$name is not known", e.position)
if (!silent) log.warn(s"$name is not known. If it is a label, consider wrapping it in label(...).", e.position)
None
}
}

View File

@ -28,6 +28,7 @@ class AssemblySuite extends FunSuite with Matchers with AppendedClues {
| // w&x
| asm void main () {
| lda #3
| sta label(.l)+1
| sta .l+1
| .l: lda #55
| sta output
@ -36,6 +37,56 @@ class AssemblySuite extends FunSuite with Matchers with AppendedClues {
""".stripMargin)(_.readByte(0xc000) should equal(3))
}
test("Self-modifying assembly 2") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Motorola6809)(
"""
| byte output @$c000
| // w&x
| asm void f() {
| f_data:
| lda #$ff
| rts
| }
|
| asm void main () {
| lda f_data
| sta f_data
| lda #3
| sta label(f_data)+1
| lda #0
| jsr f_data
| sta output
| lda #lo(f)
| rts
| ignored:}
""".stripMargin)(_.readByte(0xc000) should equal(3))
}
test("Self-modifying assembly 2 (Z80)") {
EmuCrossPlatformBenchmarkRun(Cpu.Z80)(
"""
| byte output @$c000
| // w&x
| asm void f() {
| f_data:
| ld a, $ff
| ret
| }
|
| asm void main () {
| ld a,(f_data)
| ld (f_data),a
| ld a,3
| ld (label(f_data)+1),a
| ld a,0
| call f_data
| ld (output),a
| ld b,lo(f)
| ret
| ignored:}
""".stripMargin)(_.readByte(0xc000) should equal(3))
}
test("Assembly functions") {
EmuBenchmarkRun(
"""