mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-23 08:29:35 +00:00
Preliminary segment support; C16/+4 fixes
This commit is contained in:
parent
9e8a125487
commit
8a347e5058
@ -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.
|
||||
|
@ -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
|
@ -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:
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -1 +1 @@
|
||||
import c16_ted
|
||||
import c264_ted
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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 = {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
94
src/main/scala/millfork/env/Environment.scala
vendored
94
src/main/scala/millfork/env/Environment.scala
vendored
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
38
src/main/scala/millfork/env/Thing.scala
vendored
38
src/main/scala/millfork/env/Thing.scala
vendored
@ -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
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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"),
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user