1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-28 13:41:31 +00:00

Local labels in assembly

This commit is contained in:
Karol Stasiak 2020-09-01 22:00:07 +02:00
parent 539c27f13e
commit e09db3d132
12 changed files with 108 additions and 19 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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