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

Preliminary segment support; C16/+4 fixes

This commit is contained in:
Karol Stasiak 2018-03-15 23:09:19 +01:00
parent 9e8a125487
commit 8a347e5058
31 changed files with 377 additions and 250 deletions

View File

@ -4,6 +4,8 @@
* **Breaking change!** Renamed `inline` to `macro`.
* **Breaking change!** Added support for memory segments. Changed the platform definition file syntax.
* Added preliminary support for 65CE02, HuC6280 and 65816 processors.
* Added new `-O1` optimization preset; old `-O1` became `-O2`, old `-O2` became `-O3` and so on.

View File

@ -96,30 +96,49 @@ Every platform is defined in an `.ini` file with an appropriate name.
#### `[allocation]` section
* `main_org` the address for the `main` function; all the other functions will be placed after it
* `zp_pointers` either a list of comma separated zeropage addresses that can be used by the program as zeropage pointers, or `all` for all. Each value should be the address of the first of two free bytes in the zeropage.
* `himem_style` not yet supported
* `segments` a comma-separated list of segment names.
A segment named `default` is always required.
Default: `default`. In all options below, `NAME` refers to a segment name.
* `himem_start` the first address used for non-zeropage variables, or `after_code` if the variables should be allocated after the code
* `default_code_segment` the default segment for code and initialized arrays.
Note that the default segment for uninitialized arrays and variables is always `default`.
Default: `default`
* `himem_end` the last address available for non-zeropage variables
* `segment_NAME_start` the first address used for automatic allocation in the segment.
Note that the `default` segment shouldn't start before $200, as the $0-$1FF range is reserved for the zeropage and the stack.
The `main` function will be placed as close to the beginning of its segment as possible, but not necessarily at `segment_NAME_start`
* `segment_NAME_end` the last address in the segment
* `segment_NAME_codeend` the last address in the segment for code and initialized arrays.
Only uninitialized variables are allowed between `segment_NAME_codeend` and `segment_NAME_end`.
Default: the same as `segment_NAME_end`.
* `segment_NAME_datastart` the first address used for non-zeropage variables, or `after_code` if the variables should be allocated after the code.
Default: `after_code`.
#### `[output]` section
* `style` not yet supported
* `style` how multi-segment programs should be output:
* `single` output a single file, based mostly, but not necessarily only on data in the `default` segment (the default)
* `per_segment` generate a separate file with each segment
* `format` output file format; a comma-separated list of tokens:
* literal byte values
* `startaddr` little-endian 16-bit address of the first used byte of the compiled output
* `startaddr` little-endian 16-bit address of the first used byte of the compiled output (not necessarily the segment start)
* `endaddr` little-endian 16-bit address of the last used byte of the compiled output
* `endaddr` little-endian 16-bit address of the last used byte of the compiled output (usually not the segment end)
* `allocated` all used bytes
* `<addr>:<addr>` - inclusive range of bytes
* `<segment>:<addr>:<addr>` - inclusive range of bytes in a given segment
* `extension` target file extension, with or without the dot

View File

