diff --git a/docs/lang/preprocessor.md b/docs/lang/preprocessor.md index d7cbfc3d..1bacdd62 100644 --- a/docs/lang/preprocessor.md +++ b/docs/lang/preprocessor.md @@ -30,19 +30,35 @@ You can also define feature values using the `-D` command line option. ### Built-in features +The following features are defined based on the chosen CPU and compilation options: + * `ARCH_6502` – 1 if compiling for 6502, 0 otherwise -* `CPUFEATURE_65C02` – 1 if compiling for 65C02-compatible processor, 0 otherwise - -* `CPUFEATURE_65CE02` – 1 if compiling for 65CE02-compatible processor, 0 otherwise - * `ARCH_I80` – 1 if compiling for Intel 8080-like processor, 0 otherwise -* `CPUFEATURE_8080` – 1 if compiling for Intel 8080-compatible processor, 0 otherwise +* `CPU_65C02`, `CPU_65CE02`, `CPU_65816`, `CPU_HUC6280`, `CPU_8080`, `CPU_GAMEBOY`, `CPU_Z80` +– 1 if compiling for the exact given processor, 0 otherwise -* `CPUFEATURE_GAMEBOY` – 1 if compiling for Sharp LR35902-compatible processor, 0 otherwise +* `CPU_6502` – 1 if compiling for any pre-65C02 6502-like processor, 0 otherwise -* `CPUFEATURE_Z80` – 1 if compiling for Z80-compatible processor, 0 otherwise +* `CPUFEATURE_DECIMAL_MODE` – 1 if decimal mode is enabled, 0 otherwise + +* `CPUFEATURE_65C02`, `CPUFEATURE_65CE02`, `CPUFEATURE_HUC6280`, `CPUFEATURE_65816_EMULATION`, `CPUFEATURE_65816_NATIVE`, +`CPUFEATURE_8080`, `CPUFEATURE_GAMEBOY`, `CPUFEATURE_Z80`, +`CPUFEATURE_6502_ILLEGALS`, `CPUFEATURE_Z80_ILLEGALS` – 1 if given instruction subset is enabled, 0 otherwise + +* `OPTIMIZE_FOR_SIZE`, `OPTIMIZE_FOR_SPEED`, `OPTIMIZE_INLINE`, `OPTIMIZE_IPO` +– 1 if given optimization setting is enabled, 0 otherwise + +* `SYNTAX_INTEL`, `SYNTAX_ZILOG` – 1 if given assembly syntax is chosen, 0 otherwise; doesn't take this file's pragmas into account + +* `USES_ZPREG` – 1 if the zeropage pseudoregister is used, 0 otherwise + +* `ZPREG_SIZE` – size of the pseudoregister in bytes + +* `USES_IX_STACK`, `USES_IY_STACK` – 1 if given index register is used as a base pointer for stack-allocated variables, 0 otherwise + +* `USES_SHADOW_REGISTERS` – 1 if interrupts preserve old registers in the shadow registers, 0 otherwise ### Commonly used features @@ -52,12 +68,18 @@ You can also define feature values using the `-D` command line option. * `CBM_64` – 1 if the target is an 8-bit Commodore computer compatible with Commodore 64, 0 otherwise +* `CBM_264` – 1 if the target is an 8-bit Commodore computer from the 264 line, 0 otherwise + * `KEYBOARD` – 1 if the target has a keyboard, 0 otherwise * `JOYSTICKS` – the maximum number of joysticks using standard hardware configurations, may be 0 * `HAS_BITMAP_MODE` – 1 if the target has a display mode with every pixel addressable, 0 otherwise +* `MOS_6510` – 1 if the target uses a MOS 6510-compatible processor (with an I/O port at $0000/$0001) + +* `CPM` – 1 if the target is CP/M, 0 otherwise + ### Built-in preprocessor functions and operators The `defined` function returns 1 if the feature is defined, 0 otherwise. diff --git a/src/main/scala/millfork/CompilationOptions.scala b/src/main/scala/millfork/CompilationOptions.scala index 9c384ad2..71e92777 100644 --- a/src/main/scala/millfork/CompilationOptions.scala +++ b/src/main/scala/millfork/CompilationOptions.scala @@ -10,6 +10,7 @@ case class CompilationOptions(platform: Platform, commandLineFlags: Map[CompilationFlag.Value, Boolean], outputFileName: Option[String], zpRegisterSize: Int, + featureOverrides: Map[String, Long], jobContext: JobContext) { import CompilationFlag._ @@ -162,6 +163,38 @@ case class CompilationOptions(platform: Platform, } } } + + def features: Map[String, Long] = { + @inline + def toLong(b: Boolean): Long = if (b) 1L else 0L + val featuresFromOptions = Map[String, Long]( + "OPTIMIZE_FOR_SIZE" -> toLong(flag(CompilationFlag.OptimizeForSize)), + "OPTIMIZE_FOR_SPEED" -> toLong(flag(CompilationFlag.OptimizeForSpeed)), + "OPTIMIZE_INLINE" -> toLong(flag(CompilationFlag.InlineFunctions)), + "OPTIMIZE_IPO" -> toLong(flag(CompilationFlag.InterproceduralOptimization)), + "CPUFEATURE_DECIMAL_MODE" -> toLong(flag(CompilationFlag.DecimalMode)), + "CPUFEATURE_Z80" -> toLong(flag(CompilationFlag.EmitZ80Opcodes)), + "CPUFEATURE_EZ80" -> toLong(flag(CompilationFlag.EmitEZ80Opcodes)), + "CPUFEATURE_8080" -> toLong(flag(CompilationFlag.EmitIntel8080Opcodes)), + "CPUFEATURE_GAMEBOY" -> toLong(flag(CompilationFlag.EmitSharpOpcodes)), + "CPUFEATURE_65C02" -> toLong(flag(CompilationFlag.EmitCmosOpcodes)), + "CPUFEATURE_65CE02" -> toLong(flag(CompilationFlag.Emit65CE02Opcodes)), + "CPUFEATURE_HUC6280" -> toLong(flag(CompilationFlag.EmitHudsonOpcodes)), + "CPUFEATURE_65816_EMULATION" -> toLong(flag(CompilationFlag.EmitEmulation65816Opcodes)), + "CPUFEATURE_65816_NATIVE" -> toLong(flag(CompilationFlag.EmitNative65816Opcodes)), + "CPUFEATURE_6502_ILLEGALS" -> toLong(platform.cpuFamily == CpuFamily.M6502 && flag(CompilationFlag.EmitIllegals)), + "CPUFEATURE_Z80_ILLEGALS" -> toLong(platform.cpuFamily == CpuFamily.I80 && flag(CompilationFlag.EmitIllegals)), + "SYNTAX_INTEL" -> toLong(platform.cpuFamily == CpuFamily.I80 && flag(CompilationFlag.UseIntelSyntaxForInput)), + "SYNTAX_ZILOG" -> toLong(platform.cpuFamily == CpuFamily.I80 && !flag(CompilationFlag.UseIntelSyntaxForInput)), + "USES_ZPREG" -> toLong(platform.cpuFamily == CpuFamily.M6502 && zpRegisterSize > 0), + "USES_IX_STACK" -> toLong(flag(CompilationFlag.UseIxForStack)), + "USES_IY_STACK" -> toLong(flag(CompilationFlag.UseIyForStack)), + "USES_SHADOW_REGISTERS" -> toLong(flag(CompilationFlag.UseShadowRegistersForInterrupts)), + "ZPREG_SIZE" -> (if (platform.cpuFamily == CpuFamily.M6502) zpRegisterSize.toLong else 0) + ) + platform.features ++ featuresFromOptions ++ featureOverrides + } + } object CpuFamily extends Enumeration { diff --git a/src/main/scala/millfork/Main.scala b/src/main/scala/millfork/Main.scala index ca26feed..620c578c 100644 --- a/src/main/scala/millfork/Main.scala +++ b/src/main/scala/millfork/Main.scala @@ -80,8 +80,8 @@ object Main { val platform = Platform.lookupPlatformFile(c.includePath, c.platform.getOrElse { errorReporting.info("No platform selected, defaulting to `c64`") "c64" - }, c.features) - val options = CompilationOptions(platform, c.flags, c.outputFileName, c.zpRegisterSize.getOrElse(platform.zpRegisterSize), JobContext(errorReporting, new LabelGenerator)) + }) + val options = CompilationOptions(platform, c.flags, c.outputFileName, c.zpRegisterSize.getOrElse(platform.zpRegisterSize), c.features, JobContext(errorReporting, new LabelGenerator)) errorReporting.debug("Effective flags: ") options.flags.toSeq.sortBy(_._1).foreach{ case (f, b) => errorReporting.debug(f" $f%-30s : $b%s") diff --git a/src/main/scala/millfork/Platform.scala b/src/main/scala/millfork/Platform.scala index 3e018fc1..234f24e8 100644 --- a/src/main/scala/millfork/Platform.scala +++ b/src/main/scala/millfork/Platform.scala @@ -42,18 +42,18 @@ class Platform( object Platform { - def lookupPlatformFile(includePath: List[String], platformName: String, featureOverrides: Map[String, Long])(implicit log: Logger): Platform = { + def lookupPlatformFile(includePath: List[String], platformName: String)(implicit log: Logger): Platform = { includePath.foreach { dir => val file = Paths.get(dir, platformName + ".ini").toFile log.debug("Checking " + file) if (file.exists()) { - return load(file, featureOverrides) + return load(file) } } log.fatal(s"Platfom definition `$platformName` not found", None) } - def load(file: File, featureOverrides: Map[String, Long])(implicit log: Logger): Platform = { + def load(file: File)(implicit log: Logger): Platform = { val conf = new INIConfiguration() val bytes = Files.readAllBytes(file.toPath) conf.read(new StringReader(new String(bytes, StandardCharsets.UTF_8))) @@ -219,7 +219,7 @@ object Platform { startingModules, codec, srcCodec, - builtInFeatures ++ definedFeatures ++ featureOverrides, + builtInFeatures ++ definedFeatures, outputPackager, codeAllocators.toMap, variableAllocators.toMap, @@ -232,29 +232,25 @@ object Platform { outputStyle) } - @inline - private def toLong(b: Boolean): Long = if (b) 1L else 0L - def builtInCpuFeatures(cpu: Cpu.Value): Map[String, Long] = { + @inline + def toLong(b: Boolean): Long = if (b) 1L else 0L Map[String, Long]( "ARCH_6502" -> toLong(CpuFamily.forType(cpu) == CpuFamily.M6502), - "CPUFEATURE_65C02" -> toLong(Cpu.defaultFlags(cpu).contains(CompilationFlag.EmitCmosOpcodes)), - "CPUFEATURE_65CE02" -> toLong(Cpu.defaultFlags(cpu).contains(CompilationFlag.Emit65CE02Opcodes)), + "CPU_6502" -> toLong(Set(Cpu.Mos, Cpu.StrictMos, Cpu.Ricoh, Cpu.StrictRicoh)(cpu)), + "CPU_65C02" -> toLong(cpu == Cpu.Cmos), + "CPU_65CE02" -> toLong(cpu == Cpu.CE02), + "CPU_65816" -> toLong(cpu == Cpu.Sixteen), + "CPU_HUC6280" -> toLong(cpu == Cpu.HuC6280), "ARCH_I80" -> toLong(CpuFamily.forType(cpu) == CpuFamily.I80), - "CPUFEATURE_Z80" -> toLong(Cpu.defaultFlags(cpu).contains(CompilationFlag.EmitZ80Opcodes)), - "CPUFEATURE_8080" -> toLong(Cpu.defaultFlags(cpu).contains(CompilationFlag.EmitIntel8080Opcodes)), - "CPUFEATURE_GAMEBOY" -> toLong(Cpu.defaultFlags(cpu).contains(CompilationFlag.EmitSharpOpcodes)), + "CPU_Z80" -> toLong(cpu == Cpu.Z80), + "CPU_EZ80" -> toLong(cpu == Cpu.EZ80), + "CPU_8080" -> toLong(cpu == Cpu.Intel8080), + "CPU_GAMEBOY" -> toLong(cpu == Cpu.Sharp), "ARCH_X86" -> toLong(CpuFamily.forType(cpu) == CpuFamily.I86), "ARCH_6800" -> toLong(CpuFamily.forType(cpu) == CpuFamily.M6800), "ARCH_ARM" -> toLong(CpuFamily.forType(cpu) == CpuFamily.ARM), - "ARCH_68K" -> toLong(CpuFamily.forType(cpu) == CpuFamily.M68K), - "HAS_HARDWARE_MULTIPLY" -> (cpu match { - case Cpu.EZ80 => 1L - case _ => CpuFamily.forType(cpu) match { - case CpuFamily.M6502 | CpuFamily.I80 | CpuFamily.M6800 => 0L - case CpuFamily.I86 | CpuFamily.ARM | CpuFamily.M68K => 1L - } - }) + "ARCH_68K" -> toLong(CpuFamily.forType(cpu) == CpuFamily.M68K) // TODO ) } diff --git a/src/test/scala/millfork/test/emu/EmuRun.scala b/src/test/scala/millfork/test/emu/EmuRun.scala index 9deec760..98fb05f4 100644 --- a/src/test/scala/millfork/test/emu/EmuRun.scala +++ b/src/test/scala/millfork/test/emu/EmuRun.scala @@ -34,7 +34,7 @@ object EmuRun { val source = Files.readAllLines(Paths.get(filename), StandardCharsets.US_ASCII).asScala.mkString("\n") val options = CompilationOptions(EmuPlatform.get(millfork.Cpu.Mos), Map( CompilationFlag.LenientTextEncoding -> true - ), None, 4, JobContext(TestErrorReporting.log, new LabelGenerator)) + ), None, 4, Map(), JobContext(TestErrorReporting.log, new LabelGenerator)) val PreprocessingResult(preprocessedSource, features, _) = Preprocessor.preprocessForTest(options, source) TestErrorReporting.log.info(s"Parsing $filename") MosParser("", preprocessedSource, "", options, features).toAst match { @@ -139,7 +139,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], CompilationFlag.OptimizeForSpeed -> blastProcessing, CompilationFlag.OptimizeForSonicSpeed -> blastProcessing // CompilationFlag.CheckIndexOutOfBounds -> true, - ), None, 4, JobContext(log, new LabelGenerator)) + ), None, 4, Map(), JobContext(log, new LabelGenerator)) log.hasErrors = false log.verbosity = 999 var effectiveSource = source diff --git a/src/test/scala/millfork/test/emu/EmuZ80Run.scala b/src/test/scala/millfork/test/emu/EmuZ80Run.scala index 1e23cec7..be9648f9 100644 --- a/src/test/scala/millfork/test/emu/EmuZ80Run.scala +++ b/src/test/scala/millfork/test/emu/EmuZ80Run.scala @@ -43,7 +43,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio CompilationFlag.OptimizeForSize -> this.optimizeForSize, CompilationFlag.EmitIllegals -> (cpu == millfork.Cpu.Z80), CompilationFlag.LenientTextEncoding -> true) - val options = CompilationOptions(platform, millfork.Cpu.defaultFlags(cpu).map(_ -> true).toMap ++ extraFlags, None, 0, JobContext(log, new LabelGenerator)) + val options = CompilationOptions(platform, millfork.Cpu.defaultFlags(cpu).map(_ -> true).toMap ++ extraFlags, None, 0, Map(), JobContext(log, new LabelGenerator)) log.hasErrors = false log.verbosity = 999 var effectiveSource = source diff --git a/src/test/scala/millfork/test/emu/ShouldNotCompile.scala b/src/test/scala/millfork/test/emu/ShouldNotCompile.scala index 5abf66c1..b81d9ff0 100644 --- a/src/test/scala/millfork/test/emu/ShouldNotCompile.scala +++ b/src/test/scala/millfork/test/emu/ShouldNotCompile.scala @@ -28,7 +28,7 @@ object ShouldNotCompile extends Matchers { val log = TestErrorReporting.log println(source) val platform = EmuPlatform.get(cpu) - val options = CompilationOptions(platform, Map(CompilationFlag.LenientTextEncoding -> true), None, platform.zpRegisterSize, JobContext(log, new LabelGenerator)) + val options = CompilationOptions(platform, Map(CompilationFlag.LenientTextEncoding -> true), None, platform.zpRegisterSize, Map(), JobContext(log, new LabelGenerator)) log.hasErrors = false log.verbosity = 999 var effectiveSource = source