1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-13 14:29:29 +00:00

Const arrays

This commit is contained in:
Karol Stasiak 2019-04-29 22:57:40 +02:00
parent 41e6bddfd9
commit d9f88cdfad
31 changed files with 207 additions and 74 deletions

View File

@ -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 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. * Added hint for identifiers with typos.
* Aliases now also support subfields. * Aliases now also support subfields.

View File

@ -10,6 +10,8 @@ even up to hardware damage.
* array overruns: indexing past the end of an array leads to undefined behaviour * 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 * 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 * reading uninitialized variables: will return undefined values

View File

@ -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 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, 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. regardless of which code segment the current function is located it. This may be subject to change in future releases.

View File

@ -106,12 +106,14 @@ An array is a continuous sequence of bytes in memory.
Syntax: 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, * `<segment>`: segment name; if absent,
then defaults to `default_code_segment` as defined for the platform if the array has initial values, then defaults to `default_code_segment` as defined for the platform if the array has initial values,
or to `default` if it doesn't. 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. * `<element type>`: type of the elements of the array.
It must be of size 1 byte. It must be of size 1 byte.
If omitted, the default is `byte`. If omitted, the default is `byte`.

View File

@ -155,13 +155,13 @@ void createStarScreen() {
} }
} }
array starfieldCols = [ const array starfieldCols = [
14,10,12,15,14,13,12,11,10,14, 14,10,12,15,14,13,12,11,10,14,
14,10,14,15,14,13,12,11,10,12 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, 058,092,073,064,091,062,093,081,066,094,
086,059,079,087,080,071,076,067,082,095, 086,059,079,087,080,071,076,067,082,095,
100,078,099,060,075,063,084,065,083,096, 100,078,099,060,075,063,084,065,083,096,

View File

@ -33,7 +33,7 @@ asm void stabilize(byte x){
RTS RTS
} }
array colours = [ const array colours = [
$06, $06, $06, $0e, $06, $0e, $06, $06, $06, $0e, $06, $0e,
$0e, $06, $0e, $0e, $0e, $03, $0e, $06, $0e, $0e, $0e, $03,
$0e, $03, $03, $0e, $03, $03, $0e, $03, $03, $0e, $03, $03,

View File

@ -72,13 +72,13 @@ void hardscroll() {
} }
} }
array scroll_text = [ const array scroll_text = [
"this is a simple scroller demo written in millfork. " scr, "this is a simple scroller demo written in millfork. " scr,
"as you can see, you don't need assembly to do simple effects. " scr, "as you can see, you don't need assembly to do simple effects. " scr,
" " 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, 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 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, 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,

View File

@ -1,6 +1,6 @@
import stdio import stdio
array p = [ const array p = [
"this is an example", 13, "this is an example", 13,
"of {red}multiline {yellow}{reverse}", "of {red}multiline {yellow}{reverse}",
#if CBM #if CBM
@ -11,7 +11,7 @@ array p = [
"{reverseoff} {white}text" "{reverseoff} {white}text"
] ]
array s = [ const array s = [
"and this is an example " scr, "and this is an example " scr,
"of text done in screencodes" scr "of text done in screencodes" scr
] ]

View File

@ -87,7 +87,7 @@ void load_charset() {
} }
} }
array charset = [ const array charset = [
$00,$00,$00,$00,$00,$00,$00,$00, $00,$00,$00,$00,$00,$00,$00,$00,
$18,$18,$18,$18,$00,$00,$18,$00, $18,$18,$18,$18,$00,$00,$18,$00,
$66,$66,$66,$00,$00,$00,$00,$00, $66,$66,$66,$00,$00,$00,$00,$00,

View File

@ -136,7 +136,7 @@ void scroll_screen() {
} }
} }
array bg = [ const array bg = [
" " ascii, " " ascii,
"12345678901234567890123456789012" ascii, "12345678901234567890123456789012" ascii,
" " 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,$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 $0E,$20,$22,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
] ]
segment(chrrom) 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, $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, $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, $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) 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, $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 $18,$24,$66,$99,$99,$66,$24,$18,$00,$18,$18,$66,$66,$18,$18,$00
] ]

