mirror of
https://github.com/KarolS/millfork.git
synced 2025-03-24 10:33:53 +00:00
Local labels in assembly
This commit is contained in:
parent
539c27f13e
commit
e09db3d132
@ -2,6 +2,8 @@
|
||||
|
||||
## Current version
|
||||
|
||||
* Added local labels in assembly.
|
||||
|
||||
* Added alternate decimal operators (with `$` instead of `'`).
|
||||
|
||||
* Added `z80next` as an alternate name for the ZX Spectrum Next's processor (#55).
|
||||
|
@ -48,9 +48,9 @@ Indentation is not important:
|
||||
INC z
|
||||
|
||||
|
||||
Label names have to start with a letter and can contain digits, underscores and letters.
|
||||
This means than they cannot start with a period like in many other assemblers.
|
||||
Similarly, anonymous labels designated with `+` or `-` are also not supported.
|
||||
Global label names have to start with a letter and can contain digits, underscores and letters.
|
||||
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.
|
||||
|
||||
Assembly can refer to variables and constants defined in Millfork,
|
||||
but you need to be careful with using absolute vs immediate addressing:
|
||||
|
@ -23,9 +23,9 @@ Indentation is not important:
|
||||
INC c
|
||||
|
||||
|
||||
Label names have to start with a letter and can contain digits, underscores and letters.
|
||||
This means than they cannot start with a period like in many other assemblers.
|
||||
Similarly, anonymous labels designated with `+` or `-` are also not supported.
|
||||
Global label names have to start with a letter and can contain digits, underscores and letters.
|
||||
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.
|
||||
|
||||
Assembly can refer to variables and constants defined in Millfork,
|
||||
but you need to be careful with using absolute vs immediate addressing:
|
||||
|
@ -48,9 +48,9 @@ Indentation is not important:
|
||||
INR c
|
||||
|
||||
|
||||
Label names have to start with a letter and can contain digits, underscores and letters.
|
||||
This means than they cannot start with a period like in many other assemblers.
|
||||
Similarly, anonymous labels designated with `+` or `-` are also not supported.
|
||||
Global label names have to start with a letter and can contain digits, underscores and letters.
|
||||
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.
|
||||
|
||||
Assembly can refer to variables and constants defined in Millfork,
|
||||
but you need to be careful with using absolute vs immediate addressing:
|
||||
|
@ -343,7 +343,8 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
||||
// TODO: hmmm
|
||||
case VariableExpression(name) =>
|
||||
if (OpcodeClasses.ShortBranching(o) || o == JMP || o == LABEL || o == CHANGED_MEM || OpcodeClasses.HudsonTransfer(o)) {
|
||||
MemoryAddressConstant(Label(name))
|
||||
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)
|
||||
}
|
||||
|
@ -229,7 +229,8 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
||||
// TODO: hmmm
|
||||
case VariableExpression(name) =>
|
||||
if (Seq(JP, JR, DJNZ, LABEL, CHANGED_MEM).contains(op)) {
|
||||
MemoryAddressConstant(Label(name))
|
||||
val fqName = if (name.startsWith(".")) env.prefix + name else name
|
||||
MemoryAddressConstant(Label(fqName))
|
||||
} else {
|
||||
env.evalForAsm(expression).orElse(env.maybeGet[ThingInMemory](name).map(_.toAddress)).getOrElse(MemoryAddressConstant(Label(name)))
|
||||
}
|
||||
|
@ -672,6 +672,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
case ConstantArrayElementExpression(c) => Some(c)
|
||||
case GeneratedConstantExpression(c, t) => Some(c)
|
||||
case VariableExpression(name) =>
|
||||
if (name.startsWith(".")) return Some(MemoryAddressConstant(Label(prefix + name)))
|
||||
vv match {
|
||||
case Some(m) if m.contains(name) => Some(m(name))
|
||||
case _ => maybeGet[ConstantThing](name).map(_.value)
|
||||
@ -928,7 +929,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
case VariableExpression(name) =>
|
||||
import MOpcode._
|
||||
if (MOpcode.Branching(op) || op == LABEL || op == CHANGED_MEM) {
|
||||
return Some(MemoryAddressConstant(Label(name)))
|
||||
val fqName = if (name.startsWith(".")) prefix + name else name
|
||||
return Some(MemoryAddressConstant(Label(fqName)))
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
@ -940,7 +942,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
result match {
|
||||
case Some(x) => result
|
||||
case None =>
|
||||
if (name.startsWith(".")) Some(Label(name).toAddress)
|
||||
if (name.startsWith(".")) Some(Label(prefix + name).toAddress)
|
||||
else {
|
||||
if (!silent) log.warn(s"$name is not known", e.position)
|
||||
None
|
||||
|
@ -143,7 +143,7 @@ case class M6809Parser(filename: String,
|
||||
}
|
||||
|
||||
// TODO: label and instruction in one line
|
||||
val asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => M6809AssemblyStatement(MOpcode.LABEL, NonExistent, VariableExpression(l), Elidability.Elidable))
|
||||
val asmLabel: P[ExecutableStatement] = ((".".? ~ identifier).! ~ HWS ~ ":" ~/ HWS).map(l => M6809AssemblyStatement(MOpcode.LABEL, NonExistent, VariableExpression(l), Elidability.Elidable))
|
||||
|
||||
val asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall(false)).map(ExpressionStatement)
|
||||
|
||||
|
@ -160,6 +160,8 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
}
|
||||
}
|
||||
|
||||
val localLabelAtom : P[Expression] = ("." ~ identifier).!.map(VariableExpression)
|
||||
|
||||
val textLiteral: P[List[Expression]] = P(position() ~ doubleQuotedString ~/ HWS ~ codec).map {
|
||||
case (p, s, TextCodecWithFlags(co, zt, lp, lenient)) =>
|
||||
var characters = co.encode(options.log, None, s.codePoints().toArray.toList, options, lenient = lenient).map(c => LiteralExpression(c, 1).pos(p))
|
||||
@ -182,9 +184,9 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
|
||||
val literalAtomWithIntel: P[LiteralExpression] = binaryAtom | hexAtom | octalAtom | quaternaryAtom | intelHexAtom | decimalAtom | charAtom
|
||||
|
||||
val atom: P[Expression] = P(position() ~ (variableAtom | literalAtom | textLiteralAtom)).map{case (p,a) => a.pos(p)}
|
||||
val atom: P[Expression] = P(position() ~ (variableAtom | localLabelAtom | literalAtom | textLiteralAtom)).map{case (p,a) => a.pos(p)}
|
||||
|
||||
val atomWithIntel: P[Expression] = P(position() ~ (variableAtom | literalAtomWithIntel | textLiteralAtom)).map{case (p,a) => a.pos(p)}
|
||||
val atomWithIntel: P[Expression] = P(position() ~ (variableAtom | localLabelAtom | literalAtomWithIntel | textLiteralAtom)).map{case (p,a) => a.pos(p)}
|
||||
|
||||
val quotedAtom: P[String] = variableAtom.! | literalAtomWithIntel.map{
|
||||
case LiteralExpression(value, _) => value.toString
|
||||
|
@ -21,7 +21,7 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
|
||||
def fastAlignmentForFunctions: MemoryAlignment = WithinPageAlignment
|
||||
|
||||
// TODO: label and instruction in one line
|
||||
val asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => MosAssemblyStatement(Opcode.LABEL, AddrMode.DoesNotExist, VariableExpression(l), Elidability.Elidable))
|
||||
val asmLabel: P[ExecutableStatement] = ((".".? ~ identifier).! ~ HWS ~ ":" ~/ HWS).map(l => MosAssemblyStatement(Opcode.LABEL, AddrMode.DoesNotExist, VariableExpression(l), Elidability.Elidable))
|
||||
|
||||
// def zeropageAddrModeHint: P[Option[Boolean]] = Pass
|
||||
|
||||
|
@ -52,7 +52,7 @@ case class Z80Parser(filename: String,
|
||||
} yield ParameterDeclaration(typ, appc).pos(p)
|
||||
|
||||
// TODO: label and instruction in one line
|
||||
val asmLabel: P[ExecutableStatement] = (identifier ~ HWS ~ ":" ~/ HWS).map(l => Z80AssemblyStatement(ZOpcode.LABEL, NoRegisters, None, VariableExpression(l), elidability = Elidability.Elidable))
|
||||
val asmLabel: P[ExecutableStatement] = ((".".? ~ identifier).! ~ HWS ~ ":" ~/ HWS).map(l => Z80AssemblyStatement(ZOpcode.LABEL, NoRegisters, None, VariableExpression(l), elidability = Elidability.Elidable))
|
||||
|
||||
val asmMacro: P[ExecutableStatement] = ("+" ~/ HWS ~/ functionCall(false)).map(ExpressionStatement)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package millfork.test
|
||||
import millfork.Cpu
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuOptimizedCmosRun, EmuOptimizedHudsonRun, EmuOptimizedRun, EmuUndocumentedRun, EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedHudsonRun, EmuUnoptimizedRun, ShouldNotCompile}
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuOptimizedCmosRun, EmuOptimizedHudsonRun, EmuOptimizedRun, EmuUndocumentedRun, EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedHudsonRun, EmuUnoptimizedM6809Run, EmuUnoptimizedRun, EmuUnoptimizedZ80Run, ShouldNotCompile}
|
||||
import org.scalatest.{AppendedClues, FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
@ -379,4 +379,85 @@ class AssemblySuite extends FunSuite with Matchers with AppendedClues {
|
||||
m.readByte(0xc001) should equal(3)
|
||||
}
|
||||
|
||||
|
||||
test("Compile local labels properly (6502)") {
|
||||
val m = EmuUnoptimizedRun(
|
||||
"""
|
||||
|byte output1 @$c001
|
||||
|byte output2 @$c002
|
||||
|noinline asm byte one() {
|
||||
| jmp .l
|
||||
| .l:
|
||||
| lda #1
|
||||
| rts
|
||||
|}
|
||||
|noinline asm byte two() {
|
||||
| jmp .l
|
||||
| .l:
|
||||
| lda #2
|
||||
| rts
|
||||
|}
|
||||
|
|
||||
|void main() {
|
||||
| output1 = one()
|
||||
| output2 = two()
|
||||
|}
|
||||
|""".stripMargin)
|
||||
m.readByte(0xc001) should equal(1)
|
||||
m.readByte(0xc002) should equal(2)
|
||||
}
|
||||
|
||||
test("Compile local labels properly (Z80)") {
|
||||
val m = EmuUnoptimizedZ80Run(
|
||||
"""
|
||||
|byte output1 @$c001
|
||||
|byte output2 @$c002
|
||||
|noinline asm byte one() {
|
||||
| jp .l
|
||||
| .l:
|
||||
| ld a,1
|
||||
| ret
|
||||
|}
|
||||
|noinline asm byte two() {
|
||||
| jp .l
|
||||
| .l:
|
||||
| ld a,2
|
||||
| ret
|
||||
|}
|
||||
|
|
||||
|void main() {
|
||||
| output1 = one()
|
||||
| output2 = two()
|
||||
|}
|
||||
|""".stripMargin)
|
||||
m.readByte(0xc001) should equal(1)
|
||||
m.readByte(0xc002) should equal(2)
|
||||
}
|
||||
|
||||
test("Compile local labels properly (6809)") {
|
||||
val m = EmuUnoptimizedM6809Run(
|
||||
"""
|
||||
|byte output1 @$c001
|
||||
|byte output2 @$c002
|
||||
|noinline asm byte one() {
|
||||
| jmp .l
|
||||
| .l:
|
||||
| ldb #1
|
||||
| rts
|
||||
|}
|
||||
|noinline asm byte two() {
|
||||
| jmp .l
|
||||
| .l:
|
||||
| ldb #2
|
||||
| rts
|
||||
|}
|
||||
|
|
||||
|void main() {
|
||||
| output1 = one()
|
||||
| output2 = two()
|
||||
|}
|
||||
|""".stripMargin)
|
||||
m.readByte(0xc001) should equal(1)
|
||||
m.readByte(0xc002) should equal(2)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user