mirror of
https://github.com/KarolS/millfork.git
synced 2025-02-23 03:28:57 +00:00
Allow refering to labels from other functions in assembly (fixes #101)
This commit is contained in:
parent
ff6106a838
commit
196ad6542f
@ -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.
|
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.
|
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,
|
Assembly can refer to variables and constants defined in Millfork,
|
||||||
but you need to be careful with using absolute vs immediate addressing:
|
but you need to be careful with using absolute vs immediate addressing:
|
||||||
|
|
||||||
|
@ -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.
|
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.
|
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,
|
Assembly can refer to variables and constants defined in Millfork,
|
||||||
but you need to be careful with using absolute vs immediate addressing:
|
but you need to be careful with using absolute vs immediate addressing:
|
||||||
|
|
||||||
|
@ -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.
|
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.
|
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,
|
Assembly can refer to variables and constants defined in Millfork,
|
||||||
but you need to be careful with using absolute vs immediate addressing:
|
but you need to be careful with using absolute vs immediate addressing:
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import millfork.assembly.m6809.{Inherent, MLine, MOpcode, NonExistent}
|
|||||||
import millfork.compiler.{AbstractCompiler, AbstractExpressionCompiler, AbstractStatementCompiler, BranchSpec, CompilationContext}
|
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.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.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
|
* @author Karol Stasiak
|
||||||
@ -58,12 +58,21 @@ object M6809StatementCompiler extends AbstractStatementCompiler[MLine] {
|
|||||||
}
|
}
|
||||||
(eval ++ epilogue ++ rts) -> Nil
|
(eval ++ epilogue ++ rts) -> Nil
|
||||||
case M6809AssemblyStatement(opcode, addrMode, expression, elidability) =>
|
case M6809AssemblyStatement(opcode, addrMode, expression, elidability) =>
|
||||||
ctx.env.evalForAsm(expression, opcode) match {
|
val e = ctx.env.evalForAsm(expression) match {
|
||||||
case Some(e) => List(MLine(opcode, addrMode, e, elidability)) -> Nil
|
case Some(e) => e
|
||||||
case None =>
|
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) =>
|
case Assignment(destination, source) =>
|
||||||
if (destination == BlackHoleExpression) return M6809ExpressionCompiler.compile(ctx, source, MExpressionTarget.NOTHING, BranchSpec.None) -> Nil
|
if (destination == BlackHoleExpression) return M6809ExpressionCompiler.compile(ctx, source, MExpressionTarget.NOTHING, BranchSpec.None) -> Nil
|
||||||
val destinationType = AbstractExpressionCompiler.getExpressionType(ctx, destination)
|
val destinationType = AbstractExpressionCompiler.getExpressionType(ctx, destination)
|
||||||
@ -107,13 +116,22 @@ object M6809StatementCompiler extends AbstractStatementCompiler[MLine] {
|
|||||||
case s:ContinueStatement =>
|
case s:ContinueStatement =>
|
||||||
compileContinueStatement(ctx, s) -> Nil
|
compileContinueStatement(ctx, s) -> Nil
|
||||||
case M6809AssemblyStatement(opcode, addrMode, expression, elidability) =>
|
case M6809AssemblyStatement(opcode, addrMode, expression, elidability) =>
|
||||||
ctx.env.evalForAsm(expression, opcode) match {
|
val silent = MOpcode.Branching(opcode) || opcode == LABEL || opcode == CHANGED_MEM
|
||||||
case Some(param) =>
|
val param: Constant = ctx.env.evalForAsm(expression, silent = silent) match {
|
||||||
List(MLine(opcode, addrMode, param, elidability)) -> Nil
|
case Some(param) => param
|
||||||
case None =>
|
case None =>
|
||||||
ctx.log.error("Invalid parameter", expression.position)
|
expression match {
|
||||||
Nil -> Nil
|
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 =>
|
case s:LabelStatement =>
|
||||||
List(MLine.label(env.prefix + s.name)) -> Nil
|
List(MLine.label(env.prefix + s.name)) -> Nil
|
||||||
case s: GotoStatement =>
|
case s: GotoStatement =>
|
||||||
|
@ -342,11 +342,10 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
x match {
|
x match {
|
||||||
// TODO: hmmm
|
// TODO: hmmm
|
||||||
case VariableExpression(name) =>
|
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
|
val fqName = if (name.startsWith(".")) env.prefix + name else name
|
||||||
MemoryAddressConstant(Label(fqName))
|
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))) =>
|
case FunctionCallExpression("byte_and_pointer$", List(z, b@VariableExpression(name))) =>
|
||||||
StructureConstant(env.get[StructType]("byte_and_pointer$"), List(
|
StructureConstant(env.get[StructType]("byte_and_pointer$"), List(
|
||||||
|
18
src/main/scala/millfork/env/Environment.scala
vendored
18
src/main/scala/millfork/env/Environment.scala
vendored
@ -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 {
|
e match {
|
||||||
// TODO: hmmm
|
case FunctionCallExpression("label", List(VariableExpression(name))) if (!name.contains(".")) =>
|
||||||
case VariableExpression(name) =>
|
return Some(Label(name).toAddress)
|
||||||
import MOpcode._
|
case FunctionCallExpression("label", List(VariableExpression(name))) if (name.startsWith(".")) =>
|
||||||
if (MOpcode.Branching(op) || op == LABEL || op == CHANGED_MEM) {
|
return Some(Label(prefix + name).toAddress)
|
||||||
val fqName = if (name.startsWith(".")) prefix + name else name
|
case FunctionCallExpression("label", _) =>
|
||||||
return Some(MemoryAddressConstant(Label(fqName)))
|
log.error("Invalid label reference", e.position)
|
||||||
}
|
return Some(Constant.Zero)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
e match {
|
e match {
|
||||||
@ -994,7 +994,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||||||
case None =>
|
case None =>
|
||||||
if (name.startsWith(".")) Some(Label(prefix + name).toAddress)
|
if (name.startsWith(".")) Some(Label(prefix + name).toAddress)
|
||||||
else {
|
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
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ class AssemblySuite extends FunSuite with Matchers with AppendedClues {
|
|||||||
| // w&x
|
| // w&x
|
||||||
| asm void main () {
|
| asm void main () {
|
||||||
| lda #3
|
| lda #3
|
||||||
|
| sta label(.l)+1
|
||||||
| sta .l+1
|
| sta .l+1
|
||||||
| .l: lda #55
|
| .l: lda #55
|
||||||
| sta output
|
| sta output
|
||||||
@ -36,6 +37,56 @@ class AssemblySuite extends FunSuite with Matchers with AppendedClues {
|
|||||||
""".stripMargin)(_.readByte(0xc000) should equal(3))
|
""".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") {
|
test("Assembly functions") {
|
||||||
EmuBenchmarkRun(
|
EmuBenchmarkRun(
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user