View File

@ -151,7 +151,7 @@ noinline void scroll_screen() {
} }
} }
array bg = [ const array bg = [
" " ascii, " " ascii,
"12345678901234567890123456789012" ascii, "12345678901234567890123456789012" ascii,
" " 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,$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 $0E,$20,$22,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
] ]
segment(chrrom0) 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, $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, $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, $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) 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, $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 $18,$24,$66,$99,$99,$66,$24,$18,$00,$18,$18,$66,$66,$18,$18,$00
] ]

View File

@ -122,9 +122,11 @@ object Main {
Files.write(path, code) Files.write(path, code)
} }
errorReporting.debug(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms") errorReporting.debug(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
c.runFileName.foreach(program => c.runFileName.foreach{ program =>
new ProcessBuilder(program, Paths.get(defaultPrgOutput).toAbsolutePath.toString).start() val outputAbsolutePath = Paths.get(defaultPrgOutput).toAbsolutePath.toString
) errorReporting.debug(s"Running: $program $outputAbsolutePath")
new ProcessBuilder(program, outputAbsolutePath).start()
}
if (platform.generateBbcMicroInfFile) { if (platform.generateBbcMicroInfFile) {
val start = platform.codeAllocators("default").startAt val start = platform.codeAllocators("default").startAt
val codeLength = result.code("default").length val codeLength = result.code("default").length

View File

@ -9,6 +9,7 @@ import millfork.assembly.mos.{AddrMode, opt, _}
import millfork.assembly.mos.AddrMode._ import millfork.assembly.mos.AddrMode._
import millfork.env._ import millfork.env._
import millfork.error.FatalErrorReporting import millfork.error.FatalErrorReporting
import millfork.node.LiteralExpression
/** /**
* These optimizations should not remove opportunities for more complex optimizations to trigger. * These optimizations should not remove opportunities for more complex optimizations to trigger.
@ -1063,6 +1064,30 @@ object AlwaysGoodOptimizations {
(HasOpcodeIn(LAX, LDA) & RefersTo("__sp", 0) (HasOpcodeIn(LAX, LDA) & RefersTo("__sp", 0)
& XContainsSoftwareStackPointer) ~~> (_ => List(AssemblyLine.implied(TXA))), & 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", val PointlessStackStore = new RuleBasedAssemblyOptimization("Pointless stack store",

View File

@ -3,8 +3,8 @@ package millfork.assembly.z80.opt
import millfork.assembly.AssemblyOptimization import millfork.assembly.AssemblyOptimization
import millfork.assembly.z80.{opt, _} import millfork.assembly.z80.{opt, _}
import millfork.assembly.z80.ZOpcode._ import millfork.assembly.z80.ZOpcode._
import millfork.env.{CompoundConstant, Constant, MathOperator, NumericConstant} import millfork.env.{CompoundConstant, Constant, InitializedArray, MathOperator, MemoryAddressConstant, NumericConstant}
import millfork.node.ZRegister import millfork.node.{LiteralExpression, ZRegister}
import ZRegister._ import ZRegister._
import millfork.DecimalUtils._ import millfork.DecimalUtils._
import millfork.error.FatalErrorReporting import millfork.error.FatalErrorReporting
@ -131,6 +131,30 @@ object AlwaysGoodI80Optimizations {
code.init :+ ZLine.ldImm16(register, hi.<<(8) + lo) 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", val PointlessLoad = new RuleBasedAssemblyOptimization("Pointless load",

View File

@ -612,6 +612,12 @@ case class MatchParameter(i: Int) extends AssemblyLinePattern {
override def hitRate: Double = 0.929 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 { case class IsLabelMatching(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
line.opcode == ZOpcode.LABEL && ctx.addObject(i, line.parameter.quickSimplify) line.opcode == ZOpcode.LABEL && ctx.addObject(i, line.parameter.quickSimplify)

View File

@ -155,7 +155,7 @@ abstract class AbstractReturnDispatch[T <: AbstractCode] {
val a = InitializedArray(label + "$" + ix + ".array", None, (paramMins(ix) to paramMaxes(ix)).map { key => val a = InitializedArray(label + "$" + ix + ".array", None, (paramMins(ix) to paramMaxes(ix)).map { key =>
map(key)._2.lift(ix).getOrElse(LiteralExpression(0, 1)) map(key)._2.lift(ix).getOrElse(LiteralExpression(0, 1))
}.toList, }.toList,
ctx.function.declaredBank, b, b, NoAlignment) ctx.function.declaredBank, b, b, readOnly = true, NoAlignment)
env.registerUnnamedArray(a) env.registerUnnamedArray(a)
a a
} }

View File

@ -338,7 +338,7 @@ abstract class AbstractStatementPreprocessor(ctx: CompilationContext, statements
case TextLiteralExpression(characters) => case TextLiteralExpression(characters) =>
val name = genName(characters) val name = genName(characters)
if (ctx.env.maybeGet[Thing](name).isEmpty) { 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) VariableExpression(name).pos(pos)
case VariableExpression(v) if currentVarValues.contains(v) => case VariableExpression(v) if currentVarValues.contains(v) =>

View File

@ -191,12 +191,12 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
val reg = ctx.env.get[VariableInMemory]("__reg") val reg = ctx.env.get[VariableInMemory]("__reg")
val compileIndex = compile(ctx, indexExpression, Some(MosExpressionCompiler.getExpressionType(ctx, indexExpression) -> RegisterVariable(MosRegister.YA, w)), BranchSpec.None) val compileIndex = compile(ctx, indexExpression, Some(MosExpressionCompiler.getExpressionType(ctx, indexExpression) -> RegisterVariable(MosRegister.YA, w)), BranchSpec.None)
val prepareRegister = pointy match { val prepareRegister = pointy match {
case ConstantPointy(addr, _, _, _, _, _) => case p:ConstantPointy =>
List( List(
AssemblyLine.implied(CLC), AssemblyLine.implied(CLC),
AssemblyLine.immediate(ADC, addr.hiByte), AssemblyLine.immediate(ADC, p.value.hiByte),
AssemblyLine.zeropage(STA, reg, 1), AssemblyLine.zeropage(STA, reg, 1),
AssemblyLine.immediate(LDA, addr.loByte), AssemblyLine.immediate(LDA, p.value.loByte),
AssemblyLine.zeropage(STA, reg)) AssemblyLine.zeropage(STA, reg))
case VariablePointy(addr, _, _, true) => case VariablePointy(addr, _, _, true) =>
List( List(
@ -323,6 +323,9 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
(pointy, variableIndex, variableIndexSize, totalIndexSize) match { (pointy, variableIndex, variableIndexSize, totalIndexSize) match {
case (p: ConstantPointy, None, _, _) => 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))) List(AssemblyLine.absolute(store, env.genRelativeVariable(p.value + constIndex, b, zeropage = false)))
case (p: VariablePointy, _, _, 2) => case (p: VariablePointy, _, _, 2) =>
wrapWordIndexingStorage(prepareWordIndexing(ctx, p, indexExpr)) wrapWordIndexingStorage(prepareWordIndexing(ctx, p, indexExpr))
@ -330,9 +333,15 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
// TODO: optimize? // TODO: optimize?
wrapWordIndexingStorage(prepareWordIndexing(ctx, p, indexExpr)) wrapWordIndexingStorage(prepareWordIndexing(ctx, p, indexExpr))
case (p: ConstantPointy, Some(v), 2, _) => 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") 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, _) => case (p: ConstantPointy, Some(v), 1, _) =>
if (p.readOnly) {
ctx.log.error("Writing to a constant array", target.position)
}
storeToArrayAtUnknownIndex(v, p.value) storeToArrayAtUnknownIndex(v, p.value)
//TODO: should there be a type check or a zeropage check? //TODO: should there be a type check or a zeropage check?
case (pointerVariable@VariablePointy(varAddr, _, _, true), None, _, 0 | 1) => case (pointerVariable@VariablePointy(varAddr, _, _, true), None, _, 0 | 1) =>
@ -801,7 +810,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
None, None,
if (constantIndex.isProvablyZero) a.size else None, if (constantIndex.isProvablyZero) a.size else None,
env.get[VariableType]("word"), env.get[VariableType]("word"),
a.elementType, NoAlignment), v) ++ loadFromReg() a.elementType, NoAlignment, a.readOnly), v) ++ loadFromReg()
case (a: VariablePointy, _, 2, _) => case (a: VariablePointy, _, 2, _) =>
prepareWordIndexing(ctx, a, indexExpr) ++ loadFromReg() prepareWordIndexing(ctx, a, indexExpr) ++ loadFromReg()
case (p: VariablePointy, _, 0 | 1, _) if !p.zeropage => case (p: VariablePointy, _, 0 | 1, _) if !p.zeropage =>

View File

@ -41,7 +41,7 @@ object MosReturnDispatch extends AbstractReturnDispatch[AssemblyLine] {
} }
if (useJmpaix) { 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) env.registerUnnamedArray(jumpTable)
if (copyParams.isEmpty) { if (copyParams.isEmpty) {
val loadIndex = MosExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None) 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 { } else {
val loadIndex = MosExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(MosRegister.X, b)), BranchSpec.None) 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 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, 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(jumpTableLo)
env.registerUnnamedArray(jumpTableHi) env.registerUnnamedArray(jumpTableHi)
val actualJump = if (ctx.options.flag(CompilationFlag.LUnixRelocatableCode)) { val actualJump = if (ctx.options.flag(CompilationFlag.LUnixRelocatableCode)) {

View File

@ -23,7 +23,7 @@ object Z80BulkMemoryOperations {
val sourceOffset = removeVariableOnce(f.variable, source.index).getOrElse(return compileForStatement(ctx, f)._1) val sourceOffset = removeVariableOnce(f.variable, source.index).getOrElse(return compileForStatement(ctx, f)._1)
if (!sourceOffset.isPure) 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 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, compileMemoryBulk(ctx, target, f,
useDEForTarget = true, useDEForTarget = true,
preferDecreasing = false, preferDecreasing = false,
@ -51,7 +51,7 @@ object Z80BulkMemoryOperations {
case _ => SumExpression(List(false -> targetOffset, false -> f.start), decimal = false) case _ => SumExpression(List(false -> targetOffset, false -> f.start), decimal = false)
} }
val array = if (target.name != f.variable) target.name else "$0000" 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 { val calculateSize = f.direction match {
case ForDirection.DownTo => case ForDirection.DownTo =>
Z80ExpressionCompiler.stashHLIfChanged(ctx, Z80ExpressionCompiler.compileToBC(ctx, SumExpression(List(false -> f.start, true -> f.end), decimal = false))) Z80ExpressionCompiler.stashHLIfChanged(ctx, Z80ExpressionCompiler.compileToBC(ctx, SumExpression(List(false -> f.start, true -> f.end), decimal = false)))
@ -180,7 +180,7 @@ object Z80BulkMemoryOperations {
useDEForTarget = true, useDEForTarget = true,
preferDecreasing = true, preferDecreasing = true,
isSmall => if (isSmall) { 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, } else break,
next => loads ++ (c.nextC :+ ZLine.register(next, ZRegister.HL)), next => loads ++ (c.nextC :+ ZLine.register(next, ZRegister.HL)),
_ => None _ => None
@ -424,7 +424,7 @@ object Z80BulkMemoryOperations {
} }
val next = if (decreasing) DEC_16 else INC_16 val next = if (decreasing) DEC_16 else INC_16
val calculateSourceValue = loadA(next) 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) val extraInitializationPair = extraAddressCalculations(smallCount)
// TODO: figure the optimal compilation order // TODO: figure the optimal compilation order
val loading = if (useDEForTarget) { val loading = if (useDEForTarget) {

View File

@ -446,7 +446,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
} }
} }
case i: IndexedExpression => 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 List(ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr)) => loadByte(addr, target, volatile = false)
case code => code ++ loadByteViaHL(target) case code => code ++ loadByteViaHL(target)
} }
@ -841,7 +841,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
val (l, r, size) = assertArithmeticAssignmentLike(ctx, params) val (l, r, size) = assertArithmeticAssignmentLike(ctx, params)
size match { size match {
case 1 => case 1 =>
calculateAddressToAppropriatePointer(ctx, l) match { calculateAddressToAppropriatePointer(ctx, l, forWriting = true) match {
case Some((lvo, code)) => case Some((lvo, code)) =>
code ++ code ++
(ZLine.ld8(ZRegister.A, lvo) :: (ZLine.ld8(ZRegister.A, lvo) ::
@ -858,7 +858,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
val (l, r, size) = assertArithmeticAssignmentLike(ctx, params) val (l, r, size) = assertArithmeticAssignmentLike(ctx, params)
size match { size match {
case 1 => case 1 =>
calculateAddressToAppropriatePointer(ctx, l) match { calculateAddressToAppropriatePointer(ctx, l, forWriting = true) match {
case Some((lvo, code)) => case Some((lvo, code)) =>
code ++ code ++
(ZLine.ld8(ZRegister.A, lvo) :: (ZLine.ld8(ZRegister.A, lvo) ::
@ -883,7 +883,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
case "*'=" => case "*'=" =>
assertAllArithmeticBytes("Long multiplication not supported", ctx, params) assertAllArithmeticBytes("Long multiplication not supported", ctx, params)
val (l, r, 1) = assertArithmeticAssignmentLike(ctx, params) val (l, r, 1) = assertArithmeticAssignmentLike(ctx, params)
calculateAddressToAppropriatePointer(ctx, l) match { calculateAddressToAppropriatePointer(ctx, l, forWriting = true) match {
case Some((lvo, code)) => case Some((lvo, code)) =>
code ++ code ++
(ZLine.ld8(ZRegister.A, lvo) :: (ZLine.ld8(ZRegister.A, lvo) ::
@ -1003,7 +1003,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
} }
def calculateLoadAndStoreForByte(ctx: CompilationContext, expr: LhsExpression): (List[ZLine], List[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)) => case Some((LocalVariableAddressViaHL, calculate)) =>
(calculate :+ ZLine.ld8(ZRegister.A, ZRegister.MEM_HL)) -> List(ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)) (calculate :+ ZLine.ld8(ZRegister.A, ZRegister.MEM_HL)) -> List(ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
case Some((LocalVariableAddressViaIX(offset), calculate)) => 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 val env = ctx.env
expr match { expr match {
case VariableExpression(name) => case VariableExpression(name) =>
@ -1035,7 +1035,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
Some(LocalVariableAddressViaHL -> calculateStackAddressToHL(ctx, v)) 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 i:DerefExpression => Some(LocalVariableAddressViaHL -> compileDerefPointer(ctx, i))
case _:SeparateBytesExpression => None case _:SeparateBytesExpression => None
case _ => ??? 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 env = ctx.env
val pointy = env.getPointy(i.name) val pointy = env.getPointy(i.name)
AbstractExpressionCompiler.checkIndexType(ctx, pointy, i.index) AbstractExpressionCompiler.checkIndexType(ctx, pointy, i.index)
pointy match { 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 { env.evalVariableAndConstantSubParts(i.index) match {
case (None, offset) => List(ZLine.ldImm16(ZRegister.HL, (baseAddr + offset).quickSimplify)) case (None, offset) => List(ZLine.ldImm16(ZRegister.HL, (baseAddr + offset).quickSimplify))
case (Some(index), offset) => case (Some(index), offset) =>
@ -1324,7 +1327,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
} }
} }
case i:IndexedExpression => 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 List(ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr)) => storeA(ctx, addr, 1, signedSource)
case code => if (code.exists(changesA)) { case code => if (code.exists(changesA)) {
List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ stashDEIfChanged(ctx, code) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.E) List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ stashDEIfChanged(ctx, code) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.E)

View File

@ -51,8 +51,8 @@ object Z80ReturnDispatch extends AbstractReturnDispatch[ZLine] {
} }
val copyParams = pair._2.reverse.flatten val copyParams = pair._2.reverse.flatten
val offsetAfterParams = pair._1 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 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, 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(jumpTableLo)
env.registerUnnamedArray(jumpTableHi) env.registerUnnamedArray(jumpTableHi)

View File

@ -70,7 +70,7 @@ object Z80Shifting {
} else if (i >= 8) { } else if (i >= 8) {
ZLine.ldImm8(ZRegister.A, 0) :: Z80ExpressionCompiler.storeA(ctx, lhs, signedSource = false) ZLine.ldImm8(ZRegister.A, 0) :: Z80ExpressionCompiler.storeA(ctx, lhs, signedSource = false)
} else { } else {
Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, lhs) match { Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, lhs, forWriting = true) match {
case Some((register, l)) => case Some((register, l)) =>
// for shifting left: // for shifting left:
// ADD A = 4 cycles // ADD A = 4 cycles

View File

@ -318,7 +318,7 @@ object ZBuiltIns {
} }
def perform8BitInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, opcode: ZOpcode.Value, decimal: Boolean = false): List[ZLine] = { 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) ctx.log.error("Invalid left-hand-side expression", lhs.position)
LocalVariableAddressViaHL -> Nil LocalVariableAddressViaHL -> Nil
} }

View File

@ -85,7 +85,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
def getAllFixedAddressObjects: List[(String, Int, Int)] = { def getAllFixedAddressObjects: List[(String, Int, Int)] = {
things.values.flatMap { things.values.flatMap {
case RelativeArray(_, NumericConstant(addr, _), size, declaredBank, _, _) => case RelativeArray(_, NumericConstant(addr, _), size, declaredBank, _, _, _) =>
List((declaredBank.getOrElse("default"), addr.toInt, size)) List((declaredBank.getOrElse("default"), addr.toInt, size))
case RelativeVariable(_, NumericConstant(addr, _), typ, _, declaredBank, _) => case RelativeVariable(_, NumericConstant(addr, _), typ, _, declaredBank, _) =>
List((declaredBank.getOrElse("default"), addr.toInt, typ.size)) List((declaredBank.getOrElse("default"), addr.toInt, typ.size))
@ -374,13 +374,13 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
InitializedMemoryVariable InitializedMemoryVariable
UninitializedMemoryVariable UninitializedMemoryVariable
getArrayOrPointer(name) match { getArrayOrPointer(name) match {
case th@InitializedArray(_, _, cs, _, i, e, _) => ConstantPointy(th.toAddress, Some(name), Some(cs.length), i, e, th.alignment) 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, _) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e, th.alignment) 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) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e, NoAlignment) 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 => case ConstantThing(_, value, typ) if typ.size <= 2 && typ.isPointy =>
val e = get[VariableType](typ.pointerTargetName) val e = get[VariableType](typ.pointerTargetName)
val w = get[VariableType]("word") 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=> case th:VariableInMemory if th.typ.isPointy=>
val e = get[VariableType](th.typ.pointerTargetName) val e = get[VariableType](th.typ.pointerTargetName)
val w = get[VariableType]("word") 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") log.error(s"$name is not a valid pointer or array")
val b = get[VariableType]("byte") val b = get[VariableType]("byte")
val w = get[VariableType]("word") 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 Some(m) if m.contains(name) => Some(m(name))
case _ => maybeGet[ConstantThing](name).map(_.value) 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 _: DerefExpression => None
case _: IndirectFieldExpression => None case _: IndirectFieldExpression => None
case _: DerefDebuggingExpression => None case _: DerefDebuggingExpression => None
@ -1206,6 +1217,9 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
} }
stmt.elements match { stmt.elements match {
case None => case None =>
if (stmt.const && stmt.address.isEmpty) {
log.error(s"Constant array `${stmt.name}` without contents nor address", stmt.position)
}
stmt.length match { stmt.length match {
case None => log.error(s"Array `${stmt.name}` without size nor contents", stmt.position) case None => log.error(s"Array `${stmt.name}` without size nor contents", stmt.position)
case Some(l) => 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 alignment = stmt.alignment.getOrElse(defaultArrayAlignment(options, length))
val array = address match { val array = address match {
case None => UninitializedArray(stmt.name + ".array", length.toInt, 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, 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) addThing(array, stmt.position)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank, alignment, isVolatile = false), stmt.position, options, Some(e)) 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) => 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 contents = extractArrayContents(contents1)
val indexType = stmt.length match { val indexType = stmt.length match {
case None => // array arr = [...] case None => // array arr = [...]
@ -1304,7 +1321,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
for (element <- contents) { for (element <- contents) {
AbstractExpressionCompiler.checkAssignmentType(this, element, e) 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) addThing(array, stmt.position)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None,
declaredBank = stmt.bank, alignment, isVolatile = false), stmt.position, options, Some(e)) 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 b = get[VariableType]("byte")
val v = get[Type]("void") val v = get[Type]("void")
if (options.flag(CompilationFlag.OptimizeForSonicSpeed)) { 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 { program.declarations.foreach {
case a: AliasDefinitionStatement => registerAlias(a) 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 (CpuFamily.forType(options.platform.cpu) == CpuFamily.M6502) {
if (!things.contains("__constant8")) { 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 (options.flag(CompilationFlag.SoftwareStack)) {
if (!things.contains("__sp")) { if (!things.contains("__sp")) {
things("__sp") = UninitializedMemoryVariable("__sp", b, VariableAllocationMethod.Auto, None, NoAlignment, isVolatile = false) 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)
} }
} }
} }

View File

@ -6,14 +6,23 @@ trait Pointy {
def name: Option[String] def name: Option[String]
def indexType: VariableType def indexType: VariableType
def elementType: VariableType def elementType: VariableType
def readOnly: Boolean
} }
case class StackVariablePointy(offset: Int, indexType: VariableType, elementType: VariableType) extends Pointy { case class StackVariablePointy(offset: Int, indexType: VariableType, elementType: VariableType) extends Pointy {
override def name: Option[String] = None override def name: Option[String] = None
override def readOnly: Boolean = false
} }
case class VariablePointy(addr: Constant, indexType: VariableType, elementType: VariableType, zeropage: Boolean) extends Pointy { case class VariablePointy(addr: Constant, indexType: VariableType, elementType: VariableType, zeropage: Boolean) extends Pointy {
override def name: Option[String] = None 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

View File

@ -266,9 +266,10 @@ trait MfArray extends ThingInMemory with IndexableThing {
override def isVolatile: Boolean = false override def isVolatile: Boolean = false
/* TODO: what if larger elements? */ /* TODO: what if larger elements? */
def sizeInBytes: Int 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 toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
override def alloc: VariableAllocationMethod.Value = VariableAllocationMethod.Static 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 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 toAddress: Constant = address
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false) 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 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 shouldGenerate = true
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false) override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)

