From 51599c96154b03a3eca1532408317c37aded6ae0 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Wed, 26 Jun 2019 18:33:59 +0200 Subject: [PATCH] Allow defining free zeropage bytes instead of pointers --- CHANGELOG.md | 2 ++ docs/api/custom-platform.md | 6 +++- include/a8.ini | 2 +- include/apple2.ini | 2 +- include/bbcmicro.ini | 2 +- include/c64_crt8k.ini | 2 +- include/lunix.ini | 2 +- include/nes_mmc4.ini | 2 +- include/nes_small.ini | 2 +- include/vcs.ini | 2 +- src/main/scala/millfork/Platform.scala | 33 ++++++++++++++----- .../millfork/output/AbstractAssembler.scala | 9 +++-- .../millfork/output/VariableAllocator.scala | 12 +++---- .../scala/millfork/test/emu/EmuPlatform.scala | 2 +- 14 files changed, 50 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc5b09c0..c4901c1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,8 @@ * **Potentially breaking change!** Commodore 128 target no longer defines `CBM_64` feature. +* 6502 targets can now define free zeropage bytes, not only pointers. + * 6502: Fixed optimizations using index registers. * 6502: Fixed optimizations of comparisons. diff --git a/docs/api/custom-platform.md b/docs/api/custom-platform.md index 74068aae..14c08d7f 100644 --- a/docs/api/custom-platform.md +++ b/docs/api/custom-platform.md @@ -118,7 +118,11 @@ See the [preprocessor documentation](../lang/preprocessor.md) for more info. * `zp_pointers` – either a list of comma separated zeropage addresses that can be used by the program as zeropage pointers, or `all` for all. Each value should be the address of the first of two free bytes in the zeropage. -Only used for 6502-based targets. +Only used for 6502-based targets. Cannot be used together with `zp_bytes`. + +* `zp_bytes` – +either a list of comma separated zeropage byte addresses or address ranges that can be used by the program in zeropage, or `all` for all. +Only used for 6502-based targets. Cannot be used together with `zp_pointers`. * `segments` – a comma-separated list of segment names. A segment named `default` is always required. diff --git a/include/a8.ini b/include/a8.ini index e0737f40..1b4512b1 100644 --- a/include/a8.ini +++ b/include/a8.ini @@ -5,7 +5,7 @@ encoding=atascii [allocation] ; TODO -zp_pointers=$80,$82,$84,$86,$88,$8a,$8c,$8e,$90,$92,$94,$96,$98,$9a,$9c,$9e,$a0,$a2,$a4 +zp_bytes=$80-$A5 segment_default_start=$2000 ; TODO segment_default_end=$3fff diff --git a/include/apple2.ini b/include/apple2.ini index 4d5cb2da..89d5b089 100644 --- a/include/apple2.ini +++ b/include/apple2.ini @@ -7,7 +7,7 @@ lenient_encoding=true [allocation] ; TODO -zp_pointers=6,8,$EB,$ED,$FA,$FC +zp_bytes=6-9, $EB-$EE, $FA-$FD segment_default_start=$0C00 segment_default_end=$95FF diff --git a/include/bbcmicro.ini b/include/bbcmicro.ini index 292e8d6a..595125d3 100644 --- a/include/bbcmicro.ini +++ b/include/bbcmicro.ini @@ -7,7 +7,7 @@ modules=bbc_kernal,bbc_hardware,default_panic,stdlib [allocation] ; TODO -zp_pointers=$70, $72, $74, $76, $78, $7A, $7C, $7E, $80, $82, $84, $86, $88, $8A, $8C, $8E +zp_bytes=$70-$8F ; $0E00 increases chances it will work on Electrons segment_default_start=$0E00 ; The following is for Model B; for Model A, consider changing it to $31FF diff --git a/include/c64_crt8k.ini b/include/c64_crt8k.ini index 9d79b44b..09de9c24 100644 --- a/include/c64_crt8k.ini +++ b/include/c64_crt8k.ini @@ -10,7 +10,7 @@ ro_arrays=true [allocation] -zp_pointers=2-$ff +zp_bytes=2-$ff segments=default,prgrom default_code_segment=prgrom segment_default_start=$800 diff --git a/include/lunix.ini b/include/lunix.ini index f82677b2..17b31162 100644 --- a/include/lunix.ini +++ b/include/lunix.ini @@ -9,7 +9,7 @@ lunix=true [allocation] -zp_pointers=$80-$bf +zp_bytes=$80-$bf segments=default default_code_segment=default segment_default_start=$1006 diff --git a/include/nes_mmc4.ini b/include/nes_mmc4.ini index 0d38356f..ed718f7e 100644 --- a/include/nes_mmc4.ini +++ b/include/nes_mmc4.ini @@ -14,7 +14,7 @@ modules=nes_hardware,nes_routines,default_panic,nes_mmc4,stdlib ro_arrays=true [allocation] -zp_pointers=all +zp_bytes=all segments=default,ram,prgrom0,prgrom1,prgrom2,prgrom3,prgrom4,prgrom5,prgrom6,prgrom7,chrrom0,chrrom1 default_code_segment=prgrom7 diff --git a/include/nes_small.ini b/include/nes_small.ini index b054316c..93ed288c 100644 --- a/include/nes_small.ini +++ b/include/nes_small.ini @@ -10,7 +10,7 @@ modules=nes_hardware,nes_routines,default_panic,stdlib ro_arrays=true [allocation] -zp_pointers=all +zp_bytes=all segments=default,prgrom,chrrom default_code_segment=prgrom diff --git a/include/vcs.ini b/include/vcs.ini index 18d1d1d5..e5f2ea2d 100644 --- a/include/vcs.ini +++ b/include/vcs.ini @@ -10,7 +10,7 @@ zeropage_register=false [allocation] -zp_pointers=$80,$82,$84,$86,$88,$8a,$8c,$8e,$90,$92,$94,$96,$98,$9a,$9c,$9e,$a0,$a2,$a4 +zp_bytes=$80-$a5 segments=default,prgrom default_code_segment=prgrom diff --git a/src/main/scala/millfork/Platform.scala b/src/main/scala/millfork/Platform.scala index fbb99dab..f8a6973e 100644 --- a/src/main/scala/millfork/Platform.scala +++ b/src/main/scala/millfork/Platform.scala @@ -29,7 +29,7 @@ class Platform( val codeAllocators: Map[String, UpwardByteAllocator], val variableAllocators: Map[String, VariableAllocator], val zpRegisterSize: Int, - val freeZpPointers: List[Int], + val freeZpBytes: List[Int], val fileExtension: String, val generateBbcMicroInfFile: Boolean, val generateGameBoyChecksums: Boolean, @@ -166,14 +166,29 @@ object Platform { bankDataStarts(b).foreach(dataStarts => if (dataStarts >= bankEnds(b)) log.error(s"Segment $b has invalid range")) }) - val freePointers = as.get(classOf[String], "zp_pointers", "all") match { - case "all" => List.tabulate(128)(_ * 2) - case xs => xs.split("[, ]+").flatMap(parseNumberOrRange).toList + val freePointers: Option[List[Int]] = as.get(classOf[String], "zp_pointers", "") match { + case "all" => Some(List.tabulate(128)(_ * 2)) + case "" => None + case xs => Some(xs.split("[, ]+").flatMap(s => parseNumberOrRange(s, 2)).toList) + } + val freeExplicitBytes: Option[List[Int]] = as.get(classOf[String], "zp_bytes", "") match { + case "all" => Some(List.tabulate(256)(identity)) + case "" => None + case xs => Some(xs.split("[, ]+").flatMap(s => parseNumberOrRange(s, 1)).toList) + } + + val freeZpBytes: List[Int] = (freePointers, freeExplicitBytes) match { + case (Some(l), None) => l.flatMap(i => List(i, i+1)) + case (None, Some(l)) => l + case (None, None) => List.tabulate(256)(identity) + case (Some(_), Some(l)) => + log.error(s"Cannot define both zp_pointers and zp_bytes") + l } val codeAllocators = banks.map(b => b -> new UpwardByteAllocator(bankStarts(b), bankCodeEnds(b))) val variableAllocators = banks.map(b => b -> new VariableAllocator( - if (b == "default" && CpuFamily.forType(cpu) == CpuFamily.M6502) freePointers else Nil, bankDataStarts(b) match { + if (b == "default" && CpuFamily.forType(cpu) == CpuFamily.M6502) freeZpBytes else Nil, bankDataStarts(b) match { case None => new AfterCodeByteAllocator(bankEnds(b)) case Some(start) => new UpwardByteAllocator(start, bankEnds(b)) })) @@ -232,7 +247,7 @@ object Platform { codeAllocators.toMap, variableAllocators.toMap, zpRegisterSize, - freePointers, + freeZpBytes, if (fileExtension == "" || fileExtension.startsWith(".")) fileExtension else "." + fileExtension, generateBbcMicroInfFile, generateGameBoyChecksums, @@ -269,15 +284,15 @@ object Platform { ) } - def parseNumberOrRange(s:String)(implicit log: Logger): Seq[Int] = { + def parseNumberOrRange(s:String, step: Int)(implicit log: Logger): Seq[Int] = { if (s.contains("-")) { val segments = s.split("-") if (segments.length != 2) { log.fatal(s"Invalid range: `$s`") } - Range(parseNumber(segments(0)), parseNumber(segments(1)), 2) + Range(parseNumber(segments(0).trim()), parseNumber(segments(1).trim()), step) } else { - Seq(parseNumber(s)) + Seq(parseNumber(s.trim())) } } diff --git a/src/main/scala/millfork/output/AbstractAssembler.scala b/src/main/scala/millfork/output/AbstractAssembler.scala index 6d7f5b01..833401a5 100644 --- a/src/main/scala/millfork/output/AbstractAssembler.scala +++ b/src/main/scala/millfork/output/AbstractAssembler.scala @@ -184,9 +184,8 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program val variableAllocators = platform.variableAllocators val zpOccupied = mem.banks("default").occupied (0 until 0x100).foreach(i => zpOccupied(i) = true) - platform.freeZpPointers.foreach { i => + platform.freeZpBytes.foreach { i => zpOccupied(i) = false - zpOccupied(i + 1) = false } val optimizations = unfilteredOptimizations.filter(_.requiredFlags.forall(options.flag)) @@ -430,9 +429,9 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program 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) + if (platform.freeZpBytes.nonEmpty) { + val zpUsageOffset = platform.freeZpBytes.min + val zeropageOccupation = zpOccupied.slice(zpUsageOffset, platform.freeZpBytes.max + 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)) diff --git a/src/main/scala/millfork/output/VariableAllocator.scala b/src/main/scala/millfork/output/VariableAllocator.scala index e5255d4d..e5566933 100644 --- a/src/main/scala/millfork/output/VariableAllocator.scala +++ b/src/main/scala/millfork/output/VariableAllocator.scala @@ -68,14 +68,14 @@ class UpwardByteAllocator(val startAt: Int, val endBefore: Int) extends ByteAllo override def preferredOrder: Option[List[Int]] = None } -class ZeropageAllocator(val freeZpPointers: List[Int]) extends ByteAllocator { +class ZeropageAllocator(val freeZpBytes: List[Int]) extends ByteAllocator { def notifyAboutEndOfCode(org: Int): Unit = () - override def preferredOrder: Option[List[Int]] = if (freeZpPointers.isEmpty) None else Some(freeZpPointers.flatMap(i => Seq(i,i+1))) + override def preferredOrder: Option[List[Int]] = if (freeZpBytes.isEmpty) None else Some(freeZpBytes) - override def startAt: Int = if (freeZpPointers.isEmpty) 2 else freeZpPointers.min + override def startAt: Int = if (freeZpBytes.isEmpty) 2 else freeZpBytes.min - override def endBefore: Int = if (freeZpPointers.isEmpty) 2 else freeZpPointers.max + 2 + override def endBefore: Int = if (freeZpBytes.isEmpty) 2 else freeZpBytes.max + 1 } class AfterCodeByteAllocator(val endBefore: Int) extends ByteAllocator { @@ -85,9 +85,9 @@ class AfterCodeByteAllocator(val endBefore: Int) extends ByteAllocator { override def preferredOrder: Option[List[Int]] = None } -class VariableAllocator(pointers: List[Int], private val bytes: ByteAllocator) { +class VariableAllocator(zpBytes: List[Int], private val bytes: ByteAllocator) { - val zeropage: ByteAllocator = new ZeropageAllocator(pointers) + val zeropage: ByteAllocator = new ZeropageAllocator(zpBytes) private val variableMap = mutable.Map[Int, mutable.Map[Int, Set[VariableVertex]]]() diff --git a/src/test/scala/millfork/test/emu/EmuPlatform.scala b/src/test/scala/millfork/test/emu/EmuPlatform.scala index 7abfba3a..49285a32 100644 --- a/src/test/scala/millfork/test/emu/EmuPlatform.scala +++ b/src/test/scala/millfork/test/emu/EmuPlatform.scala @@ -8,7 +8,7 @@ import millfork.{Cpu, CpuFamily, OutputStyle, Platform, ViceDebugOutputFormat} * @author Karol Stasiak */ object EmuPlatform { - private val pointers: List[Int] = (0 until 256 by 2).toList + private val pointers: List[Int] = (0 until 256).toList def get(cpu: Cpu.Value) = new Platform( cpu,