package millfork

import millfork.buildinfo.BuildInfo
import millfork.compiler.LabelGenerator
import millfork.error.{ConsoleLogger, Logger}
import millfork.parser.TextCodecRepository

/**
  * @author Karol Stasiak
  */
case class CompilationOptions(platform: Platform,
                              commandLineFlags: Map[CompilationFlag.Value, Boolean],
                              outputFileName: Option[String],
                              zpRegisterSize: Int,
                              featureOverrides: Map[String, Long],
                              textCodecRepository: TextCodecRepository,
                              jobContext: JobContext) {

  import CompilationFlag._
  import Cpu._

  @inline
  def log: Logger = jobContext.log
  @inline
  def nextLabel: LabelGenerator = jobContext.nextLabel
  @inline
  def isBigEndian: Boolean = platform.isBigEndian

  val flags: Map[CompilationFlag.Value, Boolean] = CompilationFlag.values.map { f =>
    f -> commandLineFlags.getOrElse(f, platform.flagOverrides.getOrElse(f, Cpu.defaultFlags(platform.cpu)(f)))
  }.toMap

  def flag(f: CompilationFlag.Value): Boolean = flags(f)

  {
    log.setFatalWarnings(flags(CompilationFlag.FatalWarnings))

    var invalids = Set[CompilationFlag.Value]()

    if (CpuFamily.forType(platform.cpu) != CpuFamily.M6502) invalids ++= Set(
      EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitWdcOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes, LUnixRelocatableCode,
      PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, LUnixRelocatableCode, RorWarning, SoftwareStack)

    if (CpuFamily.forType(platform.cpu) != CpuFamily.I80 && CpuFamily.forType(platform.cpu) != CpuFamily.I86) invalids ++= Set(
      EmitIntel8085Opcodes, EmitIntel8080Opcodes, UseIxForStack, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)

    if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(
      EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes,
      UseIyForStack, UseIxForScratch, UseIyForScratch, UseShadowRegistersForInterrupts)

    if (CpuFamily.forType(platform.cpu) != CpuFamily.M6809) invalids ++= Set(
      UseUForStack, UseYForStack)

    invalids = invalids.filter(flags)

    if (invalids.nonEmpty) {
      log.error("Invalid flags enabled for the current CPU family: " + invalids.mkString(", "))
    }
    if (CpuFamily.forType(platform.cpu) != CpuFamily.M6502 && zpRegisterSize > 0) {
      log.error("Invalid flags enabled for the current CPU family: zp_register")
    }
    CpuFamily.forType(platform.cpu) match {
      case CpuFamily.M6502 =>
        if (flags(DecimalMode)) {
          if (platform.cpu == Ricoh || platform.cpu == StrictRicoh) {
            log.warn("Decimal mode enabled for Ricoh architecture")
          }
        }
        if (platform.cpu == Sixteen) {
          if (flags(LargeCode)) {
            log.warn("Large code model doesn't work correctly yet")
          }
        }
        if (platform.cpu != Sixteen) {
          if (flags(LargeCode)) {
            log.error("Cannot use large code model on architectures other than 65816")
          }
          if (flags(ReturnWordsViaAccumulator)) {
            log.error("Cannot return words via accumulator on non-65816 architecture")
          }
          if (flags(EmitNative65816Opcodes) || flags(EmitEmulation65816Opcodes)) {
            log.error("65816 opcodes enabled for non-65816 architecture")
          }
        }
        if (platform.cpu != CE02) {
          if (flags(Emit65CE02Opcodes)) {
            log.error("65CE02 opcodes enabled for non-65CE02 architecture")
          }
        }
        if (flags(Emit65CE02Opcodes)) {
          log.warn("65CE02 opcodes are highly experimental")
        }
        if (platform.cpu != HuC6280) {
          if (flags(EmitHudsonOpcodes)) {
            log.error("HuC6280 opcodes enabled for non-HuC6280 architecture")
          }
        }
        if (!CmosCompatible(platform.cpu)) {
          if (!flags(PreventJmpIndirectBug)) {
            log.warn("JMP bug prevention should be enabled for non-CMOS architecture")
          }
          if (flags(EmitCmosOpcodes)) {
            log.error("CMOS opcodes enabled for non-CMOS architecture")
          }
        }
        if (flags(EmitIllegals)) {
          if (CmosCompatible(platform.cpu)) {
            log.error("Illegal opcodes enabled for architecture that doesn't support them")
          }
          if (platform.cpu == StrictRicoh || platform.cpu == StrictMos) {
            log.warn("Illegal opcodes enabled for strict architecture")
          }
        }
      case CpuFamily.I80 =>
        if (flags(EmitIllegals)) {
          if (platform.cpu != Z80 && platform.cpu != Intel8085 && platform.cpu != Z80Next) {
            log.error("Illegal opcodes enabled for architecture that doesn't support them")
          }
        }
        if (flags(UseIxForStack) || flags(UseIxForScratch)) {
          if (!Z80Compatible(platform.cpu)) {
            log.error("IX register enabled for architecture that doesn't support it")
          } else if (!flags(EmitZ80Opcodes)) {
            log.error("IX register is enabled but instructions using it are disabled")
          }
        }
        if (flags(UseIyForStack) || flags(UseIyForScratch)) {
          if (!Z80Compatible(platform.cpu)) {
            log.error("IY register enabled for architecture that doesn't support it")
          } else if (!flags(EmitZ80Opcodes)) {
            log.error("IY register is enabled but instructions using it are disabled")
          }
        }
        if (flags(UseIxForScratch) && flags(UseIxForStack)) {
          log.error("Cannot use the IX register for both stack variables and scratch simultaneously")
        }
        if (flags(UseIyForScratch) && flags(UseIyForStack)) {
          log.error("Cannot use the IY register for both stack variables and scratch simultaneously")
        }
        if (flags(UseIxForStack) && flags(UseIyForStack)) {
          log.error("Cannot use both IX and IY registers for stack variables simultaneously")
        }
        if (flags(UseShadowRegistersForInterrupts)) {
          if (!Z80Compatible(platform.cpu)) {
            log.error("Shadow registers enabled for architecture that doesn't support them")
          } else if (!flags(EmitZ80Opcodes)) {
            log.error("Shadow registers are enabled but instructions using them are disabled")
          }
        }
        if (flags(EmitZ80Opcodes)) {
          if (!Z80Compatible(platform.cpu)) {
            log.error("Z80 opcodes enabled for architecture that doesn't support them")
          }
        }
        if (flags(EmitEZ80Opcodes)) {
          if (platform.cpu != EZ80) {
            log.error("eZ80 opcodes enabled for architecture that doesn't support them")
          }
        }
        if (flags(EmitSharpOpcodes)) {
          if (platform.cpu != Sharp) {
            log.error("Sharp LR35902 opcodes enabled for architecture that doesn't support them")
          }
        }
        if (flags(EmitZ80NextOpcodes)) {
          if (platform.cpu != Z80Next) {
            log.error("ZX Spectrum Next opcodes enabled for architecture that doesn't support them")
          }
        }
        if (flags(EmitExtended80Opcodes)) {
          if (platform.cpu != Sharp && !Z80Compatible(platform.cpu)) {
            log.error("Extended 8080-like opcodes enabled for architecture that doesn't support them")
          }
        }
        if (flags(EmitIntel8080Opcodes)) {
          if (!Intel8080Compatible(platform.cpu)) {
            log.error("Intel 8080 opcodes enabled for architecture that doesn't support them")
          }
        }
        if (flags(EmitIntel8085Opcodes)) {
          if (!Intel8085Compatible(platform.cpu)) {
            log.error("Intel 8085 opcodes enabled for architecture that doesn't support them")
          }
        }
        if (!flags(CompilationFlag.EmitSharpOpcodes) && !flags(CompilationFlag.EmitIntel8080Opcodes)) {
          log.error("Either Sharp LR35902 or Intel 8080 opcodes have to be enabled")
        }
      case CpuFamily.I86 =>
      case CpuFamily.M6809 =>
        if (flags(UseUForStack) && flags(UseYForStack)) {
          log.error("Cannot use both U and Y registers for stack variables simultaneously")
        }
        if (!flags(UseUForStack) && !flags(UseYForStack)) {
          log.error("You need to use either the U register or the Y register as the base pointer. This might be relaxed in the future.")
        }
    }
  }

  private def parseVersion(): Long = {
    val tokens = BuildInfo.version.split("[.-]")

    def extract(ix: Int):Int = {
      tokens.lift(ix).filter(_ != "SNAPSHOT").getOrElse("0").toInt
    }

    val major = extract(0)
    val minor = extract(1)
    val patch = extract(2)
    major * 10000 + minor * 100 + patch
  }

  def features: Map[String, Long] = {
    @inline
    def toLong(b: Boolean): Long = if (b) 1L else 0L
    val featuresFromOptions = Map[String, Long](
      "MILLFORK_VERSION" -> parseVersion(),
      "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_8085" -> toLong(flag(CompilationFlag.EmitIntel8085Opcodes)),
      "CPUFEATURE_GAMEBOY" -> toLong(flag(CompilationFlag.EmitSharpOpcodes)),
      "CPUFEATURE_65C02" -> toLong(flag(CompilationFlag.EmitCmosOpcodes)),
      "CPUFEATURE_65SC02" -> toLong(flag(CompilationFlag.EmitSC02Opcodes)),
      "CPUFEATURE_ROCKWELL_65C02" -> toLong(flag(CompilationFlag.EmitRockwellOpcodes)),
      "CPUFEATURE_WDC_65C02" -> toLong(flag(CompilationFlag.EmitWdcOpcodes)),
      "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(flag(CompilationFlag.EmitZ80Opcodes) && flag(CompilationFlag.EmitIllegals)),
      "CPUFEATURE_Z80_NEXT" -> toLong(flag(CompilationFlag.EmitZ80NextOpcodes)),
      "CPUFEATURE_8085_ILLEGALS" -> toLong(flag(CompilationFlag.EmitIntel8080Opcodes) && flag(CompilationFlag.EmitIllegals)),
      "BIG_ENDIAN" -> toLong(Cpu.isBigEndian(platform.cpu)),
      "LITTLE_ENDIAN" -> toLong(!Cpu.isBigEndian(platform.cpu)),
      "INIT_RW_MEMORY" -> toLong(platform.ramInitialValuesBank.isDefined),
      "SYNTAX_INTEL" -> toLong(platform.cpuFamily == CpuFamily.I80 && flag(CompilationFlag.UseIntelSyntaxForInput)),
      "SYNTAX_ZILOG" -> toLong(platform.cpuFamily == CpuFamily.I80 && !flag(CompilationFlag.UseIntelSyntaxForInput)),
      "TINY_RW_MEMORY" -> toLong(platform.variableAllocators("default").totalHimemSize <= 256),
      "USES_ZPREG" -> toLong(platform.cpuFamily == CpuFamily.M6502 && zpRegisterSize > 0),
      "USES_IX_STACK" -> toLong(flag(CompilationFlag.UseIxForStack)),
      "USES_IY_STACK" -> toLong(flag(CompilationFlag.UseIyForStack)),
      "USES_U_STACK" -> toLong(flag(CompilationFlag.UseUForStack)),
      "USES_Y_STACK" -> toLong(flag(CompilationFlag.UseYForStack)),
      "USES_SOFTWARE_STACK" -> toLong(flag(CompilationFlag.SoftwareStack)),
      "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 {
  /**
    * Family based on the MOS 6502 processor and its descendants
    */
  val M6502: CpuFamily.Value = Value
  /**
    * Family based on the Intel 8080 processor and similar architectures
    */
  val I80: CpuFamily.Value = Value
  /**
    * Family based on the Motorola 6800 processor
    */
  val M6800: CpuFamily.Value = Value
  /**
    * Family based on the Motorola 6809 processor
    */
  val M6809: CpuFamily.Value = Value
  /**
    * Family based on the Intel 8086/8088 processor and its descendants
    */
  val I86: CpuFamily.Value = Value
  /**
    * Family based on the Motorola 68000 processor and its descendants
    */
  val M68K: CpuFamily.Value = Value
  /**
    * Family based on the ARM processors
    */
  val ARM: CpuFamily.Value = Value

  def forType(cpu: Cpu.Value): CpuFamily.Value = {
    import Cpu._
    cpu match {
      case Mos | StrictMos | Ricoh | StrictRicoh | Cmos | SC02 | Rockwell | Wdc | HuC6280 | CE02 | Sixteen => M6502
      case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | EZ80 | Z80Next => I80
      case Intel8086 | Intel80186 => I86
      case Cpu.Motorola6809 => M6809
    }
  }

  def isBigEndian(family: CpuFamily.Value): Boolean = family match {
    case M6502 | I80 | I86 | ARM => false
    case M6800 | M6809 | M68K => true
  }
}

object Cpu extends Enumeration {

  /**
    * The MOS 6502/6507/6510 processor
    */
  val Mos: Cpu.Value = Value
  /**
    * The MOS 6502/6507/6510 processor, without illegal instructions
    */
  val StrictMos: Cpu.Value = Value
  /**
    * The Ricoh 2A03/2A07 processor
    */
  val Ricoh: Cpu.Value = Value
  /**
    * The Ricoh 2A03/2A07 processor, without illegal instructions
    */
  val StrictRicoh: Cpu.Value = Value
  /**
    * The base 65C02 processor
    */
  val Cmos: Cpu.Value = Value
  /**
    * The 65SC02 processor
    */
  val SC02: Cpu.Value = Value
  /**
    * The Rockwell 65C02 processor
    */
  val Rockwell: Cpu.Value = Value
  /**
    * The WDC 65C02 processor
    */
  val Wdc: Cpu.Value = Value
  /**
    * The Hudson Soft HuC6280 processor
    */
  val HuC6280: Cpu.Value = Value
  /**
    * The CSG 65CE02 processor
    */
  val CE02: Cpu.Value = Value
  /**
    * The WDC 65816 processor
    */
  val Sixteen: Cpu.Value = Value
  /**
    * The Intel 8080 processor
    */
  val Intel8080: Cpu.Value = Value
  /**
    * The Intel 8085 processor
    */
  val Intel8085: Cpu.Value = Value
  /**
    * The Intel 8085 processor, without illegal instructions
    */
  val StrictIntel8085: Cpu.Value = Value
  /**
    * The Zilog Z80 processor
    */
  val Z80: Cpu.Value = Value
  /**
    * The Zilog Z80 processor, without illegal instructions
    */
  val StrictZ80: Cpu.Value = Value
  /**
    * The Zilog eZ80 processor
    */
  val EZ80: Cpu.Value = Value
  /**
    * The Z80 core from the ZX Spectrum Next
    */
  val Z80Next: Cpu.Value = Value
  /**
    * The Sharp LR35902 processor
    */
  val Sharp: Cpu.Value = Value
  /**
    * The Intel 8086 or 8088 processor
    */
  val Intel8086: Cpu.Value = Value
  /**
    * The Intel 80186 or 80188 processor
    */
  val Intel80186: Cpu.Value = Value
  /**
    * The Motorola 6809 processor
    */
  val Motorola6809: Cpu.Value = Value

  /**
    * Processors that can run code for WDC 65C02
    */
  val CmosCompatible: Set[Cpu.Value] = Set(Cmos, HuC6280, CE02, Sixteen, SC02, Rockwell, Wdc)
  /**
    * Processors that can run code for Zilog Z80
    */
  val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, EZ80, Z80Next)
  /**
    * Processors that can run code for Intel 8080
    */
  val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, EZ80, Z80Next)
  /**
    * Processors that can run code for Intel 8085
    */
  val Intel8085Compatible: Set[Cpu.Value] = Set(Intel8085, StrictIntel8085)

  import CompilationFlag._

  private val alwaysDefaultFlags = Set(
    VariableOverlap, CompactReturnDispatchParams, FunctionFallthrough, RegisterVariables, FunctionDeduplication, EnableBreakpoints,
    GenericWarnings, UselessCodeWarning, BuggyCodeWarning, FallbackValueUseWarning, DeprecationWarning, NonZeroTerminatedLiteralWarning, CallToOverlappingBankWarning,
  )

  private val mosAlwaysDefaultFlags = alwaysDefaultFlags

  private val i80AlwaysDefaultFlags = alwaysDefaultFlags

  private val m6809AlwaysDefaultFlags = alwaysDefaultFlags

  def defaultFlags(x: Cpu.Value): Set[CompilationFlag.Value] = x match {
    case StrictMos =>
      mosAlwaysDefaultFlags ++ Set(DecimalMode, PreventJmpIndirectBug)
    case Mos =>
      mosAlwaysDefaultFlags ++ Set(DecimalMode, PreventJmpIndirectBug)
    case Ricoh =>
      mosAlwaysDefaultFlags ++ Set(PreventJmpIndirectBug)
    case StrictRicoh =>
      mosAlwaysDefaultFlags ++ Set(PreventJmpIndirectBug)
    case Cmos =>
      mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes)
    case SC02 =>
      mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitSC02Opcodes)
    case Rockwell =>
      mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes)
    case Wdc =>
      mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitWdcOpcodes)
    case HuC6280 =>
      mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitHudsonOpcodes)
    case CE02 =>
      mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, Emit65CE02Opcodes)
    case Sixteen =>
      mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitSC02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes, ReturnWordsViaAccumulator)
    case Intel8080 =>
      i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
    case StrictIntel8085 | Intel8085 =>
      i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitIntel8085Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
    case StrictZ80 | Z80 =>
      i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts)
    case Z80Next =>
      i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitIllegals, EmitZ80NextOpcodes)
    case EZ80 =>
      i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitEZ80Opcodes)
    case Sharp =>
      i80AlwaysDefaultFlags ++ Set(EmitExtended80Opcodes, EmitSharpOpcodes)
    case Intel8086 | Intel80186 =>
      i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, UseIxForStack, EmitIntel8085Opcodes, EmitIllegals)
    case Motorola6809 =>
      m6809AlwaysDefaultFlags
  }

  def fromString(name: String)(implicit log: Logger): Cpu.Value = name match {
    case "nmos" => Mos
    case "6502" => Mos
    case "6510" => Mos
    case "strict" => StrictMos
    case "strictnmos" => StrictMos
    case "strict6502" => StrictMos
    case "strict6510" => StrictMos
    case "cmos" => Cmos
    case "65sc02" => SC02
    case "sc02" => SC02
    case "65c02" => Rockwell
    case "c02" => Rockwell
    case "w65c02" => Wdc
    case "wdc65c02" => Wdc
    case "wdc" => Wdc
    case "hudson" => HuC6280
    case "huc6280" => HuC6280
    case "c6280" => HuC6280
    case "6280" => HuC6280
    case "65ce02" => CE02
    case "ce02" => CE02
    case "65816" => Sixteen
    case "65c816" => Sixteen
    case "816" => Sixteen
    case "5a22" => Sixteen
    case "ricoh" => Ricoh
    case "2a03" => Ricoh
    case "2a07" => Ricoh
    case "strictricoh" => StrictRicoh
    case "strict2a03" => StrictRicoh
    case "strict2a07" => StrictRicoh
    case "z80" => Z80
    case "strictz80" => Z80
    case "zx80next" => Z80Next
    case "z80next" => Z80Next
      // disabled for now:
//    case "ez80" => EZ80
    case "gameboy"  => Sharp
    case "gb"  => Sharp
    case "sharp"  => Sharp
    case "lr35902"  => Sharp
    case "8080" => Intel8080
    case "i8080" => Intel8080
    case "intel8080" => Intel8080
    case "8085" => Intel8085
    case "i8085" => Intel8085
    case "intel8085" => Intel8085
    case "strict8085" => StrictIntel8085
    case "stricti8085" => StrictIntel8085
    case "strictintel8085" => StrictIntel8085
    case "intel8086" => Intel8086
    case "i8086" => Intel8086
    case "8086" => Intel8086
    case "intel80186" => Intel80186
    case "i80186" => Intel80186
    case "80186" => Intel80186
    case "intel80286" => Intel80186
    case "i80286" => Intel80186
    case "80286" => Intel80186
      // undocumented for now
    case "6809" => Motorola6809
    case _ => log.fatal("Unknown CPU achitecture: " + name)
  }

  def getMaxSizeReturnableViaRegisters(cpu: Cpu.Value, compilationOptions: CompilationOptions): Int =
    CpuFamily.forType(cpu) match {
      case CpuFamily.M6502 => 2
      case CpuFamily.I80 | CpuFamily.I86 => 4
      case CpuFamily.M6809 => 2
      case _ => ???
    }

  def isBigEndian(cpu: Cpu.Value): Boolean = CpuFamily.isBigEndian(CpuFamily.forType(cpu))
}