View File

@ -366,6 +366,7 @@ case class ArrayDeclarationStatement(name: String,
length: Option[Expression], length: Option[Expression],
elementType: String, elementType: String,
address: Option[Expression], address: Option[Expression],
const: Boolean,
elements: Option[ArrayContents], elements: Option[ArrayContents],
alignment: Option[MemoryAlignment]) extends DeclarationStatement { alignment: Option[MemoryAlignment]) extends DeclarationStatement {
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)

View File

@ -253,7 +253,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
}) })
env.allPreallocatables.filterNot(o => unusedRuntimeObjects(o.name)).foreach { 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 bank = thing.bank(options)
val bank0 = mem.banks(bank) val bank0 = mem.banks(bank)
var index = address.toInt var index = address.toInt
@ -277,7 +277,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
}).mkString(", ")) }).mkString(", "))
} }
initializedVariablesSize += items.length initializedVariablesSize += items.length
case thing@InitializedArray(name, Some(_), items, _, _, _, _) => ??? case thing@InitializedArray(name, Some(_), items, _, _, _, _, _) => ???
case f: NormalFunction if f.address.isDefined => case f: NormalFunction if f.address.isDefined =>
val bank = f.bank(options) val bank = f.bank(options)
val bank0 = mem.banks(bank) val bank0 = mem.banks(bank)
@ -357,7 +357,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
justAfterCode += "default" -> (index + 1) justAfterCode += "default" -> (index + 1)
} }
env.allPreallocatables.filterNot(o => unusedRuntimeObjects(o.name)).foreach { 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 bank = thing.bank(options)
val bank0 = mem.banks(bank) val bank0 = mem.banks(bank)
var index = codeAllocators(bank).allocateBytes(bank0, options, items.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment) var index = codeAllocators(bank).allocateBytes(bank0, options, items.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)

View File

@ -238,6 +238,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
val arrayDefinition: P[Seq[ArrayDeclarationStatement]] = for { val arrayDefinition: P[Seq[ArrayDeclarationStatement]] = for {
p <- position() p <- position()
bank <- bankDeclaration bank <- bankDeclaration
const <- ("const".! ~ HWS).?
_ <- "array" ~ !letterOrDigit _ <- "array" ~ !letterOrDigit
elementType <- ("(" ~/ AWS ~/ identifier ~ AWS ~ ")").? ~/ HWS elementType <- ("(" ~/ AWS ~/ identifier ~ AWS ~ ")").? ~/ HWS
name <- identifier ~ HWS name <- identifier ~ HWS
@ -245,7 +246,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
alignment <- alignmentDeclaration(fastAlignmentForFunctions).? ~/ HWS alignment <- alignmentDeclaration(fastAlignmentForFunctions).? ~/ HWS
addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).? ~/ HWS addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).? ~/ HWS
contents <- ("=" ~/ HWS ~/ arrayContents).? ~/ 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] = { def tightMfExpression(allowIntelHex: Boolean, allowTopLevelIndexing: Boolean): P[Expression] = {
val a = if (allowIntelHex) atomWithIntel else atom 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 fieldPath <- (HWS ~ "->" ~/ AWS ~/ identifier ~/ index.rep).rep
} yield (expr, firstIndices, fieldPath) match { } yield (expr, firstIndices, fieldPath) match {
case (_, Seq(), Seq()) => expr case (_, Seq(), Seq()) => expr
case (VariableExpression(vname), Seq(i), Seq()) => IndexedExpression(vname, i).asInstanceOf[T] case (VariableExpression(vname), Seq(i), Seq()) => IndexedExpression(vname, i).pos(expr.position).asInstanceOf[T]
case _ => IndirectFieldExpression(expr, firstIndices, fieldPath).asInstanceOf[T] case _ => IndirectFieldExpression(expr, firstIndices, fieldPath).pos(expr.position).asInstanceOf[T]
} }
// def mfLhsExpression: P[LhsExpression] = for { // def mfLhsExpression: P[LhsExpression] = for {

View File

@ -270,4 +270,31 @@ class ArraySuite extends FunSuite with Matchers {
m.readByte(0xc000) should equal(0x44) 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)
}
} }