@ -2,9 +2,11 @@
Syntax:
`[<modifiers>] <return_type> <name> ( <params> ) [@ <address>] { <body> }`
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [@ <address>] { <body> }`
`asm <return_type> <name> ( <params> ) @ <address> extern`
`[segment (<segment>)] asm <return_type> <name> ( <params> ) @ <address> extern`
* `<segment>`: segment name; if absent, then defaults to `default_code_segment` as defined for the platform
* `<modifiers>`: zero or more of the following:

View File

@ -18,7 +18,9 @@ or a top level of a function (*local* variables).
Syntax:
`[<storage>] <type> <name> [@<address>] [= <initial_value>]`
`[segment(<segment>)] [<storage>] <type> <name> [@<address>] [= <initial_value>]`
* `<segment>`: segment name; if absent, then defaults to `default`.
* `<storage>` can be only specified for local variables. It can be either `stack`, `static`, `register` or nothing.
`register` is only a hint for the optimizer.
@ -44,7 +46,11 @@ An array is a continuous sequence of bytes in memory.
Syntax:
`array <name> [[<size>]] [@<address>] [= <initial_values>]`
`[segment(<segment>)] array <name> [[<size>]] [@<address>] [= <initial_values>]`
* `<segment>`: segment name; if absent,
then defaults to `default_code_segment` as defined for the platform if the array has initial values,
or to `default` if it doesn't.
TODO

View File

@ -4,18 +4,15 @@ modules=a8_kernel,default_panic
[allocation]
main_org=$2000
; TODO
zp_pointers=$80,$82,$84,$86,$88,$8a,$8c,$8e,$90,$92,$94,$96,$98,$9a,$9c,$9e,$a0,$a2,$a4
;TODO
himem_style=per_bank
himem_start=after_code
;TODO
himem_end=$3FFF
segment_default_start=$2000
; TODO
segment_default_end=$3fff
[output]
;TODO
style=per_bank
style=single
format=$FF,$FF,$E0,$02,$E1,$02,startaddr,startaddr,endaddr,allocated
extension=xex

View File

@ -4,17 +4,14 @@ modules=apple2_kernel,default_panic
[allocation]
main_org=$0C00
; TODO
zp_pointers=6,8,$EB,$ED,$FA,$FC
;TODO
himem_style=per_bank
himem_start=after_code
himem_end=$95FF
segment_default_start=$0C00
segment_default_end=$95FF
[output]
;TODO
style=per_bank
style=single
format=allocated
extension=a2

View File

@ -4,16 +4,13 @@ modules=c128_hardware,loader_1c01,c128_kernal,default_panic
[allocation]
main_org=$1C0D
; TODO
zp_pointers=$C1,$C3,$FB,$FD,$39,$3B,$3D,$43,$45,$47,$4B
himem_style=per_bank
himem_start=after_code
; TODO
himem_end=$FEFF
segment_default_start=$1C0D
segment_default_end=$FEFF
[output]
style=per_bank
style=single
format=startaddr,allocated
extension=prg

View File

@ -4,15 +4,13 @@ modules=loader_1001,c264_kernal,c264_hardware,default_panic
[allocation]
main_org=$100D
; TODO
zp_pointers=$C1,$C3,$FB,$FD,$39,$3B,$3D,$43,$4B
himem_style=per_bank
himem_start=after_code
himem_end=$3FFF
segment_default_start=$100D
segment_default_end=$3FFF
[output]
style=per_bank
style=single
format=startaddr,allocated
extension=prg

View File

@ -1 +1 @@
import c16_ted
import c264_ted

View File

@ -13,8 +13,8 @@ const byte light_red = $32
const byte dark_grey = $21
const byte dark_gray = $21
const byte medium_grey = $31
const byte medium gray = $31
const byte medium_gray = $31
const byte light_green = $55
const byte light_blue = $36
const byte light_grey = $41
const byte light_gray = $41
const byte light_gray = $41

View File

@ -7,22 +7,23 @@ arch=nmos
; modules to load
modules=c64_hardware,loader_0801,c64_kernal,c64_panic,stdlib
; optionally: default flags
emit_illegals=false
emit_illegals=true
[allocation]
; where the main function should be allocated, also the start of bank 0
main_org=$80D
; list of free zp pointer locations (these assume that some BASIC routines will keep working)
zp_pointers=$C1,$C3,$FB,$FD,$39,$3B,$3D,$43,$45,$47,$4B
; where to allocate non-zp variables
himem_style=per_bank
himem_start=after_code
himem_end=$9FFF
segments=default
default_code_segment=default
segment_default_start=$80D
segment_default_codeend=$9fff
segment_default_datastart=after_code
segment_default_end=$cfff
[output]
; how the banks are laid out in the output files; so far, there is no bank support in the compiler yet
style=per_bank
style=single
; output file format
; startaddr - little-endian address of the first used byte in the bank
; endaddr - little-endian address of the last used byte in the bank

View File

@ -7,14 +7,13 @@ modules=c64_hardware,loader_0801,c64_kernal,c64_panic,stdlib
emit_65816=emulation
[allocation]
main_org=$80D
zp_pointers=$C1,$C3,$FB,$FD,$39,$3B,$3D,$43,$45,$47,$4B
himem_style=per_bank
himem_start=after_code
himem_end=$9FFF
segment_default_start=$80D
segment_default_codeend=$9fff
segment_default_end=$cfff
[output]
style=per_bank
style=single
format=startaddr,allocated
extension=prg

View File

@ -10,12 +10,12 @@ emit_65816=native
[allocation]
main_org=$811
zp_pointers=$C1,$C3,$FB,$FD,$39,$3B,$3D,$43,$45,$47,$4B
himem_style=per_bank
himem_start=after_code
himem_end=$9FFF
segment_default_start=$80D
segment_default_codeend=$9fff
segment_default_end=$cfff
[output]
style=per_bank
style=single
format=startaddr,allocated
extension=prg

View File

@ -4,15 +4,13 @@ modules=loader_0401,pet_kernal,default_panic
[allocation]
main_org=$40D
; TODO
zp_pointers=$C1,$C3,$FB,$FD,$39,$3B,$3D,$43,$4B
himem_style=per_bank
himem_start=after_code
himem_end=$FFF
segment_default_start=$40D
segment_default_end=$FFF
[output]
style=per_bank
style=single
format=startaddr,allocated
extension=prg

View File

@ -1,15 +1,13 @@
[compilation]
arch=nmos
modules=c264_loader,c264_kernal,c264_hardware,default_panic
modules=loader_1001,c264_kernal,c264_hardware,default_panic
[allocation]
main_org=$100D
; TODO
zp_pointers=$C1,$C3,$FB,$FD,$39,$3B,$3D,$43,$4B
himem_style=per_bank
himem_start=after_code
himem_end=$7FFF
segment_default_start=$100D
segment_default_end=$3FFF
[output]
style=per_bank

View File

@ -4,15 +4,13 @@ modules=loader_1001,vic20_kernal,default_panic
[allocation]
main_org=$100D
; TODO
zp_pointers=$C1,$C3,$FB,$FD,$39,$3B,$3D,$43,$4B
himem_style=per_bank
himem_start=after_code
himem_end=$1CFF
segment_default_start=$100D
segment_default_end=$1CFF
[output]
style=per_bank
style=single
format=startaddr,allocated
extension=prg

View File

@ -4,15 +4,13 @@ modules=loader_0401,vic20_kernal,default_panic
[allocation]
main_org=$40D
; TODO
zp_pointers=$C1,$C3,$FB,$FD,$39,$3B,$3D,$43,$4B
himem_style=per_bank
himem_start=after_code
himem_end=$1CFF
segment_default_start=$40D
segment_default_end=$1CFF
[output]
style=per_bank
style=single
format=startaddr,allocated
extension=prg

View File

@ -4,15 +4,13 @@ modules=loader_1201,vic20_kernal,default_panic
[allocation]
main_org=$120D
; TODO
zp_pointers=$C1,$C3,$FB,$FD,$39,$3B,$3D,$43,$4B
himem_style=per_bank
himem_start=after_code
himem_end=$1FFF
segment_default_start=$120D
segment_default_end=$1FFF
[output]
style=per_bank
style=single
format=startaddr,allocated
extension=prg

View File

@ -73,7 +73,16 @@ object Main {
val output = c.outputFileName.getOrElse("a")
val assOutput = output + ".asm"
val labelOutput = output + ".lbl"
val prgOutput = if (!output.endsWith(platform.fileExtension)) output + platform.fileExtension else output
// val prgOutputs = (platform.outputStyle match {
// case OutputStyle.Single => List("default")
// case OutputStyle.PerBank => platform.bankNumbers.keys.toList
// }).map(bankName => bankName -> {
// if (bankName == "default") {
// if (output.endsWith(platform.fileExtension)) output else output + platform.fileExtension
// } else {
// s"${output.stripSuffix(platform.fileExtension)}.$bankName${platform.fileExtension}"
// }
// }).toMap
val unoptimized = new SourceLoadingQueue(
initialFilenames = c.inputFileNames,
@ -114,7 +123,7 @@ object Main {
}
// compile
val assembler = new Assembler(program, env)
val assembler = new Assembler(program, env, platform)
val result = assembler.assemble(callGraph, assemblyOptimizations, options)
ErrorReporting.assertNoErrors("Codegen failed")
ErrorReporting.debug(f"Unoptimized code size: ${assembler.unoptimizedCodeSize}%5d B")
@ -140,13 +149,22 @@ object Main {
s"al ${a.toHexString} .$normalized"
}.mkString("\n").getBytes(StandardCharsets.UTF_8))
}
val path = Paths.get(prgOutput)
ErrorReporting.debug("Writing output to " + path.toAbsolutePath)
ErrorReporting.debug(s"Total output size: ${result.code.length} bytes")
Files.write(path, result.code)
val defaultPrgOutput = if (output.endsWith(platform.fileExtension)) output else output + platform.fileExtension
result.code.foreach{
case (bankName, code) =>
val prgOutput = if (bankName == "default") {
defaultPrgOutput
} else {
s"${output.stripSuffix(platform.fileExtension)}.$bankName${platform.fileExtension}"
}
val path = Paths.get(prgOutput)
ErrorReporting.debug("Writing output to " + path.toAbsolutePath)
ErrorReporting.debug(s"Total output size: ${code.length} bytes")
Files.write(path, code)
}
ErrorReporting.debug(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
c.runFileName.foreach(program =>
new ProcessBuilder(program, path.toAbsolutePath.toString).start()
new ProcessBuilder(program, Paths.get(defaultPrgOutput).toAbsolutePath.toString).start()
)
}

View File

@ -12,32 +12,25 @@ import org.apache.commons.configuration2.INIConfiguration
* @author Karol Stasiak
*/
object OutputStyle extends Enumeration {
val Single, PerBank = Value
}
class Platform(
val cpu: Cpu.Value,
val flagOverrides: Map[CompilationFlag.Value, Boolean],
val startingModules: List[String],
val outputPackager: OutputPackager,
val codeAllocator: UpwardByteAllocator,
val variableAllocator: VariableAllocator,
val codeAllocators: Map[String, UpwardByteAllocator],
val variableAllocators: Map[String, VariableAllocator],
val fileExtension: String,
var defaultCodeBank: Int = 0,
val bankNumbers: Map[String, Int],
val defaultCodeBank: String,
val outputStyle: OutputStyle.Value
)
object Platform {
val C64 = new Platform(
Cpu.Mos,
Map(),
List("c64_hardware", "c64_loader"),
SequenceOutput(List(StartAddressOutput, AllocatedDataOutput)),
new UpwardByteAllocator(0x80D, 0xA000),
new VariableAllocator(
List(0xC1, 0xC3, 0xFB, 0xFD, 0x39, 0x3B, 0x3D, 0x43, 0x4B),
new AfterCodeByteAllocator(0xA000)
),
".prg"
)
def lookupPlatformFile(includePath: List[String], platformName: String): Platform = {
includePath.foreach { dir =>
val file = Paths.get(dir, platformName + ".ini").toFile
@ -79,7 +72,7 @@ object Platform {
}).toMap ++ CompilationFlag.fromString.flatMap { case (k, f) =>
val value = cs.get(classOf[String], k, "")
value.toLowerCase match {
case "" => None
case "" | null => None
case "false" | "off" | "no" | "0" => Some(f -> false)
case "true" | "on" | "yes" | "1" => Some(f -> true)
case _ =>
@ -90,23 +83,66 @@ object Platform {
val startingModules = cs.get(classOf[String], "modules", "").split("[, ]+").filter(_.nonEmpty).toList
val as = conf.getSection("allocation")
val org = as.get(classOf[String], "main_org", "") match {
case "" => ErrorReporting.fatal(s"Undefined main_org")
case m => parseNumber(m)
val banks = as.get(classOf[String], "segments", "default").split("[, ]+").filter(_.nonEmpty).toList
if (!banks.contains("default")) {
ErrorReporting.error("A segment named `default` is required")
}
if (banks.toSet.size != banks.length) {
ErrorReporting.error("Duplicate segment name")
}
val BankRegex = """\A[A-Za-z0-9_]+\z""".r
banks.foreach {
case BankRegex(_*) => // ok
case b => ErrorReporting.error(s"Invalid segment name: `$b`")
}
val bankStarts = banks.map(b => b -> (as.get(classOf[String], s"segment_${b}_start") match {
case "" | null => ErrorReporting.error(s"Undefined segment_${b}_start"); 0
case x => parseNumber(x)
})).toMap
val bankDataStarts = banks.map(b => b -> (as.get(classOf[String], s"segment_${b}_datastart", "after_code") match {
case "" | "after_code" => None
case x => Some(parseNumber(x))
})).toMap
val bankEnds = banks.map(b => b -> (as.get(classOf[String], s"segment_${b}_end") match {
case "" | null => ErrorReporting.error(s"Undefined segment_${b}_end"); 0xffff
case x => parseNumber(x)
})).toMap
val bankCodeEnds = banks.map(b => b -> (as.get(classOf[String], s"segment_${b}_codeend", "") match {
case "" => bankEnds(b)
case x => parseNumber(x)
})).toMap
val defaultCodeBank = as.get(classOf[String], "default_code_segment") match {
case "" | null => "default"
case x => x
}
// used by 65816:
val bankNumbers = banks.map(b => b -> (as.get(classOf[String], s"segment_${b}_bank", "00") match {
case "" => 0
case x => parseNumber(x)
})).toMap
// TODO: validate stuff
banks.foreach(b => {
if (bankNumbers(b) < 0 || bankNumbers(b) > 255) ErrorReporting.error(s"Segment $b has invalid bank")
if (bankStarts(b) >= bankCodeEnds(b)) ErrorReporting.error(s"Segment $b has invalid range")
if (bankCodeEnds(b) > bankEnds(b)) ErrorReporting.error(s"Segment $b has invalid range")
if (bankStarts(b) >= bankEnds(b)) ErrorReporting.error(s"Segment $b has invalid range")
bankDataStarts(b).foreach(dataStarts => if (dataStarts >= bankEnds(b)) ErrorReporting.error(s"Segment $b has invalid range"))
})
val freePointers = as.get(classOf[String], "zp_pointers", "all") match {
case "all" => List.tabulate(128)(_ * 2)
case xs => xs.split("[, ]+").map(parseNumber).toList
}
val himemEnd = as.get(classOf[String], "himem_end", "") match {
case "" => ErrorReporting.fatal(s"Undefined himem_end")
case end => parseNumber(end) + 1
}
val byteAllocator = as.get(classOf[String], "himem_start", "") match {
case "" => ErrorReporting.fatal(s"Undefined himem_start")
case "after_code" => new AfterCodeByteAllocator(himemEnd)
case start => new UpwardByteAllocator(parseNumber(start), himemEnd)
}
val codeAllocators = banks.map(b => b -> new UpwardByteAllocator(bankStarts(b), bankCodeEnds(b)))
val variableAllocators = banks.map(b => b -> new VariableAllocator(
if (b == "default") freePointers else Nil, bankDataStarts(b) match {
case None => new AfterCodeByteAllocator(bankEnds(b))
case Some(start) => new UpwardByteAllocator(start, bankEnds(b))
}))
val os = conf.getSection("output")
val outputPackager = SequenceOutput(os.get(classOf[String], "format", "").split("[, ]+").filter(_.nonEmpty).map {
@ -114,18 +150,26 @@ object Platform {
case "endaddr" => EndAddressOutput
case "allocated" => AllocatedDataOutput
case n => n.split(":").filter(_.nonEmpty) match {
case Array(b, s, e) => BankFragmentOutput(parseNumber(b), parseNumber(s), parseNumber(e))
case Array(b, s, e) => BankFragmentOutput(b, parseNumber(s), parseNumber(e))
case Array(s, e) => CurrentBankFragmentOutput(parseNumber(s), parseNumber(e))
case Array(b) => ConstOutput(parseNumber(b).toByte)
case x => ErrorReporting.fatal(s"Invalid output format: `$x`")
}
}.toList)
var fileExtension = os.get(classOf[String], "extension", ".bin")
val fileExtension = os.get(classOf[String], "extension", ".bin")
val outputStyle = os.get(classOf[String], "style", "single") match {
case "" | "single" => OutputStyle.Single
case "per_bank" | "per_segment" => OutputStyle.PerBank
case x => ErrorReporting.fatal(s"Invalid output style: `$x`")
}
new Platform(cpu, flagOverrides, startingModules, outputPackager,
new UpwardByteAllocator(org, 0xffff),
new VariableAllocator(freePointers, byteAllocator),
if (fileExtension.startsWith(".")) fileExtension else "." + fileExtension)
codeAllocators.toMap,
variableAllocators.toMap,
if (fileExtension.startsWith(".")) fileExtension else "." + fileExtension,
bankNumbers,
defaultCodeBank,
outputStyle)
}
def parseNumber(s: String): Int = {

View File

@ -386,7 +386,7 @@ object ExpressionCompiler {
def callingContext(ctx: CompilationContext, v: MemoryVariable): CompilationContext = {
val result = new Environment(Some(ctx.env), "")
result.registerVariable(VariableDeclarationStatement(v.name, v.typ.name, stack = false, global = false, constant = false, volatile = false, register = false, initialValue = None, address = None), ctx.options)
result.registerVariable(VariableDeclarationStatement(v.name, v.typ.name, stack = false, global = false, constant = false, volatile = false, register = false, initialValue = None, address = None, bank = v.declaredBank), ctx.options)
ctx.copy(env = result)
}

View File

@ -126,7 +126,8 @@ object ReturnDispatch {
val paramArrays = stmt.params.indices.map { ix =>
val a = InitializedArray(label + "$" + ix + ".array", None, (paramMins(ix) to paramMaxes(ix)).map { key =>
map(key)._2.lift(ix).getOrElse(Constant.Zero)
}.toList)
}.toList,
ctx.function.declaredBank)
env.registerUnnamedArray(a)
a
}
@ -146,7 +147,7 @@ object ReturnDispatch {
}
if (useJmpaix) {
val jumpTable = InitializedArray(label + "$jt.array", None, (actualMin to actualMax).flatMap(i => List(map(i)._1.loByte, map(i)._1.hiByte)).toList)
val jumpTable = InitializedArray(label + "$jt.array", None, (actualMin to actualMax).flatMap(i => List(map(i)._1.loByte, map(i)._1.hiByte)).toList, ctx.function.declaredBank)
env.registerUnnamedArray(jumpTable)
if (copyParams.isEmpty) {
val loadIndex = ExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(Register.A, b)), BranchSpec.None)
@ -161,8 +162,8 @@ object ReturnDispatch {
}
} else {
val loadIndex = ExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(Register.X, b)), BranchSpec.None)
val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => (map(i)._1 - 1).loByte).toList)
val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => (map(i)._1 - 1).hiByte).toList)
val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => (map(i)._1 - 1).loByte).toList, ctx.function.declaredBank)
val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => (map(i)._1 - 1).hiByte).toList, ctx.function.declaredBank)
env.registerUnnamedArray(jumpTableLo)
env.registerUnnamedArray(jumpTableHi)
loadIndex ++ copyParams ++ List(

View File

@ -23,7 +23,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
private val relVarId = new AtomicLong
def genRelativeVariable(constant: Constant, typ: Type, zeropage: Boolean): RelativeVariable = {
val variable = RelativeVariable(".rv__" + relVarId.incrementAndGet().formatted("%06d"), constant, typ, zeropage = zeropage)
val variable = RelativeVariable(".rv__" + relVarId.incrementAndGet().formatted("%06d"), constant, typ, zeropage = zeropage, declaredBank = None /*TODO*/)
addThing(variable, None)
variable
}
@ -68,19 +68,19 @@ class Environment(val parent: Option[Environment], val prefix: String) {
case _ => Nil
}.toList
def getAllFixedAddressObjects: List[(Int, Int)] = {
def getAllFixedAddressObjects: List[(String, Int, Int)] = {
things.values.flatMap {
case RelativeArray(_, NumericConstant(addr, _), size) =>
List(addr.toInt -> size)
case RelativeVariable(_, NumericConstant(addr, _), typ, _) =>
List(addr.toInt -> typ.size)
case RelativeArray(_, NumericConstant(addr, _), size, declaredBank) =>
List((declaredBank.getOrElse("default"), addr.toInt, size))
case RelativeVariable(_, NumericConstant(addr, _), typ, _, declaredBank) =>
List((declaredBank.getOrElse("default"), addr.toInt, typ.size))
case f: NormalFunction =>
f.environment.getAllFixedAddressObjects
case _ => Nil
}.toList
}
def allocateVariables(nf: Option[NormalFunction], mem: MemoryBank, callGraph: CallGraph, allocator: VariableAllocator, options: CompilationOptions, onEachVariable: (String, Int) => Unit): Unit = {
def allocateVariables(nf: Option[NormalFunction], mem: CompiledMemory, callGraph: CallGraph, allocators: Map[String, VariableAllocator], options: CompilationOptions, onEachVariable: (String, Int) => Unit): Unit = {
val b = get[Type]("byte")
val p = get[Type]("pointer")
val params = nf.fold(List[String]()) { f =>
@ -104,6 +104,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
}
}
} else GlobalVertex
val bank = m.bank(options)
m.alloc match {
case VariableAllocationMethod.None =>
Nil
@ -111,7 +112,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
m.sizeInBytes match {
case 2 =>
val addr =
allocator.allocatePointer(mem, callGraph, vertex)
allocators(bank).allocatePointer(mem.banks(bank), callGraph, vertex)
onEachVariable(m.name, addr)
List(
ConstantThing(m.name.stripPrefix(prefix) + "`", NumericConstant(addr, 2), p)
@ -125,13 +126,13 @@ class Environment(val parent: Option[Environment], val prefix: String) {
case 0 => Nil
case 2 =>
val addr =
allocator.allocateBytes(mem, callGraph, vertex, options, 2, initialized = false, writeable = true)
allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, 2, initialized = false, writeable = true)
onEachVariable(m.name, addr)
List(
ConstantThing(m.name.stripPrefix(prefix) + "`", NumericConstant(addr, 2), p)
)
case count =>
val addr = allocator.allocateBytes(mem, callGraph, vertex, options, count, initialized = false, writeable = true)
val addr = allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, count, initialized = false, writeable = true)
onEachVariable(m.name, addr)
List(
ConstantThing(m.name.stripPrefix(prefix) + "`", NumericConstant(addr, 2), p)
@ -139,7 +140,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
}
}
case f: NormalFunction =>
f.environment.allocateVariables(Some(f), mem, callGraph, allocator, options, onEachVariable)
f.environment.allocateVariables(Some(f), mem, callGraph, allocators, options, onEachVariable)
Nil
case _ => Nil
}.toList
@ -200,9 +201,9 @@ class Environment(val parent: Option[Environment], val prefix: String) {
InitializedMemoryVariable
UninitializedMemoryVariable
getArrayOrPointer(name) match {
case th@InitializedArray(_, _, cs) => ConstantPointy(th.toAddress, Some(cs.length))
case th@UninitializedArray(_, size) => ConstantPointy(th.toAddress, Some(size))
case th@RelativeArray(_, _, size) => ConstantPointy(th.toAddress, Some(size))
case th@InitializedArray(_, _, cs, _) => ConstantPointy(th.toAddress, Some(cs.length))
case th@UninitializedArray(_, size, _) => ConstantPointy(th.toAddress, Some(size))
case th@RelativeArray(_, _, size, _) => ConstantPointy(th.toAddress, Some(size))
case ConstantThing(_, value, typ) if typ.size <= 2 => ConstantPointy(value, None)
case th:VariableInMemory => VariablePointy(th.toAddress)
case _ =>
@ -480,7 +481,8 @@ class Environment(val parent: Option[Environment], val prefix: String) {
resultType,
params,
addr,
env
env,
stmt.bank
)
addThing(mangled, stmt.position)
registerAddressConstant(mangled, stmt.position)
@ -498,12 +500,15 @@ class Environment(val parent: Option[Environment], val prefix: String) {
}
val needsExtraRTS = !stmt.isMacro && !stmt.assembly && (statements.isEmpty || !statements.last.isInstanceOf[ReturnStatement])
if (stmt.isMacro) {
if (stmt.bank.isDefined) {
ErrorReporting.error("Macro functions cannot be in a defined segment", stmt.position)
}
val mangled = MacroFunction(
name,
resultType,
params,
env,
executableStatements ++ (if (needsExtraRTS) List(AssemblyStatement.implied(Opcode.RTS, elidable = true)) else Nil),
executableStatements ++ (if (needsExtraRTS) List(AssemblyStatement.implied(Opcode.RTS, elidable = true)) else Nil)
)
addThing(mangled, stmt.position)
} else {
@ -522,7 +527,8 @@ class Environment(val parent: Option[Environment], val prefix: String) {
interrupt = stmt.interrupt,
kernalInterrupt = stmt.kernalInterrupt,
reentrant = stmt.reentrant,
position = stmt.position
position = stmt.position,
declaredBank = stmt.bank
)
addThing(mangled, stmt.position)
registerAddressConstant(mangled, stmt.position)
@ -544,13 +550,13 @@ class Environment(val parent: Option[Environment], val prefix: String) {
stmt.assemblyParamPassingConvention match {
case ByVariable(name) =>
val zp = typ.name == "pointer" // TODO
val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto)
val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto, None)
addThing(v, stmt.position)
registerAddressConstant(v, stmt.position)
if (typ.size == 2) {
val addr = v.toAddress
addThing(RelativeVariable(v.name + ".hi", addr + 1, b, zeropage = zp), stmt.position)
addThing(RelativeVariable(v.name + ".lo", addr, b, zeropage = zp), stmt.position)
addThing(RelativeVariable(v.name + ".hi", addr + 1, b, zeropage = zp, None), stmt.position)
addThing(RelativeVariable(v.name + ".lo", addr, b, zeropage = zp, None), stmt.position)
}
case ByRegister(_) => ()
case ByConstant(name) =>
@ -558,10 +564,10 @@ class Environment(val parent: Option[Environment], val prefix: String) {
addThing(v, stmt.position)
case ByReference(name) =>
val addr = UnexpandedConstant(prefix + name, typ.size)
val v = RelativeVariable(prefix + name, addr, p, zeropage = false)
val v = RelativeVariable(prefix + name, addr, p, zeropage = false, None)
addThing(v, stmt.position)
addThing(RelativeVariable(v.name + ".hi", addr + 1, b, zeropage = false), stmt.position)
addThing(RelativeVariable(v.name + ".lo", addr, b, zeropage = false), stmt.position)
addThing(RelativeVariable(v.name + ".hi", addr + 1, b, zeropage = false, None), stmt.position)
addThing(RelativeVariable(v.name + ".lo", addr, b, zeropage = false, None), stmt.position)
}
}
@ -589,16 +595,19 @@ class Environment(val parent: Option[Environment], val prefix: String) {
case NumericConstant(length, _) =>
if (length > 0xffff || length < 0) ErrorReporting.error(s"Array `${stmt.name}` has invalid length", stmt.position)
val array = address match {
case None => UninitializedArray(stmt.name + ".array", length.toInt)
case Some(aa) => RelativeArray(stmt.name + ".array", aa, length.toInt)
case None => UninitializedArray(stmt.name + ".array", length.toInt,
declaredBank = stmt.bank)
case Some(aa) => RelativeArray(stmt.name + ".array", aa, length.toInt,
declaredBank = stmt.bank)
}
addThing(array, stmt.position)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None), stmt.position)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank), stmt.position)
val a = address match {
case None => array.toAddress
case Some(aa) => aa
}
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false), stmt.position)
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false,
declaredBank = stmt.bank), stmt.position)
addThing(ConstantThing(stmt.name, a, p), stmt.position)
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(stmt.name + ".lo", a.loByte.quickSimplify, b), stmt.position)
@ -625,14 +634,17 @@ class Environment(val parent: Option[Environment], val prefix: String) {
if (length > 0xffff || length < 0) ErrorReporting.error(s"Array `${stmt.name}` has invalid length", stmt.position)
val address = stmt.address.map(a => eval(a).getOrElse(Constant.error(s"Array `${stmt.name}` has non-constant address", stmt.position)))
val data = contents.map(x => eval(x).getOrElse(Constant.error(s"Array `${stmt.name}` has non-constant contents", stmt.position)))
val array = InitializedArray(stmt.name + ".array", address, data)
val array = InitializedArray(stmt.name + ".array", address, data,
declaredBank = stmt.bank)
addThing(array, stmt.position)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None), stmt.position)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None,
declaredBank = stmt.bank), stmt.position)
val a = address match {
case None => array.toAddress
case Some(aa) => aa
}
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false), stmt.position)
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false,
declaredBank = stmt.bank), stmt.position)
addThing(ConstantThing(stmt.name, a, p), stmt.position)
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(stmt.name + ".lo", a.loByte.quickSimplify, b), stmt.position)
@ -704,12 +716,14 @@ class Environment(val parent: Option[Environment], val prefix: String) {
if (alloc != VariableAllocationMethod.Static && stmt.initialValue.isDefined) {
ErrorReporting.error(s"`$name` cannot be preinitialized`", position)
}
val v = stmt.initialValue.fold[MemoryVariable](UninitializedMemoryVariable(prefix + name, typ, alloc)){ive =>
val v = stmt.initialValue.fold[MemoryVariable](UninitializedMemoryVariable(prefix + name, typ, alloc,
declaredBank = stmt.bank)){ive =>
if (options.flags(CompilationFlag.ReadOnlyArrays)) {
ErrorReporting.warn("Initialized variable in read-only segment", options, position)
}
val ivc = eval(ive).getOrElse(Constant.error(s"Initial value of `$name` is not a constant", position))
InitializedMemoryVariable(name, None, typ, ivc)
InitializedMemoryVariable(name, None, typ, ivc,
declaredBank = stmt.bank)
}
registerAddressConstant(v, stmt.position)
(v, v.toAddress)
@ -719,7 +733,8 @@ class Environment(val parent: Option[Environment], val prefix: String) {
case NumericConstant(n, _) => n < 0x100
case _ => false
}
val v = RelativeVariable(prefix + name, addr, typ, zeropage = zp)
val v = RelativeVariable(prefix + name, addr, typ, zeropage = zp,
declaredBank = stmt.bank)
registerAddressConstant(v, stmt.position)
(v, addr)
})
@ -728,8 +743,10 @@ class Environment(val parent: Option[Environment], val prefix: String) {
addThing(ConstantThing(v.name + "`", addr, b), stmt.position)
}
if (typ.size == 2) {
addThing(RelativeVariable(prefix + name + ".hi", addr + 1, b, zeropage = v.zeropage), stmt.position)
addThing(RelativeVariable(prefix + name + ".lo", addr, b, zeropage = v.zeropage), stmt.position)
addThing(RelativeVariable(prefix + name + ".hi", addr + 1, b, zeropage = v.zeropage,
declaredBank = stmt.bank), stmt.position)
addThing(RelativeVariable(prefix + name + ".lo", addr, b, zeropage = v.zeropage,
declaredBank = stmt.bank), stmt.position)
}
}
}
@ -771,7 +788,8 @@ class Environment(val parent: Option[Environment], val prefix: String) {
def collectDeclarations(program: Program, options: CompilationOptions): Unit = {
if (options.flag(CompilationFlag.OptimizeForSonicSpeed)) {
addThing(InitializedArray("identity$", None, List.tabulate(256)(n => NumericConstant(n, 1))), None)
addThing(InitializedArray("identity$", None, List.tabulate(256)(n => NumericConstant(n, 1)),
declaredBank = None), None)
}
program.declarations.foreach {
case f: FunctionDeclarationStatement => registerFunction(f, options)
@ -782,6 +800,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
if (options.flag(CompilationFlag.ZeropagePseudoregister) && !things.contains("__reg")) {
registerVariable(VariableDeclarationStatement(
name = "__reg",
bank = None,
typ = "pointer",
global = true,
stack = false,
@ -792,7 +811,8 @@ class Environment(val parent: Option[Environment], val prefix: String) {
address = None), options)
}
if (!things.contains("__constant8")) {
things("__constant8") = InitializedArray("__constant8", None, List(NumericConstant(8, 1)))
things("__constant8") = InitializedArray("__constant8", None, List(NumericConstant(8, 1)),
declaredBank = None)
}
}

View File

@ -81,10 +81,10 @@ sealed trait ThingInMemory extends Thing {
def toAddress: Constant
var farFlag: Option[Boolean] = None
var declaredBank: Option[Int] = None
val declaredBank: Option[String]
def isFar(compilationOptions: CompilationOptions): Boolean
def bank(compilationOptions: CompilationOptions): Int
def bank(compilationOptions: CompilationOptions): String
}
sealed trait PreallocableThing extends ThingInMemory {
@ -101,8 +101,10 @@ case class Label(name: String) extends ThingInMemory {
override def isFar(compilationOptions: CompilationOptions): Boolean =
compilationOptions.flag(CompilationFlag.LargeCode) && farFlag.getOrElse(true)
override def bank(compilationOptions: CompilationOptions): Int =
override def bank(compilationOptions: CompilationOptions): String =
declaredBank.getOrElse(compilationOptions.platform.defaultCodeBank)
override val declaredBank: Option[String] = None
}
sealed trait Variable extends TypedThing with VariableLikeThing
@ -117,8 +119,8 @@ sealed trait VariableInMemory extends Variable with ThingInMemory with Indexable
override def isFar(compilationOptions: CompilationOptions): Boolean =
!zeropage && farFlag.getOrElse(false)
override def bank(compilationOptions: CompilationOptions): Int =
declaredBank.getOrElse(0)
override def bank(compilationOptions: CompilationOptions): String =
declaredBank.getOrElse("default")
}
case class RegisterVariable(register: Register.Value, typ: Type) extends Variable {
@ -149,7 +151,7 @@ abstract class MemoryVariable extends VariableInMemory {
def alloc: VariableAllocationMethod.Value
}
case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableAllocationMethod.Value) extends MemoryVariable with UninitializedMemory {
case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableAllocationMethod.Value, declaredBank: Option[String]) extends MemoryVariable with UninitializedMemory {
override def sizeInBytes: Int = typ.size
override def zeropage: Boolean = alloc == VariableAllocationMethod.Zeropage
@ -157,7 +159,7 @@ case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableA
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
}
case class InitializedMemoryVariable(name: String, address: Option[Constant], typ: Type, initialValue: Constant) extends MemoryVariable with PreallocableThing {
case class InitializedMemoryVariable(name: String, address: Option[Constant], typ: Type, initialValue: Constant, declaredBank: Option[String]) extends MemoryVariable with PreallocableThing {
override def zeropage: Boolean = false
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
@ -169,33 +171,33 @@ case class InitializedMemoryVariable(name: String, address: Option[Constant], ty
trait MfArray extends ThingInMemory with IndexableThing
case class UninitializedArray(name: String, sizeInBytes: Int) extends MfArray with UninitializedMemory {
case class UninitializedArray(name: String, sizeInBytes: Int, declaredBank: Option[String]) extends MfArray with UninitializedMemory {
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
override def alloc = VariableAllocationMethod.Static
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
override def bank(compilationOptions: CompilationOptions): Int = declaredBank.getOrElse(0)
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse("default")
}
case class RelativeArray(name: String, address: Constant, sizeInBytes: Int) extends MfArray {
case class RelativeArray(name: String, address: Constant, sizeInBytes: Int, declaredBank: Option[String]) extends MfArray {
override def toAddress: Constant = address
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
override def bank(compilationOptions: CompilationOptions): Int = declaredBank.getOrElse(0)
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse("default")
}
case class InitializedArray(name: String, address: Option[Constant], contents: List[Constant]) extends MfArray with PreallocableThing {
case class InitializedArray(name: String, address: Option[Constant], contents: List[Constant], declaredBank: Option[String]) extends MfArray with PreallocableThing {
override def shouldGenerate = true
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
override def bank(compilationOptions: CompilationOptions): Int = declaredBank.getOrElse(0)
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse(compilationOptions.platform.defaultCodeBank)
}
case class RelativeVariable(name: String, address: Constant, typ: Type, zeropage: Boolean) extends VariableInMemory {
case class RelativeVariable(name: String, address: Constant, typ: Type, zeropage: Boolean, declaredBank: Option[String]) extends VariableInMemory {
override def toAddress: Constant = address
}
@ -231,7 +233,7 @@ sealed trait FunctionInMemory extends MangledFunction with ThingInMemory {
override def isFar(compilationOptions: CompilationOptions): Boolean =
compilationOptions.flag(CompilationFlag.LargeCode) && farFlag.getOrElse(true)
override def bank(compilationOptions: CompilationOptions): Int =
override def bank(compilationOptions: CompilationOptions): String =
declaredBank.getOrElse(compilationOptions.platform.defaultCodeBank)
}
@ -239,7 +241,8 @@ case class ExternFunction(name: String,
returnType: Type,
params: ParamSignature,
address: Constant,
environment: Environment) extends FunctionInMemory {
environment: Environment,
declaredBank: Option[String]) extends FunctionInMemory {
override def toAddress: Constant = address
override def interrupt = false
@ -255,7 +258,8 @@ case class NormalFunction(name: String,
interrupt: Boolean,
kernalInterrupt: Boolean,
reentrant: Boolean,
position: Option[Position]) extends FunctionInMemory with PreallocableThing {
position: Option[Position],
declaredBank: Option[String]) extends FunctionInMemory with PreallocableThing {
override def shouldGenerate = true
}

View File

@ -93,6 +93,7 @@ case class TypeDefinitionStatement(name: String, parent: String) extends Declara
case class VariableDeclarationStatement(name: String,
typ: String,
bank: Option[String],
global: Boolean,
stack: Boolean,
constant: Boolean,
@ -104,6 +105,7 @@ case class VariableDeclarationStatement(name: String,
}
case class ArrayDeclarationStatement(name: String,
bank: Option[String],
length: Option[Expression],
address: Option[Expression],
elements: Option[List[Expression]]) extends DeclarationStatement {
@ -120,6 +122,7 @@ case class ImportStatement(filename: String) extends DeclarationStatement {
case class FunctionDeclarationStatement(name: String,
resultType: String,
params: List[ParameterDeclaration],
bank: Option[String],
address: Option[Expression],
statements: Option[List[Statement]],
isMacro: Boolean,

View File

@ -6,7 +6,7 @@ import millfork.compiler.{CompilationContext, MfCompiler}
import millfork.env._
import millfork.error.ErrorReporting
import millfork.node.{CallGraph, Program}
import millfork.{CompilationFlag, CompilationOptions, Tarjan}
import millfork._
import scala.collection.mutable
@ -14,41 +14,41 @@ import scala.collection.mutable
* @author Karol Stasiak
*/
case class AssemblerOutput(code: Array[Byte], asm: Array[String], labels: List[(String, Int)])
case class AssemblerOutput(code: Map[String, Array[Byte]], asm: Array[String], labels: List[(String, Int)])
class Assembler(private val program: Program, private val rootEnv: Environment) {
class Assembler(private val program: Program, private val rootEnv: Environment, private val platform: Platform) {
private var env = rootEnv.allThings
var unoptimizedCodeSize: Int = 0
var optimizedCodeSize: Int = 0
var initializedVariablesSize: Int = 0
val mem = new CompiledMemory
val mem = new CompiledMemory(platform.bankNumbers.keys.toList)
val labelMap: mutable.Map[String, Int] = mutable.Map()
private val bytesToWriteLater = mutable.ListBuffer[(Int, Int, Constant)]()
private val wordsToWriteLater = mutable.ListBuffer[(Int, Int, Constant)]()
private val bytesToWriteLater = mutable.ListBuffer[(String, Int, Constant)]()
private val wordsToWriteLater = mutable.ListBuffer[(String, Int, Constant)]()
def writeByte(bank: Int, addr: Int, value: Byte): Unit = {
def writeByte(bank: String, addr: Int, value: Byte): Unit = {
mem.banks(bank).occupied(addr) = true
mem.banks(bank).initialized(addr) = true
mem.banks(bank).readable(addr) = true
mem.banks(bank).output(addr) = value.toByte
}
def writeByte(bank: Int, addr: Int, value: Constant): Unit = {
def writeByte(bank: String, addr: Int, value: Constant): Unit = {
mem.banks(bank).occupied(addr) = true
mem.banks(bank).initialized(addr) = true
mem.banks(bank).readable(addr) = true
value match {
case NumericConstant(x, _) =>
if (x > 0xffff) ErrorReporting.error("Byte overflow")
mem.banks(0).output(addr) = x.toByte
if (x > 0xff) ErrorReporting.error("Byte overflow")
mem.banks(bank).output(addr) = x.toByte
case _ =>
bytesToWriteLater += ((bank, addr, value))
}
}
def writeWord(bank: Int, addr: Int, value: Constant): Unit = {
def writeWord(bank: String, addr: Int, value: Constant): Unit = {
mem.banks(bank).occupied(addr) = true
mem.banks(bank).occupied(addr + 1) = true
mem.banks(bank).initialized(addr) = true
@ -202,15 +202,15 @@ class Assembler(private val program: Program, private val rootEnv: Environment)
}
}
val bank0 = mem.banks(0)
env.allPreallocatables.foreach {
case InitializedArray(name, Some(NumericConstant(address, _)), items) =>
case thing@InitializedArray(name, Some(NumericConstant(address, _)), items, _) =>
val bank = thing.bank(options)
val bank0 = mem.banks(bank)
var index = address.toInt
assembly.append("* = $" + index.toHexString)
assembly.append(name)
for (item <- items) {
writeByte(0, index, item)
writeByte(bank, index, item)
bank0.occupied(index) = true
bank0.initialized(index) = true
bank0.writeable(index) = true
@ -221,13 +221,15 @@ class Assembler(private val program: Program, private val rootEnv: Environment)
assembly.append(" !byte " + group.mkString(", "))
}
initializedVariablesSize += items.length
case InitializedArray(name, Some(_), items) => ???
case thing@InitializedArray(name, Some(_), items, _) => ???
case f: NormalFunction if f.address.isDefined =>
val bank = f.bank(options)
val bank0 = mem.banks(bank)
val index = f.address.get.asInstanceOf[NumericConstant].value.toInt
val code = compiledFunctions(f.name)
if (code.nonEmpty) {
labelMap(f.name) = index
val end = outputFunction(code, index, assembly, options)
val end = outputFunction(bank, code, index, assembly, options)
for(i <- index until end) {
bank0.occupied(index) = true
bank0.initialized(index) = true
@ -237,68 +239,77 @@ class Assembler(private val program: Program, private val rootEnv: Environment)
case _ =>
}
val codeAllocator = new VariableAllocator(Nil, platform.codeAllocator)
var justAfterCode = platform.codeAllocator.startAt
val codeAllocators = platform.codeAllocators.mapValues(new VariableAllocator(Nil, _))
var justAfterCode = platform.codeAllocators.mapValues(a => a.startAt)
env.allPreallocatables.foreach {
case f: NormalFunction if f.address.isEmpty && f.name == "main" =>
val bank = f.bank(options)
val code = compiledFunctions(f.name)
if (code.nonEmpty) {
val size = code.map(_.sizeInBytes).sum
val index = codeAllocator.allocateBytes(bank0, options, size, initialized = true, writeable = false)
val index = codeAllocators(bank).allocateBytes(mem.banks(bank), options, size, initialized = true, writeable = false)
labelMap(f.name) = index
justAfterCode = outputFunction(code, index, assembly, options)
justAfterCode += bank -> outputFunction(bank, code, index, assembly, options)
}
case _ =>
}
env.allPreallocatables.foreach {
case f: NormalFunction if f.address.isEmpty && f.name != "main" =>
val bank = f.bank(options)
val bank0 = mem.banks(bank)
val code = compiledFunctions(f.name)
if (code.nonEmpty) {
val size = code.map(_.sizeInBytes).sum
val index = codeAllocator.allocateBytes(bank0, options, size, initialized = true, writeable = false)
val index = codeAllocators(bank).allocateBytes(bank0, options, size, initialized = true, writeable = false)
labelMap(f.name) = index
justAfterCode = outputFunction(code, index, assembly, options)
justAfterCode += bank -> outputFunction(bank, code, index, assembly, options)
}
case _ =>
}
env.allPreallocatables.foreach {
case InitializedArray(name, None, items) =>
var index = codeAllocator.allocateBytes(bank0, options, items.size, initialized = true, writeable = true)
case thing@InitializedArray(name, None, items, _) =>
val bank = thing.bank(options)
val bank0 = mem.banks(bank)
var index = codeAllocators(bank).allocateBytes(bank0, options, items.size, initialized = true, writeable = true)
labelMap(name) = index
assembly.append("* = $" + index.toHexString)
assembly.append(name)
for (item <- items) {
writeByte(0, index, item)
writeByte(bank, index, item)
index += 1
}
items.grouped(16).foreach {group =>
assembly.append(" !byte " + group.mkString(", "))
}
initializedVariablesSize += items.length
justAfterCode = index
case m@InitializedMemoryVariable(name, None, typ, value) =>
var index = codeAllocator.allocateBytes(bank0, options, typ.size, initialized = true, writeable = true)
justAfterCode += bank -> index
case m@InitializedMemoryVariable(name, None, typ, value, _) =>
val bank = m.bank(options)
val bank0 = mem.banks(bank)
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size, initialized = true, writeable = true)
labelMap(name) = index
val altName = m.name.stripPrefix(env.prefix) + "`"
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
assembly.append("* = $" + index.toHexString)
assembly.append(name)
for (i <- 0 until typ.size) {
writeByte(0, index, value.subbyte(i))
writeByte(bank, index, value.subbyte(i))
assembly.append(" !byte " + value.subbyte(i).quickSimplify)
index += 1
}
initializedVariablesSize += typ.size
justAfterCode = index
justAfterCode += bank -> index
case _ =>
}
env.getAllFixedAddressObjects.foreach {
case (addr, size) =>
case (bank, addr, size) =>
val bank0 = mem.banks(bank)
for(i <- 0 until size) bank0.occupied(addr + i) = true
}
val variableAllocator = platform.variableAllocator
variableAllocator.notifyAboutEndOfCode(justAfterCode)
env.allocateVariables(None, bank0, callGraph, variableAllocator, options, labelMap.put)
val variableAllocators = platform.variableAllocators
variableAllocators.foreach{case (b,a) => a.notifyAboutEndOfCode(justAfterCode(b))}
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put)
env = rootEnv.allThings
@ -327,7 +338,12 @@ class Assembler(private val program: Program, private val rootEnv: Environment)
assembly += f" ; $$$v%04X = $l%s"
}
AssemblerOutput(platform.outputPackager.packageOutput(mem, 0), assembly.toArray, labelMap.toList)
// TODO:
val code = (platform.outputStyle match {
case OutputStyle.Single => List("default")
case OutputStyle.PerBank => platform.bankNumbers.keys.toList
}).map(b => b -> platform.outputPackager.packageOutput(mem, b)).toMap
AssemblerOutput(code, assembly.toArray, labelMap.toList)
}
private def compileFunction(f: NormalFunction, optimizations: Seq[AssemblyOptimization], options: CompilationOptions, inlinedFunctions: Map[String, List[AssemblyLine]]): List[AssemblyLine] = {
@ -363,7 +379,7 @@ class Assembler(private val program: Program, private val rootEnv: Environment)
else code
}
private def outputFunction(code: List[AssemblyLine], startFrom: Int, assOut: mutable.ArrayBuffer[String], options: CompilationOptions): Int = {
private def outputFunction(bank: String, code: List[AssemblyLine], startFrom: Int, assOut: mutable.ArrayBuffer[String], options: CompilationOptions): Int = {
var index = startFrom
assOut.append("* = $" + startFrom.toHexString)
import millfork.assembly.AddrMode._
@ -378,24 +394,24 @@ class Assembler(private val program: Program, private val rootEnv: Environment)
case AssemblyLine(_, DoesNotExist, _, _) =>
()
case AssemblyLine(op, Implied, _, _) =>
writeByte(0, index, Assembler.opcodeFor(op, Implied, options))
writeByte(bank, index, Assembler.opcodeFor(op, Implied, options))
index += 1
case AssemblyLine(op, Relative, param, _) =>
writeByte(0, index, Assembler.opcodeFor(op, Relative, options))
writeByte(0, index + 1, param - (index + 2))
writeByte(bank, index, Assembler.opcodeFor(op, Relative, options))
writeByte(bank, index + 1, param - (index + 2))
index += 2
case AssemblyLine(op, am@(Immediate | ZeroPage | ZeroPageX | ZeroPageY | IndexedY | IndexedX | IndexedZ | LongIndexedY | LongIndexedZ | Stack), param, _) =>
writeByte(0, index, Assembler.opcodeFor(op, am, options))
writeByte(0, index + 1, param)
writeByte(bank, index, Assembler.opcodeFor(op, am, options))
writeByte(bank, index + 1, param)
index += 2
case AssemblyLine(op, am@(WordImmediate | Absolute | AbsoluteY | AbsoluteX | Indirect | AbsoluteIndexedX), param, _) =>
writeByte(0, index, Assembler.opcodeFor(op, am, options))
writeWord(0, index + 1, param)
writeByte(bank, index, Assembler.opcodeFor(op, am, options))
writeWord(bank, index + 1, param)
index += 3
case AssemblyLine(op, am@(LongAbsolute | LongAbsoluteX | LongIndirect), param, _) =>
writeByte(0, index, Assembler.opcodeFor(op, am, options))
writeWord(0, index + 1, param)
writeByte(0, index + 3, extractBank(param, options))
writeByte(bank, index, Assembler.opcodeFor(op, am, options))
writeWord(bank, index + 1, param)
writeByte(bank, index + 3, extractBank(param, options))
index += 4
}
}

View File

@ -5,8 +5,8 @@ import scala.collection.mutable
/**
* @author Karol Stasiak
*/
class CompiledMemory {
val banks = mutable.Map(0 -> new MemoryBank)
class CompiledMemory(bankNames: List[String]) {
val banks = mutable.Map(bankNames.map(_ -> new MemoryBank): _*)
}
class MemoryBank {

View File

@ -6,11 +6,11 @@ import java.io.ByteArrayOutputStream
* @author Karol Stasiak
*/
trait OutputPackager {
def packageOutput(mem: CompiledMemory, bank: Int): Array[Byte]
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte]
}
case class SequenceOutput(children: List[OutputPackager]) extends OutputPackager {
def packageOutput(mem: CompiledMemory, bank: Int): Array[Byte] = {
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
val baos = new ByteArrayOutputStream
children.foreach { c =>
val a = c.packageOutput(mem, bank)
@ -21,39 +21,39 @@ case class SequenceOutput(children: List[OutputPackager]) extends OutputPackager
}
case class ConstOutput(byte: Byte) extends OutputPackager {
def packageOutput(mem: CompiledMemory, bank: Int): Array[Byte] = Array(byte)
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = Array(byte)
}
case class CurrentBankFragmentOutput(start: Int, end: Int) extends OutputPackager {
def packageOutput(mem: CompiledMemory, bank: Int): Array[Byte] = {
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
val b = mem.banks(bank)
b.output.slice(start, end + 1)
}
}
case class BankFragmentOutput(alwaysBank: Int, start: Int, end: Int) extends OutputPackager {
def packageOutput(mem: CompiledMemory, bank: Int): Array[Byte] = {
case class BankFragmentOutput(alwaysBank: String, start: Int, end: Int) extends OutputPackager {
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
val b = mem.banks(alwaysBank)
b.output.slice(start, end + 1)
}
}
object StartAddressOutput extends OutputPackager {
def packageOutput(mem: CompiledMemory, bank: Int): Array[Byte] = {
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
val b = mem.banks(bank)
Array(b.start.toByte, b.start.>>(8).toByte)
}
}
object EndAddressOutput extends OutputPackager {
def packageOutput(mem: CompiledMemory, bank: Int): Array[Byte] = {
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
val b = mem.banks(bank)
Array(b.end.toByte, b.end.>>(8).toByte)
}
}
object AllocatedDataOutput extends OutputPackager {
def packageOutput(mem: CompiledMemory, bank: Int): Array[Byte] = {
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
val b = mem.banks(bank)
b.output.slice(b.start, b.end + 1)
}

View File

@ -140,6 +140,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
def variableDefinition(implicitlyGlobal: Boolean): P[Seq[DeclarationStatement]] = for {
p <- position()
bank <- bankDeclaration
flags <- flags("const", "static", "volatile", "stack", "register") ~ HWS
typ <- identifier ~ SWS
name <- identifier ~/ HWS ~/ Pass
@ -148,6 +149,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
_ <- &(EOL) ~/ ""
} yield {
Seq(VariableDeclarationStatement(name, typ,
bank,
global = implicitlyGlobal || flags("static"),
stack = flags("stack"),
constant = flags("const"),
@ -229,11 +231,12 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
def arrayDefinition: P[Seq[ArrayDeclarationStatement]] = for {
p <- position()
bank <- bankDeclaration
name <- "array" ~ !letterOrDigit ~/ SWS ~ identifier ~ HWS
length <- ("[" ~/ AWS ~/ mlExpression(nonStatementLevel) ~ AWS ~ "]").? ~ HWS
addr <- ("@" ~/ HWS ~/ mlExpression(1)).? ~/ HWS
contents <- ("=" ~/ HWS ~/ arrayContents).? ~/ HWS
} yield Seq(ArrayDeclarationStatement(name, length, addr, contents).pos(p))
} yield Seq(ArrayDeclarationStatement(name, bank, length, addr, contents).pos(p))
def tightMlExpression: P[Expression] = P(mlParenExpr | functionCall | mlIndexedExpression | atom) // TODO
@ -463,8 +466,14 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mlExpression(nonStatementLevel)
} yield Seq(DoWhileStatement(body.toList, Nil, condition))
def bankDeclaration: P[Option[String]] = ("segment" ~/ AWS ~/ "(" ~/ AWS ~/ identifier ~/ AWS ~/ ")" ~/ AWS).?
def functionDefinition: P[Seq[DeclarationStatement]] = for {
p <- position()
bank <- bankDeclaration
flags <- flags("asm", "inline", "interrupt", "macro", "noinline", "reentrant", "kernal_interrupt") ~ HWS
returnType <- identifier ~ SWS
name <- identifier ~ HWS
@ -510,6 +519,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
case None => ()
}
Seq(FunctionDeclarationStatement(name, returnType, params.toList,
bank,
addr,
statements,
flags("macro"),

View File

@ -1,7 +1,7 @@
package millfork.test.emu
import millfork.output.{AfterCodeByteAllocator, CurrentBankFragmentOutput, UpwardByteAllocator, VariableAllocator}
import millfork.{Cpu, Platform}
import millfork.{Cpu, OutputStyle, Platform}
/**
* @author Karol Stasiak
@ -12,8 +12,11 @@ object EmuPlatform {
Map(),
Nil,
CurrentBankFragmentOutput(0, 0xffff),
new UpwardByteAllocator(0x200, 0xb000),
new VariableAllocator((0 until 256 by 2).toList, new AfterCodeByteAllocator(0xff00)),
".bin"
Map("default" -> new UpwardByteAllocator(0x200, 0xb000)),
Map("default" -> new VariableAllocator((0 until 256 by 2).toList, new AfterCodeByteAllocator(0xff00))),
".bin",
Map("default" -> 0),
"default",
OutputStyle.Single
)
}

View File

@ -147,14 +147,14 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
// compile
val env2 = new Environment(None, "")
env2.collectDeclarations(program, options)
val assembler = new Assembler(program, env2)
val assembler = new Assembler(program, env2, platform)
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
println(";;; compiled: -----------------")
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
println(";;; ---------------------------")
assembler.labelMap.foreach { case (l, addr) => println(f"$l%-15s $$$addr%04x") }
val optimizedSize = assembler.mem.banks(0).initialized.count(identity).toLong
val optimizedSize = assembler.mem.banks("default").initialized.count(identity).toLong
if (unoptimizedSize == optimizedSize) {
println(f"Size: $unoptimizedSize%5d B")
} else {
@ -165,7 +165,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
ErrorReporting.assertNoErrors("Code generation failed")
val memoryBank = assembler.mem.banks(0)
val memoryBank = assembler.mem.banks("default")
if (source.contains("return [")) {
for (_ <- 0 until 10; i <- 0xfffe.to(0, -1)) {
if (memoryBank.readable(i)) memoryBank.readable(i + 1) = true
@ -173,14 +173,14 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
}
platform.cpu match {
case millfork.Cpu.Cmos =>
runViaSymon(memoryBank, platform.codeAllocator.startAt, CpuBehavior.CMOS_6502)
runViaSymon(memoryBank, platform.codeAllocators("default").startAt, CpuBehavior.CMOS_6502)
case millfork.Cpu.Ricoh =>
runViaHalfnes(memoryBank, platform.codeAllocator.startAt)
runViaHalfnes(memoryBank, platform.codeAllocators("default").startAt)
case millfork.Cpu.Mos =>
ErrorReporting.fatal("There's no NMOS emulator with decimal mode support")
Timings(-1, -1) -> memoryBank
case millfork.Cpu.StrictMos | millfork.Cpu.StrictRicoh =>
runViaSymon(memoryBank, platform.codeAllocator.startAt, CpuBehavior.NMOS_6502)
runViaSymon(memoryBank, platform.codeAllocators("default").startAt, CpuBehavior.NMOS_6502)
case _ =>
ErrorReporting.trace("No emulation support for " + platform.cpu)
Timings(-1, -1) -> memoryBank