mirror of
https://github.com/KarolS/millfork.git
synced 2024-09-29 09:55:38 +00:00
Allow setting segments for multiple declarations at once
This commit is contained in:
parent
8d8859b55f
commit
20f4baf2b2
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
## Current version
|
## Current version
|
||||||
|
|
||||||
|
* Added `segment` block statement.
|
||||||
|
|
||||||
* Added goto.
|
* Added goto.
|
||||||
|
|
||||||
* Added arrays of elements of size greater than byte.
|
* Added arrays of elements of size greater than byte.
|
||||||
|
@ -155,6 +155,20 @@ TODO
|
|||||||
|
|
||||||
A function can be declared at the top level. For more details, see [Functions](./functions.md)
|
A function can be declared at the top level. For more details, see [Functions](./functions.md)
|
||||||
|
|
||||||
|
### Segment blocks
|
||||||
|
|
||||||
|
Instead of repeating the segment name in multiple declarations, you can wrap them in a block:
|
||||||
|
|
||||||
|
segment (s) {
|
||||||
|
byte x
|
||||||
|
word y
|
||||||
|
}
|
||||||
|
|
||||||
|
is equivalent to:
|
||||||
|
|
||||||
|
segment (s) byte x
|
||||||
|
segment (s) word y
|
||||||
|
|
||||||
## `import` statements
|
## `import` statements
|
||||||
|
|
||||||
import <module>
|
import <module>
|
||||||
|
@ -311,6 +311,12 @@ sealed trait DeclarationStatement extends Statement {
|
|||||||
def name: String
|
def name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed trait BankedDeclarationStatement extends DeclarationStatement {
|
||||||
|
def bank: Option[String]
|
||||||
|
def name: String
|
||||||
|
def withChangedBank(bank: String): BankedDeclarationStatement
|
||||||
|
}
|
||||||
|
|
||||||
case class TypeDefinitionStatement(name: String, parent: String) extends DeclarationStatement {
|
case class TypeDefinitionStatement(name: String, parent: String) extends DeclarationStatement {
|
||||||
override def getAllExpressions: List[Expression] = Nil
|
override def getAllExpressions: List[Expression] = Nil
|
||||||
}
|
}
|
||||||
@ -325,8 +331,10 @@ case class VariableDeclarationStatement(name: String,
|
|||||||
register: Boolean,
|
register: Boolean,
|
||||||
initialValue: Option[Expression],
|
initialValue: Option[Expression],
|
||||||
address: Option[Expression],
|
address: Option[Expression],
|
||||||
alignment: Option[MemoryAlignment]) extends DeclarationStatement {
|
alignment: Option[MemoryAlignment]) extends BankedDeclarationStatement {
|
||||||
override def getAllExpressions: List[Expression] = List(initialValue, address).flatten
|
override def getAllExpressions: List[Expression] = List(initialValue, address).flatten
|
||||||
|
|
||||||
|
override def withChangedBank(bank: String): BankedDeclarationStatement = copy(bank = Some(bank))
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ArrayContents extends Node {
|
trait ArrayContents extends Node {
|
||||||
@ -416,8 +424,10 @@ case class ArrayDeclarationStatement(name: String,
|
|||||||
address: Option[Expression],
|
address: Option[Expression],
|
||||||
const: Boolean,
|
const: Boolean,
|
||||||
elements: Option[ArrayContents],
|
elements: Option[ArrayContents],
|
||||||
alignment: Option[MemoryAlignment]) extends DeclarationStatement {
|
alignment: Option[MemoryAlignment]) extends BankedDeclarationStatement {
|
||||||
override def getAllExpressions: List[Expression] = List(length, address).flatten ++ elements.fold(List[Expression]())(_.getAllExpressions)
|
override def getAllExpressions: List[Expression] = List(length, address).flatten ++ elements.fold(List[Expression]())(_.getAllExpressions)
|
||||||
|
|
||||||
|
override def withChangedBank(bank: String): BankedDeclarationStatement = copy(bank = Some(bank))
|
||||||
}
|
}
|
||||||
|
|
||||||
case class ParameterDeclaration(typ: String,
|
case class ParameterDeclaration(typ: String,
|
||||||
@ -441,8 +451,10 @@ case class FunctionDeclarationStatement(name: String,
|
|||||||
assembly: Boolean,
|
assembly: Boolean,
|
||||||
interrupt: Boolean,
|
interrupt: Boolean,
|
||||||
kernalInterrupt: Boolean,
|
kernalInterrupt: Boolean,
|
||||||
reentrant: Boolean) extends DeclarationStatement {
|
reentrant: Boolean) extends BankedDeclarationStatement {
|
||||||
override def getAllExpressions: List[Expression] = address.toList ++ statements.getOrElse(Nil).flatMap(_.getAllExpressions)
|
override def getAllExpressions: List[Expression] = address.toList ++ statements.getOrElse(Nil).flatMap(_.getAllExpressions)
|
||||||
|
|
||||||
|
override def withChangedBank(bank: String): BankedDeclarationStatement = copy(bank = Some(bank))
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait ExecutableStatement extends Statement
|
sealed trait ExecutableStatement extends Statement
|
||||||
|
@ -102,7 +102,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
|
|
||||||
val atomWithIntel: P[Expression] = P(position() ~ (variableAtom | literalAtomWithIntel | 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 globalVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(true)
|
val globalVariableDefinition: P[Seq[BankedDeclarationStatement]] = variableDefinition(true)
|
||||||
val localVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(false)
|
val localVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(false)
|
||||||
|
|
||||||
def singleVariableDefinition: P[(Position, String, Option[Expression], Option[Expression], Option[MemoryAlignment])] = for {
|
def singleVariableDefinition: P[(Position, String, Option[Expression], Option[Expression], Option[MemoryAlignment])] = for {
|
||||||
@ -113,7 +113,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
alignment = None // TODO
|
alignment = None // TODO
|
||||||
} yield (p, name, addr, initialValue, alignment)
|
} yield (p, name, addr, initialValue, alignment)
|
||||||
|
|
||||||
def variableDefinition(implicitlyGlobal: Boolean): P[Seq[DeclarationStatement]] = for {
|
def variableDefinition(implicitlyGlobal: Boolean): P[Seq[BankedDeclarationStatement]] = for {
|
||||||
p <- position()
|
p <- position()
|
||||||
bank <- bankDeclaration
|
bank <- bankDeclaration
|
||||||
flags <- variableFlags ~ HWS
|
flags <- variableFlags ~ HWS
|
||||||
@ -479,7 +479,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel, false)
|
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mfExpression(nonStatementLevel, false)
|
||||||
} yield Seq(DoWhileStatement(body.toList, Nil, condition))
|
} yield Seq(DoWhileStatement(body.toList, Nil, condition))
|
||||||
|
|
||||||
val functionDefinition: P[Seq[DeclarationStatement]] = for {
|
val functionDefinition: P[Seq[BankedDeclarationStatement]] = for {
|
||||||
p <- position()
|
p <- position()
|
||||||
bank <- bankDeclaration
|
bank <- bankDeclaration
|
||||||
flags <- functionFlags ~ HWS
|
flags <- functionFlags ~ HWS
|
||||||
@ -560,9 +560,22 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
fields <- compoundTypeFields ~/ Pass
|
fields <- compoundTypeFields ~/ Pass
|
||||||
} yield Seq(UnionDefinitionStatement(name, fields).pos(p))
|
} yield Seq(UnionDefinitionStatement(name, fields).pos(p))
|
||||||
|
|
||||||
|
val segmentBlock: P[Seq[BankedDeclarationStatement]] = for {
|
||||||
|
bankName <- "segment" ~ AWS ~ "(" ~ AWS ~ identifier ~ AWS ~ ")" ~ AWS ~ "{" ~/ AWS
|
||||||
|
body <- locatableDefinition.rep(sep = EOL)
|
||||||
|
_ <- AWS ~ "}" ~/ Pass
|
||||||
|
} yield {
|
||||||
|
body.flatten.map{ stmt =>
|
||||||
|
if (stmt.bank.isEmpty) stmt.withChangedBank(bankName)
|
||||||
|
else stmt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def locatableDefinition: P[Seq[BankedDeclarationStatement]] = segmentBlock | arrayDefinition | functionDefinition | globalVariableDefinition
|
||||||
|
|
||||||
val program: Parser[Program] = for {
|
val program: Parser[Program] = for {
|
||||||
_ <- Start ~/ AWS ~/ Pass
|
_ <- Start ~/ AWS ~/ Pass
|
||||||
definitions <- (importStatement | arrayDefinition | aliasDefinition | enumDefinition | structDefinition | unionDefinition | functionDefinition | globalVariableDefinition).rep(sep = EOL)
|
definitions <- (importStatement | aliasDefinition | enumDefinition | structDefinition | unionDefinition | locatableDefinition).rep(sep = EOL)
|
||||||
_ <- AWS ~ End
|
_ <- AWS ~ End
|
||||||
} yield Program(definitions.flatten.toList)
|
} yield Program(definitions.flatten.toList)
|
||||||
|
|
||||||
|
35
src/test/scala/millfork/test/SegmentSuite.scala
Normal file
35
src/test/scala/millfork/test/SegmentSuite.scala
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package millfork.test
|
||||||
|
|
||||||
|
import millfork.Cpu
|
||||||
|
import millfork.test.emu.EmuUnoptimizedCrossPlatformRun
|
||||||
|
import org.scalatest.{FunSuite, Matchers}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karol Stasiak
|
||||||
|
*/
|
||||||
|
class SegmentSuite extends FunSuite with Matchers {
|
||||||
|
test("Segments") {
|
||||||
|
val source =
|
||||||
|
"""
|
||||||
|
| segment(second) byte a1
|
||||||
|
| segment(third) byte a2
|
||||||
|
| segment(second) {
|
||||||
|
| byte a3
|
||||||
|
| segment(third) byte a4
|
||||||
|
| }
|
||||||
|
| byte a5
|
||||||
|
| byte output @$c000
|
||||||
|
| void main() {
|
||||||
|
| output = 0
|
||||||
|
| if a1.addr.hi & $e0 == $80 { output += 1 }
|
||||||
|
| if a2.addr.hi & $e0 == $a0 { output += 1 }
|
||||||
|
| if a3.addr.hi & $e0 == $80 { output += 1 }
|
||||||
|
| if a4.addr.hi & $e0 == $a0 { output += 1 }
|
||||||
|
| if a5.addr.hi & $e0 == $00 { output += 1 }
|
||||||
|
| }
|
||||||
|
""".stripMargin
|
||||||
|
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80)(source) { m =>
|
||||||
|
m.readByte(0xc000) should equal(source.count(_ == '+'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,16 +18,24 @@ object EmuPlatform {
|
|||||||
TextCodec.Ascii,
|
TextCodec.Ascii,
|
||||||
Platform.builtInCpuFeatures(cpu),
|
Platform.builtInCpuFeatures(cpu),
|
||||||
CurrentBankFragmentOutput(0, 0xffff),
|
CurrentBankFragmentOutput(0, 0xffff),
|
||||||
Map("default" -> (if (cpu == Cpu.Intel8086) new UpwardByteAllocator(0x100, 0xb000) else new UpwardByteAllocator(0x200, 0xb000))),
|
Map(
|
||||||
Map("default" -> new VariableAllocator(
|
"default" -> (if (cpu == Cpu.Intel8086) new UpwardByteAllocator(0x100, 0xb000) else new UpwardByteAllocator(0x200, 0xb000)),
|
||||||
|
"second" -> new UpwardByteAllocator(0x8000, 0xa000),
|
||||||
|
"third" -> new UpwardByteAllocator(0xa000, 0xc000)
|
||||||
|
),
|
||||||
|
Map(
|
||||||
|
"default" -> new VariableAllocator(
|
||||||
if (CpuFamily.forType(cpu) == CpuFamily.M6502) pointers else Nil,
|
if (CpuFamily.forType(cpu) == CpuFamily.M6502) pointers else Nil,
|
||||||
new AfterCodeByteAllocator(0x200, 0xff00))),
|
new AfterCodeByteAllocator(0x200, 0xff00)),
|
||||||
|
"second" -> new VariableAllocator(Nil, new AfterCodeByteAllocator(0x8000, 0xa000)),
|
||||||
|
"third" -> new VariableAllocator(Nil, new AfterCodeByteAllocator(0xa000, 0xc000))
|
||||||
|
),
|
||||||
if (CpuFamily.forType(cpu) == CpuFamily.M6502) 4 else 0,
|
if (CpuFamily.forType(cpu) == CpuFamily.M6502) 4 else 0,
|
||||||
pointers,
|
pointers,
|
||||||
".bin",
|
".bin",
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
Map("default" -> 0),
|
Map("default" -> 0, "second" -> 2, "third" -> 3),
|
||||||
"default",
|
"default",
|
||||||
None,
|
None,
|
||||||
ViceDebugOutputFormat,
|
ViceDebugOutputFormat,
|
||||||
|
Loading…
Reference in New Issue
Block a user