mirror of
https://github.com/KarolS/millfork.git
synced 2024-06-13 14:29:29 +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 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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
]
|
]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
]
|
]
|
|
@ -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
|
||||||
]
|
]
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) =>
|
||||||
|
|
|
@ -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 =>
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
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)] = {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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 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
|
||||||
|
|
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
|
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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user