object CompilationFlag extends Enumeration {
  val
  // common compilation options:
  EmitIllegals, DecimalMode, LenientTextEncoding, LineNumbersInAssembly, SourceInAssembly, EnableBreakpoints,
  // compilation options for MOS:
  EmitCmosOpcodes, EmitCmosNopOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitWdcOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
  PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack,
  // compilation options for I80
  EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
  UseShadowRegistersForInterrupts,
  UseIxForStack, UseIyForStack,
  UseIxForScratch, UseIyForScratch,
  UseIntelSyntaxForInput,
  UseIntelSyntaxForOutput,
  // compilation options for 6809
  UseUForStack, UseYForStack,
  // optimization options:
  OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed, OptimizeForDebugging,
  DangerousOptimizations, InlineFunctions, InterproceduralOptimization,
  FunctionFallthrough, RegisterVariables, FunctionDeduplication, SubroutineExtraction,
  OptimizeStdlib,
  UseOptimizationHints,
  // memory allocation options
  VariableOverlap, CompactReturnDispatchParams, LUnixRelocatableCode,
  // runtime check options
  CheckIndexOutOfBounds,
  // special options
  SingleThreaded,
  // warning options
  GenericWarnings,
  UselessCodeWarning,
  BuggyCodeWarning,
  DeprecationWarning,
  FallbackValueUseWarning,
  ExtraComparisonWarnings,
  RorWarning,
  NonZeroTerminatedLiteralWarning,
  CallToOverlappingBankWarning,
  FatalWarnings,
  // special options for internal compiler use
  EnableInternalTestSyntax,
  InternalCurrentlyOptimizingForMeasurement = Value

