2017-12-06 23:23:30 +00:00
package millfork
2018-12-24 01:38:28 +00:00
import java.io.File
2017-12-06 23:23:30 +00:00
import java.nio.charset.StandardCharsets
import java.nio.file. { Files , Paths }
import java.util.Locale
2020-02-27 00:33:34 +00:00
import millfork.assembly.m6809.opt. { M6809OptimizationPresets , VeryLateM6809AssemblyOptimizations }
2018-06-12 20:46:20 +00:00
import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.opt._
2020-02-27 00:33:34 +00:00
import millfork.assembly.z80.opt. { VeryLateI80AssemblyOptimizations , Z80OptimizationPresets }
2017-12-06 23:29:10 +00:00
import millfork.buildinfo.BuildInfo
2017-12-06 23:23:30 +00:00
import millfork.cli. { CliParser , CliStatus }
2018-07-31 16:16:36 +00:00
import millfork.compiler.LabelGenerator
2018-06-12 20:46:20 +00:00
import millfork.compiler.mos.MosCompiler
2017-12-06 23:23:30 +00:00
import millfork.env.Environment
2018-07-31 16:16:36 +00:00
import millfork.error. { ConsoleLogger , Logger }
2017-12-06 23:23:30 +00:00
import millfork.node.StandardCallGraph
2018-07-01 22:31:47 +00:00
import millfork.output._
2020-04-30 23:31:54 +00:00
import millfork.parser. { MSourceLoadingQueue , MosSourceLoadingQueue , TextCodecRepository , ZSourceLoadingQueue }
2017-12-06 23:23:30 +00:00
2018-12-27 00:18:29 +00:00
2017-12-06 23:23:30 +00:00
object Main {
def main ( args : Array [ String ] ) : Unit = {
2018-07-30 16:15:44 +00:00
val errorReporting = new ConsoleLogger
implicit val __implicitLogger : Logger = errorReporting
2017-12-06 23:23:30 +00:00
if ( args . isEmpty ) {
2018-07-30 16:15:44 +00:00
errorReporting . info ( "For help, use --help" )
2017-12-06 23:23:30 +00:00
}
2018-07-30 16:15:44 +00:00
2018-02-26 00:06:09 +00:00
val startTime = System . nanoTime ( )
2018-12-24 01:38:28 +00:00
val ( status , c0 ) = parser ( errorReporting ) . parse ( Context ( errorReporting , Nil ) , args . toList )
2017-12-06 23:23:30 +00:00
status match {
case CliStatus . Quit => return
case CliStatus . Failed =>
2018-07-30 16:15:44 +00:00
errorReporting . fatalQuit ( "Invalid command line" )
2017-12-06 23:23:30 +00:00
case CliStatus . Ok => ( )
}
2018-07-30 16:15:44 +00:00
errorReporting . assertNoErrors ( "Invalid command line" )
2018-12-24 01:38:28 +00:00
errorReporting . verbosity = c0 . verbosity . getOrElse ( 0 )
if ( c0 . inputFileNames . isEmpty ) {
2018-07-30 16:15:44 +00:00
errorReporting . fatalQuit ( "No input files" )
2017-12-06 23:23:30 +00:00
}
2018-06-25 20:42:12 +00:00
2018-07-30 16:15:44 +00:00
errorReporting . debug ( "millfork version " + BuildInfo . version )
errorReporting . trace ( s" Copyright (C) $copyrightYears Karol Stasiak " )
errorReporting . trace ( "This program comes with ABSOLUTELY NO WARRANTY." )
errorReporting . trace ( "This is free software, and you are welcome to redistribute it under certain conditions" )
errorReporting . trace ( "You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/" )
2018-12-27 00:18:29 +00:00
val c = fixMissingIncludePath ( c0 ) . filloutFlags ( )
2018-12-24 01:38:28 +00:00
if ( c . includePath . isEmpty ) {
errorReporting . warn ( "Failed to detect the default include directory, consider using the -I option" )
}
2018-06-25 20:42:12 +00:00
2020-04-30 23:31:54 +00:00
val textCodecRepository = new TextCodecRepository ( "." : : c . includePath )
2018-12-24 01:38:28 +00:00
val platform = Platform . lookupPlatformFile ( "." : : c . includePath , c . platform . getOrElse {
2018-07-30 16:15:44 +00:00
errorReporting . info ( "No platform selected, defaulting to `c64`" )
2017-12-06 23:23:30 +00:00
"c64"
2020-04-30 23:31:54 +00:00
} , textCodecRepository )
val options = CompilationOptions ( platform , c . flags , c . outputFileName , c . zpRegisterSize . getOrElse ( platform . zpRegisterSize ) , c . features , textCodecRepository , JobContext ( errorReporting , new LabelGenerator ) )
2018-07-30 16:15:44 +00:00
errorReporting . debug ( "Effective flags: " )
2018-03-03 00:21:57 +00:00
options . flags . toSeq . sortBy ( _ . _1 ) . foreach {
2018-07-30 16:15:44 +00:00
case ( f , b ) => errorReporting . debug ( f" $f %-30s : $b %s " )
2018-03-03 00:21:57 +00:00
}
2017-12-06 23:23:30 +00:00
2019-10-21 22:14:38 +00:00
val output = c . outputFileName match {
case Some ( ofn ) => ofn
case None => c . inputFileNames match {
case List ( ifn ) if ifn . endsWith ( ".mfk" ) =>
new File ( ifn . stripSuffix ( ".mfk" ) ) . getName
case _ => "a"
}
}
2020-01-03 14:01:55 +00:00
val outputParent = new File ( output ) . getParentFile
2020-01-03 15:13:54 +00:00
if ( outputParent ne null ) {
if ( outputParent . exists ( ) ) {
if ( ! outputParent . canWrite || ! outputParent . isDirectory ) {
errorReporting . warn ( s" The output directory ` ${ outputParent . getAbsolutePath } ` cannot be written to. " )
}
} else {
if ( ! outputParent . mkdirs ( ) ) {
errorReporting . warn ( s" Failed to create the output directory ` ${ outputParent . getAbsolutePath } `` " )
}
2020-01-03 14:01:55 +00:00
}
}
2017-12-06 23:23:30 +00:00
val assOutput = output + ".asm"
2018-03-15 22:09:19 +00:00
// val prgOutputs = (platform.outputStyle match {
// case OutputStyle.Single => List("default")
// case OutputStyle.PerBank => platform.bankNumbers.keys.toList
// }).map(bankName => bankName -> {
// if (bankName == "default") {
// if (output.endsWith(platform.fileExtension)) output else output + platform.fileExtension
// } else {
// s"${output.stripSuffix(platform.fileExtension)}.$bankName${platform.fileExtension}"
// }
// }).toMap
2017-12-06 23:23:30 +00:00
2018-06-12 20:46:20 +00:00
val result : AssemblerOutput = CpuFamily . forType ( platform . cpu ) match {
case CpuFamily . M6502 => assembleForMos ( c , platform , options )
2018-07-01 22:31:47 +00:00
case CpuFamily . I80 => assembleForI80 ( c , platform , options )
2019-05-31 15:03:35 +00:00
case CpuFamily . I86 => assembleForI86 ( c , platform , options )
2019-07-28 22:55:24 +00:00
case CpuFamily . M6809 => assembleForM6809 ( c , platform , options )
2018-06-12 20:46:20 +00:00
}
if ( c . outputAssembly ) {
val path = Paths . get ( assOutput )
2018-07-30 16:15:44 +00:00
errorReporting . debug ( "Writing assembly to " + path . toAbsolutePath )
2018-06-12 20:46:20 +00:00
Files . write ( path , result . asm . mkString ( "\n" ) . getBytes ( StandardCharsets . UTF_8 ) )
}
if ( c . outputLabels ) {
2019-06-14 09:39:11 +00:00
def labelUnimportance ( l : String ) : Int = {
if ( l . startsWith ( "." ) ) 8
else if ( l . startsWith ( "__" ) ) 7
else 0
}
val sortedLabels = result . labels . groupBy ( _ . _2 ) . values . map ( _ . minBy ( a => labelUnimportance ( a . _1 ) -> a . _1 ) ) . toSeq . sortBy ( _ . _2 )
2020-03-15 22:48:27 +00:00
val sortedBreakpoints = result . breakpoints
2019-06-14 09:39:11 +00:00
val format = c . outputLabelsFormatOverride . getOrElse ( platform . outputLabelsFormat )
val basename = if ( format . addOutputExtension ) output + platform . fileExtension else output
if ( format . filePerBank ) {
2020-03-15 22:48:27 +00:00
val banks = sortedLabels . map ( _ . _2 . _1 ) . toSet ++ sortedBreakpoints . map ( _ . _2 ) . toSet
banks . foreach { bank =>
val labels = sortedLabels . filter ( _ . _2 . _1 . == ( bank ) )
val breakpoints = sortedBreakpoints . filter ( _ . _1 . == ( bank ) )
2019-06-14 09:39:11 +00:00
val labelOutput = basename + format . fileExtension ( bank )
val path = Paths . get ( labelOutput )
errorReporting . debug ( "Writing labels to " + path . toAbsolutePath )
2020-03-15 22:48:27 +00:00
Files . write ( path , format . formatAll ( labels , breakpoints ) . getBytes ( StandardCharsets . UTF_8 ) )
2019-06-14 09:39:11 +00:00
}
} else {
val labelOutput = basename + format . fileExtension ( 0 )
val path = Paths . get ( labelOutput )
errorReporting . debug ( "Writing labels to " + path . toAbsolutePath )
2020-03-15 22:48:27 +00:00
Files . write ( path , format . formatAll ( sortedLabels , sortedBreakpoints ) . getBytes ( StandardCharsets . UTF_8 ) )
2019-06-14 09:39:11 +00:00
}
2018-06-12 20:46:20 +00:00
}
val defaultPrgOutput = if ( output . endsWith ( platform . fileExtension ) ) output else output + platform . fileExtension
result . code . foreach {
case ( bankName , code ) =>
val prgOutput = if ( bankName == "default" ) {
2019-01-11 14:17:48 +00:00
if ( platform . generateGameBoyChecksums ) {
code ( 0x14d ) = ( 0x0134 to 0x14c ) . map ( code ) . map ( _ ^ 0xff ) . sum . toByte
val globalChecksum = code . map ( _ & 0xff ) . sum
code ( 0x14f ) = globalChecksum . toByte
code ( 0x14e ) = globalChecksum . >> ( 8 ) . toByte
}
2018-06-12 20:46:20 +00:00
defaultPrgOutput
} else {
s" ${ output . stripSuffix ( platform . fileExtension ) } . $bankName$ {platform.fileExtension} "
}
val path = Paths . get ( prgOutput )
2018-07-30 16:15:44 +00:00
errorReporting . debug ( "Writing output to " + path . toAbsolutePath )
errorReporting . debug ( s" Total output size: ${ code . length } bytes " )
2018-06-12 20:46:20 +00:00
Files . write ( path , code )
2020-04-06 15:54:59 +00:00
if ( platform . generateBbcMicroInfFile ) {
val start = platform . codeAllocators ( bankName ) . startAt
val codeLength = code . length
Files . write ( Paths . get ( prgOutput + ".inf" ) ,
2020-04-06 20:53:15 +00:00
f" ${ path . getFileName } %s ${ start } %04X ${ start } %04X ${ codeLength } %04X " . getBytes ( StandardCharsets . UTF_8 ) )
2020-04-06 15:54:59 +00:00
}
2018-06-12 20:46:20 +00:00
}
2018-07-30 16:15:44 +00:00
errorReporting . debug ( s" Total time: ${ Math . round ( ( System . nanoTime ( ) - startTime ) / 1 e6 ) } ms " )
2019-04-29 20:57:40 +00:00
c . runFileName . foreach { program =>
2020-08-01 20:20:11 +00:00
if ( File . separatorChar == '\\' ) {
if ( ! new File ( program ) . exists ( ) && ! new File ( program + ".exe" ) . exists ( ) ) {
errorReporting . error ( s" Program $program does not exist " )
}
} else {
if ( ! new File ( program ) . exists ( ) ) {
errorReporting . error ( s" Program $program does not exist " )
}
2020-07-31 11:26:44 +00:00
}
2019-04-29 20:57:40 +00:00
val outputAbsolutePath = Paths . get ( defaultPrgOutput ) . toAbsolutePath . toString
2020-07-31 11:26:44 +00:00
val isBatch = File . separatorChar == '\\' && program . toLowerCase ( Locale . ROOT ) . endsWith ( ".bat" )
val cmdline = if ( isBatch ) {
List ( "cmd" , "/c" , program ) ++ c . runParams : + outputAbsolutePath
} else {
program +: c . runParams : + outputAbsolutePath
}
2019-09-14 00:40:03 +00:00
errorReporting . debug ( s" Running: ${ cmdline . mkString ( " " ) } " )
new ProcessBuilder ( cmdline . toArray : _ * ) . directory ( new File ( program ) . getParentFile ) . start ( )
2019-04-29 20:57:40 +00:00
}
2018-06-12 20:46:20 +00:00
}
2018-12-24 01:38:28 +00:00
private def getDefaultIncludePath : Either [ String , String ] = {
try {
2019-06-12 22:16:45 +00:00
var where = getExecutablePath . getParentFile
2018-12-24 01:38:28 +00:00
if ( ( where . getName == "scala-2.12" || where . getName == "scala-2.13" ) && where . getParentFile . getName == "target" ) {
where = where . getParentFile . getParentFile
}
val dir = new File ( where . getAbsolutePath + File . separatorChar + "include" )
if ( dir . exists ( ) ) {
Right ( dir . getAbsolutePath )
} else {
Left ( s" The ${ dir . getAbsolutePath } directory doesn't exist " )
}
} catch {
2019-06-12 22:16:45 +00:00
case e : Exception => Left ( e . toString )
}
}
private def getExecutablePath : File = {
try {
new File ( Class . forName ( "org.graalvm.nativeimage.ProcessProperties" ) . getMethod ( "getExecutableName" ) . invoke ( null ) . asInstanceOf [ String ] )
} catch {
case _ : Exception => try {
new File ( getClass . getProtectionDomain . getCodeSource . getLocation . toURI )
}
2018-12-24 01:38:28 +00:00
}
}
private def getAllDefaultPlatforms : Seq [ String ] = {
( getDefaultIncludePath match {
case Left ( _ ) => Seq (
2019-06-12 22:16:45 +00:00
"c64" , "c64_scpu" , "c64_scpu16" , "c64_crt8k" , "c64_crt16k" , "lunix" ,
2018-12-24 01:38:28 +00:00
"vic20" , "vic20_3k" , "vic20_8k" , "vic20_a000" ,
"c16" , "plus4" , "pet" , "c128" ,
"a8" , "bbcmicro" , "apple2" ,
2019-09-19 23:43:58 +00:00
"nes_mmc4" , "nes_small" , "atari_lynx" , "vcs" , "gb_small" ,
2019-06-12 22:16:45 +00:00
"zxspectrum" , "zxspectrum_8080" , "pc88" , "cpc464" , "msx_crt" ,
"cpm" , "cpm_z80" , "dos_com" )
2018-12-24 01:38:28 +00:00
case Right ( path ) =>
2020-04-27 10:40:49 +00:00
Seq ( new File ( "." ) . list ( ) , new File ( "." , "platform" ) . list ( ) , new File ( path ) . list ( ) , new File ( path , "platform" ) . list ( ) )
2018-12-24 01:38:28 +00:00
. filter ( _ ne null )
. flatMap ( _ . toSeq )
. filter ( _ . endsWith ( ".ini" ) )
. map ( _ . stripSuffix ( ".ini" ) )
} ) . sorted
}
private def fixMissingIncludePath ( c : Context ) ( implicit log : Logger ) : Context = {
if ( c . includePath . isEmpty ) {
getDefaultIncludePath match {
case Left ( err ) =>
log . debug ( s" Failed to find the default include path: $err " )
case Right ( path ) =>
log . debug ( s" Automatically detected include path: $path " )
2021-03-13 20:42:11 +00:00
return c . copy ( includePath = List ( System . getProperty ( "user.dir" ) , path ) ++ c . extraIncludePath )
2018-12-24 01:38:28 +00:00
}
}
2021-03-13 20:42:11 +00:00
c . copy ( includePath = System . getProperty ( "user.dir" ) : : ( c . includePath ++ c . extraIncludePath ) )
2018-12-24 01:38:28 +00:00
}
2018-06-12 20:46:20 +00:00
private def assembleForMos ( c : Context , platform : Platform , options : CompilationOptions ) : AssemblerOutput = {
val optLevel = c . optimizationLevel . getOrElse ( 0 )
val unoptimized = new MosSourceLoadingQueue (
2017-12-06 23:23:30 +00:00
initialFilenames = c . inputFileNames ,
includePath = c . includePath ,
options = options ) . run ( )
val program = if ( optLevel > 0 ) {
2018-01-08 00:17:25 +00:00
OptimizationPresets . NodeOpt . foldLeft ( unoptimized ) ( ( p , opt ) => p . applyNodeOptimization ( opt , options ) )
2017-12-06 23:23:30 +00:00
} else {
2018-12-27 00:18:29 +00:00
OptimizationPresets . NodeOpt0 . foldLeft ( unoptimized ) ( ( p , opt ) => p . applyNodeOptimization ( opt , options ) )
2017-12-06 23:23:30 +00:00
}
2018-07-30 16:15:44 +00:00
val callGraph = new StandardCallGraph ( program , options . log )
2019-07-17 18:48:31 +00:00
program . checkSegments ( options . log , platform . codeAllocators . keySet )
options . log . assertNoErrors ( "Build failed due to undefined segments" )
2017-12-06 23:23:30 +00:00
2019-04-16 14:56:23 +00:00
val env = new Environment ( None , "" , platform . cpuFamily , options )
2017-12-06 23:23:30 +00:00
env . collectDeclarations ( program , options )
2018-02-26 16:49:55 +00:00
val assemblyOptimizations = optLevel match {
case 0 => Nil
case 1 => OptimizationPresets . QuickPreset
case i if i >= 9 => List ( SuperOptimizer )
case _ =>
2018-03-03 20:36:26 +00:00
val goodExtras = List (
if ( options . flag ( CompilationFlag . EmitEmulation65816Opcodes ) ) SixteenOptimizations . AllForEmulation else Nil ,
if ( options . flag ( CompilationFlag . EmitNative65816Opcodes ) ) SixteenOptimizations . AllForNative else Nil ,
2018-07-06 20:45:59 +00:00
if ( options . zpRegisterSize > 0 ) ZeropageRegisterOptimizations . All else Nil
2018-03-03 20:36:26 +00:00
) . flatten
2018-02-26 16:49:55 +00:00
val extras = List (
if ( options . flag ( CompilationFlag . EmitIllegals ) ) UndocumentedOptimizations . All else Nil ,
2018-03-03 00:21:57 +00:00
if ( options . flag ( CompilationFlag . Emit65CE02Opcodes ) ) CE02Optimizations . All else Nil ,
2020-03-17 11:20:16 +00:00
if ( options . flag ( CompilationFlag . EmitCmosOpcodes ) ) CmosOptimizations . All else NmosOptimizations . All ,
2018-03-03 00:21:57 +00:00
if ( options . flag ( CompilationFlag . EmitHudsonOpcodes ) ) HudsonOptimizations . All else Nil ,
if ( options . flag ( CompilationFlag . EmitEmulation65816Opcodes ) ) SixteenOptimizations . AllForEmulation else Nil ,
2018-03-03 20:36:26 +00:00
if ( options . flag ( CompilationFlag . EmitNative65816Opcodes ) ) SixteenOptimizations . AllForNative else Nil ,
2018-07-30 16:15:44 +00:00
if ( options . flag ( CompilationFlag . DangerousOptimizations ) ) DangerousOptimizations . All else Nil
2018-02-26 16:49:55 +00:00
) . flatten
2018-03-03 20:36:26 +00:00
val goodCycle = List . fill ( optLevel - 2 ) ( OptimizationPresets . Good ++ goodExtras ) . flatten
2018-08-07 19:37:25 +00:00
val mainCycle = List . fill ( optLevel - 1 ) ( OptimizationPresets . AssOpt ++ extras ) . flatten
goodCycle ++ mainCycle ++ goodCycle
2017-12-06 23:23:30 +00:00
}
// compile
2018-06-12 20:46:20 +00:00
val assembler = new MosAssembler ( program , env , platform )
2020-02-27 00:33:34 +00:00
val result = assembler . assemble ( callGraph , assemblyOptimizations , options , if ( optLevel <= 1 ) ( _ , _ ) => Nil else VeryLateMosAssemblyOptimizations . All )
2018-07-30 16:15:44 +00:00
options . log . assertNoErrors ( "Codegen failed" )
options . log . debug ( f" Unoptimized code size: ${ assembler . unoptimizedCodeSize } %5d B " )
options . log . debug ( f" Optimized code size: ${ assembler . optimizedCodeSize } %5d B " )
options . log . debug ( f" Gain: ${ ( 100L * ( assembler . unoptimizedCodeSize - assembler . optimizedCodeSize ) / assembler . unoptimizedCodeSize . toDouble ) . round } %5d%% " )
options . log . debug ( f" Initialized variables: ${ assembler . initializedVariablesSize } %5d B " )
2018-06-12 20:46:20 +00:00
result
2017-12-06 23:23:30 +00:00
}
2018-07-01 22:31:47 +00:00
private def assembleForI80 ( c : Context , platform : Platform , options : CompilationOptions ) : AssemblerOutput = {
val optLevel = c . optimizationLevel . getOrElse ( 0 )
val unoptimized = new ZSourceLoadingQueue (
initialFilenames = c . inputFileNames ,
includePath = c . includePath ,
options = options ) . run ( )
val program = if ( optLevel > 0 ) {
OptimizationPresets . NodeOpt . foldLeft ( unoptimized ) ( ( p , opt ) => p . applyNodeOptimization ( opt , options ) )
} else {
2018-12-27 00:18:29 +00:00
OptimizationPresets . NodeOpt0 . foldLeft ( unoptimized ) ( ( p , opt ) => p . applyNodeOptimization ( opt , options ) )
2018-07-01 22:31:47 +00:00
}
2018-07-30 16:15:44 +00:00
val callGraph = new StandardCallGraph ( program , options . log )
2018-07-01 22:31:47 +00:00
2019-04-16 14:56:23 +00:00
val env = new Environment ( None , "" , platform . cpuFamily , options )
2018-07-01 22:31:47 +00:00
env . collectDeclarations ( program , options )
val assemblyOptimizations = optLevel match {
case 0 => Nil
2019-06-12 10:06:02 +00:00
case _ =>
if ( options . flag ( CompilationFlag . EmitZ80Opcodes ) )
Z80OptimizationPresets . GoodForZ80
else if ( options . flag ( CompilationFlag . EmitIntel8080Opcodes ) )
Z80OptimizationPresets . GoodForIntel8080
else if ( options . flag ( CompilationFlag . EmitSharpOpcodes ) )
Z80OptimizationPresets . GoodForSharp
else Nil
2018-07-01 22:31:47 +00:00
}
// compile
val assembler = new Z80Assembler ( program , env , platform )
2020-02-27 00:33:34 +00:00
val result = assembler . assemble ( callGraph , assemblyOptimizations , options , if ( optLevel <= 1 ) ( _ , _ ) => Nil else VeryLateI80AssemblyOptimizations . All )
2018-07-30 16:15:44 +00:00
options . log . assertNoErrors ( "Codegen failed" )
options . log . debug ( f" Unoptimized code size: ${ assembler . unoptimizedCodeSize } %5d B " )
2019-05-31 15:03:35 +00:00
options . log . debug ( f" Optimized code size: ${ assembler . optimizedCodeSize } %5d B " )
options . log . debug ( f" Gain: ${ ( 100L * ( assembler . unoptimizedCodeSize - assembler . optimizedCodeSize ) / assembler . unoptimizedCodeSize . toDouble ) . round } %5d%% " )
options . log . debug ( f" Initialized variables: ${ assembler . initializedVariablesSize } %5d B " )
result
}
2019-07-28 22:55:24 +00:00
private def assembleForM6809 ( c : Context , platform : Platform , options : CompilationOptions ) : AssemblerOutput = {
val optLevel = c . optimizationLevel . getOrElse ( 0 )
val unoptimized = new MSourceLoadingQueue (
initialFilenames = c . inputFileNames ,
includePath = c . includePath ,
options = options ) . run ( )
val program = if ( optLevel > 0 ) {
OptimizationPresets . NodeOpt . foldLeft ( unoptimized ) ( ( p , opt ) => p . applyNodeOptimization ( opt , options ) )
} else {
OptimizationPresets . NodeOpt0 . foldLeft ( unoptimized ) ( ( p , opt ) => p . applyNodeOptimization ( opt , options ) )
}
val callGraph = new StandardCallGraph ( program , options . log )
val env = new Environment ( None , "" , platform . cpuFamily , options )
env . collectDeclarations ( program , options )
2019-10-08 13:11:22 +00:00
val assemblyOptimizations = M6809OptimizationPresets . forLevel ( optLevel )
2019-07-28 22:55:24 +00:00
// compile
val assembler = new M6809Assembler ( program , env , platform )
2020-02-27 00:33:34 +00:00
val result = assembler . assemble ( callGraph , assemblyOptimizations , options , if ( optLevel <= 1 ) ( _ , _ ) => Nil else VeryLateM6809AssemblyOptimizations . All )
2019-07-28 22:55:24 +00:00
options . log . assertNoErrors ( "Codegen failed" )
options . log . debug ( f" Unoptimized code size: ${ assembler . unoptimizedCodeSize } %5d B " )
options . log . debug ( f" Optimized code size: ${ assembler . optimizedCodeSize } %5d B " )
options . log . debug ( f" Gain: ${ ( 100L * ( assembler . unoptimizedCodeSize - assembler . optimizedCodeSize ) / assembler . unoptimizedCodeSize . toDouble ) . round } %5d%% " )
options . log . debug ( f" Initialized variables: ${ assembler . initializedVariablesSize } %5d B " )
result
}
2019-05-31 15:03:35 +00:00
private def assembleForI86 ( c : Context , platform : Platform , options : CompilationOptions ) : AssemblerOutput = {
val optLevel = c . optimizationLevel . getOrElse ( 0 )
val unoptimized = new ZSourceLoadingQueue (
initialFilenames = c . inputFileNames ,
includePath = c . includePath ,
options = options ) . run ( )
val program = if ( optLevel > 0 ) {
OptimizationPresets . NodeOpt . foldLeft ( unoptimized ) ( ( p , opt ) => p . applyNodeOptimization ( opt , options ) )
} else {
OptimizationPresets . NodeOpt0 . foldLeft ( unoptimized ) ( ( p , opt ) => p . applyNodeOptimization ( opt , options ) )
}
val callGraph = new StandardCallGraph ( program , options . log )
val env = new Environment ( None , "" , platform . cpuFamily , options )
env . collectDeclarations ( program , options )
val assemblyOptimizations = optLevel match {
case 0 => Nil
case _ => Z80OptimizationPresets . GoodForIntel8080
}
// compile
val assembler = new Z80ToX86Crossassembler ( program , env , platform )
2020-02-27 00:33:34 +00:00
val result = assembler . assemble ( callGraph , assemblyOptimizations , options , if ( optLevel <= 1 ) ( _ , _ ) => Nil else VeryLateI80AssemblyOptimizations . All )
2019-05-31 15:03:35 +00:00
options . log . assertNoErrors ( "Codegen failed" )
options . log . debug ( f" Unoptimized code size: ${ assembler . unoptimizedCodeSize } %5d B " )
2018-07-30 16:15:44 +00:00
options . log . debug ( f" Optimized code size: ${ assembler . optimizedCodeSize } %5d B " )
options . log . debug ( f" Gain: ${ ( 100L * ( assembler . unoptimizedCodeSize - assembler . optimizedCodeSize ) / assembler . unoptimizedCodeSize . toDouble ) . round } %5d%% " )
options . log . debug ( f" Initialized variables: ${ assembler . initializedVariablesSize } %5d B " )
2018-07-01 22:31:47 +00:00
result
}
2018-12-16 23:35:32 +00:00
//noinspection NameBooleanParameters
2018-07-30 16:15:44 +00:00
private def parser ( errorReporting : Logger ) : CliParser [ Context ] = new CliParser [ Context ] {
2017-12-06 23:23:30 +00:00
fluff ( "Main options:" , "" )
2019-10-21 22:14:38 +00:00
parameter ( "-o" , "--out" ) . placeholder ( "<file>" ) . action { ( p , c ) =>
2017-12-06 23:23:30 +00:00
assertNone ( c . outputFileName , "Output already defined" )
c . copy ( outputFileName = Some ( p ) )
2019-10-21 22:14:38 +00:00
} . description ( "The output file name, without extension." )
2017-12-06 23:23:30 +00:00
2020-09-15 15:08:22 +00:00
flag ( "-s" ) . repeatable ( ) . action { c =>
2017-12-06 23:23:30 +00:00
c . copy ( outputAssembly = true )
} . description ( "Generate also the assembly output." )
2020-09-15 15:08:22 +00:00
flag ( "-g" ) . repeatable ( ) . action { c =>
2017-12-06 23:23:30 +00:00
c . copy ( outputLabels = true )
2019-06-14 09:39:11 +00:00
} . 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." )
2017-12-06 23:23:30 +00:00
2020-03-15 22:48:27 +00:00
boolean ( "-fbreakpoints" , "-fno-breakpoints" ) . action ( ( c , v ) =>
c . changeFlag ( CompilationFlag . EnableBreakpoints , v )
) . description ( "Include breakpoints in the label file. Requires either -g or -G." )
2017-12-06 23:23:30 +00:00
parameter ( "-t" , "--target" ) . placeholder ( "<platform>" ) . action { ( p , c ) =>
assertNone ( c . platform , "Platform already defined" )
c . copy ( platform = Some ( p ) )
2018-12-24 01:38:28 +00:00
} . description ( s" Target platform, any of: \n ${ getAllDefaultPlatforms . grouped ( 10 ) . map ( _ . mkString ( ", " ) ) . mkString ( ",\n" ) } . " )
2017-12-06 23:23:30 +00:00
parameter ( "-I" , "--include-dir" ) . repeatable ( ) . placeholder ( "<dir>;<dir>;..." ) . action { ( paths , c ) =>
val n = paths . split ( ";" )
c . copy ( includePath = c . includePath ++ n )
2018-12-24 01:38:28 +00:00
} . description ( "Include paths for modules. If not given, the default path is used: " + getDefaultIncludePath . fold ( identity , identity ) )
2017-12-06 23:23:30 +00:00
2019-06-14 09:28:26 +00:00
parameter ( "-i" , "--add-include-dir" ) . repeatable ( ) . placeholder ( "<dir>" ) . action { ( path , c ) =>
c . copy ( extraIncludePath = c . extraIncludePath : + path )
} . description ( "Add a directory to include paths." )
2017-12-06 23:23:30 +00:00
parameter ( "-r" , "--run" ) . placeholder ( "<program>" ) . action { ( p , c ) =>
assertNone ( c . runFileName , "Run program already defined" )
c . copy ( runFileName = Some ( p ) )
} . description ( "Program to run after successful compilation." )
2019-09-14 00:40:03 +00:00
parameter ( "-R" , "--run-param" ) . placeholder ( "<param>" ) . action { ( p , c ) =>
c . copy ( runParams = c . runParams : + p )
} . description ( "Adds a commandline parameter to the program launched with -r" )
2018-07-12 16:30:35 +00:00
parameter ( "-D" , "--define" ) . placeholder ( "<feature>=<value>" ) . action { ( p , c ) =>
val tokens = p . split ( '=' )
if ( tokens . length == 2 ) {
2019-08-16 15:52:07 +00:00
import java.lang.Long.parseLong
2018-07-12 16:30:35 +00:00
assertNone ( c . features . get ( tokens ( 0 ) ) , "Feature already defined" )
try {
2019-08-16 15:52:07 +00:00
val parsed = if ( tokens ( 1 ) . startsWith ( "$" ) ) {
parseLong ( tokens ( 1 ) . tail , 16 )
} else if ( tokens ( 1 ) . startsWith ( "%" ) ) {
parseLong ( tokens ( 1 ) . tail , 2 )
} else if ( tokens ( 1 ) . endsWith ( "h" ) || tokens ( 1 ) . endsWith ( "H" ) ) {
parseLong ( tokens ( 1 ) . init , 16 )
} else if ( tokens ( 1 ) . startsWith ( "0B" ) || tokens ( 1 ) . startsWith ( "0b" ) ) {
parseLong ( tokens ( 1 ) . drop ( 2 ) , 2 )
} else if ( tokens ( 1 ) . startsWith ( "0X" ) || tokens ( 1 ) . startsWith ( "0x" ) ) {
parseLong ( tokens ( 1 ) . drop ( 2 ) , 16 )
} else if ( tokens ( 1 ) . startsWith ( "0Q" ) || tokens ( 1 ) . startsWith ( "0q" ) ) {
parseLong ( tokens ( 1 ) . drop ( 2 ) , 4 )
} else if ( tokens ( 1 ) . startsWith ( "0O" ) || tokens ( 1 ) . startsWith ( "0o" ) ) {
parseLong ( tokens ( 1 ) . drop ( 2 ) , 8 )
} else {
tokens ( 1 ) . toLong
}
c . copy ( features = c . features + ( tokens ( 0 ) -> parsed ) )
2018-07-12 16:30:35 +00:00
} catch {
case _ : java . lang . NumberFormatException =>
2019-08-16 15:52:07 +00:00
errorReporting . fatal ( s" Invalid number used in -D option: ${ tokens ( 1 ) } " )
2018-07-12 16:30:35 +00:00
}
} else {
2018-07-30 16:15:44 +00:00
errorReporting . fatal ( "Invalid syntax for -D option" )
2018-07-12 16:30:35 +00:00
}
} . description ( "Define a feature value for the preprocessor." )
2020-09-15 15:08:22 +00:00
boolean ( "-finput_intel_syntax" , "-finput_zilog_syntax" ) . repeatable ( ) . action ( ( c , v ) =>
2018-08-03 11:23:37 +00:00
c . changeFlag ( CompilationFlag . UseIntelSyntaxForInput , v )
) . description ( "Select syntax for assembly source input." )
2020-09-15 15:08:22 +00:00
boolean ( "-foutput_intel_syntax" , "-foutput_zilog_syntax" ) . repeatable ( ) . action ( ( c , v ) =>
2018-08-03 11:23:37 +00:00
c . changeFlag ( CompilationFlag . UseIntelSyntaxForOutput , v )
) . description ( "Select syntax for assembly output." )
2020-09-15 15:08:22 +00:00
boolean ( "--syntax=intel" , "--syntax=zilog" ) . repeatable ( ) . action ( ( c , v ) =>
2018-08-03 11:23:37 +00:00
c . changeFlag ( CompilationFlag . UseIntelSyntaxForInput , v ) . changeFlag ( CompilationFlag . UseIntelSyntaxForOutput , v )
) . description ( "Select syntax for assembly input and output." )
2020-09-15 15:08:22 +00:00
boolean ( "-fline-numbers" , "-fno-line-numbers" ) . repeatable ( ) . action ( ( c , v ) =>
2018-12-14 14:42:31 +00:00
c . changeFlag ( CompilationFlag . LineNumbersInAssembly , v )
) . description ( "Show source line numbers in assembly." )
2020-09-15 15:08:22 +00:00
boolean ( "-fsource-in-asm" , "-fno-source-in-asm" ) . repeatable ( ) . action ( ( c , v ) =>
2018-12-16 23:35:32 +00:00
if ( v ) {
c . changeFlag ( CompilationFlag . SourceInAssembly , true ) . changeFlag ( CompilationFlag . LineNumbersInAssembly , true )
} else {
c . changeFlag ( CompilationFlag . LineNumbersInAssembly , false )
}
) . description ( "Show source in assembly." )
2017-12-06 23:23:30 +00:00
endOfFlags ( "--" ) . description ( "Marks the end of options." )
fluff ( "" , "Verbosity options:" , "" )
2020-09-15 15:08:22 +00:00
flag ( "-q" , "--quiet" ) . repeatable ( ) . action { c =>
2017-12-06 23:23:30 +00:00
assertNone ( c . verbosity , "Cannot use -v and -q together" )
c . copy ( verbosity = Some ( - 1 ) )
} . description ( "Supress all messages except for errors." )
private val verbose = flag ( "-v" , "--verbose" ) . maxCount ( 3 ) . action { c =>
2018-07-30 16:15:44 +00:00
if ( c . verbosity . exists ( _ < 0 ) ) errorReporting . error ( "Cannot use -v and -q together" , None )
2017-12-06 23:23:30 +00:00
c . copy ( verbosity = Some ( 1 + c . verbosity . getOrElse ( 0 ) ) )
} . description ( "Increase verbosity." )
flag ( "-vv" ) . repeatable ( ) . action ( c => verbose . encounter ( verbose . encounter ( verbose . encounter ( c ) ) ) ) . description ( "Increase verbosity even more." )
flag ( "-vvv" ) . repeatable ( ) . action ( c => verbose . encounter ( verbose . encounter ( c ) ) ) . description ( "Increase verbosity even more." )
fluff ( "" , "Code generation options:" , "" )
boolean ( "-fcmos-ops" , "-fno-cmos-ops" ) . action { ( c , v ) =>
2019-11-04 13:28:36 +00:00
if ( v ) {
c . changeFlag ( CompilationFlag . EmitCmosOpcodes , true )
} else {
c . changeFlag ( CompilationFlag . EmitCmosOpcodes , false )
c . changeFlag ( CompilationFlag . EmitSC02Opcodes , false )
c . changeFlag ( CompilationFlag . EmitRockwellOpcodes , false )
c . changeFlag ( CompilationFlag . EmitWdcOpcodes , false )
c . changeFlag ( CompilationFlag . Emit65CE02Opcodes , false )
c . changeFlag ( CompilationFlag . EmitHudsonOpcodes , false )
c . changeFlag ( CompilationFlag . EmitNative65816Opcodes , false )
c . changeFlag ( CompilationFlag . EmitEmulation65816Opcodes , false )
}
} . description ( "Whether should emit the core 65C02 opcodes." )
boolean ( "-f65sc02-ops" , "-fno-65sc02-ops" ) . action { ( c , v ) =>
if ( v ) {
c . changeFlag ( CompilationFlag . EmitSC02Opcodes , true )
c . changeFlag ( CompilationFlag . EmitCmosOpcodes , true )
} else {
c . changeFlag ( CompilationFlag . EmitSC02Opcodes , false )
c . changeFlag ( CompilationFlag . EmitRockwellOpcodes , false )
c . changeFlag ( CompilationFlag . EmitWdcOpcodes , false )
c . changeFlag ( CompilationFlag . Emit65CE02Opcodes , false )
c . changeFlag ( CompilationFlag . EmitHudsonOpcodes , false )
}
} . description ( "Whether should emit 65SC02 opcodes." )
boolean ( "-frockwell-ops" , "-fno-rockwell-ops" ) . action { ( c , v ) =>
if ( v ) {
c . changeFlag ( CompilationFlag . EmitRockwellOpcodes , true )
c . changeFlag ( CompilationFlag . EmitSC02Opcodes , true )
c . changeFlag ( CompilationFlag . EmitCmosOpcodes , true )
} else {
c . changeFlag ( CompilationFlag . EmitRockwellOpcodes , false )
c . changeFlag ( CompilationFlag . EmitWdcOpcodes , false )
c . changeFlag ( CompilationFlag . Emit65CE02Opcodes , false )
c . changeFlag ( CompilationFlag . EmitHudsonOpcodes , false )
}
} . description ( "Whether should emit Rockwell 65C02 opcodes." )
boolean ( "-fwdc-ops" , "-fno-wdc-ops" ) . action { ( c , v ) =>
if ( v ) {
c . changeFlag ( CompilationFlag . EmitWdcOpcodes , true )
c . changeFlag ( CompilationFlag . EmitRockwellOpcodes , true )
c . changeFlag ( CompilationFlag . EmitSC02Opcodes , true )
c . changeFlag ( CompilationFlag . EmitCmosOpcodes , true )
} else {
c . changeFlag ( CompilationFlag . EmitWdcOpcodes , false )
}
} . description ( "Whether should emit WDC 65C02 opcodes." )
2018-03-03 00:21:57 +00:00
boolean ( "-f65ce02-ops" , "-fno-65ce02-ops" ) . action { ( c , v ) =>
2019-11-04 13:28:36 +00:00
if ( v ) {
c . changeFlag ( CompilationFlag . Emit65CE02Opcodes , true )
c . changeFlag ( CompilationFlag . EmitRockwellOpcodes , true )
c . changeFlag ( CompilationFlag . EmitSC02Opcodes , true )
c . changeFlag ( CompilationFlag . EmitCmosOpcodes , true )
} else {
c . changeFlag ( CompilationFlag . Emit65CE02Opcodes , false )
}
2018-03-03 00:21:57 +00:00
} . description ( "Whether should emit 65CE02 opcodes." )
boolean ( "-fhuc6280-ops" , "-fno-huc6280-ops" ) . action { ( c , v ) =>
2019-11-04 13:28:36 +00:00
if ( v ) {
c . changeFlag ( CompilationFlag . EmitHudsonOpcodes , true )
c . changeFlag ( CompilationFlag . EmitRockwellOpcodes , true )
c . changeFlag ( CompilationFlag . EmitSC02Opcodes , true )
c . changeFlag ( CompilationFlag . EmitCmosOpcodes , true )
} else {
c . changeFlag ( CompilationFlag . EmitCmosOpcodes , false )
}
2018-08-06 17:15:41 +00:00
} . description ( "Whether should emit HuC6280 opcodes." )
2020-09-15 15:08:22 +00:00
flag ( "-fno-65816-ops" ) . repeatable ( ) . action { c =>
2018-03-03 00:21:57 +00:00
c . changeFlag ( CompilationFlag . EmitEmulation65816Opcodes , b = false )
c . changeFlag ( CompilationFlag . EmitNative65816Opcodes , b = false )
c . changeFlag ( CompilationFlag . ReturnWordsViaAccumulator , b = false )
} . description ( "Don't emit 65816 opcodes." )
2020-09-15 15:08:22 +00:00
flag ( "-femulation-65816-ops" ) . repeatable ( ) . action { c =>
2018-03-03 00:21:57 +00:00
c . changeFlag ( CompilationFlag . EmitEmulation65816Opcodes , b = true )
c . changeFlag ( CompilationFlag . EmitNative65816Opcodes , b = false )
c . changeFlag ( CompilationFlag . ReturnWordsViaAccumulator , b = false )
2018-06-04 14:24:18 +00:00
} . description ( "Emit 65816 opcodes in emulation mode (experimental)." )
2020-09-15 15:08:22 +00:00
flag ( "-fnative-65816-ops" ) . repeatable ( ) . action { c =>
2018-03-03 00:21:57 +00:00
c . changeFlag ( CompilationFlag . EmitEmulation65816Opcodes , b = true )
c . changeFlag ( CompilationFlag . EmitNative65816Opcodes , b = true )
2018-06-04 14:24:18 +00:00
} . description ( "Emit 65816 opcodes in native mode (very experimental and buggy)." )
2018-03-03 00:21:57 +00:00
boolean ( "-flarge-code" , "-fsmall-code" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . LargeCode , v )
} . description ( "Whether should use 24-bit or 16-bit jumps to subroutines (not yet implemented)." ) . hidden ( )
2019-06-12 10:06:02 +00:00
boolean ( "-f8085-ops" , "-fno-8085-ops" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . EmitIntel8085Opcodes , v )
} . description ( "Whether should emit Intel 8085 opcodes." )
boolean ( "-fz80-ops" , "-fno-z80-ops" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . EmitZ80Opcodes , v ) . changeFlag ( CompilationFlag . EmitExtended80Opcodes , v )
} . description ( "Whether should emit Z80 opcodes." )
2017-12-06 23:23:30 +00:00
boolean ( "-fillegals" , "-fno-illegals" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . EmitIllegals , v )
2019-06-12 10:06:02 +00:00
} . description ( "Whether should emit illegal (undocumented) opcodes. On 6502, requires -O2 or higher to have an effect." )
2018-08-06 17:15:41 +00:00
flag ( "-fzp-register=[0-15]" ) . description ( "Set the size of the zeropage pseudoregister (6502 only)." ) . dummy ( )
( 0 to 15 ) . foreach ( i =>
flag ( "-fzp-register=" + i ) . action ( c => c . copy ( zpRegisterSize = Some ( i ) ) ) . hidden ( )
)
flag ( "-fzp-register" ) . action { c =>
c . copy ( zpRegisterSize = Some ( 4 ) )
} . description ( "Alias for -fzp-register=4." )
flag ( "-fno-zp-register" ) . action { c =>
c . copy ( zpRegisterSize = Some ( 0 ) )
} . description ( "Alias for -fzp-register=0." )
2017-12-06 23:23:30 +00:00
boolean ( "-fjmp-fix" , "-fno-jmp-fix" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . PreventJmpIndirectBug , v )
2018-08-06 17:15:41 +00:00
} . description ( "Whether should prevent indirect JMP bug on page boundary (6502 only)." )
2017-12-06 23:23:30 +00:00
boolean ( "-fdecimal-mode" , "-fno-decimal-mode" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . DecimalMode , v )
2018-08-03 11:06:23 +00:00
} . description ( "Whether hardware decimal mode should be used (6502 only)." )
2017-12-06 23:23:30 +00:00
boolean ( "-fvariable-overlap" , "-fno-variable-overlap" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . VariableOverlap , v )
2018-12-27 00:18:29 +00:00
} . description ( "Whether variables should overlap if their scopes do not intersect. Enabled by default." )
2018-01-30 16:38:32 +00:00
boolean ( "-fcompact-dispatch-params" , "-fno-compact-dispatch-params" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . CompactReturnDispatchParams , v )
2018-12-27 00:18:29 +00:00
} . description ( "Whether parameter values in return dispatch statements may overlap other objects. Enabled by default." )
2018-01-08 00:17:25 +00:00
boolean ( "-fbounds-checking" , "-fno-bounds-checking" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . VariableOverlap , v )
} . description ( "Whether should insert bounds checking on array access." )
2018-07-06 22:58:44 +00:00
boolean ( "-flenient-encoding" , "-fno-lenient-encoding" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . LenientTextEncoding , v )
} . description ( "Whether the compiler should replace invalid characters in string literals that use the default encodings." )
2018-08-01 10:50:05 +00:00
boolean ( "-fshadow-irq" , "-fno-shadow-irq" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . UseShadowRegistersForInterrupts , v )
} . description ( "Whether shadow registers should be used in interrupt routines (Z80 only)" )
2020-09-15 15:08:22 +00:00
flag ( "-fuse-ix-for-stack" ) . repeatable ( ) . action { c =>
2018-08-01 10:50:05 +00:00
c . changeFlag ( CompilationFlag . UseIxForStack , true ) . changeFlag ( CompilationFlag . UseIyForStack , false )
} . description ( "Use IX as base pointer for stack variables (Z80 only)" )
2020-09-15 15:08:22 +00:00
flag ( "-fuse-iy-for-stack" ) . repeatable ( ) . action { c =>
2018-08-01 10:50:05 +00:00
c . changeFlag ( CompilationFlag . UseIyForStack , true ) . changeFlag ( CompilationFlag . UseIxForStack , false )
} . description ( "Use IY as base pointer for stack variables (Z80 only)" )
2020-09-15 15:08:22 +00:00
flag ( "-fuse-u-for-stack" ) . repeatable ( ) . action { c =>
2020-07-08 23:50:18 +00:00
c . changeFlag ( CompilationFlag . UseUForStack , true ) . changeFlag ( CompilationFlag . UseYForStack , false )
2019-08-15 22:46:46 +00:00
} . description ( "Use U as base pointer for stack variables (6809 only)" ) . hidden ( )
2020-09-15 15:08:22 +00:00
flag ( "-fuse-y-for-stack" ) . repeatable ( ) . action { c =>
2020-07-08 23:50:18 +00:00
c . changeFlag ( CompilationFlag . UseYForStack , true ) . changeFlag ( CompilationFlag . UseUForStack , false )
2019-08-15 22:46:46 +00:00
} . description ( "Use Y as base pointer for stack variables (6809 only)" ) . hidden ( )
2018-08-01 10:50:05 +00:00
boolean ( "-fuse-ix-for-scratch" , "-fno-use-ix-for-scratch" ) . action { ( c , v ) =>
if ( v ) {
c . changeFlag ( CompilationFlag . UseIxForScratch , true ) . changeFlag ( CompilationFlag . UseIxForStack , false )
} else {
c . changeFlag ( CompilationFlag . UseIxForScratch , false )
}
} . description ( "Use IX as base pointer for stack variables (Z80 only)" )
boolean ( "-fuse-iy-for-scratch" , "-fno-use-iy-for-scratch" ) . action { ( c , v ) =>
if ( v ) {
c . changeFlag ( CompilationFlag . UseIyForScratch , true ) . changeFlag ( CompilationFlag . UseIyForStack , false )
} else {
c . changeFlag ( CompilationFlag . UseIyForScratch , false )
}
} . description ( "Use IY as base pointer for stack variables (Z80 only)" )
2020-09-15 15:08:22 +00:00
flag ( "-fno-use-index-for-stack" ) . repeatable ( ) . action { c =>
2018-08-01 10:50:05 +00:00
c . changeFlag ( CompilationFlag . UseIyForStack , false ) . changeFlag ( CompilationFlag . UseIxForStack , false )
} . description ( "Don't use either IX or IY as base pointer for stack variables (Z80 only)" )
2020-09-15 15:08:22 +00:00
flag ( "-fno-use-uy-for-stack" ) . repeatable ( ) . action { c =>
2019-08-15 22:46:46 +00:00
c . changeFlag ( CompilationFlag . UseUForStack , false ) . changeFlag ( CompilationFlag . UseYForStack , false )
} . description ( "Don't use either U or Y as base pointer for stack variables (6809 only)" ) . hidden ( )
2018-12-14 21:01:52 +00:00
boolean ( "-fsoftware-stack" , "-fno-software-stack" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . SoftwareStack , v )
} . description ( "Use software stack for stack variables (6502 only)" )
2017-12-06 23:23:30 +00:00
fluff ( "" , "Optimization options:" , "" )
2020-09-15 15:08:22 +00:00
flag ( "-O0" ) . repeatable ( ) . action { c =>
2017-12-06 23:23:30 +00:00
assertNone ( c . optimizationLevel , "Optimization level already defined" )
c . copy ( optimizationLevel = Some ( 0 ) )
} . description ( "Disable all optimizations." )
2020-09-15 15:08:22 +00:00
flag ( "-O" ) . repeatable ( ) . action { c =>
2017-12-06 23:23:30 +00:00
assertNone ( c . optimizationLevel , "Optimization level already defined" )
c . copy ( optimizationLevel = Some ( 1 ) )
} . description ( "Optimize code." )
2018-02-01 21:39:38 +00:00
for ( i <- 1 to 9 ) {
2017-12-06 23:23:30 +00:00
val f = flag ( "-O" + i ) . action { c =>
assertNone ( c . optimizationLevel , "Optimization level already defined" )
c . copy ( optimizationLevel = Some ( i ) )
} . description ( "Optimize code even more." )
2018-12-24 01:38:28 +00:00
if ( i == 1 || i > 4 ) f . hidden ( )
2017-12-06 23:23:30 +00:00
}
2021-03-14 23:44:14 +00:00
boolean ( "-fhints" , "-fnohints" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . UseOptimizationHints , v )
} . description ( "Whether optimization hints should be used." )
2020-09-15 15:08:22 +00:00
flag ( "--inline" ) . repeatable ( ) . action { c =>
2017-12-20 01:50:52 +00:00
c . changeFlag ( CompilationFlag . InlineFunctions , true )
2018-07-01 17:19:30 +00:00
} . description ( "Inline functions automatically." ) . hidden ( )
boolean ( "-finline" , "-fno-inline" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . InlineFunctions , v )
2018-02-26 01:41:18 +00:00
} . description ( "Inline functions automatically." )
2020-09-15 15:08:22 +00:00
flag ( "--ipo" ) . repeatable ( ) . action { c =>
2018-06-25 19:29:04 +00:00
c . changeFlag ( CompilationFlag . InterproceduralOptimization , true )
} . description ( "Interprocedural optimization." ) . hidden ( )
boolean ( "--fipo" , "--fno-ipo" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . InterproceduralOptimization , v )
2018-07-01 17:19:30 +00:00
} . description ( "Interprocedural optimization." ) . hidden ( )
boolean ( "-fipo" , "-fno-ipo" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . InterproceduralOptimization , v )
2018-06-25 19:29:04 +00:00
} . description ( "Interprocedural optimization." )
2018-12-21 21:33:27 +00:00
boolean ( "-foptimize-stdlib" , "-fno-optimize-stdlib" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . OptimizeStdlib , v )
} . description ( "Optimize standard library calls." )
2018-12-27 00:18:29 +00:00
boolean ( "-fsubroutine-extraction" , "-fno-subroutine-extraction" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . SubroutineExtraction , v )
} . description ( "Extract identical code fragments into subroutines." )
boolean ( "-ffunction-fallthrough" , "-fno-function-fallthrough" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . FunctionFallthrough , v )
} . description ( "Replace tail calls by simply putting one function after another. Enabled by default." )
boolean ( "-ffunction-deduplication" , "-fno-function-deduplication" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . FunctionDeduplication , v )
} . description ( "Merge identical functions into one function. Enabled by default." )
boolean ( "-fregister-variables" , "-fno-register-variables" ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . RegisterVariables , v )
} . description ( "Allow moving local variables into CPU registers. Enabled by default." )
2020-09-15 15:08:22 +00:00
flag ( "-Os" , "--size" ) . repeatable ( ) . action { c =>
2018-03-16 13:12:38 +00:00
c . changeFlag ( CompilationFlag . OptimizeForSize , true ) .
changeFlag ( CompilationFlag . OptimizeForSpeed , false ) .
changeFlag ( CompilationFlag . OptimizeForSonicSpeed , false )
2018-12-27 00:18:29 +00:00
} . description ( "Prefer smaller code even if it is slightly slower (experimental). Implies -fsubroutine-extraction." )
2020-09-15 15:08:22 +00:00
flag ( "-Of" , "--fast" ) . repeatable ( ) . action { c =>
2018-03-16 13:12:38 +00:00
c . changeFlag ( CompilationFlag . OptimizeForSize , false ) .
changeFlag ( CompilationFlag . OptimizeForSpeed , true ) .
changeFlag ( CompilationFlag . OptimizeForSonicSpeed , false )
2018-12-27 00:18:29 +00:00
} . description ( "Prefer faster code even if it is slightly bigger (experimental). Implies -finline." )
2020-09-15 15:08:22 +00:00
flag ( "-Ob" , "--blast-processing" ) . repeatable ( ) . action { c =>
2018-03-16 13:12:38 +00:00
c . changeFlag ( CompilationFlag . OptimizeForSize , false ) .
changeFlag ( CompilationFlag . OptimizeForSpeed , true ) .
2018-12-27 00:18:29 +00:00
changeFlag ( CompilationFlag . OptimizeForSonicSpeed , true )
2018-07-01 17:19:30 +00:00
} . description ( "Prefer faster code even if it is much bigger (experimental). Implies -finline." )
2020-09-15 15:08:22 +00:00
flag ( "--dangerous-optimizations" ) . repeatable ( ) . action { c =>
2017-12-06 23:23:30 +00:00
c . changeFlag ( CompilationFlag . DangerousOptimizations , true )
2018-07-01 17:19:30 +00:00
} . description ( "Use dangerous optimizations (experimental)." ) . hidden ( )
2018-12-21 21:33:27 +00:00
boolean ( "-fdangerous-optimizations" , "-fno-dangerous-optimizations" ) . action { ( c , v ) =>
2018-07-01 17:19:30 +00:00
c . changeFlag ( CompilationFlag . DangerousOptimizations , v )
2018-12-27 00:18:29 +00:00
} . description ( "Use dangerous optimizations (experimental). Implies -fipo and -foptimize-stdlib." )
2020-09-15 15:08:22 +00:00
flag ( "-Og" , "--optimize-debugging" ) . repeatable ( ) . action { c =>
2018-12-27 00:18:29 +00:00
c . changeFlag ( CompilationFlag . OptimizeForDebugging , true )
} . description ( "Disable optimizations that make debugging harder (experimental)." )
2017-12-06 23:23:30 +00:00
fluff ( "" , "Warning options:" , "" )
2020-09-15 15:08:22 +00:00
flag ( "-Wall" , "--Wall" ) . repeatable ( ) . action { c =>
2017-12-06 23:23:30 +00:00
CompilationFlag . allWarnings . foldLeft ( c ) { ( c , f ) => c . changeFlag ( f , true ) }
} . description ( "Enable extra warnings." )
2020-09-15 15:08:22 +00:00
flag ( "-Wnone" , "--Wnone" ) . repeatable ( ) . action { c =>
2020-03-15 23:03:01 +00:00
CompilationFlag . allWarnings . foldLeft ( c ) { ( c , f ) => c . changeFlag ( f , false ) }
} . description ( "Disable all warnings." )
2020-09-15 15:08:22 +00:00
flag ( "-Wfatal" , "--Wfatal" ) . repeatable ( ) . action { c =>
2017-12-06 23:23:30 +00:00
c . changeFlag ( CompilationFlag . FatalWarnings , true )
} . description ( "Treat warnings as errors." )
2021-02-24 02:04:53 +00:00
fluff ( "" , "Specific warning options:" , "" )
boolean ( "-Wbuggy" , "-Wno-buggy" ) . repeatable ( ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . BuggyCodeWarning , v )
} . description ( "Whether should warn about code that may cause surprising behaviours or even miscompilation. Default: enabled." )
boolean ( "-Wdeprecation" , "-Wno-deprecation" ) . repeatable ( ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . DeprecationWarning , v )
} . description ( "Whether should warn about deprecated aliases. Default: enabled." )
boolean ( "-Wextra-comparisons" , "-Wno-extra-comparisons" ) . repeatable ( ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . ExtraComparisonWarnings , v )
} . description ( "Whether should warn about simplifiable unsigned integer comparisons. Default: disabled." )
boolean ( "-Wfallback" , "-Wno-fallback" ) . repeatable ( ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . FallbackValueUseWarning , v )
} . description ( "Whether should warn about the use of default values by text codecs, the preprocessor, and array literals. Default: enabled." )
boolean ( "-Wmissing-output" , "-Wno-missing-output" ) . repeatable ( ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . DataMissingInOutputWarning , v )
} . description ( "Whether should warn about data that is missing in output files. Default: enabled." )
boolean ( "-Woverlapping-call" , "-Wno-overlapping-call" ) . repeatable ( ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . CallToOverlappingBankWarning , v )
} . description ( "Whether should warn about calls to functions in a different, yet overlapping segment. Default: enabled." )
boolean ( "-Wror" , "-Wno-ror" ) . repeatable ( ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . RorWarning , v )
} . description ( "Whether should warn about the ROR instruction (6502 only). Default: disabled." )
boolean ( "-Wuseless" , "-Wno-useless" ) . repeatable ( ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . UselessCodeWarning , v )
} . description ( "Whether should warn about code that does nothing. Default: enabled." )
2021-03-14 23:44:14 +00:00
boolean ( "-Whints" , "-Wno-hints" ) . repeatable ( ) . action { ( c , v ) =>
c . changeFlag ( CompilationFlag . UnsupportedOptimizationHintWarning , v )
} . description ( "Whether should warn about unsupported optimization hints. Default: enabled." )
2017-12-06 23:23:30 +00:00
fluff ( "" , "Other options:" , "" )
2019-10-23 22:48:32 +00:00
expansion ( "-Xd" ) ( "-O1" , "-s" , "-fsource-in-asm" , "-g" ) . description ( "Do a debug build. Equivalent to -O1 -s -fsource-in-asm -g" )
expansion ( "-Xr" ) ( "-O4" , "-s" , "-fsource-in-asm" , "-finline" , "-fipo" , "-foptimize-stdlib" ) . description ( "Do a release build. Equivalent to -O4 -s -fsource-in-asm -finline -fipo -foptimize-stdlib" )
2020-09-15 15:08:22 +00:00
flag ( "--single-threaded" ) . repeatable ( ) . action ( c =>
2018-03-06 00:01:22 +00:00
c . changeFlag ( CompilationFlag . SingleThreaded , true )
) . description ( "Run the compiler in a single thread." )
2017-12-06 23:23:30 +00:00
flag ( "--help" ) . action ( c => {
2018-06-25 20:42:12 +00:00
println ( "millfork version " + BuildInfo . version )
println ( s" Copyright (C) $copyrightYears Karol Stasiak " )
println ( "This program comes with ABSOLUTELY NO WARRANTY." )
println ( "This is free software, and you are welcome to redistribute it under certain conditions" )
println ( "You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/" )
println ( )
2017-12-06 23:23:30 +00:00
printHelp ( 20 ) . foreach ( println ( _ ) )
assumeStatus ( CliStatus . Quit )
c
} ) . description ( "Display this message." )
2020-07-19 21:33:03 +00:00
flag ( "--version" , "-version" ) . action ( c => {
2017-12-06 23:29:10 +00:00
println ( "millfork version " + BuildInfo . version )
2017-12-06 23:23:30 +00:00
assumeStatus ( CliStatus . Quit )
System . exit ( 0 )
c
} ) . description ( "Print the version and quit." )
default . action { ( p , c ) =>
if ( p . startsWith ( "-" ) ) {
2018-07-30 16:15:44 +00:00
errorReporting . error ( s" Invalid option ` $p ` " , None )
2017-12-06 23:23:30 +00:00
c
} else {
c . copy ( inputFileNames = c . inputFileNames : + p )
}
}
def assertNone [ T ] ( value : Option [ T ] , msg : String ) : Unit = {
if ( value . isDefined ) {
2018-07-30 16:15:44 +00:00
errorReporting . error ( msg , None )
2017-12-06 23:23:30 +00:00
}
}
}
}