1
0
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:
Karol Stasiak 2019-06-14 11:39:11 +02:00
parent 5ca6988039
commit 3852b2dbe9
38 changed files with 253 additions and 84 deletions

2
.gitignore vendored
View File

@ -24,6 +24,8 @@ examples/lunix/
*.asm
*.lbl
*.nl
*.fns
*.sym
*.deb
*.xex
*.nes

View File

@ -10,6 +10,8 @@
* Support for Intel 8085, together with illegal instructions.
* More label file formats.
* Added `memory_barrier` macro.
* Added `random` module.

View File

@ -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.

View File

@ -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`.

View File

@ -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

View File

@ -25,5 +25,6 @@ HAS_BITMAP_MODE=1
style=single
format=startaddr,allocated
extension=prg
labels=vice

View File

@ -24,5 +24,6 @@ HAS_BITMAP_MODE=1
style=single
format=startaddr,allocated
extension=prg
labels=vice

View File

@ -43,5 +43,6 @@ style=single
format=startaddr,allocated
; default output file extension
extension=prg
labels=vice

View File

@ -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

View File

@ -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

View File

@ -27,5 +27,6 @@ HAS_BITMAP_MODE=1
style=single
format=startaddr,allocated
extension=prg
labels=vice

View File

@ -29,5 +29,6 @@ HAS_BITMAP_MODE=1
style=single
format=startaddr,allocated
extension=prg
labels=vice

View File

@ -30,5 +30,6 @@ style=single
format=rom:0:$7fff
gb_checksum=true
extension=gb
labels=sym

View File

@ -30,5 +30,6 @@ HAS_BITMAP_MODE=1
style=lunix
format=$ff,$fe,0,21,pagecount,startpage,allocated
extension=prg
labels=vice

View File

@ -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

View File

@ -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

View File

@ -23,5 +23,6 @@ HAS_BITMAP_MODE=0
style=single
format=startaddr,allocated
extension=prg
labels=vice

View File

@ -24,5 +24,6 @@ HAS_BITMAP_MODE=1
style=per_bank
format=startaddr,allocated
extension=prg
labels=vice

View File

@ -23,5 +23,6 @@ HAS_BITMAP_MODE=1
style=single
format=startaddr,allocated
extension=prg
labels=vice

View File

@ -23,5 +23,6 @@ HAS_BITMAP_MODE=1
style=single
format=startaddr,allocated
extension=prg
labels=vice

View File

@ -23,5 +23,6 @@ HAS_BITMAP_MODE=1
style=single
format=startaddr,allocated
extension=prg
labels=vice

View File

@ -30,5 +30,6 @@ HAS_BITMAP_MODE=1
style=single
format=$00,$a0,prgrom:$a000:$bfff
extension=crt
labels=vice

View File

@ -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) {

View 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
}

View File

@ -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")

View File

@ -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)
}

View File

@ -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

View File

@ -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

View File

@ -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)
)

View File

@ -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] =

View File

@ -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)

View File

@ -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
}

View File

@ -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))

View File

@ -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)

View File

@ -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) {

View File

@ -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
)
}

View File

@ -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) {

View File

@ -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) {