  val allWarnings: Set[CompilationFlag.Value] = Set(GenericWarnings, UselessCodeWarning, BuggyCodeWarning, DeprecationWarning, FallbackValueUseWarning, ExtraComparisonWarnings, NonZeroTerminatedLiteralWarning, CallToOverlappingBankWarning)

  val fromString: Map[String, CompilationFlag.Value] = Map(
    "lunix" -> LUnixRelocatableCode,
    "emit_illegals" -> EmitIllegals,
    "emit_cmos" -> EmitCmosOpcodes,
    "emit_65sc02" -> EmitSC02Opcodes,
    "emit_rockwell" -> EmitRockwellOpcodes,
    "emit_wdc" -> EmitWdcOpcodes,
    "emit_65ce02" -> Emit65CE02Opcodes,
    "emit_huc6280" -> EmitHudsonOpcodes,
    "emit_z80" -> EmitZ80Opcodes,
    "emit_ez80" -> EmitEZ80Opcodes,
    "emit_x80" -> EmitExtended80Opcodes,
    "emit_8080" -> EmitIntel8080Opcodes,
    "emit_8085" -> EmitIntel8085Opcodes,
    "emit_sharp" -> EmitSharpOpcodes,
    "ix_stack" -> UseIxForStack,
    "iy_stack" -> UseIyForStack,
    "ix_scratch" -> UseIxForScratch,
    "iy_scratch" -> UseIyForScratch,
    "u_stack" -> UseUForStack,
    "y_stack" -> UseYForStack,
    "software_stack" -> SoftwareStack,
    "use_shadow_registers_for_irq" -> UseShadowRegistersForInterrupts,
    "output_intel_syntax" -> UseIntelSyntaxForOutput,
    "input_intel_syntax" -> UseIntelSyntaxForInput,
    "ipo" -> InterproceduralOptimization,
    "optimize_stdlib" -> OptimizeStdlib,
    "function_fallthrough" -> FunctionFallthrough,
    "function_deduplication" -> FunctionDeduplication,
    "subroutine_extraction" -> SubroutineExtraction,
    "inline" -> InlineFunctions,
    "dangerous_optimizations" -> DangerousOptimizations,
    "decimal_mode" -> DecimalMode,
    "ror_warn" -> RorWarning,
    "prevent_jmp_indirect_bug" -> PreventJmpIndirectBug,
    "compact_dispatch_params" -> CompactReturnDispatchParams,
    "lenient_encoding" -> LenientTextEncoding,
    "breakpoints" -> EnableBreakpoints,
  )

}