mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-10 20: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
|
*.asm
|
||||||
*.lbl
|
*.lbl
|
||||||
*.nl
|
*.nl
|
||||||
|
*.fns
|
||||||
|
*.sym
|
||||||
*.deb
|
*.deb
|
||||||
*.xex
|
*.xex
|
||||||
*.nes
|
*.nes
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
* Support for Intel 8085, together with illegal instructions.
|
* Support for Intel 8085, together with illegal instructions.
|
||||||
|
|
||||||
|
* More label file formats.
|
||||||
|
|
||||||
* Added `memory_barrier` macro.
|
* Added `memory_barrier` macro.
|
||||||
|
|
||||||
* Added `random` module.
|
* 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`.
|
* `-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.
|
* `-I <dir>;<dir>` – The include directories.
|
||||||
Those directories are searched for modules and platform definitions.
|
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.
|
* `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`.
|
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
|
#### `[output]` section
|
||||||
|
|
||||||
* `style` – how multi-segment programs should be output:
|
* `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
|
* `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
|
* `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:
|
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
|
* `-o FILENAME` – specifies the base name for your output file, an appropriate file extension will be appended:
|
||||||
(`prg` for Commodore,
|
`prg` for Commodore;
|
||||||
`xex` for Atari computers,
|
`crt` for Commodore cartridges;
|
||||||
`a2` for Apple,
|
`xex` for Atari computers;
|
||||||
`asm` for assembly output,
|
`a2` for Apple;
|
||||||
`lbl` for label file,
|
`dsk` for PC-88;
|
||||||
`inf` for BBC file metadata,
|
`tap` for ZX Spectrum;
|
||||||
`dsk` for PC-88,
|
`rom` for MSX cartridges;
|
||||||
`tap` for ZX Spectrum,
|
`com` for CP/M;
|
||||||
`rom` for MSX cartridges,
|
`nes` for Famicom;
|
||||||
`com` for CP/M,
|
`bin` for Atari 2600;
|
||||||
`nes` for Famicom,
|
`inf` for BBC file metadata;
|
||||||
`bin` for Atari 2600)
|
`asm` for assembly output;
|
||||||
|
`lbl`, `nl`, `fns`, or `sym` for label file
|
||||||
|
|
||||||
* `-t PLATFORM` – specifies the target platform.
|
* `-t PLATFORM` – specifies the target platform.
|
||||||
Each platform is defined in an `.ini` file in the include directory.
|
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
|
* `-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
|
* `-r PROGRAM` – automatically launch given program after successful compilation
|
||||||
|
|
||||||
|
@ -25,5 +25,6 @@ HAS_BITMAP_MODE=1
|
|||||||
style=single
|
style=single
|
||||||
format=startaddr,allocated
|
format=startaddr,allocated
|
||||||
extension=prg
|
extension=prg
|
||||||
|
labels=vice
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,5 +24,6 @@ HAS_BITMAP_MODE=1
|
|||||||
style=single
|
style=single
|
||||||
format=startaddr,allocated
|
format=startaddr,allocated
|
||||||
extension=prg
|
extension=prg
|
||||||
|
labels=vice
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,5 +43,6 @@ style=single
|
|||||||
format=startaddr,allocated
|
format=startaddr,allocated
|
||||||
; default output file extension
|
; default output file extension
|
||||||
extension=prg
|
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, \
|
$43,$48,$49,$50, 0,0,$40,$10, 0,0, 0,0, $80,$00, $40,$00, \
|
||||||
prgrom:$8000:$bfff
|
prgrom:$8000:$bfff
|
||||||
extension=crt
|
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, \
|
$43,$48,$49,$50, 0,0,$20,$10, 0,0, 0,0, $80,$00, $20,$00, \
|
||||||
prgrom:$8000:$9fff
|
prgrom:$8000:$9fff
|
||||||
extension=crt
|
extension=crt
|
||||||
|
labels=vice
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,5 +27,6 @@ HAS_BITMAP_MODE=1
|
|||||||
style=single
|
style=single
|
||||||
format=startaddr,allocated
|
format=startaddr,allocated
|
||||||
extension=prg
|
extension=prg
|
||||||
|
labels=vice
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,5 +29,6 @@ HAS_BITMAP_MODE=1
|
|||||||
style=single
|
style=single
|
||||||
format=startaddr,allocated
|
format=startaddr,allocated
|
||||||
extension=prg
|
extension=prg
|
||||||
|
labels=vice
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,5 +30,6 @@ style=single
|
|||||||
format=rom:0:$7fff
|
format=rom:0:$7fff
|
||||||
gb_checksum=true
|
gb_checksum=true
|
||||||
extension=gb
|
extension=gb
|
||||||
|
labels=sym
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,5 +30,6 @@ HAS_BITMAP_MODE=1
|
|||||||
style=lunix
|
style=lunix
|
||||||
format=$ff,$fe,0,21,pagecount,startpage,allocated
|
format=$ff,$fe,0,21,pagecount,startpage,allocated
|
||||||
extension=prg
|
extension=prg
|
||||||
|
labels=vice
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,33 +21,43 @@ default_code_segment=prgrom7
|
|||||||
|
|
||||||
segment_default_start=$200
|
segment_default_start=$200
|
||||||
segment_default_end=$7ff
|
segment_default_end=$7ff
|
||||||
|
segment_default_bank=$ff
|
||||||
|
|
||||||
segment_ram_start=$6000
|
segment_ram_start=$6000
|
||||||
segment_ram_end=$7fff
|
segment_ram_end=$7fff
|
||||||
|
segment_ram_bank=$ff
|
||||||
|
|
||||||
segment_prgrom7_start=$c000
|
segment_prgrom7_start=$c000
|
||||||
segment_prgrom7_end=$ffff
|
segment_prgrom7_end=$ffff
|
||||||
|
segment_prgrom7_bank=7
|
||||||
|
|
||||||
segment_prgrom0_start=$8000
|
segment_prgrom0_start=$8000
|
||||||
segment_prgrom0_end=$bfff
|
segment_prgrom0_end=$bfff
|
||||||
|
segment_prgrom0_bank=0
|
||||||
|
|
||||||
segment_prgrom1_start=$8000
|
segment_prgrom1_start=$8000
|
||||||
segment_prgrom1_end=$bfff
|
segment_prgrom1_end=$bfff
|
||||||
|
segment_prgrom1_bank=1
|
||||||
|
|
||||||
segment_prgrom2_start=$8000
|
segment_prgrom2_start=$8000
|
||||||
segment_prgrom2_end=$bfff
|
segment_prgrom2_end=$bfff
|
||||||
|
segment_prgrom2_bank=2
|
||||||
|
|
||||||
segment_prgrom3_start=$8000
|
segment_prgrom3_start=$8000
|
||||||
segment_prgrom3_end=$bfff
|
segment_prgrom3_end=$bfff
|
||||||
|
segment_prgrom3_bank=3
|
||||||
|
|
||||||
segment_prgrom4_start=$8000
|
segment_prgrom4_start=$8000
|
||||||
segment_prgrom4_end=$bfff
|
segment_prgrom4_end=$bfff
|
||||||
|
segment_prgrom4_bank=4
|
||||||
|
|
||||||
segment_prgrom5_start=$8000
|
segment_prgrom5_start=$8000
|
||||||
segment_prgrom5_end=$bfff
|
segment_prgrom5_end=$bfff
|
||||||
|
segment_prgrom5_bank=5
|
||||||
|
|
||||||
segment_prgrom6_start=$8000
|
segment_prgrom6_start=$8000
|
||||||
segment_prgrom6_end=$bfff
|
segment_prgrom6_end=$bfff
|
||||||
|
segment_prgrom6_bank=6
|
||||||
|
|
||||||
segment_chrrom0_start=$0000
|
segment_chrrom0_start=$0000
|
||||||
segment_chrrom0_end=$ffff
|
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
|
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
|
extension=nes
|
||||||
|
labels=nesasm
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ default_code_segment=prgrom
|
|||||||
|
|
||||||
segment_default_start=$200
|
segment_default_start=$200
|
||||||
segment_default_end=$7ff
|
segment_default_end=$7ff
|
||||||
|
segment_default_bank=$ff
|
||||||
|
|
||||||
segment_prgrom_start=$8000
|
segment_prgrom_start=$8000
|
||||||
segment_prgrom_end=$ffff
|
segment_prgrom_end=$ffff
|
||||||
@ -35,5 +36,6 @@ HAS_BITMAP_MODE=0
|
|||||||
style=single
|
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
|
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
|
extension=nes
|
||||||
|
labels=nesasm
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,5 +23,6 @@ HAS_BITMAP_MODE=0
|
|||||||
style=single
|
style=single
|
||||||
format=startaddr,allocated
|
format=startaddr,allocated
|
||||||
extension=prg
|
extension=prg
|
||||||
|
labels=vice
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,5 +24,6 @@ HAS_BITMAP_MODE=1
|
|||||||
style=per_bank
|
style=per_bank
|
||||||
format=startaddr,allocated
|
format=startaddr,allocated
|
||||||
extension=prg
|
extension=prg
|
||||||
|
labels=vice
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,5 +23,6 @@ HAS_BITMAP_MODE=1
|
|||||||
style=single
|
style=single
|
||||||
format=startaddr,allocated
|
format=startaddr,allocated
|
||||||
extension=prg
|
extension=prg
|
||||||
|
labels=vice
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,5 +23,6 @@ HAS_BITMAP_MODE=1
|
|||||||
style=single
|
style=single
|
||||||
format=startaddr,allocated
|
format=startaddr,allocated
|
||||||
extension=prg
|
extension=prg
|
||||||
|
labels=vice
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,5 +23,6 @@ HAS_BITMAP_MODE=1
|
|||||||
style=single
|
style=single
|
||||||
format=startaddr,allocated
|
format=startaddr,allocated
|
||||||
extension=prg
|
extension=prg
|
||||||
|
labels=vice
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,5 +30,6 @@ HAS_BITMAP_MODE=1
|
|||||||
style=single
|
style=single
|
||||||
format=$00,$a0,prgrom:$a000:$bfff
|
format=$00,$a0,prgrom:$a000:$bfff
|
||||||
extension=crt
|
extension=crt
|
||||||
|
labels=vice
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,7 +15,9 @@ case class Context(errorReporting: Logger,
|
|||||||
platform: Option[String] = None,
|
platform: Option[String] = None,
|
||||||
outputAssembly: Boolean = false,
|
outputAssembly: Boolean = false,
|
||||||
outputLabels: Boolean = false,
|
outputLabels: Boolean = false,
|
||||||
|
outputLabelsFormatOverride: Option[DebugOutputFormat] = None,
|
||||||
includePath: List[String] = Nil,
|
includePath: List[String] = Nil,
|
||||||
|
extraIncludePath: Seq[String] = IndexedSeq(),
|
||||||
flags: Map[CompilationFlag.Value, Boolean] = Map(),
|
flags: Map[CompilationFlag.Value, Boolean] = Map(),
|
||||||
features: Map[String, Long] = Map(),
|
features: Map[String, Long] = Map(),
|
||||||
verbosity: Option[Int] = None) {
|
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 output = c.outputFileName.getOrElse("a")
|
||||||
val assOutput = output + ".asm"
|
val assOutput = output + ".asm"
|
||||||
val labelOutput = output + ".lbl"
|
|
||||||
// val prgOutputs = (platform.outputStyle match {
|
// val prgOutputs = (platform.outputStyle match {
|
||||||
// case OutputStyle.Single => List("default")
|
// case OutputStyle.Single => List("default")
|
||||||
// case OutputStyle.PerBank => platform.bankNumbers.keys.toList
|
// case OutputStyle.PerBank => platform.bankNumbers.keys.toList
|
||||||
@ -91,17 +90,27 @@ object Main {
|
|||||||
Files.write(path, result.asm.mkString("\n").getBytes(StandardCharsets.UTF_8))
|
Files.write(path, result.asm.mkString("\n").getBytes(StandardCharsets.UTF_8))
|
||||||
}
|
}
|
||||||
if (c.outputLabels) {
|
if (c.outputLabels) {
|
||||||
val path = Paths.get(labelOutput)
|
def labelUnimportance(l: String): Int = {
|
||||||
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
|
if (l.startsWith(".")) 8
|
||||||
Files.write(path, result.labels.sortWith { (a, b) =>
|
else if (l.startsWith("__")) 7
|
||||||
val aLocal = a._1.head == '.'
|
else 0
|
||||||
val bLocal = b._1.head == '.'
|
}
|
||||||
if (aLocal == bLocal) a._1 < b._1
|
val sortedLabels = result.labels.groupBy(_._2).values.map(_.minBy(a => labelUnimportance(a._1) -> a._1)).toSeq.sortBy(_._2)
|
||||||
else b._1 < a._1
|
val format = c.outputLabelsFormatOverride.getOrElse(platform.outputLabelsFormat)
|
||||||
}.groupBy(_._2).values.map(_.head).toSeq.sortBy(_._2).map { case (l, a) =>
|
val basename = if (format.addOutputExtension) output + platform.fileExtension else output
|
||||||
val normalized = l.replace('$', '_').replace('.', '_')
|
if (format.filePerBank) {
|
||||||
s"al ${a.toHexString} .$normalized"
|
sortedLabels.groupBy(_._2._1).foreach{ case (bank, labels) =>
|
||||||
}.mkString("\n").getBytes(StandardCharsets.UTF_8))
|
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
|
val defaultPrgOutput = if (output.endsWith(platform.fileExtension)) output else output + platform.fileExtension
|
||||||
result.code.foreach{
|
result.code.foreach{
|
||||||
@ -336,7 +345,14 @@ object Main {
|
|||||||
|
|
||||||
flag("-g").action { c =>
|
flag("-g").action { c =>
|
||||||
c.copy(outputLabels = true)
|
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) =>
|
parameter("-t", "--target").placeholder("<platform>").action { (p, c) =>
|
||||||
assertNone(c.platform, "Platform already defined")
|
assertNone(c.platform, "Platform already defined")
|
||||||
|
@ -3,6 +3,7 @@ package millfork
|
|||||||
import java.io.{File, StringReader}
|
import java.io.{File, StringReader}
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.nio.file.{Files, Paths}
|
import java.nio.file.{Files, Paths}
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
import millfork.error.Logger
|
import millfork.error.Logger
|
||||||
import millfork.output._
|
import millfork.output._
|
||||||
@ -34,6 +35,7 @@ class Platform(
|
|||||||
val generateGameBoyChecksums: Boolean,
|
val generateGameBoyChecksums: Boolean,
|
||||||
val bankNumbers: Map[String, Int],
|
val bankNumbers: Map[String, Int],
|
||||||
val defaultCodeBank: String,
|
val defaultCodeBank: String,
|
||||||
|
val outputLabelsFormat: DebugOutputFormat,
|
||||||
val outputStyle: OutputStyle.Value
|
val outputStyle: OutputStyle.Value
|
||||||
) {
|
) {
|
||||||
def hasZeroPage: Boolean = cpuFamily == CpuFamily.M6502
|
def hasZeroPage: Boolean = cpuFamily == CpuFamily.M6502
|
||||||
@ -201,6 +203,10 @@ object Platform {
|
|||||||
case "lunix" => OutputStyle.LUnix
|
case "lunix" => OutputStyle.LUnix
|
||||||
case x => log.fatal(s"Invalid output style: `$x`")
|
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)
|
val builtInFeatures = builtInCpuFeatures(cpu)
|
||||||
|
|
||||||
@ -232,6 +238,7 @@ object Platform {
|
|||||||
generateGameBoyChecksums,
|
generateGameBoyChecksums,
|
||||||
bankNumbers,
|
bankNumbers,
|
||||||
defaultCodeBank,
|
defaultCodeBank,
|
||||||
|
debugOutputFormat,
|
||||||
outputStyle)
|
outputStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import millfork.node.NiceFunctionProperty
|
|||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
case class OptimizationContext(options: CompilationOptions,
|
case class OptimizationContext(options: CompilationOptions,
|
||||||
labelMap: Map[String, Int],
|
labelMap: Map[String, (Int, Int)],
|
||||||
zreg: Option[ThingInMemory],
|
zreg: Option[ThingInMemory],
|
||||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)]) {
|
niceFunctionProperties: Set[(NiceFunctionProperty, String)]) {
|
||||||
@inline
|
@inline
|
||||||
|
@ -102,10 +102,10 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
||||||
val labelMap: Map[String, Int],
|
val labelMap: Map[String, (Int, Int)],
|
||||||
val zeropageRegister: Option[ThingInMemory],
|
val zeropageRegister: Option[ThingInMemory],
|
||||||
val niceFunctionProperties: Set[(NiceFunctionProperty, String)],
|
val niceFunctionProperties: Set[(NiceFunctionProperty, String)],
|
||||||
val labeUseCount: String => Int) {
|
val labelUseCount: String => Int) {
|
||||||
@inline
|
@inline
|
||||||
def log: Logger = compilationOptions.log
|
def log: Logger = compilationOptions.log
|
||||||
@inline
|
@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 inside the block, then it's internal
|
||||||
// if a jump leads outside the block, then it's external
|
// 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 = {
|
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, _) =>
|
ISC | DCP | LAX | SAX | RLA | RRA | SLO | SRE, AddrMode.Absolute, p, Elidability.Elidable, _) =>
|
||||||
p match {
|
p match {
|
||||||
case NumericConstant(n, _) => n <= 255
|
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,
|
case CompoundConstant(MathOperator.Plus,
|
||||||
MemoryAddressConstant(th),
|
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
|
||||||
}
|
}
|
||||||
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,
|
callGraph: CallGraph,
|
||||||
allocators: Map[String, VariableAllocator],
|
allocators: Map[String, VariableAllocator],
|
||||||
options: CompilationOptions,
|
options: CompilationOptions,
|
||||||
onEachVariable: (String, Int) => Unit,
|
onEachVariable: (String, (Int, Int)) => Unit,
|
||||||
pass: Int,
|
pass: Int,
|
||||||
forZpOnly: Boolean): Unit = {
|
forZpOnly: Boolean): Unit = {
|
||||||
if (forZpOnly && !options.platform.hasZeroPage) {
|
if (forZpOnly && !options.platform.hasZeroPage) {
|
||||||
@ -155,14 +155,15 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||||||
}
|
}
|
||||||
} else GlobalVertex
|
} else GlobalVertex
|
||||||
val bank = m.bank(options)
|
val bank = m.bank(options)
|
||||||
|
val bank0 = mem.banks(bank)
|
||||||
m.alloc match {
|
m.alloc match {
|
||||||
case VariableAllocationMethod.None =>
|
case VariableAllocationMethod.None =>
|
||||||
Nil
|
Nil
|
||||||
case VariableAllocationMethod.Zeropage =>
|
case VariableAllocationMethod.Zeropage =>
|
||||||
if (forZpOnly || !options.platform.hasZeroPage) {
|
if (forZpOnly || !options.platform.hasZeroPage) {
|
||||||
val addr =
|
val addr =
|
||||||
allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Zeropage, alignment = m.alignment)
|
allocators(bank).allocateBytes(bank0, callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Zeropage, alignment = m.alignment)
|
||||||
onEachVariable(m.name, addr)
|
onEachVariable(m.name, bank0.index -> addr)
|
||||||
List(
|
List(
|
||||||
ConstantThing(m.name.stripPrefix(prefix) + "`", NumericConstant(addr, 2), p)
|
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) + "`"
|
val graveName = m.name.stripPrefix(prefix) + "`"
|
||||||
if (forZpOnly) {
|
if (forZpOnly) {
|
||||||
if (bank == "default") {
|
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 None => Nil
|
||||||
case Some(addr) =>
|
case Some(addr) =>
|
||||||
onEachVariable(m.name, addr)
|
onEachVariable(m.name, bank0.index -> addr)
|
||||||
List(
|
List(
|
||||||
ConstantThing(m.name.stripPrefix(prefix) + "`", NumericConstant(addr, 2), p)
|
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)) {
|
} else if (things.contains(graveName)) {
|
||||||
Nil
|
Nil
|
||||||
} else {
|
} else {
|
||||||
val addr = allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Either, alignment = m.alignment)
|
val addr = allocators(bank).allocateBytes(bank0, callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Either, alignment = m.alignment)
|
||||||
onEachVariable(m.name, addr)
|
onEachVariable(m.name, bank0.index -> addr)
|
||||||
List(
|
List(
|
||||||
ConstantThing(graveName, NumericConstant(addr, 2), p)
|
ConstantThing(graveName, NumericConstant(addr, 2), p)
|
||||||
)
|
)
|
||||||
|
@ -16,7 +16,7 @@ import millfork.node.NiceFunctionProperty.IsLeaf
|
|||||||
* @author Karol Stasiak
|
* @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,
|
abstract class AbstractAssembler[T <: AbstractCode](private val program: Program,
|
||||||
private val rootEnv: Environment,
|
private val rootEnv: Environment,
|
||||||
@ -30,8 +30,8 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||||||
var initializedVariablesSize: Int = 0
|
var initializedVariablesSize: Int = 0
|
||||||
protected val log: Logger = rootEnv.log
|
protected val log: Logger = rootEnv.log
|
||||||
|
|
||||||
val mem = new CompiledMemory(platform.bankNumbers.keys.toList)
|
val mem = new CompiledMemory(platform.bankNumbers.toList)
|
||||||
val labelMap: mutable.Map[String, Int] = mutable.Map()
|
val labelMap: mutable.Map[String, (Int, Int)] = mutable.Map()
|
||||||
private val bytesToWriteLater = mutable.ListBuffer[(String, Int, Constant)]()
|
private val bytesToWriteLater = mutable.ListBuffer[(String, Int, Constant)]()
|
||||||
private val wordsToWriteLater = 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) =>
|
case MemoryAddressConstant(th) =>
|
||||||
try {
|
try {
|
||||||
if (labelMap.contains(th.name)) return labelMap(th.name)
|
if (labelMap.contains(th.name)) return labelMap(th.name)._2
|
||||||
if (labelMap.contains(th.name + "`")) return labelMap(th.name)
|
if (labelMap.contains(th.name + "`")) return labelMap(th.name)._2
|
||||||
if (labelMap.contains(th.name + ".addr")) return labelMap.getOrElse[Int](th.name, labelMap(th.name + ".array"))
|
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 x1 = env.maybeGet[ConstantThing](th.name).map(_.value)
|
||||||
val x2 = 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)
|
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)
|
log.fatal("Stack overflow " + c)
|
||||||
}
|
}
|
||||||
case UnexpandedConstant(name, _) =>
|
case UnexpandedConstant(name, _) =>
|
||||||
if (labelMap.contains(name)) labelMap(name)
|
if (labelMap.contains(name)) labelMap(name)._2
|
||||||
else ???
|
else ???
|
||||||
case SubbyteConstant(cc, i) => deepConstResolve(cc).>>>(i * 8).&(0xff)
|
case SubbyteConstant(cc, i) => deepConstResolve(cc).>>>(i * 8).&(0xff)
|
||||||
case CompoundConstant(operator, lc, rc) =>
|
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
|
val index = f.address.get.asInstanceOf[NumericConstant].value.toInt
|
||||||
compiledFunctions(f.name) match {
|
compiledFunctions(f.name) match {
|
||||||
case NormalCompiledFunction(_, functionCode, _, _) =>
|
case NormalCompiledFunction(_, functionCode, _, _) =>
|
||||||
labelMap(f.name) = index
|
labelMap(f.name) = bank0.index -> index
|
||||||
val end = outputFunction(bank, functionCode, index, assembly, options)
|
val end = outputFunction(bank, functionCode, index, assembly, options)
|
||||||
for (i <- index until end) {
|
for (i <- index until end) {
|
||||||
bank0.occupied(index) = true
|
bank0.occupied(index) = true
|
||||||
@ -308,14 +308,16 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||||||
// already done before
|
// already done before
|
||||||
case (name, NormalCompiledFunction(bank, functionCode, false, alignment)) =>
|
case (name, NormalCompiledFunction(bank, functionCode, false, alignment)) =>
|
||||||
val size = functionCode.map(_.sizeInBytes).sum
|
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)
|
val bank0 = mem.banks(bank)
|
||||||
labelMap(name) = index
|
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)
|
justAfterCode += bank -> outputFunction(bank, functionCode, index, assembly, options)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
sortedCompilerFunctions.foreach {
|
sortedCompilerFunctions.foreach {
|
||||||
case (name, RedirectedFunction(_, target, offset)) =>
|
case (name, RedirectedFunction(_, target, offset)) =>
|
||||||
labelMap(name) = labelMap(target) + offset
|
val tuple = labelMap(target)
|
||||||
|
labelMap(name) = tuple._1 -> (tuple._2 + offset)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,7 +334,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||||||
if (bank != "default") ???
|
if (bank != "default") ???
|
||||||
val bank0 = mem.banks(bank)
|
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)
|
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 altName = m.name.stripPrefix(env.prefix) + "`"
|
||||||
val thing = if (name.endsWith(".addr")) env.get[ThingInMemory](name.stripSuffix(".addr")) else env.get[ThingInMemory](name + ".array")
|
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"))
|
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 bank = thing.bank(options)
|
||||||
val bank0 = mem.banks(bank)
|
val bank0 = mem.banks(bank)
|
||||||
var index = codeAllocators(bank).allocateBytes(bank0, options, items.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
var index = codeAllocators(bank).allocateBytes(bank0, options, items.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
||||||
labelMap(name) = index
|
labelMap(name) = bank0.index -> index
|
||||||
assembly.append("* = $" + index.toHexString)
|
assembly.append("* = $" + index.toHexString)
|
||||||
assembly.append(name)
|
assembly.append(name)
|
||||||
for (item <- items) {
|
for (item <- items) {
|
||||||
@ -385,7 +387,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||||||
val bank = m.bank(options)
|
val bank = m.bank(options)
|
||||||
val bank0 = mem.banks(bank)
|
val bank0 = mem.banks(bank)
|
||||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
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) + "`"
|
val altName = m.name.stripPrefix(env.prefix) + "`"
|
||||||
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
|
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
|
||||||
assembly.append("* = $" + index.toHexString)
|
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, 2, forZpOnly = false)
|
||||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, 3, forZpOnly = false)
|
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, 3, forZpOnly = false)
|
||||||
|
|
||||||
|
val defaultBank = mem.banks("default").index
|
||||||
if (platform.freeZpPointers.nonEmpty) {
|
if (platform.freeZpPointers.nonEmpty) {
|
||||||
val zpUsageOffset = platform.freeZpPointers.min
|
val zpUsageOffset = platform.freeZpPointers.min
|
||||||
val zeropageOccupation = zpOccupied.slice(zpUsageOffset, platform.freeZpPointers.max + 2)
|
val zeropageOccupation = zpOccupied.slice(zpUsageOffset, platform.freeZpPointers.max + 2)
|
||||||
labelMap += "__zeropage_usage" -> (zeropageOccupation.lastIndexOf(true) - zeropageOccupation.indexOf(true) + 1)
|
labelMap += "__zeropage_usage" -> (defaultBank, zeropageOccupation.lastIndexOf(true) - zeropageOccupation.indexOf(true) + 1)
|
||||||
labelMap += "__zeropage_first" -> (zpUsageOffset + (zeropageOccupation.indexOf(true) max 0))
|
labelMap += "__zeropage_first" -> (defaultBank, zpUsageOffset + (zeropageOccupation.indexOf(true) max 0))
|
||||||
labelMap += "__zeropage_last" -> (zpUsageOffset + (zeropageOccupation.lastIndexOf(true) max 0))
|
labelMap += "__zeropage_last" -> (defaultBank, zpUsageOffset + (zeropageOccupation.lastIndexOf(true) max 0))
|
||||||
labelMap += "__zeropage_end" -> (zpUsageOffset + zeropageOccupation.lastIndexOf(true) + 1)
|
labelMap += "__zeropage_end" -> (defaultBank, zpUsageOffset + zeropageOccupation.lastIndexOf(true) + 1)
|
||||||
} else {
|
} else {
|
||||||
labelMap += "__zeropage_usage" -> 0
|
labelMap += "__zeropage_usage" -> (defaultBank -> 0)
|
||||||
labelMap += "__zeropage_first" -> 3
|
labelMap += "__zeropage_first" -> (defaultBank -> 3)
|
||||||
labelMap += "__zeropage_last" -> 2
|
labelMap += "__zeropage_last" -> (defaultBank -> 2)
|
||||||
labelMap += "__zeropage_end" -> 3
|
labelMap += "__zeropage_end" -> (defaultBank -> 3)
|
||||||
}
|
}
|
||||||
labelMap += "__heap_start" -> variableAllocators("default").heapStart
|
labelMap += "__heap_start" -> (defaultBank -> variableAllocators("default").heapStart)
|
||||||
|
|
||||||
env = rootEnv.allThings
|
env = rootEnv.allThings
|
||||||
|
|
||||||
@ -449,10 +452,10 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||||||
mem.banks(bank).end = end
|
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"
|
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"
|
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)
|
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,
|
private def compileFunction(f: NormalFunction,
|
||||||
optimizations: Seq[AssemblyOptimization[T]],
|
optimizations: Seq[AssemblyOptimization[T]],
|
||||||
options: CompilationOptions,
|
options: CompilationOptions,
|
||||||
inlinedFunctions: Map[String, List[T]],
|
inlinedFunctions: Map[String, List[T]],
|
||||||
labelMap: Map[String, Int],
|
labelMap: Map[String, (Int, Int)],
|
||||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)]): List[T] = {
|
niceFunctionProperties: Set[(NiceFunctionProperty, String)]): List[T] = {
|
||||||
log.debug("Compiling: " + f.name, f.position)
|
log.debug("Compiling: " + f.name, f.position)
|
||||||
val unoptimized: List[T] =
|
val unoptimized: List[T] =
|
||||||
|
@ -5,12 +5,12 @@ import scala.collection.mutable
|
|||||||
/**
|
/**
|
||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
class CompiledMemory(bankNames: List[String]) {
|
class CompiledMemory(bankNames: List[(String, Int)]) {
|
||||||
var programName = "MILLFORK"
|
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 readByte(addr: Int): Int = output(addr) & 0xff
|
||||||
|
|
||||||
def readWord(addr: Int): Int = readByte(addr) + (readByte(addr + 1) << 8)
|
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(BYTE, _, _) => log.fatal("BYTE opcode failure")
|
||||||
case AssemblyLine0(_, RawByte, _) => log.fatal("BYTE opcode failure")
|
case AssemblyLine0(_, RawByte, _) => log.fatal("BYTE opcode failure")
|
||||||
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(labelName))) =>
|
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(labelName))) =>
|
||||||
labelMap(labelName) = index
|
val bank0 = mem.banks(bank)
|
||||||
|
labelMap(labelName) = bank0.index -> index
|
||||||
index
|
index
|
||||||
case AssemblyLine0(_, DoesNotExist, _) =>
|
case AssemblyLine0(_, DoesNotExist, _) =>
|
||||||
index
|
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._
|
import Opcode._
|
||||||
code.map {
|
code.map {
|
||||||
case l@AssemblyLine(LDA | STA | CMP |
|
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, _) =>
|
ISC | DCP | LAX | SAX | RLA | RRA | SLO | SRE, AddrMode.Absolute, p, Elidability.Elidable, _) =>
|
||||||
p match {
|
p match {
|
||||||
case NumericConstant(n, _) => if (n <= 255) l.copy(addrMode = AddrMode.ZeroPage) else l
|
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,
|
case CompoundConstant(MathOperator.Plus,
|
||||||
MemoryAddressConstant(th),
|
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
|
case _ => l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +73,8 @@ class Z80Assembler(program: Program,
|
|||||||
|
|
||||||
try { instr match {
|
try { instr match {
|
||||||
case ZLine0(LABEL, NoRegisters, MemoryAddressConstant(Label(labelName))) =>
|
case ZLine0(LABEL, NoRegisters, MemoryAddressConstant(Label(labelName))) =>
|
||||||
labelMap(labelName) = index
|
val bank0 = mem.banks(bank)
|
||||||
|
labelMap(labelName) = bank0.index -> index
|
||||||
index
|
index
|
||||||
case ZLine0(BYTE, NoRegisters, param) =>
|
case ZLine0(BYTE, NoRegisters, param) =>
|
||||||
writeByte(bank, index, 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))
|
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
|
} 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))
|
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._
|
import Z80ToX86Crossassembler._
|
||||||
instr match {
|
instr match {
|
||||||
case ZLine0(LABEL, NoRegisters, MemoryAddressConstant(Label(labelName))) =>
|
case ZLine0(LABEL, NoRegisters, MemoryAddressConstant(Label(labelName))) =>
|
||||||
labelMap(labelName) = index
|
val bank0 = mem.banks(bank)
|
||||||
|
labelMap(labelName) = bank0.index -> index
|
||||||
index
|
index
|
||||||
case ZLine0(BYTE, NoRegisters, param) =>
|
case ZLine0(BYTE, NoRegisters, param) =>
|
||||||
writeByte(bank, index, param)
|
writeByte(bank, index, param)
|
||||||
|
@ -74,7 +74,7 @@ class EmuI86Run(nodeOptimizations: List[NodeOptimization], assemblyOptimizations
|
|||||||
}
|
}
|
||||||
|
|
||||||
def apply2(source: String): (Timings, MemoryBank) = {
|
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.out.flush()
|
||||||
Console.err.flush()
|
Console.err.flush()
|
||||||
val log = TestErrorReporting.log
|
val log = TestErrorReporting.log
|
||||||
@ -139,7 +139,7 @@ class EmuI86Run(nodeOptimizations: List[NodeOptimization], assemblyOptimizations
|
|||||||
println(";;; compiled: -----------------")
|
println(";;; compiled: -----------------")
|
||||||
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("////; DISCARD_")).foreach(println)
|
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("////; DISCARD_")).foreach(println)
|
||||||
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
|
val optimizedSize = assembler.mem.banks("default").initialized.count(identity).toLong
|
||||||
if (unoptimizedSize == optimizedSize) {
|
if (unoptimizedSize == optimizedSize) {
|
||||||
|
@ -2,7 +2,7 @@ package millfork.test.emu
|
|||||||
|
|
||||||
import millfork.output.{AfterCodeByteAllocator, CurrentBankFragmentOutput, UpwardByteAllocator, VariableAllocator}
|
import millfork.output.{AfterCodeByteAllocator, CurrentBankFragmentOutput, UpwardByteAllocator, VariableAllocator}
|
||||||
import millfork.parser.TextCodec
|
import millfork.parser.TextCodec
|
||||||
import millfork.{Cpu, CpuFamily, OutputStyle, Platform}
|
import millfork.{Cpu, CpuFamily, OutputStyle, Platform, ViceDebugOutputFormat}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
@ -29,6 +29,7 @@ object EmuPlatform {
|
|||||||
false,
|
false,
|
||||||
Map("default" -> 0),
|
Map("default" -> 0),
|
||||||
"default",
|
"default",
|
||||||
|
ViceDebugOutputFormat,
|
||||||
OutputStyle.Single
|
OutputStyle.Single
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
|||||||
println(";;; compiled: -----------------")
|
println(";;; compiled: -----------------")
|
||||||
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
|
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
|
||||||
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
|
val optimizedSize = assembler.mem.banks("default").initialized.count(identity).toLong
|
||||||
if (unoptimizedSize == optimizedSize) {
|
if (unoptimizedSize == optimizedSize) {
|
||||||
|
@ -138,7 +138,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
|||||||
println(";;; compiled: -----------------")
|
println(";;; compiled: -----------------")
|
||||||
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("////; DISCARD_")).foreach(println)
|
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("////; DISCARD_")).foreach(println)
|
||||||
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
|
val optimizedSize = assembler.mem.banks("default").initialized.count(identity).toLong
|
||||||
if (unoptimizedSize == optimizedSize) {
|
if (unoptimizedSize == optimizedSize) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user