mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-22 16:31:02 +00:00
Const arrays
This commit is contained in:
parent
41e6bddfd9
commit
d9f88cdfad
@ -22,6 +22,8 @@
|
||||
|
||||
* Arrays can now have elements of types other than `byte` (still limited in size to 1 byte though).
|
||||
|
||||
* Arrays can now be constant.
|
||||
|
||||
* Added hint for identifiers with typos.
|
||||
|
||||
* Aliases now also support subfields.
|
||||
|
@ -10,6 +10,8 @@ even up to hardware damage.
|
||||
|
||||
* array overruns: indexing past the end of an array leads to undefined behaviour
|
||||
|
||||
* writing to arrays defined as `const`
|
||||
|
||||
* stray pointers: indexing a pointer that doesn't point to a valid object or indexing it past the end of the pointed object leads to undefined behaviour
|
||||
|
||||
* reading uninitialized variables: will return undefined values
|
||||
|
@ -21,6 +21,8 @@ It is not allowed in any other places.
|
||||
|
||||
String literals can be used as either array initializers or expressions of type `pointer`.
|
||||
|
||||
String literals are equivalent to constanr arrays. Writing to them via their pointer is undefined behaviour.
|
||||
|
||||
If a string literal is used as an expression, then the text data will be located in the default code segment,
|
||||
regardless of which code segment the current function is located it. This may be subject to change in future releases.
|
||||
|
||||
|
@ -106,12 +106,14 @@ An array is a continuous sequence of bytes in memory.
|
||||
|
||||
Syntax:
|
||||
|
||||
`[segment(<segment>)] array [(<element type>)] <name> [[<size>]] [align ( <alignment> )] [@<address>] [= <initial_values>]`
|
||||
`[segment(<segment>)] [const] array [(<element type>)] <name> [[<size>]] [align ( <alignment> )] [@<address>] [= <initial_values>]`
|
||||
|
||||
* `<segment>`: segment name; if absent,
|
||||
then defaults to `default_code_segment` as defined for the platform if the array has initial values,
|
||||
or to `default` if it doesn't.
|
||||
|
||||
* if `const` is present, the array is read-only. Read-only arrays have to have a fixed address and/or defined contents.
|
||||
|
||||
* `<element type>`: type of the elements of the array.
|
||||
It must be of size 1 byte.
|
||||
If omitted, the default is `byte`.
|
||||
|
@ -155,13 +155,13 @@ void createStarScreen() {
|
||||
}
|
||||
}
|
||||
|
||||
array starfieldCols = [
|
||||
const array starfieldCols = [
|
||||
14,10,12,15,14,13,12,11,10,14,
|
||||
14,10,14,15,14,13,12,11,10,12
|
||||
]
|
||||
|
||||
|
||||
array starfieldRow = [
|
||||
const array starfieldRow = [
|
||||
058,092,073,064,091,062,093,081,066,094,
|
||||
086,059,079,087,080,071,076,067,082,095,
|
||||
100,078,099,060,075,063,084,065,083,096,
|
||||
|
@ -33,7 +33,7 @@ asm void stabilize(byte x){
|
||||
RTS
|
||||
}
|
||||
|
||||
array colours = [
|
||||
const array colours = [
|
||||
$06, $06, $06, $0e, $06, $0e,
|
||||
$0e, $06, $0e, $0e, $0e, $03,
|
||||
$0e, $03, $03, $0e, $03, $03,
|
||||
|
@ -72,13 +72,13 @@ void hardscroll() {
|
||||
}
|
||||
}
|
||||
|
||||
array scroll_text = [
|
||||
const array scroll_text = [
|
||||
"this is a simple scroller demo written in millfork. " scr,
|
||||
"as you can see, you don't need assembly to do simple effects. " scr,
|
||||
" " scr
|
||||
]
|
||||
|
||||
array logo = [
|
||||
const array logo = [
|
||||
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
|
||||
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
|
||||
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
|
||||
@ -106,7 +106,7 @@ array logo = [
|
||||
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
|
||||
]
|
||||
|
||||
array logo_colours = [
|
||||
const array logo_colours = [
|
||||
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
|
||||
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
|
||||
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import stdio
|
||||
|
||||
array p = [
|
||||
const array p = [
|
||||
"this is an example", 13,
|
||||
"of {red}multiline {yellow}{reverse}",
|
||||
#if CBM
|
||||
@ -11,7 +11,7 @@ array p = [
|
||||
"{reverseoff} {white}text"
|
||||
]
|
||||
|
||||
array s = [
|
||||
const array s = [
|
||||
"and this is an example " scr,
|
||||
"of text done in screencodes" scr
|
||||
]
|
||||
|
@ -87,7 +87,7 @@ void load_charset() {
|
||||
}
|
||||
}
|
||||
|
||||
array charset = [
|
||||
const array charset = [
|
||||
$00,$00,$00,$00,$00,$00,$00,$00,
|
||||
$18,$18,$18,$18,$00,$00,$18,$00,
|
||||
$66,$66,$66,$00,$00,$00,$00,$00,
|
||||
|
@ -136,7 +136,7 @@ void scroll_screen() {
|
||||
}
|
||||
}
|
||||
|
||||
array bg = [
|
||||
const array bg = [
|
||||
" " ascii,
|
||||
"12345678901234567890123456789012" ascii,
|
||||
" " ascii,
|
||||
@ -174,13 +174,13 @@ array bg = [
|
||||
]
|
||||
|
||||
|
||||
array palette = [
|
||||
const array palette = [
|
||||
$0E,$00,$0E,$19,$00,$00,$00,$00,$00,$00,$00,$00,$01,$00,$01,$21,
|
||||
$0E,$20,$22,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
]
|
||||
|
||||
segment(chrrom)
|
||||
array charset @$0200 = [
|
||||
const array charset @$0200 = [
|
||||
$00,$00,$00,$00,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,
|
||||
$18,$18,$18,$18,$00,$00,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,
|
||||
$66,$66,$66,$00,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,
|
||||
@ -248,7 +248,7 @@ array charset @$0200 = [
|
||||
]
|
||||
|
||||
segment(chrrom)
|
||||
array sprites @$1000 = [
|
||||
const array sprites @$1000 = [
|
||||
$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,
|
||||
$18,$24,$66,$99,$99,$66,$24,$18,$00,$18,$18,$66,$66,$18,$18,$00
|
||||
]
|
@ -151,7 +151,7 @@ noinline void scroll_screen() {
|
||||
}
|
||||
}
|
||||
|
||||
array bg = [
|
||||
const array bg = [
|
||||
" " ascii,
|
||||
"12345678901234567890123456789012" ascii,
|
||||
" " ascii,
|
||||
@ -189,13 +189,13 @@ array bg = [
|
||||
]
|
||||
|
||||
|
||||
array palette = [
|
||||
const array palette = [
|
||||
$0E,$00,$0E,$19,$00,$00,$00,$00,$00,$00,$00,$00,$01,$00,$01,$21,
|
||||
$0E,$20,$22,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
]
|
||||
|
||||
segment(chrrom0)
|
||||
array charset @$6200 = [
|
||||
const array charset @$6200 = [
|
||||
$00,$00,$00,$00,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,
|
||||
$18,$18,$18,$18,$00,$00,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,
|
||||
$66,$66,$66,$00,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,
|
||||
@ -263,7 +263,7 @@ array charset @$6200 = [
|
||||
]
|
||||
|
||||
segment(chrrom1)
|
||||
array sprites @$A000 = [
|
||||
const array sprites @$A000 = [
|
||||
$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,
|
||||
$18,$24,$66,$99,$99,$66,$24,$18,$00,$18,$18,$66,$66,$18,$18,$00
|
||||
]
|
@ -122,9 +122,11 @@ object Main {
|
||||
Files.write(path, code)
|
||||
}
|
||||
errorReporting.debug(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
|
||||
c.runFileName.foreach(program =>
|
||||
new ProcessBuilder(program, Paths.get(defaultPrgOutput).toAbsolutePath.toString).start()
|
||||
)
|
||||
c.runFileName.foreach{ program =>
|
||||
val outputAbsolutePath = Paths.get(defaultPrgOutput).toAbsolutePath.toString
|
||||
errorReporting.debug(s"Running: $program $outputAbsolutePath")
|
||||
new ProcessBuilder(program, outputAbsolutePath).start()
|
||||
}
|
||||
if (platform.generateBbcMicroInfFile) {
|
||||
val start = platform.codeAllocators("default").startAt
|
||||
val codeLength = result.code("default").length
|
||||
|
@ -9,6 +9,7 @@ import millfork.assembly.mos.{AddrMode, opt, _}
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.env._
|
||||
import millfork.error.FatalErrorReporting
|
||||
import millfork.node.LiteralExpression
|
||||
|
||||
/**
|
||||
* These optimizations should not remove opportunities for more complex optimizations to trigger.
|
||||
@ -1063,6 +1064,30 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
(HasOpcodeIn(LAX, LDA) & RefersTo("__sp", 0)
|
||||
& XContainsSoftwareStackPointer) ~~> (_ => List(AssemblyLine.implied(TXA))),
|
||||
|
||||
(Elidable & HasOpcodeIn(LDA, LDX, LDY, ADC, SBC, EOR, AND, ORA) & HasAddrModeIn(Absolute, ZeroPage, LongAbsolute) & HasParameterWhere{
|
||||
case MemoryAddressConstant(i: InitializedArray) => i.readOnly
|
||||
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(i: InitializedArray), NumericConstant(offset, _)) =>
|
||||
i.readOnly && offset >= 0 && offset < i.sizeInBytes
|
||||
case CompoundConstant(MathOperator.Plus, NumericConstant(offset, _), MemoryAddressConstant(i: InitializedArray)) =>
|
||||
i.readOnly && offset >= 0 && offset < i.sizeInBytes
|
||||
case _ => false
|
||||
}) ~~> { code =>
|
||||
val p = code.head.parameter match {
|
||||
case MemoryAddressConstant(i: InitializedArray) =>
|
||||
i.contents.head
|
||||
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(i: InitializedArray), NumericConstant(offset, _)) =>
|
||||
i.contents(offset.toInt)
|
||||
case CompoundConstant(MathOperator.Plus, NumericConstant(offset, _), MemoryAddressConstant(i: InitializedArray)) =>
|
||||
i.contents(offset.toInt)
|
||||
}
|
||||
p match {
|
||||
case LiteralExpression(n, s) =>
|
||||
code.head.copy(addrMode = Immediate, parameter = NumericConstant(n, s)) :: Nil
|
||||
case _ =>
|
||||
code
|
||||
}
|
||||
},
|
||||
))
|
||||
|
||||
val PointlessStackStore = new RuleBasedAssemblyOptimization("Pointless stack store",
|
||||
|
@ -3,8 +3,8 @@ package millfork.assembly.z80.opt
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.z80.{opt, _}
|
||||
import millfork.assembly.z80.ZOpcode._
|
||||
import millfork.env.{CompoundConstant, Constant, MathOperator, NumericConstant}
|
||||
import millfork.node.ZRegister
|
||||
import millfork.env.{CompoundConstant, Constant, InitializedArray, MathOperator, MemoryAddressConstant, NumericConstant}
|
||||
import millfork.node.{LiteralExpression, ZRegister}
|
||||
import ZRegister._
|
||||
import millfork.DecimalUtils._
|
||||
import millfork.error.FatalErrorReporting
|
||||
@ -131,6 +131,30 @@ object AlwaysGoodI80Optimizations {
|
||||
code.init :+ ZLine.ldImm16(register, hi.<<(8) + lo)
|
||||
}
|
||||
),
|
||||
|
||||
(Elidable & HasOpcode(LD) & HasRegisters(TwoRegisters(A, MEM_ABS_8)) & HasParameterWhere {
|
||||
case MemoryAddressConstant(i: InitializedArray) => i.readOnly
|
||||
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(i: InitializedArray), NumericConstant(offset, _)) =>
|
||||
i.readOnly && offset >= 0 && offset < i.sizeInBytes
|
||||
case CompoundConstant(MathOperator.Plus, NumericConstant(offset, _), MemoryAddressConstant(i: InitializedArray)) =>
|
||||
i.readOnly && offset >= 0 && offset < i.sizeInBytes
|
||||
case _ => false
|
||||
}) ~~> { code =>
|
||||
val p = code.head.parameter match {
|
||||
case MemoryAddressConstant(i: InitializedArray) =>
|
||||
i.contents.head
|
||||
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(i: InitializedArray), NumericConstant(offset, _)) =>
|
||||
i.contents(offset.toInt)
|
||||
case CompoundConstant(MathOperator.Plus, NumericConstant(offset, _), MemoryAddressConstant(i: InitializedArray)) =>
|
||||
i.contents(offset.toInt)
|
||||
}
|
||||
p match {
|
||||
case LiteralExpression(n, s) =>
|
||||
code.head.copy(registers = TwoRegisters(A, IMM_8), parameter = NumericConstant(n, s)) :: Nil
|
||||
case _ =>
|
||||
code
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
val PointlessLoad = new RuleBasedAssemblyOptimization("Pointless load",
|
||||
|
@ -612,6 +612,12 @@ case class MatchParameter(i: Int) extends AssemblyLinePattern {
|
||||
override def hitRate: Double = 0.929
|
||||
}
|
||||
|
||||
case class HasParameterWhere(predicate: Constant => Boolean) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = predicate(line.parameter)
|
||||
|
||||
override def hitRate: Double = 0.332
|
||||
}
|
||||
|
||||
case class IsLabelMatching(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
line.opcode == ZOpcode.LABEL && ctx.addObject(i, line.parameter.quickSimplify)
|
||||
|
@ -155,7 +155,7 @@ abstract class AbstractReturnDispatch[T <: AbstractCode] {
|
||||
val a = InitializedArray(label + "$" + ix + ".array", None, (paramMins(ix) to paramMaxes(ix)).map { key =>
|
||||
map(key)._2.lift(ix).getOrElse(LiteralExpression(0, 1))
|
||||
}.toList,
|
||||
ctx.function.declaredBank, b, b, NoAlignment)
|
||||
ctx.function.declaredBank, b, b, readOnly = true, NoAlignment)
|
||||
env.registerUnnamedArray(a)
|
||||
a
|
||||
}
|
||||
|
@ -338,7 +338,7 @@ abstract class AbstractStatementPreprocessor(ctx: CompilationContext, statements
|
||||
case TextLiteralExpression(characters) =>
|
||||
val name = genName(characters)
|
||||
if (ctx.env.maybeGet[Thing](name).isEmpty) {
|
||||
ctx.env.root.registerArray(ArrayDeclarationStatement(name, None, None, "byte", None, Some(LiteralContents(characters)), None).pos(pos), ctx.options)
|
||||
ctx.env.root.registerArray(ArrayDeclarationStatement(name, None, None, "byte", None, const = true, Some(LiteralContents(characters)), None).pos(pos), ctx.options)
|
||||
}
|
||||
VariableExpression(name).pos(pos)
|
||||
case VariableExpression(v) if currentVarValues.contains(v) =>
|
||||
|
@ -191,12 +191,12 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val compileIndex = compile(ctx, indexExpression, Some(MosExpressionCompiler.getExpressionType(ctx, indexExpression) -> RegisterVariable(MosRegister.YA, w)), BranchSpec.None)
|
||||
val prepareRegister = pointy match {
|
||||
case ConstantPointy(addr, _, _, _, _, _) =>
|
||||
case p:ConstantPointy =>
|
||||
List(
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.immediate(ADC, addr.hiByte),
|
||||
AssemblyLine.immediate(ADC, p.value.hiByte),
|
||||
AssemblyLine.zeropage(STA, reg, 1),
|
||||
AssemblyLine.immediate(LDA, addr.loByte),
|
||||
AssemblyLine.immediate(LDA, p.value.loByte),
|
||||
AssemblyLine.zeropage(STA, reg))
|
||||
case VariablePointy(addr, _, _, true) =>
|
||||
List(
|
||||
@ -323,6 +323,9 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
|
||||
(pointy, variableIndex, variableIndexSize, totalIndexSize) match {
|
||||
case (p: ConstantPointy, None, _, _) =>
|
||||
if (p.readOnly) {
|
||||
ctx.log.error("Writing to a constant array", target.position)
|
||||
}
|
||||
List(AssemblyLine.absolute(store, env.genRelativeVariable(p.value + constIndex, b, zeropage = false)))
|
||||
case (p: VariablePointy, _, _, 2) =>
|
||||
wrapWordIndexingStorage(prepareWordIndexing(ctx, p, indexExpr))
|
||||
@ -330,9 +333,15 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
// TODO: optimize?
|
||||
wrapWordIndexingStorage(prepareWordIndexing(ctx, p, indexExpr))
|
||||
case (p: ConstantPointy, Some(v), 2, _) =>
|
||||
if (p.readOnly) {
|
||||
ctx.log.error("Writing to a constant array", target.position)
|
||||
}
|
||||
val w = env.get[VariableType]("word")
|
||||
wrapWordIndexingStorage(prepareWordIndexing(ctx, ConstantPointy(p.value + constIndex, None, if (constIndex.isProvablyZero) p.size else None, w, p.elementType, NoAlignment), v))
|
||||
wrapWordIndexingStorage(prepareWordIndexing(ctx, ConstantPointy(p.value + constIndex, None, if (constIndex.isProvablyZero) p.size else None, w, p.elementType, NoAlignment, p.readOnly), v))
|
||||
case (p: ConstantPointy, Some(v), 1, _) =>
|
||||
if (p.readOnly) {
|
||||
ctx.log.error("Writing to a constant array", target.position)
|
||||
}
|
||||
storeToArrayAtUnknownIndex(v, p.value)
|
||||
//TODO: should there be a type check or a zeropage check?
|
||||
case (pointerVariable@VariablePointy(varAddr, _, _, true), None, _, 0 | 1) =>
|
||||
@ -801,7 +810,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
None,
|
||||
if (constantIndex.isProvablyZero) a.size else None,
|
||||
env.get[VariableType]("word"),
|
||||
a.elementType, NoAlignment), v) ++ loadFromReg()
|
||||
a.elementType, NoAlignment, a.readOnly), v) ++ loadFromReg()
|
||||
case (a: VariablePointy, _, 2, _) =>
|
||||
prepareWordIndexing(ctx, a, indexExpr) ++ loadFromReg()
|
||||
case (p: VariablePointy, _, 0 | 1, _) if !p.zeropage =>
|
||||
|
@ -41,7 +41,7 @@ object MosReturnDispatch extends AbstractReturnDispatch[AssemblyLine] {
|
||||
}
|
||||
|
||||
if (useJmpaix) {
|
||||
val jumpTable = InitializedArray(label + "$jt.array", None, (actualMin to actualMax).flatMap(i => List(lobyte0(map(i)._1), hibyte0(map(i)._1))).toList, ctx.function.declaredBank, b, b, NoAlignment)
|
||||
val jumpTable = InitializedArray(label + "$jt.array", None, (actualMin to actualMax).flatMap(i => List(lobyte0(map(i)._1), hibyte0(map(i)._1))).toList, ctx.function.declaredBank, b, b, readOnly = true, NoAlignment)
|
||||
env.registerUnnamedArray(jumpTable)
|
||||
if (copyParams.isEmpty) {
|
||||
val loadIndex = MosExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None)
|
||||
@ -56,8 +56,8 @@ object MosReturnDispatch extends AbstractReturnDispatch[AssemblyLine] {
|
||||
}
|
||||
} else {
|
||||
val loadIndex = MosExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(MosRegister.X, b)), BranchSpec.None)
|
||||
val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte1(map(i)._1)).toList, ctx.function.declaredBank, b, b, NoAlignment)
|
||||
val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte1(map(i)._1)).toList, ctx.function.declaredBank, b, b, NoAlignment)
|
||||
val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte1(map(i)._1)).toList, ctx.function.declaredBank, b, b, readOnly = true, NoAlignment)
|
||||
val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte1(map(i)._1)).toList, ctx.function.declaredBank, b, b, readOnly = true, NoAlignment)
|
||||
env.registerUnnamedArray(jumpTableLo)
|
||||
env.registerUnnamedArray(jumpTableHi)
|
||||
val actualJump = if (ctx.options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
|
@ -23,7 +23,7 @@ object Z80BulkMemoryOperations {
|
||||
val sourceOffset = removeVariableOnce(f.variable, source.index).getOrElse(return compileForStatement(ctx, f)._1)
|
||||
if (!sourceOffset.isPure) return compileForStatement(ctx, f)._1
|
||||
val sourceIndexExpression = SumExpression(List(false -> sourceOffset, false -> f.start), decimal = false)
|
||||
val calculateSource = Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(source.name, sourceIndexExpression))
|
||||
val calculateSource = Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(source.name, sourceIndexExpression).pos(source.position), forWriting = false)
|
||||
compileMemoryBulk(ctx, target, f,
|
||||
useDEForTarget = true,
|
||||
preferDecreasing = false,
|
||||
@ -51,7 +51,7 @@ object Z80BulkMemoryOperations {
|
||||
case _ => SumExpression(List(false -> targetOffset, false -> f.start), decimal = false)
|
||||
}
|
||||
val array = if (target.name != f.variable) target.name else "$0000"
|
||||
val calculateAddress = Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(array, targetIndexExpression))
|
||||
val calculateAddress = Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(array, targetIndexExpression).pos(targetIndexExpression.position), forWriting = true)
|
||||
val calculateSize = f.direction match {
|
||||
case ForDirection.DownTo =>
|
||||
Z80ExpressionCompiler.stashHLIfChanged(ctx, Z80ExpressionCompiler.compileToBC(ctx, SumExpression(List(false -> f.start, true -> f.end), decimal = false)))
|
||||
@ -180,7 +180,7 @@ object Z80BulkMemoryOperations {
|
||||
useDEForTarget = true,
|
||||
preferDecreasing = true,
|
||||
isSmall => if (isSmall) {
|
||||
Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(targetForHL.name, targetForHLIndexExpression)) -> c.initC
|
||||
Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(targetForHL.name, targetForHLIndexExpression).pos(targetForHLIndexExpression.position), forWriting = false) -> c.initC
|
||||
} else break,
|
||||
next => loads ++ (c.nextC :+ ZLine.register(next, ZRegister.HL)),
|
||||
_ => None
|
||||
@ -424,7 +424,7 @@ object Z80BulkMemoryOperations {
|
||||
}
|
||||
val next = if (decreasing) DEC_16 else INC_16
|
||||
val calculateSourceValue = loadA(next)
|
||||
val calculateTargetAddress = Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(target.name, targetIndexExpression))
|
||||
val calculateTargetAddress = Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(target.name, targetIndexExpression).pos(targetIndexExpression.position), forWriting = true)
|
||||
val extraInitializationPair = extraAddressCalculations(smallCount)
|
||||
// TODO: figure the optimal compilation order
|
||||
val loading = if (useDEForTarget) {
|
||||
|
@ -446,7 +446,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
case i: IndexedExpression =>
|
||||
calculateAddressToHL(ctx, i) match {
|
||||
calculateAddressToHL(ctx, i, forWriting = false) match {
|
||||
case List(ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr)) => loadByte(addr, target, volatile = false)
|
||||
case code => code ++ loadByteViaHL(target)
|
||||
}
|
||||
@ -841,7 +841,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
val (l, r, size) = assertArithmeticAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 =>
|
||||
calculateAddressToAppropriatePointer(ctx, l) match {
|
||||
calculateAddressToAppropriatePointer(ctx, l, forWriting = true) match {
|
||||
case Some((lvo, code)) =>
|
||||
code ++
|
||||
(ZLine.ld8(ZRegister.A, lvo) ::
|
||||
@ -858,7 +858,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
val (l, r, size) = assertArithmeticAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 =>
|
||||
calculateAddressToAppropriatePointer(ctx, l) match {
|
||||
calculateAddressToAppropriatePointer(ctx, l, forWriting = true) match {
|
||||
case Some((lvo, code)) =>
|
||||
code ++
|
||||
(ZLine.ld8(ZRegister.A, lvo) ::
|
||||
@ -883,7 +883,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case "*'=" =>
|
||||
assertAllArithmeticBytes("Long multiplication not supported", ctx, params)
|
||||
val (l, r, 1) = assertArithmeticAssignmentLike(ctx, params)
|
||||
calculateAddressToAppropriatePointer(ctx, l) match {
|
||||
calculateAddressToAppropriatePointer(ctx, l, forWriting = true) match {
|
||||
case Some((lvo, code)) =>
|
||||
code ++
|
||||
(ZLine.ld8(ZRegister.A, lvo) ::
|
||||
@ -1003,7 +1003,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
|
||||
def calculateLoadAndStoreForByte(ctx: CompilationContext, expr: LhsExpression): (List[ZLine], List[ZLine]) = {
|
||||
Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, expr) match {
|
||||
Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, expr, forWriting = true) match { // TODO: forWriting?
|
||||
case Some((LocalVariableAddressViaHL, calculate)) =>
|
||||
(calculate :+ ZLine.ld8(ZRegister.A, ZRegister.MEM_HL)) -> List(ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
case Some((LocalVariableAddressViaIX(offset), calculate)) =>
|
||||
@ -1020,7 +1020,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def calculateAddressToAppropriatePointer(ctx: CompilationContext, expr: LhsExpression): Option[(LocalVariableAddressOperand, List[ZLine])] = {
|
||||
def calculateAddressToAppropriatePointer(ctx: CompilationContext, expr: LhsExpression, forWriting: Boolean): Option[(LocalVariableAddressOperand, List[ZLine])] = {
|
||||
val env = ctx.env
|
||||
expr match {
|
||||
case VariableExpression(name) =>
|
||||
@ -1035,7 +1035,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
Some(LocalVariableAddressViaHL -> calculateStackAddressToHL(ctx, v))
|
||||
}
|
||||
}
|
||||
case i:IndexedExpression => Some(LocalVariableAddressViaHL -> calculateAddressToHL(ctx, i))
|
||||
case i:IndexedExpression => Some(LocalVariableAddressViaHL -> calculateAddressToHL(ctx, i, forWriting))
|
||||
case i:DerefExpression => Some(LocalVariableAddressViaHL -> compileDerefPointer(ctx, i))
|
||||
case _:SeparateBytesExpression => None
|
||||
case _ => ???
|
||||
@ -1055,12 +1055,15 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def calculateAddressToHL(ctx: CompilationContext, i: IndexedExpression): List[ZLine] = {
|
||||
def calculateAddressToHL(ctx: CompilationContext, i: IndexedExpression, forWriting: Boolean): List[ZLine] = {
|
||||
val env = ctx.env
|
||||
val pointy = env.getPointy(i.name)
|
||||
AbstractExpressionCompiler.checkIndexType(ctx, pointy, i.index)
|
||||
pointy match {
|
||||
case ConstantPointy(baseAddr, _, size, _, _, alignment) =>
|
||||
case ConstantPointy(baseAddr, _, size, _, _, alignment, readOnly) =>
|
||||
if (forWriting && readOnly) {
|
||||
ctx.log.error("Writing to a constant array", i.position)
|
||||
}
|
||||
env.evalVariableAndConstantSubParts(i.index) match {
|
||||
case (None, offset) => List(ZLine.ldImm16(ZRegister.HL, (baseAddr + offset).quickSimplify))
|
||||
case (Some(index), offset) =>
|
||||
@ -1324,7 +1327,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
case i:IndexedExpression =>
|
||||
calculateAddressToHL(ctx, i) match {
|
||||
calculateAddressToHL(ctx, i, forWriting = true) match {
|
||||
case List(ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr)) => storeA(ctx, addr, 1, signedSource)
|
||||
case code => if (code.exists(changesA)) {
|
||||
List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ stashDEIfChanged(ctx, code) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.E)
|
||||
|
@ -51,8 +51,8 @@ object Z80ReturnDispatch extends AbstractReturnDispatch[ZLine] {
|
||||
}
|
||||
val copyParams = pair._2.reverse.flatten
|
||||
val offsetAfterParams = pair._1
|
||||
val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte0(map(i)._1)).toList, ctx.function.declaredBank, b, b, NoAlignment)
|
||||
val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte0(map(i)._1)).toList, ctx.function.declaredBank, b, b, NoAlignment)
|
||||
val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte0(map(i)._1)).toList, ctx.function.declaredBank, b, b, readOnly = true, NoAlignment)
|
||||
val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte0(map(i)._1)).toList, ctx.function.declaredBank, b, b, readOnly = true, NoAlignment)
|
||||
env.registerUnnamedArray(jumpTableLo)
|
||||
env.registerUnnamedArray(jumpTableHi)
|
||||
|
||||
|
@ -70,7 +70,7 @@ object Z80Shifting {
|
||||
} else if (i >= 8) {
|
||||
ZLine.ldImm8(ZRegister.A, 0) :: Z80ExpressionCompiler.storeA(ctx, lhs, signedSource = false)
|
||||
} else {
|
||||
Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, lhs) match {
|
||||
Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, lhs, forWriting = true) match {
|
||||
case Some((register, l)) =>
|
||||
// for shifting left:
|
||||
// ADD A = 4 cycles
|
||||
|
@ -318,7 +318,7 @@ object ZBuiltIns {
|
||||
}
|
||||
|
||||
def perform8BitInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, opcode: ZOpcode.Value, decimal: Boolean = false): List[ZLine] = {
|
||||
val (lv, calculateAddress):(LocalVariableAddressOperand, List[ZLine]) = Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, lhs).getOrElse{
|
||||
val (lv, calculateAddress):(LocalVariableAddressOperand, List[ZLine]) = Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, lhs, forWriting = true).getOrElse{
|
||||
ctx.log.error("Invalid left-hand-side expression", lhs.position)
|
||||
LocalVariableAddressViaHL -> Nil
|
||||
}
|
||||
|
43
src/main/scala/millfork/env/Environment.scala
vendored
43
src/main/scala/millfork/env/Environment.scala
vendored
@ -85,7 +85,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
|
||||
def getAllFixedAddressObjects: List[(String, Int, Int)] = {
|
||||
things.values.flatMap {
|
||||
case RelativeArray(_, NumericConstant(addr, _), size, declaredBank, _, _) =>
|
||||
case RelativeArray(_, NumericConstant(addr, _), size, declaredBank, _, _, _) =>
|
||||
List((declaredBank.getOrElse("default"), addr.toInt, size))
|
||||
case RelativeVariable(_, NumericConstant(addr, _), typ, _, declaredBank, _) =>
|
||||
List((declaredBank.getOrElse("default"), addr.toInt, typ.size))
|
||||
@ -374,13 +374,13 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
InitializedMemoryVariable
|
||||
UninitializedMemoryVariable
|
||||
getArrayOrPointer(name) match {
|
||||
case th@InitializedArray(_, _, cs, _, i, e, _) => ConstantPointy(th.toAddress, Some(name), Some(cs.length), i, e, th.alignment)
|
||||
case th@UninitializedArray(_, size, _, i, e, _) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e, th.alignment)
|
||||
case th@RelativeArray(_, _, size, _, i, e) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e, NoAlignment)
|
||||
case th@InitializedArray(_, _, cs, _, i, e, ro, _) => ConstantPointy(th.toAddress, Some(name), Some(cs.length), i, e, th.alignment, readOnly = ro)
|
||||
case th@UninitializedArray(_, size, _, i, e, ro, _) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e, th.alignment, readOnly = ro)
|
||||
case th@RelativeArray(_, _, size, _, i, e, ro) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e, NoAlignment, readOnly = ro)
|
||||
case ConstantThing(_, value, typ) if typ.size <= 2 && typ.isPointy =>
|
||||
val e = get[VariableType](typ.pointerTargetName)
|
||||
val w = get[VariableType]("word")
|
||||
ConstantPointy(value, None, None, w, e, NoAlignment)
|
||||
ConstantPointy(value, None, None, w, e, NoAlignment, readOnly = false)
|
||||
case th:VariableInMemory if th.typ.isPointy=>
|
||||
val e = get[VariableType](th.typ.pointerTargetName)
|
||||
val w = get[VariableType]("word")
|
||||
@ -393,7 +393,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
log.error(s"$name is not a valid pointer or array")
|
||||
val b = get[VariableType]("byte")
|
||||
val w = get[VariableType]("word")
|
||||
ConstantPointy(Constant.Zero, None, None, w, b, NoAlignment)
|
||||
ConstantPointy(Constant.Zero, None, None, w, b, NoAlignment, readOnly = false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,7 +597,18 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
case Some(m) if m.contains(name) => Some(m(name))
|
||||
case _ => maybeGet[ConstantThing](name).map(_.value)
|
||||
}
|
||||
case IndexedExpression(_, _) => None
|
||||
case IndexedExpression(arrName, index) =>
|
||||
getPointy(arrName) match {
|
||||
case ConstantPointy(MemoryAddressConstant(arr:InitializedArray), _, _, _, _, _, _) if arr.readOnly && arr.elementType.size == 1 =>
|
||||
eval(index).flatMap {
|
||||
case NumericConstant(constIndex, _) =>
|
||||
if (constIndex >= 0 && constIndex < arr.sizeInBytes) {
|
||||
eval(arr.contents(constIndex.toInt))
|
||||
} else None
|
||||
case _ => None
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
case _: DerefExpression => None
|
||||
case _: IndirectFieldExpression => None
|
||||
case _: DerefDebuggingExpression => None
|
||||
@ -1206,6 +1217,9 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
}
|
||||
stmt.elements match {
|
||||
case None =>
|
||||
if (stmt.const && stmt.address.isEmpty) {
|
||||
log.error(s"Constant array `${stmt.name}` without contents nor address", stmt.position)
|
||||
}
|
||||
stmt.length match {
|
||||
case None => log.error(s"Array `${stmt.name}` without size nor contents", stmt.position)
|
||||
case Some(l) =>
|
||||
@ -1230,9 +1244,9 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
val alignment = stmt.alignment.getOrElse(defaultArrayAlignment(options, length))
|
||||
val array = address match {
|
||||
case None => UninitializedArray(stmt.name + ".array", length.toInt,
|
||||
declaredBank = stmt.bank, indexType, e, alignment)
|
||||
declaredBank = stmt.bank, indexType, e, stmt.const, alignment)
|
||||
case Some(aa) => RelativeArray(stmt.name + ".array", aa, length.toInt,
|
||||
declaredBank = stmt.bank, indexType, e)
|
||||
declaredBank = stmt.bank, indexType, e, stmt.const)
|
||||
}
|
||||
addThing(array, stmt.position)
|
||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank, alignment, isVolatile = false), stmt.position, options, Some(e))
|
||||
@ -1266,6 +1280,9 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
}
|
||||
}
|
||||
case Some(contents1) =>
|
||||
if (!stmt.const && options.flag(CompilationFlag.ReadOnlyArrays)) {
|
||||
log.warn(s"Initialized array `${stmt.name}` is not defined as const, but the target platform doesn't support writable initialized arrays.", stmt.position)
|
||||
}
|
||||
val contents = extractArrayContents(contents1)
|
||||
val indexType = stmt.length match {
|
||||
case None => // array arr = [...]
|
||||
@ -1304,7 +1321,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
for (element <- contents) {
|
||||
AbstractExpressionCompiler.checkAssignmentType(this, element, e)
|
||||
}
|
||||
val array = InitializedArray(stmt.name + ".array", address, contents, declaredBank = stmt.bank, indexType, e, alignment)
|
||||
val array = InitializedArray(stmt.name + ".array", address, contents, declaredBank = stmt.bank, indexType, e, readOnly = stmt.const, alignment)
|
||||
addThing(array, stmt.position)
|
||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None,
|
||||
declaredBank = stmt.bank, alignment, isVolatile = false), stmt.position, options, Some(e))
|
||||
@ -1587,7 +1604,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
val b = get[VariableType]("byte")
|
||||
val v = get[Type]("void")
|
||||
if (options.flag(CompilationFlag.OptimizeForSonicSpeed)) {
|
||||
addThing(InitializedArray("identity$", None, List.tabulate(256)(n => LiteralExpression(n, 1)), declaredBank = None, b, b, defaultArrayAlignment(options, 256)), None)
|
||||
addThing(InitializedArray("identity$", None, IndexedSeq.tabulate(256)(n => LiteralExpression(n, 1)), declaredBank = None, b, b, readOnly = true, defaultArrayAlignment(options, 256)), None)
|
||||
}
|
||||
program.declarations.foreach {
|
||||
case a: AliasDefinitionStatement => registerAlias(a)
|
||||
@ -1629,12 +1646,12 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
}
|
||||
if (CpuFamily.forType(options.platform.cpu) == CpuFamily.M6502) {
|
||||
if (!things.contains("__constant8")) {
|
||||
things("__constant8") = InitializedArray("__constant8", None, List(LiteralExpression(8, 1)), declaredBank = None, b, b, NoAlignment)
|
||||
things("__constant8") = InitializedArray("__constant8", None, List(LiteralExpression(8, 1)), declaredBank = None, b, b, readOnly = true, NoAlignment)
|
||||
}
|
||||
if (options.flag(CompilationFlag.SoftwareStack)) {
|
||||
if (!things.contains("__sp")) {
|
||||
things("__sp") = UninitializedMemoryVariable("__sp", b, VariableAllocationMethod.Auto, None, NoAlignment, isVolatile = false)
|
||||
things("__stack") = UninitializedArray("__stack", 256, None, b, b, WithinPageAlignment)
|
||||
things("__stack") = UninitializedArray("__stack", 256, None, b, b, readOnly = false, WithinPageAlignment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
src/main/scala/millfork/env/Pointy.scala
vendored
11
src/main/scala/millfork/env/Pointy.scala
vendored
@ -6,14 +6,23 @@ trait Pointy {
|
||||
def name: Option[String]
|
||||
def indexType: VariableType
|
||||
def elementType: VariableType
|
||||
def readOnly: Boolean
|
||||
}
|
||||
|
||||
case class StackVariablePointy(offset: Int, indexType: VariableType, elementType: VariableType) extends Pointy {
|
||||
override def name: Option[String] = None
|
||||
override def readOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class VariablePointy(addr: Constant, indexType: VariableType, elementType: VariableType, zeropage: Boolean) extends Pointy {
|
||||
override def name: Option[String] = None
|
||||
override def readOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class ConstantPointy(value: Constant, name: Option[String], size: Option[Int], indexType: VariableType, elementType: VariableType, alignment: MemoryAlignment) extends Pointy
|
||||
case class ConstantPointy(value: Constant,
|
||||
name: Option[String],
|
||||
size: Option[Int],
|
||||
indexType: VariableType,
|
||||
elementType: VariableType,
|
||||
alignment: MemoryAlignment,
|
||||
override val readOnly: Boolean) extends Pointy
|
||||
|
7
src/main/scala/millfork/env/Thing.scala
vendored
7
src/main/scala/millfork/env/Thing.scala
vendored
@ -266,9 +266,10 @@ trait MfArray extends ThingInMemory with IndexableThing {
|
||||
override def isVolatile: Boolean = false
|
||||
/* TODO: what if larger elements? */
|
||||
def sizeInBytes: Int
|
||||
def readOnly: Boolean
|
||||
}
|
||||
|
||||
case class UninitializedArray(name: String, /* TODO: what if larger elements? */ sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val alignment: MemoryAlignment) extends MfArray with UninitializedMemory {
|
||||
case class UninitializedArray(name: String, /* TODO: what if larger elements? */ sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val readOnly: Boolean, override val alignment: MemoryAlignment) extends MfArray with UninitializedMemory {
|
||||
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
|
||||
|
||||
override def alloc: VariableAllocationMethod.Value = VariableAllocationMethod.Static
|
||||
@ -280,7 +281,7 @@ case class UninitializedArray(name: String, /* TODO: what if larger elements? */
|
||||
override def zeropage: Boolean = false
|
||||
}
|
||||
|
||||
case class RelativeArray(name: String, address: Constant, sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType) extends MfArray {
|
||||
case class RelativeArray(name: String, address: Constant, sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val readOnly: Boolean) extends MfArray {
|
||||
override def toAddress: Constant = address
|
||||
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||
@ -290,7 +291,7 @@ case class RelativeArray(name: String, address: Constant, sizeInBytes: Int, decl
|
||||
override def zeropage: Boolean = false
|
||||
}
|
||||
|
||||
case class InitializedArray(name: String, address: Option[Constant], contents: List[Expression], declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val alignment: MemoryAlignment) extends MfArray with PreallocableThing {
|
||||
case class InitializedArray(name: String, address: Option[Constant], contents: Seq[Expression], declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val readOnly: Boolean, override val alignment: MemoryAlignment) extends MfArray with PreallocableThing {
|
||||
override def shouldGenerate = true
|
||||
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||
|
@ -366,6 +366,7 @@ case class ArrayDeclarationStatement(name: String,
|
||||
length: Option[Expression],
|
||||
elementType: String,
|
||||
address: Option[Expression],
|
||||
const: Boolean,
|
||||
elements: Option[ArrayContents],
|
||||
alignment: Option[MemoryAlignment]) extends DeclarationStatement {
|
||||
override def getAllExpressions: List[Expression] = List(length, address).flatten ++ elements.fold(List[Expression]())(_.getAllExpressions)
|
||||
|
@ -253,7 +253,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
})
|
||||
|
||||
env.allPreallocatables.filterNot(o => unusedRuntimeObjects(o.name)).foreach {
|
||||
case thing@InitializedArray(name, Some(NumericConstant(address, _)), items, _, _, _, _) =>
|
||||
case thing@InitializedArray(name, Some(NumericConstant(address, _)), items, _, _, _, _, _) =>
|
||||
val bank = thing.bank(options)
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = address.toInt
|
||||
@ -277,7 +277,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
}).mkString(", "))
|
||||
}
|
||||
initializedVariablesSize += items.length
|
||||
case thing@InitializedArray(name, Some(_), items, _, _, _, _) => ???
|
||||
case thing@InitializedArray(name, Some(_), items, _, _, _, _, _) => ???
|
||||
case f: NormalFunction if f.address.isDefined =>
|
||||
val bank = f.bank(options)
|
||||
val bank0 = mem.banks(bank)
|
||||
@ -357,7 +357,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
justAfterCode += "default" -> (index + 1)
|
||||
}
|
||||
env.allPreallocatables.filterNot(o => unusedRuntimeObjects(o.name)).foreach {
|
||||
case thing@InitializedArray(name, None, items, _, _, _, alignment) =>
|
||||
case thing@InitializedArray(name, None, items, _, _, _, _, alignment) =>
|
||||
val bank = thing.bank(options)
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, items.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
||||
|
@ -238,6 +238,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
val arrayDefinition: P[Seq[ArrayDeclarationStatement]] = for {
|
||||
p <- position()
|
||||
bank <- bankDeclaration
|
||||
const <- ("const".! ~ HWS).?
|
||||
_ <- "array" ~ !letterOrDigit
|
||||
elementType <- ("(" ~/ AWS ~/ identifier ~ AWS ~ ")").? ~/ HWS
|
||||
name <- identifier ~ HWS
|
||||
@ -245,7 +246,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
alignment <- alignmentDeclaration(fastAlignmentForFunctions).? ~/ HWS
|
||||
addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).? ~/ HWS
|
||||
contents <- ("=" ~/ HWS ~/ arrayContents).? ~/ HWS
|
||||
} yield Seq(ArrayDeclarationStatement(name, bank, length, elementType.getOrElse("byte"), addr, contents, alignment).pos(p))
|
||||
} yield Seq(ArrayDeclarationStatement(name, bank, length, elementType.getOrElse("byte"), addr, const.isDefined, contents, alignment).pos(p))
|
||||
|
||||
def tightMfExpression(allowIntelHex: Boolean, allowTopLevelIndexing: Boolean): P[Expression] = {
|
||||
val a = if (allowIntelHex) atomWithIntel else atom
|
||||
@ -322,8 +323,8 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
fieldPath <- (HWS ~ "->" ~/ AWS ~/ identifier ~/ index.rep).rep
|
||||
} yield (expr, firstIndices, fieldPath) match {
|
||||
case (_, Seq(), Seq()) => expr
|
||||
case (VariableExpression(vname), Seq(i), Seq()) => IndexedExpression(vname, i).asInstanceOf[T]
|
||||
case _ => IndirectFieldExpression(expr, firstIndices, fieldPath).asInstanceOf[T]
|
||||
case (VariableExpression(vname), Seq(i), Seq()) => IndexedExpression(vname, i).pos(expr.position).asInstanceOf[T]
|
||||
case _ => IndirectFieldExpression(expr, firstIndices, fieldPath).pos(expr.position).asInstanceOf[T]
|
||||
}
|
||||
|
||||
// def mfLhsExpression: P[LhsExpression] = for {
|
||||
|
@ -270,4 +270,31 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
m.readByte(0xc000) should equal(0x44)
|
||||
}
|
||||
}
|
||||
|
||||
test("Const arrays") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||
"""
|
||||
| const array square = [0, 1, 4, 9, 16, 25, 36, 49, 64]
|
||||
| byte five() = 5
|
||||
| byte output0 @$c000
|
||||
| byte output1 @$c001
|
||||
| void main () {
|
||||
| output0 = square[3]
|
||||
| output1 = square[five()]
|
||||
| }
|
||||
""".stripMargin) { m =>
|
||||
m.readByte(0xc000) should equal(9)
|
||||
m.readByte(0xc001) should equal(25)
|
||||
}
|
||||
}
|
||||
|
||||
test("Writing to const arrays should not compile") {
|
||||
ShouldNotCompile(
|
||||
"""
|
||||
| const array a = [0]
|
||||
| void main () {
|
||||
| a[0] = 5
|
||||
| }
|
||||
""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user