mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-23 08:29:35 +00:00
More label file formats
This commit is contained in:
parent
5ca6988039
commit
3852b2dbe9
2
.gitignore
vendored
2
.gitignore
vendored
@ -24,6 +24,8 @@ examples/lunix/
|
||||
*.asm
|
||||
*.lbl
|
||||
*.nl
|
||||
*.fns
|
||||
*.sym
|
||||
*.deb
|
||||
*.xex
|
||||
*.nes
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
* Support for Intel 8085, together with illegal instructions.
|
||||
|
||||
* More label file formats.
|
||||
|
||||
* Added `memory_barrier` macro.
|
||||
|
||||
* Added `random` module.
|
||||
|
@ -28,7 +28,20 @@ no extension for BBC micro program file,
|
||||
|
||||
* `-s` – Generate also the assembly output. It is not compatible with any assembler, but it serves purely informational purpose. The file has the same nam as the output file and the extension is `.asm`.
|
||||
|
||||
* `-g` – Generate also the label file. The label file contains labels with their addresses, with duplicates removed. It can be loaded into the monitor of the Vice emulator for debugging purposes. The file has the same name as the output file and the extension is `.lbl`.
|
||||
* `-g` – Generate also the label file. The label file contains labels with their addresses, with duplicates removed.
|
||||
It can be loaded into the monitor of the emulator for debugging purposes.
|
||||
The file has the same name as the output file.
|
||||
The extension and the file format are platform-dependent.
|
||||
|
||||
* `-G <format>` – The same as `-g`, but with the specified format:
|
||||
|
||||
* `-G vice` – format compatible with the Vice emulator. The extension is `.lbl`.
|
||||
|
||||
* `-G nesasm` – format used by the NESASM assembler. The extension is `.fns`.
|
||||
|
||||
* `-G sym` – format used by the WLA DX assembler. The extension is `.sym`.
|
||||
|
||||
* `-G fceux` – multi-file format used by the FCEUX emulator. The extension is `.nl`.
|
||||
|
||||
* `-I <dir>;<dir>` – The include directories.
|
||||
Those directories are searched for modules and platform definitions.
|
||||
|
@ -141,6 +141,9 @@ 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`.
|
||||
|
||||
* `segment_NAME_bank` – the bank number the segment belongs to. Default: `0`.
|
||||
For better debugging on NES, RAM segments should use bank number `$ff`.
|
||||
|
||||
#### `[output]` section
|
||||
|
||||
* `style` – how multi-segment programs should be output:
|
||||
@ -178,3 +181,13 @@ Default: `after_code`.
|
||||
* `bbc_inf` – should the `.inf` file with file metadata for BBC Micro be created
|
||||
|
||||
* `gb_checksum` – should the main output file be patched with Game Boy-compatible checksums
|
||||
|
||||
* `labels` – format of the label file:
|
||||
|
||||
* `vice` (the default) – format compatible with the Vice emulator. The extension is `.lbl`.
|
||||
|
||||
* `nesasm` – format used by the NESASM assembler. The extension is `.fns`.
|
||||
|
||||
* `sym` – format used by the WLA/DX assembler. The extension is `.sym`.
|
||||
|
||||
* `fceux` – multi-file format used by the FCEUX emulator. The extension is `.nl`.
|
||||
|
@ -31,19 +31,20 @@ x64 hello_world.prg
|
||||
|
||||
The following options are obligatory when compiling your sources:
|
||||
|
||||
* `-o FILENAME` – specifies the base name for your output file, an appropriate file extension will be appended
|
||||
(`prg` for Commodore,
|
||||
`xex` for Atari computers,
|
||||
`a2` for Apple,
|
||||
`asm` for assembly output,
|
||||
`lbl` for label file,
|
||||
`inf` for BBC file metadata,
|
||||
`dsk` for PC-88,
|
||||
`tap` for ZX Spectrum,
|
||||
`rom` for MSX cartridges,
|
||||
`com` for CP/M,
|
||||
`nes` for Famicom,
|
||||
`bin` for Atari 2600)
|
||||
* `-o FILENAME` – specifies the base name for your output file, an appropriate file extension will be appended:
|
||||
`prg` for Commodore;
|
||||
`crt` for Commodore cartridges;
|
||||
`xex` for Atari computers;
|
||||
`a2` for Apple;
|
||||
`dsk` for PC-88;
|
||||
`tap` for ZX Spectrum;
|
||||
`rom` for MSX cartridges;
|
||||
`com` for CP/M;
|
||||
`nes` for Famicom;
|
||||
`bin` for Atari 2600;
|
||||
`inf` for BBC file metadata;
|
||||
`asm` for assembly output;
|
||||
`lbl`, `nl`, `fns`, or `sym` for label file
|
||||
|
||||
* `-t PLATFORM` – specifies the target platform.
|
||||
Each platform is defined in an `.ini` file in the include directory.
|
||||
@ -62,7 +63,7 @@ You may be also interested in the following:
|
||||
|
||||
* `-fsource-in-asm` – show original Millfork source in the assembly output
|
||||
|
||||
* `-g` – additionally generate a label file, in format compatible with VICE emulator
|
||||
* `-g` – additionally generate a label file
|
||||
|
||||
* `-r PROGRAM` – automatically launch given program after successful compilation
|
||||
|
||||
|
@ -25,5 +25,6 @@ HAS_BITMAP_MODE=1
|
||||
style=single
|
||||
format=startaddr,allocated
|
||||
extension=prg
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -24,5 +24,6 @@ HAS_BITMAP_MODE=1
|
||||
style=single
|
||||
format=startaddr,allocated
|
||||
extension=prg
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -43,5 +43,6 @@ style=single
|
||||
format=startaddr,allocated
|
||||
; default output file extension
|
||||
extension=prg
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -37,5 +37,6 @@ format =$43,$36,$34,$20,$43,$41,$52,$54,$52,$49,$44,$47,$45,$20,$20,$20, \
|
||||
$43,$48,$49,$50, 0,0,$40,$10, 0,0, 0,0, $80,$00, $40,$00, \
|
||||
prgrom:$8000:$bfff
|
||||
extension=crt
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -37,5 +37,6 @@ format =$43,$36,$34,$20,$43,$41,$52,$54,$52,$49,$44,$47,$45,$20,$20,$20, \
|
||||
$43,$48,$49,$50, 0,0,$20,$10, 0,0, 0,0, $80,$00, $20,$00, \
|
||||
prgrom:$8000:$9fff
|
||||
extension=crt
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -27,5 +27,6 @@ HAS_BITMAP_MODE=1
|
||||
style=single
|
||||
format=startaddr,allocated
|
||||
extension=prg
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -29,5 +29,6 @@ HAS_BITMAP_MODE=1
|
||||
style=single
|
||||
format=startaddr,allocated
|
||||
extension=prg
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -30,5 +30,6 @@ style=single
|
||||
format=rom:0:$7fff
|
||||
gb_checksum=true
|
||||
extension=gb
|
||||
labels=sym
|
||||
|
||||
|
||||
|
@ -30,5 +30,6 @@ HAS_BITMAP_MODE=1
|
||||
style=lunix
|
||||
format=$ff,$fe,0,21,pagecount,startpage,allocated
|
||||
extension=prg
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -21,33 +21,43 @@ default_code_segment=prgrom7
|
||||
|
||||
segment_default_start=$200
|
||||
segment_default_end=$7ff
|
||||
segment_default_bank=$ff
|
||||
|
||||
segment_ram_start=$6000
|
||||
segment_ram_end=$7fff
|
||||
segment_ram_bank=$ff
|
||||
|
||||
segment_prgrom7_start=$c000
|
||||
segment_prgrom7_end=$ffff
|
||||
segment_prgrom7_bank=7
|
||||
|
||||
segment_prgrom0_start=$8000
|
||||
segment_prgrom0_end=$bfff
|
||||
segment_prgrom0_bank=0
|
||||
|
||||
segment_prgrom1_start=$8000
|
||||
segment_prgrom1_end=$bfff
|
||||
segment_prgrom1_bank=1
|
||||
|
||||
segment_prgrom2_start=$8000
|
||||
segment_prgrom2_end=$bfff
|
||||
segment_prgrom2_bank=2
|
||||
|
||||
segment_prgrom3_start=$8000
|
||||
segment_prgrom3_end=$bfff
|
||||
segment_prgrom3_bank=3
|
||||
|
||||
segment_prgrom4_start=$8000
|
||||
segment_prgrom4_end=$bfff
|
||||
segment_prgrom4_bank=4
|
||||
|
||||
segment_prgrom5_start=$8000
|
||||
segment_prgrom5_end=$bfff
|
||||
segment_prgrom5_bank=5
|
||||
|
||||
segment_prgrom6_start=$8000
|
||||
segment_prgrom6_end=$bfff
|
||||
segment_prgrom6_bank=6
|
||||
|
||||
segment_chrrom0_start=$0000
|
||||
segment_chrrom0_end=$ffff
|
||||
@ -67,5 +77,6 @@ style=single
|
||||
format=$4E,$45,$53,$1A, 8,16,$A0,8, 0,0,$07,0, 2,0,0,0, prgrom0:$8000:$bfff,prgrom1:$8000:$bfff,prgrom2:$8000:$bfff,prgrom3:$8000:$bfff,prgrom4:$8000:$bfff,prgrom5:$8000:$bfff,prgrom6:$8000:$bfff,prgrom7:$c000:$ffff,chrrom0:$0000:$ffff,chrrom1:$0000:$ffff
|
||||
|
||||
extension=nes
|
||||
labels=nesasm
|
||||
|
||||
|
||||
|
@ -17,6 +17,7 @@ default_code_segment=prgrom
|
||||
|
||||
segment_default_start=$200
|
||||
segment_default_end=$7ff
|
||||
segment_default_bank=$ff
|
||||
|
||||
segment_prgrom_start=$8000
|
||||
segment_prgrom_end=$ffff
|
||||
@ -35,5 +36,6 @@ HAS_BITMAP_MODE=0
|
||||
style=single
|
||||
format=$4E,$45,$53,$1A, 2,1,0,0, 0,0,0,0, 0,0,0,0, prgrom:$8000:$ffff, chrrom:$0000:$1fff
|
||||
extension=nes
|
||||
labels=nesasm
|
||||
|
||||
|
||||
|
@ -23,5 +23,6 @@ HAS_BITMAP_MODE=0
|
||||
style=single
|
||||
format=startaddr,allocated
|
||||
extension=prg
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -24,5 +24,6 @@ HAS_BITMAP_MODE=1
|
||||
style=per_bank
|
||||
format=startaddr,allocated
|
||||
extension=prg
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -23,5 +23,6 @@ HAS_BITMAP_MODE=1
|
||||
style=single
|
||||
format=startaddr,allocated
|
||||
extension=prg
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -23,5 +23,6 @@ HAS_BITMAP_MODE=1
|
||||
style=single
|
||||
format=startaddr,allocated
|
||||
extension=prg
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -23,5 +23,6 @@ HAS_BITMAP_MODE=1
|
||||
style=single
|
||||
format=startaddr,allocated
|
||||
extension=prg
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -30,5 +30,6 @@ HAS_BITMAP_MODE=1
|
||||
style=single
|
||||
format=$00,$a0,prgrom:$a000:$bfff
|
||||
extension=crt
|
||||
labels=vice
|
||||
|
||||
|
||||
|
@ -15,7 +15,9 @@ case class Context(errorReporting: Logger,
|
||||
platform: Option[String] = None,
|
||||
outputAssembly: Boolean = false,
|
||||
outputLabels: Boolean = false,
|
||||
outputLabelsFormatOverride: Option[DebugOutputFormat] = None,
|
||||
includePath: List[String] = Nil,
|
||||
extraIncludePath: Seq[String] = IndexedSeq(),
|
||||
flags: Map[CompilationFlag.Value, Boolean] = Map(),
|
||||
features: Map[String, Long] = Map(),
|
||||
verbosity: Option[Int] = None) {
|
||||
|
77
src/main/scala/millfork/DebugOutputFormat.scala
Normal file
77
src/main/scala/millfork/DebugOutputFormat.scala
Normal file
@ -0,0 +1,77 @@
|
||||
package millfork
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
object DebugOutputFormat {
|
||||
val map: Map[String, DebugOutputFormat] = Map(
|
||||
"vice" -> ViceDebugOutputFormat,
|
||||
"nesasm" -> NesasmDebugOutputFormat,
|
||||
"fns" -> NesasmDebugOutputFormat,
|
||||
"fceux" -> FceuxDebugOutputFormat,
|
||||
"nl" -> FceuxDebugOutputFormat,
|
||||
"sym" -> SymDebugOutputFormat)
|
||||
}
|
||||
|
||||
sealed trait DebugOutputFormat extends Function[(String, (Int, Int)), String] {
|
||||
|
||||
def apply(labelAndValue: (String, (Int, Int))): String = formatLine(labelAndValue._1, labelAndValue._2._1, labelAndValue._2._2)
|
||||
|
||||
def formatLine(label: String, bank: Int, value: Int): String
|
||||
|
||||
def fileExtension(bank: Int): String
|
||||
|
||||
def filePerBank: Boolean
|
||||
|
||||
def addOutputExtension: Boolean
|
||||
}
|
||||
|
||||
object ViceDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
val normalized = label.replace('$', '_').replace('.', '_')
|
||||
s"al ${value.toHexString} .$normalized"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".lbl"
|
||||
|
||||
override def filePerBank: Boolean = false
|
||||
|
||||
override def addOutputExtension: Boolean = false
|
||||
}
|
||||
|
||||
object NesasmDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
label + " = $" + value.toHexString
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".fns"
|
||||
|
||||
override def filePerBank: Boolean = false
|
||||
|
||||
override def addOutputExtension: Boolean = false
|
||||
}
|
||||
|
||||
object SymDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
f"$bank%02x:$value%04x $label%s"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".sym"
|
||||
|
||||
override def filePerBank: Boolean = false
|
||||
|
||||
override def addOutputExtension: Boolean = false
|
||||
}
|
||||
|
||||
object FceuxDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
f"$$$value%04x#$label%s#"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = if (bank == 0xff) ".ram.nl" else s".$bank.nl"
|
||||
|
||||
override def filePerBank: Boolean = true
|
||||
|
||||
override def addOutputExtension: Boolean = true
|
||||
}
|
@ -67,7 +67,6 @@ object Main {
|
||||
|
||||
val output = c.outputFileName.getOrElse("a")
|
||||
val assOutput = output + ".asm"
|
||||
val labelOutput = output + ".lbl"
|
||||
// val prgOutputs = (platform.outputStyle match {
|
||||
// case OutputStyle.Single => List("default")
|
||||
// case OutputStyle.PerBank => platform.bankNumbers.keys.toList
|
||||
@ -91,17 +90,27 @@ object Main {
|
||||
Files.write(path, result.asm.mkString("\n").getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
if (c.outputLabels) {
|
||||
val path = Paths.get(labelOutput)
|
||||
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
|
||||
Files.write(path, result.labels.sortWith { (a, b) =>
|
||||
val aLocal = a._1.head == '.'
|
||||
val bLocal = b._1.head == '.'
|
||||
if (aLocal == bLocal) a._1 < b._1
|
||||
else b._1 < a._1
|
||||
}.groupBy(_._2).values.map(_.head).toSeq.sortBy(_._2).map { case (l, a) =>
|
||||
val normalized = l.replace('$', '_').replace('.', '_')
|
||||
s"al ${a.toHexString} .$normalized"
|
||||
}.mkString("\n").getBytes(StandardCharsets.UTF_8))
|
||||
def labelUnimportance(l: String): Int = {
|
||||
if (l.startsWith(".")) 8
|
||||
else if (l.startsWith("__")) 7
|
||||
else 0
|
||||
}
|
||||
val sortedLabels = result.labels.groupBy(_._2).values.map(_.minBy(a => labelUnimportance(a._1) -> a._1)).toSeq.sortBy(_._2)
|
||||
val format = c.outputLabelsFormatOverride.getOrElse(platform.outputLabelsFormat)
|
||||
val basename = if (format.addOutputExtension) output + platform.fileExtension else output
|
||||
if (format.filePerBank) {
|
||||
sortedLabels.groupBy(_._2._1).foreach{ case (bank, labels) =>
|
||||
val labelOutput = basename + format.fileExtension(bank)
|
||||
val path = Paths.get(labelOutput)
|
||||
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
|
||||
Files.write(path, labels.map(format).mkString("\n").getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
} else {
|
||||
val labelOutput = basename + format.fileExtension(0)
|
||||
val path = Paths.get(labelOutput)
|
||||
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
|
||||
Files.write(path, sortedLabels.map(format).mkString("\n").getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
}
|
||||
val defaultPrgOutput = if (output.endsWith(platform.fileExtension)) output else output + platform.fileExtension
|
||||
result.code.foreach{
|
||||
@ -336,7 +345,14 @@ object Main {
|
||||
|
||||
flag("-g").action { c =>
|
||||
c.copy(outputLabels = true)
|
||||
}.description("Generate also the label file.")
|
||||
}.description("Generate also the label file in the default format.")
|
||||
|
||||
parameter("-G").placeholder("<format>").action { (p, c) =>
|
||||
val f = DebugOutputFormat.map.getOrElse(
|
||||
p.toLowerCase(Locale.ROOT),
|
||||
errorReporting.fatal("Invalid label file format: " + p))
|
||||
c.copy(outputLabels = true, outputLabelsFormatOverride = Some(f))
|
||||
}.description("Generate also the label file in the given format. Available options: vice, nesasm, sym.")
|
||||
|
||||
parameter("-t", "--target").placeholder("<platform>").action { (p, c) =>
|
||||
assertNone(c.platform, "Platform already defined")
|
||||
|
@ -3,6 +3,7 @@ package millfork
|
||||
import java.io.{File, StringReader}
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.{Files, Paths}
|
||||
import java.util.Locale
|
||||
|
||||
import millfork.error.Logger
|
||||
import millfork.output._
|
||||
@ -34,6 +35,7 @@ class Platform(
|
||||
val generateGameBoyChecksums: Boolean,
|
||||
val bankNumbers: Map[String, Int],
|
||||
val defaultCodeBank: String,
|
||||
val outputLabelsFormat: DebugOutputFormat,
|
||||
val outputStyle: OutputStyle.Value
|
||||
) {
|
||||
def hasZeroPage: Boolean = cpuFamily == CpuFamily.M6502
|
||||
@ -201,6 +203,10 @@ object Platform {
|
||||
case "lunix" => OutputStyle.LUnix
|
||||
case x => log.fatal(s"Invalid output style: `$x`")
|
||||
}
|
||||
val debugOutputFormatName = os.get(classOf[String], "labels", "vice")
|
||||
val debugOutputFormat = DebugOutputFormat.map.getOrElse(
|
||||
debugOutputFormatName.toLowerCase(Locale.ROOT),
|
||||
log.fatal(s"Invalid label file format: `$debugOutputFormatName`"))
|
||||
|
||||
val builtInFeatures = builtInCpuFeatures(cpu)
|
||||
|
||||
@ -232,6 +238,7 @@ object Platform {
|
||||
generateGameBoyChecksums,
|
||||
bankNumbers,
|
||||
defaultCodeBank,
|
||||
debugOutputFormat,
|
||||
outputStyle)
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import millfork.node.NiceFunctionProperty
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
case class OptimizationContext(options: CompilationOptions,
|
||||
labelMap: Map[String, Int],
|
||||
labelMap: Map[String, (Int, Int)],
|
||||
zreg: Option[ThingInMemory],
|
||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)]) {
|
||||
@inline
|
||||
|
@ -102,10 +102,10 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
|
||||
}
|
||||
|
||||
class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
||||
val labelMap: Map[String, Int],
|
||||
val labelMap: Map[String, (Int, Int)],
|
||||
val zeropageRegister: Option[ThingInMemory],
|
||||
val niceFunctionProperties: Set[(NiceFunctionProperty, String)],
|
||||
val labeUseCount: String => Int) {
|
||||
val labelUseCount: String => Int) {
|
||||
@inline
|
||||
def log: Logger = compilationOptions.log
|
||||
@inline
|
||||
@ -222,7 +222,7 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
||||
}
|
||||
// if a jump leads inside the block, then it's internal
|
||||
// if a jump leads outside the block, then it's external
|
||||
jumps == labels && labels.forall(l => labeUseCount(l) <= 1)
|
||||
jumps == labels && labels.forall(l => labelUseCount(l) <= 1)
|
||||
}
|
||||
|
||||
def zreg(i: Int): Constant = {
|
||||
@ -1530,10 +1530,10 @@ case object IsZeroPage extends AssemblyLinePattern {
|
||||
ISC | DCP | LAX | SAX | RLA | RRA | SLO | SRE, AddrMode.Absolute, p, Elidability.Elidable, _) =>
|
||||
p match {
|
||||
case NumericConstant(n, _) => n <= 255
|
||||
case MemoryAddressConstant(th) => ctx.labelMap.getOrElse(th.name, 0x800) < 0x100
|
||||
case MemoryAddressConstant(th) => ctx.labelMap.getOrElse(th.name, 0 -> 0x800)._2 < 0x100
|
||||
case CompoundConstant(MathOperator.Plus,
|
||||
MemoryAddressConstant(th),
|
||||
NumericConstant(n, _)) => ctx.labelMap.getOrElse(th.name, 0x800) + n < 0x100
|
||||
NumericConstant(n, _)) => ctx.labelMap.getOrElse(th.name, 0 -> 0x800)._2 + n < 0x100
|
||||
case _ => false
|
||||
}
|
||||
case _ => false
|
||||
|
15
src/main/scala/millfork/env/Environment.scala
vendored
15
src/main/scala/millfork/env/Environment.scala
vendored
@ -110,7 +110,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
callGraph: CallGraph,
|
||||
allocators: Map[String, VariableAllocator],
|
||||
options: CompilationOptions,
|
||||
onEachVariable: (String, Int) => Unit,
|
||||
onEachVariable: (String, (Int, Int)) => Unit,
|
||||
pass: Int,
|
||||
forZpOnly: Boolean): Unit = {
|
||||
if (forZpOnly && !options.platform.hasZeroPage) {
|
||||
@ -155,14 +155,15 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
}
|
||||
} else GlobalVertex
|
||||
val bank = m.bank(options)
|
||||
val bank0 = mem.banks(bank)
|
||||
m.alloc match {
|
||||
case VariableAllocationMethod.None =>
|
||||
Nil
|
||||
case VariableAllocationMethod.Zeropage =>
|
||||
if (forZpOnly || !options.platform.hasZeroPage) {
|
||||
val addr =
|
||||
allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Zeropage, alignment = m.alignment)
|
||||
onEachVariable(m.name, addr)
|
||||
allocators(bank).allocateBytes(bank0, callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Zeropage, alignment = m.alignment)
|
||||
onEachVariable(m.name, bank0.index -> addr)
|
||||
List(
|
||||
ConstantThing(m.name.stripPrefix(prefix) + "`", NumericConstant(addr, 2), p)
|
||||
)
|
||||
@ -175,10 +176,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
val graveName = m.name.stripPrefix(prefix) + "`"
|
||||
if (forZpOnly) {
|
||||
if (bank == "default") {
|
||||
allocators(bank).tryAllocateZeropageBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, alignment = m.alignment) match {
|
||||
allocators(bank).tryAllocateZeropageBytes(bank0, callGraph, vertex, options, m.sizeInBytes, alignment = m.alignment) match {
|
||||
case None => Nil
|
||||
case Some(addr) =>
|
||||
onEachVariable(m.name, addr)
|
||||
onEachVariable(m.name, bank0.index -> addr)
|
||||
List(
|
||||
ConstantThing(m.name.stripPrefix(prefix) + "`", NumericConstant(addr, 2), p)
|
||||
)
|
||||
@ -187,8 +188,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
} else if (things.contains(graveName)) {
|
||||
Nil
|
||||
} else {
|
||||
val addr = allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Either, alignment = m.alignment)
|
||||
onEachVariable(m.name, addr)
|
||||
val addr = allocators(bank).allocateBytes(bank0, callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Either, alignment = m.alignment)
|
||||
onEachVariable(m.name, bank0.index -> addr)
|
||||
List(
|
||||
ConstantThing(graveName, NumericConstant(addr, 2), p)
|
||||
)
|
||||
|
@ -16,7 +16,7 @@ import millfork.node.NiceFunctionProperty.IsLeaf
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
case class AssemblerOutput(code: Map[String, Array[Byte]], asm: Array[String], labels: List[(String, Int)])
|
||||
case class AssemblerOutput(code: Map[String, Array[Byte]], asm: Array[String], labels: List[(String, (Int, Int))])
|
||||
|
||||
abstract class AbstractAssembler[T <: AbstractCode](private val program: Program,
|
||||
private val rootEnv: Environment,
|
||||
@ -30,8 +30,8 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
var initializedVariablesSize: Int = 0
|
||||
protected val log: Logger = rootEnv.log
|
||||
|
||||
val mem = new CompiledMemory(platform.bankNumbers.keys.toList)
|
||||
val labelMap: mutable.Map[String, Int] = mutable.Map()
|
||||
val mem = new CompiledMemory(platform.bankNumbers.toList)
|
||||
val labelMap: mutable.Map[String, (Int, Int)] = mutable.Map()
|
||||
private val bytesToWriteLater = mutable.ListBuffer[(String, Int, Constant)]()
|
||||
private val wordsToWriteLater = mutable.ListBuffer[(String, Int, Constant)]()
|
||||
|
||||
@ -97,9 +97,9 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
}
|
||||
case MemoryAddressConstant(th) =>
|
||||
try {
|
||||
if (labelMap.contains(th.name)) return labelMap(th.name)
|
||||
if (labelMap.contains(th.name + "`")) return labelMap(th.name)
|
||||
if (labelMap.contains(th.name + ".addr")) return labelMap.getOrElse[Int](th.name, labelMap(th.name + ".array"))
|
||||
if (labelMap.contains(th.name)) return labelMap(th.name)._2
|
||||
if (labelMap.contains(th.name + "`")) return labelMap(th.name)._2
|
||||
if (labelMap.contains(th.name + ".addr")) return labelMap.getOrElse[(Int, Int)](th.name, labelMap(th.name + ".array"))._2
|
||||
val x1 = env.maybeGet[ConstantThing](th.name).map(_.value)
|
||||
val x2 = env.maybeGet[ConstantThing](th.name + "`").map(_.value)
|
||||
val x3 = env.maybeGet[NormalFunction](th.name).flatMap(_.address)
|
||||
@ -121,7 +121,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
log.fatal("Stack overflow " + c)
|
||||
}
|
||||
case UnexpandedConstant(name, _) =>
|
||||
if (labelMap.contains(name)) labelMap(name)
|
||||
if (labelMap.contains(name)) labelMap(name)._2
|
||||
else ???
|
||||
case SubbyteConstant(cc, i) => deepConstResolve(cc).>>>(i * 8).&(0xff)
|
||||
case CompoundConstant(operator, lc, rc) =>
|
||||
@ -286,7 +286,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
val index = f.address.get.asInstanceOf[NumericConstant].value.toInt
|
||||
compiledFunctions(f.name) match {
|
||||
case NormalCompiledFunction(_, functionCode, _, _) =>
|
||||
labelMap(f.name) = index
|
||||
labelMap(f.name) = bank0.index -> index
|
||||
val end = outputFunction(bank, functionCode, index, assembly, options)
|
||||
for (i <- index until end) {
|
||||
bank0.occupied(index) = true
|
||||
@ -308,14 +308,16 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
// already done before
|
||||
case (name, NormalCompiledFunction(bank, functionCode, false, alignment)) =>
|
||||
val size = functionCode.map(_.sizeInBytes).sum
|
||||
val index = codeAllocators(bank).allocateBytes(mem.banks(bank), options, size, initialized = true, writeable = false, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = index
|
||||
val bank0 = mem.banks(bank)
|
||||
val index = codeAllocators(bank).allocateBytes(bank0, options, size, initialized = true, writeable = false, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = bank0.index -> index
|
||||
justAfterCode += bank -> outputFunction(bank, functionCode, index, assembly, options)
|
||||
case _ =>
|
||||
}
|
||||
sortedCompilerFunctions.foreach {
|
||||
case (name, RedirectedFunction(_, target, offset)) =>
|
||||
labelMap(name) = labelMap(target) + offset
|
||||
val tuple = labelMap(target)
|
||||
labelMap(name) = tuple._1 -> (tuple._2 + offset)
|
||||
case _ =>
|
||||
}
|
||||
|
||||
@ -332,7 +334,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
if (bank != "default") ???
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size + 1, initialized = true, writeable = false, location = AllocationLocation.High, alignment = m.alignment)
|
||||
labelMap(name) = index + 1
|
||||
labelMap(name) = bank0.index -> (index + 1)
|
||||
val altName = m.name.stripPrefix(env.prefix) + "`"
|
||||
val thing = if (name.endsWith(".addr")) env.get[ThingInMemory](name.stripSuffix(".addr")) else env.get[ThingInMemory](name + ".array")
|
||||
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
|
||||
@ -363,7 +365,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
val bank = thing.bank(options)
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, items.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = index
|
||||
labelMap(name) = bank0.index -> index
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(name)
|
||||
for (item <- items) {
|
||||
@ -385,7 +387,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
val bank = m.bank(options)
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = index
|
||||
labelMap(name) = bank0.index -> index
|
||||
val altName = m.name.stripPrefix(env.prefix) + "`"
|
||||
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
@ -414,20 +416,21 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, 2, forZpOnly = false)
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, 3, forZpOnly = false)
|
||||
|
||||
val defaultBank = mem.banks("default").index
|
||||
if (platform.freeZpPointers.nonEmpty) {
|
||||
val zpUsageOffset = platform.freeZpPointers.min
|
||||
val zeropageOccupation = zpOccupied.slice(zpUsageOffset, platform.freeZpPointers.max + 2)
|
||||
labelMap += "__zeropage_usage" -> (zeropageOccupation.lastIndexOf(true) - zeropageOccupation.indexOf(true) + 1)
|
||||
labelMap += "__zeropage_first" -> (zpUsageOffset + (zeropageOccupation.indexOf(true) max 0))
|
||||
labelMap += "__zeropage_last" -> (zpUsageOffset + (zeropageOccupation.lastIndexOf(true) max 0))
|
||||
labelMap += "__zeropage_end" -> (zpUsageOffset + zeropageOccupation.lastIndexOf(true) + 1)
|
||||
labelMap += "__zeropage_usage" -> (defaultBank, zeropageOccupation.lastIndexOf(true) - zeropageOccupation.indexOf(true) + 1)
|
||||
labelMap += "__zeropage_first" -> (defaultBank, zpUsageOffset + (zeropageOccupation.indexOf(true) max 0))
|
||||
labelMap += "__zeropage_last" -> (defaultBank, zpUsageOffset + (zeropageOccupation.lastIndexOf(true) max 0))
|
||||
labelMap += "__zeropage_end" -> (defaultBank, zpUsageOffset + zeropageOccupation.lastIndexOf(true) + 1)
|
||||
} else {
|
||||
labelMap += "__zeropage_usage" -> 0
|
||||
labelMap += "__zeropage_first" -> 3
|
||||
labelMap += "__zeropage_last" -> 2
|
||||
labelMap += "__zeropage_end" -> 3
|
||||
labelMap += "__zeropage_usage" -> (defaultBank -> 0)
|
||||
labelMap += "__zeropage_first" -> (defaultBank -> 3)
|
||||
labelMap += "__zeropage_last" -> (defaultBank -> 2)
|
||||
labelMap += "__zeropage_end" -> (defaultBank -> 3)
|
||||
}
|
||||
labelMap += "__heap_start" -> variableAllocators("default").heapStart
|
||||
labelMap += "__heap_start" -> (defaultBank -> variableAllocators("default").heapStart)
|
||||
|
||||
env = rootEnv.allThings
|
||||
|
||||
@ -449,10 +452,10 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
mem.banks(bank).end = end
|
||||
}
|
||||
|
||||
labelMap.toList.sorted.foreach { case (l, v) =>
|
||||
labelMap.toList.sorted.foreach { case (l, (_, v)) =>
|
||||
assembly += f"$l%-30s = $$$v%04X"
|
||||
}
|
||||
labelMap.toList.sortBy { case (a, b) => b -> a }.foreach { case (l, v) =>
|
||||
labelMap.toList.sortBy { case (a, (_, v)) => v -> a }.foreach { case (l, (_, v)) =>
|
||||
assembly += f" ; $$$v%04X = $l%s"
|
||||
}
|
||||
|
||||
@ -464,13 +467,13 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
AssemblerOutput(code, assembly.toArray, labelMap.toList)
|
||||
}
|
||||
|
||||
def injectLabels(labelMap: Map[String, Int], code: List[T]): List[T]
|
||||
def injectLabels(labelMap: Map[String, (Int, Int)], code: List[T]): List[T]
|
||||
|
||||
private def compileFunction(f: NormalFunction,
|
||||
optimizations: Seq[AssemblyOptimization[T]],
|
||||
options: CompilationOptions,
|
||||
inlinedFunctions: Map[String, List[T]],
|
||||
labelMap: Map[String, Int],
|
||||
labelMap: Map[String, (Int, Int)],
|
||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)]): List[T] = {
|
||||
log.debug("Compiling: " + f.name, f.position)
|
||||
val unoptimized: List[T] =
|
||||
|
@ -5,12 +5,12 @@ import scala.collection.mutable
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
class CompiledMemory(bankNames: List[String]) {
|
||||
class CompiledMemory(bankNames: List[(String, Int)]) {
|
||||
var programName = "MILLFORK"
|
||||
val banks: mutable.Map[String, MemoryBank] = mutable.Map(bankNames.map(_ -> new MemoryBank): _*)
|
||||
val banks: mutable.Map[String, MemoryBank] = mutable.Map(bankNames.map(p => p._1 -> new MemoryBank(p._2)): _*)
|
||||
}
|
||||
|
||||
class MemoryBank {
|
||||
class MemoryBank(val index: Int) {
|
||||
def readByte(addr: Int): Int = output(addr) & 0xff
|
||||
|
||||
def readWord(addr: Int): Int = readByte(addr) + (readByte(addr + 1) << 8)
|
||||
|
@ -39,7 +39,8 @@ class MosAssembler(program: Program,
|
||||
case AssemblyLine0(BYTE, _, _) => log.fatal("BYTE opcode failure")
|
||||
case AssemblyLine0(_, RawByte, _) => log.fatal("BYTE opcode failure")
|
||||
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(labelName))) =>
|
||||
labelMap(labelName) = index
|
||||
val bank0 = mem.banks(bank)
|
||||
labelMap(labelName) = bank0.index -> index
|
||||
index
|
||||
case AssemblyLine0(_, DoesNotExist, _) =>
|
||||
index
|
||||
@ -66,7 +67,7 @@ class MosAssembler(program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
override def injectLabels(labelMap: Map[String, Int], code: List[AssemblyLine]): List[AssemblyLine] = {
|
||||
override def injectLabels(labelMap: Map[String, (Int, Int)], code: List[AssemblyLine]): List[AssemblyLine] = {
|
||||
import Opcode._
|
||||
code.map {
|
||||
case l@AssemblyLine(LDA | STA | CMP |
|
||||
@ -79,10 +80,10 @@ class MosAssembler(program: Program,
|
||||
ISC | DCP | LAX | SAX | RLA | RRA | SLO | SRE, AddrMode.Absolute, p, Elidability.Elidable, _) =>
|
||||
p match {
|
||||
case NumericConstant(n, _) => if (n <= 255) l.copy(addrMode = AddrMode.ZeroPage) else l
|
||||
case MemoryAddressConstant(th) => if (labelMap.getOrElse(th.name, 0x800) < 0x100) l.copy(addrMode = AddrMode.ZeroPage) else l
|
||||
case MemoryAddressConstant(th) => if (labelMap.getOrElse(th.name, 0 -> 0x800)._2 < 0x100) l.copy(addrMode = AddrMode.ZeroPage) else l
|
||||
case CompoundConstant(MathOperator.Plus,
|
||||
MemoryAddressConstant(th),
|
||||
NumericConstant(n, _)) => if (labelMap.getOrElse(th.name, 0x800) + n < 0x100) l.copy(addrMode = AddrMode.ZeroPage) else l
|
||||
NumericConstant(n, _)) => if (labelMap.getOrElse(th.name, 0 -> 0x800)._2 < 0x100) l.copy(addrMode = AddrMode.ZeroPage) else l
|
||||
case _ => l
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,8 @@ class Z80Assembler(program: Program,
|
||||
|
||||
try { instr match {
|
||||
case ZLine0(LABEL, NoRegisters, MemoryAddressConstant(Label(labelName))) =>
|
||||
labelMap(labelName) = index
|
||||
val bank0 = mem.banks(bank)
|
||||
labelMap(labelName) = bank0.index -> index
|
||||
index
|
||||
case ZLine0(BYTE, NoRegisters, param) =>
|
||||
writeByte(bank, index, param)
|
||||
@ -688,7 +689,7 @@ class Z80Assembler(program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
override def injectLabels(labelMap: Map[String, Int], code: List[ZLine]): List[ZLine] = code // TODO
|
||||
override def injectLabels(labelMap: Map[String, (Int, Int)], code: List[ZLine]): List[ZLine] = code // TODO
|
||||
|
||||
override def quickSimplify(code: List[ZLine]): List[ZLine] = code.map(a => a.copy(parameter = a.parameter.quickSimplify))
|
||||
|
||||
|
@ -22,7 +22,7 @@ class Z80ToX86Crossassembler(program: Program,
|
||||
} else code
|
||||
}
|
||||
|
||||
override def injectLabels(labelMap: Map[String, Int], code: List[ZLine]): List[ZLine] = code // TODO
|
||||
override def injectLabels(labelMap: Map[String, (Int, Int)], code: List[ZLine]): List[ZLine] = code // TODO
|
||||
|
||||
override def quickSimplify(code: List[ZLine]): List[ZLine] = code.map(a => a.copy(parameter = a.parameter.quickSimplify))
|
||||
|
||||
@ -74,7 +74,8 @@ class Z80ToX86Crossassembler(program: Program,
|
||||
import Z80ToX86Crossassembler._
|
||||
instr match {
|
||||
case ZLine0(LABEL, NoRegisters, MemoryAddressConstant(Label(labelName))) =>
|
||||
labelMap(labelName) = index
|
||||
val bank0 = mem.banks(bank)
|
||||
labelMap(labelName) = bank0.index -> index
|
||||
index
|
||||
case ZLine0(BYTE, NoRegisters, param) =>
|
||||
writeByte(bank, index, param)
|
||||
|
@ -74,7 +74,7 @@ class EmuI86Run(nodeOptimizations: List[NodeOptimization], assemblyOptimizations
|
||||
}
|
||||
|
||||
def apply2(source: String): (Timings, MemoryBank) = {
|
||||
if (!Settings.enableIntel8086Tests) return Timings(-1, -1) -> new MemoryBank()
|
||||
if (!Settings.enableIntel8086Tests) return Timings(-1, -1) -> new MemoryBank(0)
|
||||
Console.out.flush()
|
||||
Console.err.flush()
|
||||
val log = TestErrorReporting.log
|
||||
@ -139,7 +139,7 @@ class EmuI86Run(nodeOptimizations: List[NodeOptimization], assemblyOptimizations
|
||||
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") }
|
||||
assembler.labelMap.foreach { case (l, (_, addr)) => println(f"$l%-15s $$$addr%04x") }
|
||||
|
||||
val optimizedSize = assembler.mem.banks("default").initialized.count(identity).toLong
|
||||
if (unoptimizedSize == optimizedSize) {
|
||||
|
@ -2,7 +2,7 @@ package millfork.test.emu
|
||||
|
||||
import millfork.output.{AfterCodeByteAllocator, CurrentBankFragmentOutput, UpwardByteAllocator, VariableAllocator}
|
||||
import millfork.parser.TextCodec
|
||||
import millfork.{Cpu, CpuFamily, OutputStyle, Platform}
|
||||
import millfork.{Cpu, CpuFamily, OutputStyle, Platform, ViceDebugOutputFormat}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
@ -29,6 +29,7 @@ object EmuPlatform {
|
||||
false,
|
||||
Map("default" -> 0),
|
||||
"default",
|
||||
ViceDebugOutputFormat,
|
||||
OutputStyle.Single
|
||||
)
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
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") }
|
||||
assembler.labelMap.foreach { case (l, (_, addr)) => println(f"$l%-15s $$$addr%04x") }
|
||||
|
||||
val optimizedSize = assembler.mem.banks("default").initialized.count(identity).toLong
|
||||
if (unoptimizedSize == optimizedSize) {
|
||||
|
@ -138,7 +138,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
||||
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") }
|
||||
assembler.labelMap.foreach { case (l, (_, addr)) => println(f"$l%-15s $$$addr%04x") }
|
||||
|
||||
val optimizedSize = assembler.mem.banks("default").initialized.count(identity).toLong
|
||||
if (unoptimizedSize == optimizedSize) {
|
||||
|
Loading…
Reference in New Issue
Block a user