2018-06-12 20:46:20 +00:00
package millfork.compiler.mos
2017-12-06 23:23:30 +00:00
2019-04-15 17:45:26 +00:00
import millfork. { CompilationFlag , assembly }
2018-12-14 14:42:31 +00:00
import millfork.assembly.Elidability
2018-06-17 00:01:35 +00:00
import millfork.assembly.mos.AddrMode._
2018-06-12 20:46:20 +00:00
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
2018-06-17 00:01:35 +00:00
import millfork.compiler._
2017-12-06 23:23:30 +00:00
import millfork.env._
2018-06-12 20:46:20 +00:00
import millfork.node._
2017-12-06 23:23:30 +00:00
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
/* *
* @author Karol Stasiak
*/
2017-12-16 16:55:08 +00:00
//noinspection RedundantDefaultArgument
2017-12-06 23:23:30 +00:00
object BuiltIns {
object IndexChoice extends Enumeration {
val RequireX , PreferX , PreferY = Value
}
def wrapInSedCldIfNeeded ( decimal : Boolean , code : List [ AssemblyLine ] ) : List [ AssemblyLine ] = {
if ( decimal ) {
AssemblyLine . implied ( SED ) : : ( code :+ AssemblyLine . implied ( CLD ) )
} else {
code
}
}
def staTo ( op : Opcode . Value , l : List [ AssemblyLine ] ) : List [ AssemblyLine ] = l . map ( x => if ( x . opcode == STA ) x . copy ( opcode = op ) else x )
2018-06-18 22:00:48 +00:00
def cmpTo ( op : Opcode . Value , l : List [ AssemblyLine ] ) : List [ AssemblyLine ] = l . map ( x => if ( x . opcode == CMP ) x . copy ( opcode = op ) else x )
2017-12-06 23:23:30 +00:00
def ldTo ( op : Opcode . Value , l : List [ AssemblyLine ] ) : List [ AssemblyLine ] = l . map ( x => if ( x . opcode == LDA || x . opcode == LDX || x . opcode == LDY ) x . copy ( opcode = op ) else x )
2017-12-16 16:55:08 +00:00
def simpleOperation ( opcode : Opcode . Value , ctx : CompilationContext , source : Expression , indexChoice : IndexChoice . Value , preserveA : Boolean , commutative : Boolean , decimal : Boolean = false ) : List [ AssemblyLine ] = {
2017-12-06 23:23:30 +00:00
val env = ctx . env
val parts : ( List [ AssemblyLine ] , List [ AssemblyLine ] ) = env . eval ( source ) . fold {
val b = env . get [ Type ] ( "byte" )
source match {
2019-06-23 20:53:42 +00:00
case VariableExpression ( name ) if env . maybeGet [ Variable ] ( name ) . isDefined =>
2017-12-06 23:23:30 +00:00
val v = env . get [ Variable ] ( name )
if ( v . typ . size > 1 ) {
2018-07-30 16:15:44 +00:00
ctx . log . error ( s" Variable ` $name ` is too big for a built-in operation " , source . position )
2017-12-06 23:23:30 +00:00
return Nil
}
Nil -> AssemblyLine . variable ( ctx , opcode , v )
case IndexedExpression ( arrayName , index ) =>
2018-03-07 11:36:21 +00:00
val pointy = env . getPointy ( arrayName )
2018-07-20 20:46:53 +00:00
AbstractExpressionCompiler . checkIndexType ( ctx , pointy , index )
2018-03-07 11:36:21 +00:00
val ( variablePart , constantPart ) = env . evalVariableAndConstantSubParts ( index )
val indexerSize = variablePart . map ( v => getIndexerSize ( ctx , v ) ) . getOrElse ( 1 )
val totalIndexSize = getIndexerSize ( ctx , index )
( pointy , totalIndexSize , indexerSize , indexChoice , variablePart ) match {
case ( p : ConstantPointy , _ , _ , _ , None ) =>
Nil -> List ( AssemblyLine . absolute ( opcode , p . value + constantPart ) )
case ( p : ConstantPointy , _ , 1 , IndexChoice . RequireX | IndexChoice . PreferX , Some ( v ) ) =>
2018-07-20 20:46:53 +00:00
MosExpressionCompiler . compile ( ctx , v , Some ( b -> RegisterVariable ( MosRegister . X , pointy . indexType ) ) , NoBranching ) -> List ( AssemblyLine . absoluteX ( opcode , p . value + constantPart ) )
2018-03-07 11:36:21 +00:00
case ( p : ConstantPointy , _ , 1 , IndexChoice . PreferY , Some ( v ) ) =>
2018-07-20 20:46:53 +00:00
MosExpressionCompiler . compile ( ctx , v , Some ( b -> RegisterVariable ( MosRegister . Y , pointy . indexType ) ) , NoBranching ) -> List ( AssemblyLine . absoluteY ( opcode , p . value + constantPart ) )
2018-03-07 11:36:21 +00:00
case ( p : VariablePointy , 0 | 1 , _ , IndexChoice . PreferX | IndexChoice . PreferY , _ ) =>
2018-07-20 20:46:53 +00:00
MosExpressionCompiler . compile ( ctx , index , Some ( b -> RegisterVariable ( MosRegister . Y , pointy . indexType ) ) , NoBranching ) -> List ( AssemblyLine . indexedY ( opcode , p . addr ) )
2018-03-07 11:36:21 +00:00
case ( p : ConstantPointy , _ , 2 , IndexChoice . PreferX | IndexChoice . PreferY , Some ( v ) ) =>
2018-06-12 20:46:20 +00:00
MosExpressionCompiler . prepareWordIndexing ( ctx , p , index ) -> List ( AssemblyLine . indexedY ( opcode , env . get [ VariableInMemory ] ( "__reg" ) ) )
2018-03-07 11:36:21 +00:00
case ( p : VariablePointy , 2 , _ , IndexChoice . PreferX | IndexChoice . PreferY , _ ) =>
2018-06-12 20:46:20 +00:00
MosExpressionCompiler . prepareWordIndexing ( ctx , p , index ) -> List ( AssemblyLine . indexedY ( opcode , env . get [ VariableInMemory ] ( "__reg" ) ) )
2018-03-07 11:36:21 +00:00
case _ =>
2018-07-30 16:15:44 +00:00
ctx . log . error ( "Invalid index for simple operation argument" , index . position )
2018-03-07 11:36:21 +00:00
Nil -> Nil
2017-12-06 23:23:30 +00:00
}
2018-01-20 00:54:10 +00:00
case FunctionCallExpression ( name , List ( param ) ) if env . maybeGet [ Type ] ( name ) . isDefined =>
return simpleOperation ( opcode , ctx , param , indexChoice , preserveA , commutative , decimal )
2018-12-21 21:36:29 +00:00
case _ : FunctionCallExpression | _ : SumExpression if commutative =>
2023-01-27 16:00:29 +00:00
val code = MosExpressionCompiler . compileToA ( ctx , source )
if ( ctx . options . flags ( CompilationFlag . IdentityPage )
&& ! code . exists ( _ . concernsX )
&& AssemblyLine . treatment ( code , State . X ) == Treatment . Unchanged ) {
return List ( AssemblyLine . implied ( TAX ) ) ++ code ++ wrapInSedCldIfNeeded ( decimal , List ( AssemblyLine . absoluteX ( opcode , env . identityPage ) ) )
}
2017-12-06 23:23:30 +00:00
// TODO: is it ok?
2018-12-21 21:36:29 +00:00
if ( ctx . options . zpRegisterSize >= 1 ) {
val reg = ctx . env . get [ ThingInMemory ] ( "__reg" )
return List ( AssemblyLine . implied ( PHA ) ) ++
2023-01-27 16:00:29 +00:00
MosExpressionCompiler . fixTsx ( code ) ++
2018-12-21 21:36:29 +00:00
List ( AssemblyLine . zeropage ( STA , reg ) , AssemblyLine . implied ( PLA ) ) ++
wrapInSedCldIfNeeded ( decimal , List ( AssemblyLine . zeropage ( opcode , reg ) ) )
} else if ( ctx . options . flag ( CompilationFlag . EmitEmulation65816Opcodes ) ) {
return List ( AssemblyLine . implied ( PHA ) ) ++ MosExpressionCompiler . compileToA ( ctx . addStack ( 1 ) , source ) ++ wrapInSedCldIfNeeded ( decimal , List (
2018-03-03 13:32:11 +00:00
AssemblyLine . stackRelative ( opcode , 1 ) ,
AssemblyLine . implied ( PHX ) ) )
} else {
2018-12-21 21:36:29 +00:00
return List ( AssemblyLine . implied ( PHA ) ) ++ MosExpressionCompiler . compileToA ( ctx . addStack ( 1 ) , source ) ++ wrapInSedCldIfNeeded ( decimal , List (
2018-03-03 13:32:11 +00:00
AssemblyLine . implied ( TSX ) ,
AssemblyLine . absoluteX ( opcode , 0x101 ) ,
AssemblyLine . implied ( INX ) ,
AssemblyLine . implied ( TXS ) ) ) // this TXS is fine, it won't appear in 65816 code
}
2017-12-06 23:23:30 +00:00
case _ =>
2018-12-21 21:36:29 +00:00
if ( ctx . options . zpRegisterSize < 1 ) {
ctx . log . error ( "Right-hand-side expression requires a zero-page register" , source . position )
return Nil
}
val reg = ctx . env . get [ ThingInMemory ] ( "__reg" )
return List ( AssemblyLine . implied ( PHA ) ) ++
MosExpressionCompiler . compileToA ( ctx , source ) ++
List ( AssemblyLine . zeropage ( STA , reg ) , AssemblyLine . implied ( PLA ) ) ++
wrapInSedCldIfNeeded ( decimal , List ( AssemblyLine . zeropage ( opcode , reg ) ) )
2017-12-06 23:23:30 +00:00
}
} {
const =>
if ( const . requiredSize > 1 ) {
2018-07-30 16:15:44 +00:00
ctx . log . error ( "Constant too big for a built-in operation" , source . position )
2017-12-06 23:23:30 +00:00
}
Nil -> List ( AssemblyLine . immediate ( opcode , const ) )
}
val preparations = parts . _1
2017-12-16 16:55:08 +00:00
val finalRead = wrapInSedCldIfNeeded ( decimal , parts . _2 )
2017-12-06 23:23:30 +00:00
if ( preserveA && AssemblyLine . treatment ( preparations , State . A ) != Treatment . Unchanged ) {
2018-07-23 23:38:10 +00:00
AssemblyLine . implied ( PHA ) : : ( MosExpressionCompiler . fixTsx ( preparations ) ++ ( AssemblyLine . implied ( PLA ) :: finalRead ) )
2017-12-06 23:23:30 +00:00
} else {
preparations ++ finalRead
}
}
def insertBeforeLast ( item : AssemblyLine , list : List [ AssemblyLine ] ) : List [ AssemblyLine ] = list match {
case Nil => Nil
2018-02-24 23:45:25 +00:00
case last : : cld :: Nil if cld . opcode = = CLD => item : : last :: cld :: Nil
case last : : cld :: dex :: txs :: Nil if cld . opcode = = CLD && dex . opcode == DEX && txs . opcode == TXS => item : : last :: cld :: dex :: txs :: Nil
case last : : cld :: inx :: txs :: Nil if cld . opcode = = CLD && inx . opcode == INX && txs . opcode == TXS => item : : last :: cld :: inx :: txs :: Nil
2017-12-06 23:23:30 +00:00
case last : : dex :: txs :: Nil if dex . opcode = = DEX && txs . opcode == TXS => item : : last :: dex :: txs :: Nil
case last : : inx :: txs :: Nil if inx . opcode = = INX && txs . opcode == TXS => item : : last :: inx :: txs :: Nil
case last : : Nil => item : : last :: Nil
case first : : rest => first : : insertBeforeLast ( item , rest )
}
def compileAddition ( ctx : CompilationContext , params : List [ ( Boolean , Expression ) ] , decimal : Boolean ) : List [ AssemblyLine ] = {
2018-08-03 11:06:23 +00:00
if ( decimal && ! ctx . options . flag ( CompilationFlag . DecimalMode ) && ctx . options . zpRegisterSize < 4 ) {
ctx . log . error ( "Unsupported decimal operation. Consider increasing the size of the zeropage register." , params . head . _2 . position )
return compileAddition ( ctx , params , decimal = false )
2017-12-06 23:23:30 +00:00
}
// if (params.isEmpty) {
// return Nil
// }
val env = ctx . env
val b = env . get [ Type ] ( "byte" )
val sortedParams = params . sortBy { case ( subtract , expr ) =>
2018-01-07 22:30:43 +00:00
simplicity ( env , expr ) + ( if ( subtract ) "X" else "P" )
2017-12-06 23:23:30 +00:00
}
// TODO: merge constants
val normalizedParams = sortedParams
val h = normalizedParams . head
2018-06-12 20:46:20 +00:00
val firstParamCompiled = MosExpressionCompiler . compile ( ctx , h . _2 , Some ( b -> RegisterVariable ( MosRegister . A , b ) ) , NoBranching )
2017-12-06 23:23:30 +00:00
val firstParamSignCompiled = if ( h . _1 ) {
2017-12-16 16:55:08 +00:00
// TODO: check if decimal subtraction works correctly here
2017-12-06 23:23:30 +00:00
List ( AssemblyLine . immediate ( EOR , 0xff ) , AssemblyLine . implied ( SEC ) , AssemblyLine . immediate ( ADC , 0 ) )
} else {
Nil
}
val remainingParamsCompiled = normalizedParams . tail . flatMap { p =>
2018-08-03 11:06:23 +00:00
if ( decimal && ! ctx . options . flag ( CompilationFlag . DecimalMode ) ) {
val reg = ctx . env . get [ VariableInMemory ] ( "__reg" )
if ( p . _1 ) {
List ( AssemblyLine . zeropage ( STA , reg , 2 ) ) ++
MosExpressionCompiler . preserveZpregIfNeededDestroyingAAndX ( ctx , 2 ,
MosExpressionCompiler . compileToA ( ctx , p . _2 ) ) ++
List ( AssemblyLine . zeropage ( STA , reg , 3 ) , AssemblyLine . absolute ( JSR , ctx . env . get [ FunctionInMemory ] ( "__sub_decimal" ) ) )
} else {
List ( AssemblyLine . zeropage ( STA , reg , 2 ) , AssemblyLine . implied ( CLC ) ) ++
MosExpressionCompiler . preserveZpregIfNeededDestroyingAAndX ( ctx , 2 , MosExpressionCompiler . compileToA ( ctx , p . _2 ) ) ++
List ( AssemblyLine . zeropage ( STA , reg , 3 ) , AssemblyLine . absolute ( JSR , ctx . env . get [ FunctionInMemory ] ( "__adc_decimal" ) ) )
}
2017-12-06 23:23:30 +00:00
} else {
2018-08-03 11:06:23 +00:00
if ( p . _1 ) {
insertBeforeLast ( AssemblyLine . implied ( SEC ) , simpleOperation ( SBC , ctx , p . _2 , IndexChoice . PreferY , preserveA = true , commutative = false , decimal = decimal ) )
} else {
insertBeforeLast ( AssemblyLine . implied ( CLC ) , simpleOperation ( ADC , ctx , p . _2 , IndexChoice . PreferY , preserveA = true , commutative = true , decimal = decimal ) )
}
2017-12-06 23:23:30 +00:00
}
}
2017-12-16 16:55:08 +00:00
firstParamCompiled ++ firstParamSignCompiled ++ remainingParamsCompiled
2017-12-06 23:23:30 +00:00
}
2018-01-07 22:30:43 +00:00
private def simplicity ( env : Environment , expr : Expression ) : Char = {
val constPart = env . eval ( expr ) match {
case Some ( NumericConstant ( _ , _ ) ) => 'Z'
case Some ( _ ) => 'Y'
case None => expr match {
case VariableExpression ( _ ) => 'V'
case IndexedExpression ( _ , LiteralExpression ( _ , _ ) ) => 'K'
2018-07-21 21:59:16 +00:00
case IndexedExpression ( _ , GeneratedConstantExpression ( _ , _ ) ) => 'K'
2018-07-20 20:46:53 +00:00
case IndexedExpression ( _ , expr @VariableExpression ( v ) ) =>
env . eval ( expr ) match {
case Some ( _ ) => 'K'
case None => env . get [ Variable ] ( v ) . typ . size match {
case 1 => 'J'
case _ => 'I'
}
}
2018-03-07 11:36:21 +00:00
case IndexedExpression ( _ , VariableExpression ( v ) ) if env . get [ Variable ] ( v ) . typ . size == 1 => 'J'
2018-01-07 22:30:43 +00:00
case IndexedExpression ( _ , _ ) => 'I'
case _ => 'A'
}
}
constPart
}
2017-12-06 23:23:30 +00:00
def compileBitOps ( opcode : Opcode . Value , ctx : CompilationContext , params : List [ Expression ] ) : List [ AssemblyLine ] = {
val b = ctx . env . get [ Type ] ( "byte" )
2018-03-07 11:36:21 +00:00
val sortedParams = params . sortBy { expr => simplicity ( ctx . env , expr ) }
2017-12-06 23:23:30 +00:00
val h = sortedParams . head
2018-06-12 20:46:20 +00:00
val firstParamCompiled = MosExpressionCompiler . compile ( ctx , h , Some ( b -> RegisterVariable ( MosRegister . A , b ) ) , NoBranching )
2017-12-06 23:23:30 +00:00
val remainingParamsCompiled = sortedParams . tail . flatMap { p =>
simpleOperation ( opcode , ctx , p , IndexChoice . PreferY , preserveA = true , commutative = true )
}
firstParamCompiled ++ remainingParamsCompiled
}
2018-08-16 20:03:27 +00:00
def maybeCompileShiftFromByteToWord ( ctx : CompilationContext , l : Expression , r : Expression , left : Boolean ) : Option [ List [ AssemblyLine ] ] = {
val env = ctx . env
env . eval ( r ) match {
case Some ( NumericConstant ( n , _ ) ) =>
l match {
case FunctionCallExpression ( wordTypeName , List ( param ) ) =>
if ( AbstractExpressionCompiler . getExpressionType ( ctx , param ) . size == 1 && env . maybeGet [ Type ] ( wordTypeName ) . exists ( _ . size == 2 ) ) {
Some ( MosExpressionCompiler . compileToA ( ctx , param ) ++ BuiltIns . compileShiftFromByteToWord ( ctx , n . toInt , left ) )
} else {
None
}
2018-09-28 20:39:16 +00:00
case _ => None
2018-08-16 20:03:27 +00:00
}
case _ => None
}
}
def compileShiftFromByteToWord ( ctx : CompilationContext , count : Int , left : Boolean ) : List [ AssemblyLine ] = {
if ( count == 8 ) {
List ( AssemblyLine . implied ( TAX ) , AssemblyLine . immediate ( LDA , 0 ) )
} else {
List ( AssemblyLine . implied ( PHA ) ) ++
List . fill ( 8 - count ) ( AssemblyLine . implied ( if ( left ) LSR else ASL ) ) ++
List ( AssemblyLine . implied ( TAX ) , AssemblyLine . implied ( PLA ) ) ++
List . fill ( count ) ( AssemblyLine . implied ( if ( left ) ASL else LSR ) )
}
}
2017-12-06 23:23:30 +00:00
def compileShiftOps ( opcode : Opcode . Value , ctx : CompilationContext , l : Expression , r : Expression ) : List [ AssemblyLine ] = {
val b = ctx . env . get [ Type ] ( "byte" )
2018-06-12 20:46:20 +00:00
val firstParamCompiled = MosExpressionCompiler . compile ( ctx , l , Some ( b -> RegisterVariable ( MosRegister . A , b ) ) , NoBranching )
2017-12-06 23:23:30 +00:00
ctx . env . eval ( r ) match {
case Some ( NumericConstant ( 0 , _ ) ) =>
2018-06-12 20:46:20 +00:00
MosExpressionCompiler . compile ( ctx , l , None , NoBranching )
2017-12-06 23:23:30 +00:00
case Some ( NumericConstant ( v , _ ) ) if v > 0 =>
firstParamCompiled ++ List . fill ( v . toInt ) ( AssemblyLine . implied ( opcode ) )
case _ =>
2018-06-12 20:46:20 +00:00
val compileCounter = MosExpressionCompiler . preserveRegisterIfNeeded ( ctx , MosRegister . A ,
MosExpressionCompiler . compile ( ctx , r , Some ( b -> RegisterVariable ( MosRegister . X , b ) ) , NoBranching ) )
2018-07-31 16:16:36 +00:00
val labelSkip = ctx . nextLabel ( "ss" )
val labelRepeat = ctx . nextLabel ( "sr" )
2018-03-11 22:02:34 +00:00
val loop = List (
2019-08-03 18:31:27 +00:00
AssemblyLine . immediate ( CPX , 0 ) ,
2018-03-11 22:02:34 +00:00
AssemblyLine . relative ( BEQ , labelSkip ) ,
AssemblyLine . label ( labelRepeat ) ,
AssemblyLine . implied ( opcode ) ,
AssemblyLine . implied ( DEX ) ,
AssemblyLine . relative ( BNE , labelRepeat ) ,
AssemblyLine . label ( labelSkip ) )
firstParamCompiled ++ compileCounter ++ loop
2017-12-06 23:23:30 +00:00
}
}
2018-07-27 22:02:57 +00:00
def compileNonetOps ( ctx : CompilationContext , lhs : Expression , rhs : Expression ) : List [ AssemblyLine ] = {
2017-12-06 23:23:30 +00:00
val env = ctx . env
2018-12-14 14:43:12 +00:00
lhs match {
case FunctionCallExpression ( "nonet" , List ( lparam ) ) =>
( env . eval ( lhs ) , env . eval ( rhs ) ) match {
case ( None , Some ( NumericConstant ( 0 , _ ) ) ) =>
return MosExpressionCompiler . compile ( ctx , lparam , None , NoBranching )
case ( None , Some ( NumericConstant ( n , _ ) ) ) if n > 0 =>
return MosExpressionCompiler . compile ( ctx , lparam , None , NoBranching ) ++
( AssemblyLine . implied ( ROR ) : : List . fill ( n . toInt - 1 ) ( AssemblyLine . implied ( LSR ) ) )
case _ =>
}
case _ =>
}
2017-12-06 23:23:30 +00:00
val b = env . get [ Type ] ( "byte" )
2018-07-27 22:02:57 +00:00
val ( ldaHi , ldaLo ) = env . eval ( lhs ) match {
case Some ( c ) =>
List ( AssemblyLine . immediate ( LDA , c . hiByte ) ) -> List ( AssemblyLine . immediate ( LDA , c . loByte ) )
case _ => lhs match {
case v : VariableExpression =>
val variable = env . get [ Variable ] ( v . name )
AssemblyLine . variable ( ctx , LDA , variable , 1 ) -> AssemblyLine . variable ( ctx , LDA , variable , 0 )
case SeparateBytesExpression ( h : VariableExpression , l : VariableExpression ) =>
AssemblyLine . variable ( ctx , LDA , env . get [ Variable ] ( h . name ) , 0 ) -> AssemblyLine . variable ( ctx , LDA , env . get [ Variable ] ( l . name ) , 0 )
case _ =>
???
}
2017-12-06 23:23:30 +00:00
}
env . eval ( rhs ) match {
case Some ( NumericConstant ( 0 , _ ) ) =>
2018-06-12 20:46:20 +00:00
MosExpressionCompiler . compile ( ctx , lhs , None , NoBranching )
2017-12-06 23:23:30 +00:00
case Some ( NumericConstant ( shift , _ ) ) if shift > 0 =>
if ( ctx . options . flag ( CompilationFlag . RorWarning ) )
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "ROR instruction generated" , lhs . position )
2017-12-06 23:23:30 +00:00
ldaHi ++ List ( AssemblyLine . implied ( ROR ) ) ++ ldaLo ++ List ( AssemblyLine . implied ( ROR ) ) ++ List . fill ( shift . toInt - 1 ) ( AssemblyLine . implied ( LSR ) )
case _ =>
2018-07-30 16:15:44 +00:00
ctx . log . error ( "Non-constant shift amount" , rhs . position ) // TODO
2017-12-06 23:23:30 +00:00
Nil
}
}
def compileInPlaceByteShiftOps ( opcode : Opcode . Value , ctx : CompilationContext , lhs : LhsExpression , rhs : Expression ) : List [ AssemblyLine ] = {
val env = ctx . env
val b = env . get [ Type ] ( "byte" )
2018-06-12 20:46:20 +00:00
val firstParamCompiled = MosExpressionCompiler . compile ( ctx , lhs , Some ( b -> RegisterVariable ( MosRegister . A , b ) ) , NoBranching )
2017-12-06 23:23:30 +00:00
env . eval ( rhs ) match {
case Some ( NumericConstant ( 0 , _ ) ) =>
2018-06-12 20:46:20 +00:00
MosExpressionCompiler . compile ( ctx , lhs , None , NoBranching )
2017-12-06 23:23:30 +00:00
case Some ( NumericConstant ( v , _ ) ) if v > 0 =>
2019-07-08 16:51:49 +00:00
val result = simpleOperation ( opcode , ctx , lhs , IndexChoice . PreferX , preserveA = true , commutative = false )
result . last . addrMode match {
case AbsoluteX | Absolute | ZeroPage | ZeroPageX | LongAbsoluteX | LongAbsolute =>
result ++ List . fill ( v . toInt - 1 ) ( result . last )
case IndexedY | AbsoluteY | IndexedZ | LongIndexedZ | IndexedSY =>
result . init ++ List ( result . last . copy ( opcode = LDA ) ) ++ List . fill ( v . toInt ) ( AssemblyLine . implied ( opcode ) ) ++ List ( result . last . copy ( opcode = STA ) )
}
2017-12-06 23:23:30 +00:00
case _ =>
2018-06-12 20:46:20 +00:00
compileShiftOps ( opcode , ctx , lhs , rhs ) ++ MosExpressionCompiler . compileByteStorage ( ctx , MosRegister . A , lhs )
2017-12-06 23:23:30 +00:00
}
}
def compileInPlaceWordOrLongShiftOps ( ctx : CompilationContext , lhs : LhsExpression , rhs : Expression , aslRatherThanLsr : Boolean ) : List [ AssemblyLine ] = {
2020-07-13 20:49:23 +00:00
val reg = ctx . env . get [ VariableInMemory ] ( "__reg" )
2020-01-03 13:52:35 +00:00
lhs match {
case dx : DerefExpression =>
2021-06-21 12:20:24 +00:00
val el = if ( dx . isVolatile ) Elidability . Volatile else Elidability . Elidable
2020-01-03 13:52:35 +00:00
if ( ctx . options . zpRegisterSize < 4 ) {
ctx . log . error ( "Unsupported shift operation. Consider increasing the size of the zeropage register or simplifying the left hand side expression." , lhs . position )
return MosExpressionCompiler . compileToAX ( ctx , lhs ) ++ MosExpressionCompiler . compileToAX ( ctx , rhs )
}
2020-07-13 20:49:23 +00:00
return handleWordOrLongInPlaceModificationViaDeref ( ctx , dx , rhs , fromMsb = ! aslRatherThanLsr ) { ( ptr , reg , offset , r ) =>
2020-01-03 13:52:35 +00:00
val shiftAmount = r match {
case List ( AssemblyLine0 ( LDA , Immediate , NumericConstant ( a , _ ) ) , AssemblyLine0 ( LDX , Immediate , _ ) ) => Some ( a . toInt )
case _ => None
}
val loadToR2 =
List (
AssemblyLine . immediate ( LDY , offset ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . zeropage ( STA , reg , 2 ) ,
AssemblyLine . implied ( INY ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . zeropage ( STA , reg , 3 ) )
val storeFromR2 =
List (
AssemblyLine . immediate ( LDY , offset ) ,
AssemblyLine . zeropage ( LDA , reg , 2 ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( INY ) ,
AssemblyLine . zeropage ( LDA , reg , 3 ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) )
2020-01-03 13:52:35 +00:00
val shiftR2 =
if ( aslRatherThanLsr ) List ( AssemblyLine . zeropage ( ASL , reg , 2 ) , AssemblyLine . zeropage ( ROL , reg , 3 ) )
else List ( AssemblyLine . zeropage ( LSR , reg , 3 ) , AssemblyLine . zeropage ( ROR , reg , 2 ) )
shiftAmount match {
case Some ( 0 ) => Nil
case Some ( 1 ) if aslRatherThanLsr =>
List (
AssemblyLine . immediate ( LDY , offset ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( ASL ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( INY ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( ROL ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) )
2020-01-03 13:52:35 +00:00
case Some ( 1 ) if ! aslRatherThanLsr =>
List (
AssemblyLine . immediate ( LDY , offset + 1 ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( LSR ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( DEY ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( ROR ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) )
2020-01-03 13:52:35 +00:00
case Some ( n ) if n >= 1 && n <= 7 => // TODO: pick optimal
loadToR2 ++ List . fill ( n ) ( shiftR2 ) . flatten ++ storeFromR2
case _ =>
val labelSkip = ctx . nextLabel ( "ss" )
val labelRepeat = ctx . nextLabel ( "sr" )
List ( AssemblyLine . implied ( TAX ) , AssemblyLine . relative ( BEQ , labelSkip ) ) ++
loadToR2 ++
List ( AssemblyLine . label ( labelRepeat ) ) ++
shiftR2 ++ List (
AssemblyLine . implied ( DEX ) ,
AssemblyLine . relative ( BNE , labelRepeat ) ) ++
storeFromR2 ++ List (
AssemblyLine . label ( labelSkip ) )
}
2020-07-13 20:49:23 +00:00
} ( Left ( { size =>
val compiledRhs = MosExpressionCompiler . compileToA ( ctx , rhs )
val shiftAmount = compiledRhs match {
case List ( AssemblyLine0 ( LDA , Immediate , NumericConstant ( a , _ ) ) ) => Some ( a . toInt )
case _ => None
}
val innerLoopLabel = ctx . nextLabel ( "sr" )
val singleShift = if ( aslRatherThanLsr ) {
List (
AssemblyLine . implied ( CLC ) ,
AssemblyLine . immediate ( LDY , 0 ) ,
AssemblyLine . label ( innerLoopLabel ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , reg ) . copy ( elidability = el ) ,
2020-07-13 20:49:23 +00:00
AssemblyLine . implied ( ROL ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , reg ) . copy ( elidability = el ) ,
2020-07-13 20:49:23 +00:00
AssemblyLine . implied ( INY ) ,
AssemblyLine . immediate ( CPY , size ) ,
AssemblyLine . relative ( BNE , innerLoopLabel ) )
} else {
List (
AssemblyLine . implied ( CLC ) ,
AssemblyLine . immediate ( LDY , size - 1 ) ,
AssemblyLine . label ( innerLoopLabel ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , reg ) . copy ( elidability = el ) ,
2020-07-13 20:49:23 +00:00
AssemblyLine . implied ( ROR ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , reg ) . copy ( elidability = el ) ,
2020-07-13 20:49:23 +00:00
AssemblyLine . implied ( DEY ) ,
AssemblyLine . relative ( BPL , innerLoopLabel ) )
}
shiftAmount match {
case Some ( 0 ) => Nil
case Some ( n ) if n >= size * 8 =>
List (
AssemblyLine . immediate ( LDY , size - 1 ) ,
AssemblyLine . label ( innerLoopLabel ) ,
AssemblyLine . immediate ( LDA , 0 ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , reg ) . copy ( elidability = el ) ,
2020-07-13 20:49:23 +00:00
AssemblyLine . implied ( DEY ) ,
AssemblyLine . relative ( BPL , innerLoopLabel ) )
case Some ( 1 ) => singleShift
case Some ( n ) if n > 0 =>
val labelRepeat = ctx . nextLabel ( "sr" )
compiledRhs ++ List (
AssemblyLine . implied ( TAX ) ,
AssemblyLine . label ( labelRepeat ) ) ++ singleShift ++ List (
AssemblyLine . implied ( DEX ) ,
2020-07-17 23:16:31 +00:00
AssemblyLine . relative ( BNE , labelRepeat ) )
2020-07-13 20:49:23 +00:00
case _ =>
val labelSkip = ctx . nextLabel ( "ss" )
val labelRepeat = ctx . nextLabel ( "sr" )
compiledRhs ++ List (
AssemblyLine . implied ( TAX ) ,
AssemblyLine . relative ( BEQ , labelSkip ) ,
AssemblyLine . label ( labelRepeat ) ) ++ singleShift ++ List (
AssemblyLine . implied ( DEX ) ,
2020-07-17 23:16:31 +00:00
AssemblyLine . relative ( BNE , labelRepeat ) ,
2020-07-13 20:49:23 +00:00
AssemblyLine . label ( labelSkip ) )
}
} ) )
2020-01-03 13:52:35 +00:00
case _ =>
2019-04-15 17:45:26 +00:00
}
2017-12-06 23:23:30 +00:00
val env = ctx . env
val b = env . get [ Type ] ( "byte" )
2018-01-07 22:30:43 +00:00
val targetBytes = getStorageForEachByte ( ctx , lhs )
2017-12-06 23:23:30 +00:00
val lo = targetBytes . head
val hi = targetBytes . last
2018-03-11 22:02:34 +00:00
// TODO: this probably breaks in case of complex split word expressions
2017-12-06 23:23:30 +00:00
env . eval ( rhs ) match {
case Some ( NumericConstant ( 0 , _ ) ) =>
2018-06-12 20:46:20 +00:00
MosExpressionCompiler . compile ( ctx , lhs , None , NoBranching )
2017-12-06 23:23:30 +00:00
case Some ( NumericConstant ( shift , _ ) ) if shift > 0 =>
2018-03-03 00:21:57 +00:00
if ( ctx . options . flags ( CompilationFlag . EmitNative65816Opcodes ) ) {
targetBytes match {
2018-12-14 14:42:31 +00:00
case List ( List ( AssemblyLine0 ( STA , a1 , l ) ) , List ( AssemblyLine0 ( STA , a2 , h ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( a1 == a2 && l . + ( 1 ) . quickSimplify == h ) {
return List ( AssemblyLine . accu16 ) ++ List . fill ( shift . toInt ) ( AssemblyLine ( if ( aslRatherThanLsr ) ASL_W else LSR_W , a1 , l ) ) ++ List ( AssemblyLine . accu8 )
}
2018-03-11 22:02:34 +00:00
case _ =>
2018-03-03 00:21:57 +00:00
}
}
2017-12-06 23:23:30 +00:00
List . fill ( shift . toInt ) ( if ( aslRatherThanLsr ) {
staTo ( ASL , lo ) ++ targetBytes . tail . flatMap { b => staTo ( ROL , b ) }
} else {
if ( ctx . options . flag ( CompilationFlag . RorWarning ) )
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "ROR instruction generated" , lhs . position )
2017-12-06 23:23:30 +00:00
staTo ( LSR , hi ) ++ targetBytes . reverse . tail . flatMap { b => staTo ( ROR , b ) }
} ) . flatten
case _ =>
2018-03-11 22:02:34 +00:00
val usesX = targetBytes . exists ( _ . exists ( _ . concernsX ) )
val usesY = targetBytes . exists ( _ . exists ( _ . concernsY ) )
val ( register , decrease ) = ( usesX , usesY ) match {
2018-06-12 20:46:20 +00:00
case ( true , false ) => MosRegister . Y -> DEY
case ( false , true ) => MosRegister . X -> DEX
case ( false , false ) => MosRegister . X -> DEX
2018-03-11 22:02:34 +00:00
case ( true , true ) => ???
}
2018-06-12 20:46:20 +00:00
val compileCounter = MosExpressionCompiler . preserveRegisterIfNeeded ( ctx , MosRegister . A ,
MosExpressionCompiler . compile ( ctx , rhs , Some ( b -> RegisterVariable ( register , b ) ) , NoBranching ) )
2018-07-31 16:16:36 +00:00
val labelSkip = ctx . nextLabel ( "ss" )
val labelRepeat = ctx . nextLabel ( "sr" )
2018-03-11 22:02:34 +00:00
if ( ctx . options . flags ( CompilationFlag . EmitNative65816Opcodes ) ) {
targetBytes match {
2018-12-14 14:42:31 +00:00
case List ( List ( AssemblyLine0 ( STA , a1 , l ) ) , List ( AssemblyLine0 ( STA , a2 , h ) ) ) =>
2018-03-11 22:02:34 +00:00
if ( a1 == a2 && l . + ( 1 ) . quickSimplify == h ) {
return compileCounter ++ List (
AssemblyLine . relative ( BEQ , labelSkip ) ,
AssemblyLine . accu16 ,
AssemblyLine . label ( labelRepeat ) ,
AssemblyLine ( if ( aslRatherThanLsr ) ASL_W else LSR_W , a1 , l ) ,
AssemblyLine . implied ( decrease ) ,
AssemblyLine . relative ( BNE , labelRepeat ) ,
AssemblyLine . accu8 ,
AssemblyLine . label ( labelSkip ) )
}
case _ =>
}
}
compileCounter ++ List (
AssemblyLine . relative ( BEQ , labelSkip ) ,
AssemblyLine . label ( labelRepeat ) ) ++ ( if ( aslRatherThanLsr ) {
staTo ( ASL , lo ) ++ targetBytes . tail . flatMap { b => staTo ( ROL , b ) }
} else {
if ( ctx . options . flag ( CompilationFlag . RorWarning ) )
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "ROR instruction generated" , lhs . position )
2018-03-11 22:02:34 +00:00
staTo ( LSR , hi ) ++ targetBytes . reverse . tail . flatMap { b => staTo ( ROR , b ) }
} ) ++ List (
AssemblyLine . implied ( decrease ) ,
AssemblyLine . relative ( BNE , labelRepeat ) ,
AssemblyLine . label ( labelSkip ) )
2017-12-06 23:23:30 +00:00
}
}
def compileByteComparison ( ctx : CompilationContext , compType : ComparisonType . Value , lhs : Expression , rhs : Expression , branches : BranchSpec ) : List [ AssemblyLine ] = {
val env = ctx . env
val b = env . get [ Type ] ( "byte" )
2018-01-20 00:30:46 +00:00
if ( simplicity ( env , lhs ) >= 'J' && simplicity ( env , rhs ) < 'J' ) {
return compileByteComparison ( ctx , ComparisonType . flip ( compType ) , rhs , lhs , branches )
}
2018-06-12 20:46:20 +00:00
val firstParamCompiled = MosExpressionCompiler . compile ( ctx , lhs , Some ( b -> RegisterVariable ( MosRegister . A , b ) ) , NoBranching )
2018-01-18 21:38:17 +00:00
val maybeConstant = env . eval ( rhs )
2020-03-17 20:08:43 +00:00
if ( ctx . options . flag ( CompilationFlag . UselessCodeWarning ) ) maybeConstant match {
2017-12-06 23:23:30 +00:00
case Some ( NumericConstant ( 0 , _ ) ) =>
compType match {
case ComparisonType . LessUnsigned =>
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "Unsigned < 0 is always false" , lhs . position )
2017-12-06 23:23:30 +00:00
case ComparisonType . LessOrEqualUnsigned =>
if ( ctx . options . flag ( CompilationFlag . ExtraComparisonWarnings ) )
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "Unsigned <= 0 means the same as unsigned == 0" , lhs . position )
2017-12-06 23:23:30 +00:00
case ComparisonType . GreaterUnsigned =>
if ( ctx . options . flag ( CompilationFlag . ExtraComparisonWarnings ) )
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "Unsigned > 0 means the same as unsigned != 0" , lhs . position )
2017-12-06 23:23:30 +00:00
case ComparisonType . GreaterOrEqualUnsigned =>
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "Unsigned >= 0 is always true" , lhs . position )
2017-12-06 23:23:30 +00:00
case _ =>
}
case Some ( NumericConstant ( 1 , _ ) ) =>
if ( ctx . options . flag ( CompilationFlag . ExtraComparisonWarnings ) ) {
compType match {
case ComparisonType . LessUnsigned =>
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "Unsigned < 1 means the same as unsigned == 0" , lhs . position )
2017-12-06 23:23:30 +00:00
case ComparisonType . GreaterOrEqualUnsigned =>
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "Unsigned >= 1 means the same as unsigned != 0" , lhs . position )
2017-12-06 23:23:30 +00:00
case _ =>
}
}
case _ =>
}
2019-09-30 22:45:14 +00:00
( maybeConstant , compType ) match {
case ( Some ( NumericConstant ( 0 , _ ) ) , ComparisonType . GreaterUnsigned ) =>
return compileByteComparison ( ctx , ComparisonType . NotEqual , lhs , rhs , branches )
case ( Some ( NumericConstant ( 0 , _ ) ) , ComparisonType . LessOrEqualUnsigned ) =>
return compileByteComparison ( ctx , ComparisonType . Equal , lhs , rhs , branches )
case ( Some ( NumericConstant ( 1 , _ ) ) , ComparisonType . LessUnsigned ) =>
return compileByteComparison ( ctx , ComparisonType . Equal , lhs , rhs # - # 1 , branches )
case ( Some ( NumericConstant ( 1 , _ ) ) , ComparisonType . GreaterOrEqualUnsigned ) =>
return compileByteComparison ( ctx , ComparisonType . NotEqual , lhs , rhs # - # 1 , branches )
case ( Some ( NumericConstant ( n , 1 ) ) , ComparisonType . GreaterUnsigned ) if n >= 1 && n <= 254 =>
return compileByteComparison ( ctx , ComparisonType . GreaterOrEqualUnsigned , lhs , rhs # + # 1 , branches )
case ( Some ( NumericConstant ( n , 1 ) ) , ComparisonType . LessOrEqualUnsigned ) if n >= 1 && n <= 254 =>
return compileByteComparison ( ctx , ComparisonType . LessUnsigned , lhs , rhs # + # 1 , branches )
case _ =>
}
2018-08-03 14:21:02 +00:00
val cmpOp = if ( ComparisonType . isSigned ( compType ) ) SBC else CMP
var comparingAgainstZero = false
val secondParamCompiled0 = maybeConstant match {
2018-01-18 21:38:17 +00:00
case Some ( x ) =>
compType match {
2018-08-03 14:21:02 +00:00
case ComparisonType . Equal | ComparisonType . NotEqual | ComparisonType . LessSigned | ComparisonType . GreaterOrEqualSigned | ComparisonType . LessOrEqualSigned | ComparisonType . GreaterSigned =>
if ( x . quickSimplify . isLowestByteAlwaysEqual ( 0 ) && OpcodeClasses . ChangesAAlways ( firstParamCompiled . last . opcode ) ) {
comparingAgainstZero = true
Nil
} else List ( AssemblyLine . immediate ( cmpOp , x ) )
2018-01-18 21:38:17 +00:00
case _ =>
2018-08-03 14:21:02 +00:00
List ( AssemblyLine . immediate ( cmpOp , x ) )
2017-12-06 23:23:30 +00:00
}
2018-01-18 21:38:17 +00:00
case _ => compType match {
case ComparisonType . Equal | ComparisonType . NotEqual | ComparisonType . LessSigned | ComparisonType . GreaterOrEqualSigned =>
2018-08-03 14:21:02 +00:00
val secondParamCompiledUnoptimized = simpleOperation ( cmpOp , ctx , rhs , IndexChoice . PreferY , preserveA = true , commutative = false )
2018-01-18 21:38:17 +00:00
secondParamCompiledUnoptimized match {
2019-06-12 20:55:34 +00:00
case List ( AssemblyLine ( _ , Immediate , NumericConstant ( 0 , _ ) , Elidability . Elidable , _ ) ) =>
2018-01-18 21:38:17 +00:00
if ( OpcodeClasses . ChangesAAlways ( firstParamCompiled . last . opcode ) ) {
Nil
} else {
secondParamCompiledUnoptimized
}
case _ => secondParamCompiledUnoptimized
}
case _ =>
2018-08-03 14:21:02 +00:00
simpleOperation ( cmpOp , ctx , rhs , IndexChoice . PreferY , preserveA = true , commutative = false )
2018-01-18 21:38:17 +00:00
}
2017-12-06 23:23:30 +00:00
}
2019-05-29 12:08:42 +00:00
var secondParamCompiled = if ( cmpOp == SBC && ! comparingAgainstZero ) AssemblyLine . implied ( SEC ) : : secondParamCompiled0 else secondParamCompiled0
2017-12-06 23:23:30 +00:00
val ( effectiveComparisonType , label ) = branches match {
case NoBranching => return Nil
case BranchIfTrue ( l ) => compType -> l
case BranchIfFalse ( l ) => ComparisonType . negate ( compType ) -> l
}
2018-07-27 22:02:57 +00:00
( env . eval ( lhs ) , env . eval ( rhs ) ) match {
case ( Some ( NumericConstant ( lc , _ ) ) , Some ( NumericConstant ( rc , _ ) ) ) =>
return if ( effectiveComparisonType match {
// TODO: those masks are probably wrong
case ComparisonType . Equal =>
( lc & 0xff ) == ( rc & 0xff )
case ComparisonType . NotEqual =>
( lc & 0xff ) != ( rc & 0xff )
case ComparisonType . LessOrEqualUnsigned =>
( lc & 0xff ) <= ( rc & 0xff )
case ComparisonType . GreaterOrEqualUnsigned =>
( lc & 0xff ) >= ( rc & 0xff )
case ComparisonType . GreaterUnsigned =>
( lc & 0xff ) > ( rc & 0xff )
case ComparisonType . LessUnsigned =>
( lc & 0xff ) < ( rc & 0xff )
case ComparisonType . LessOrEqualSigned =>
lc . toByte <= rc . toByte
case ComparisonType . GreaterOrEqualSigned =>
lc . toByte >= rc . toByte
case ComparisonType . GreaterSigned =>
lc . toByte > rc . toByte
case ComparisonType . LessSigned =>
lc . toByte < rc . toByte
} ) List ( AssemblyLine . absolute ( JMP , Label ( label ) ) ) else Nil
case _ =>
}
2017-12-06 23:23:30 +00:00
val branchingCompiled = effectiveComparisonType match {
case ComparisonType . Equal =>
List ( AssemblyLine . relative ( BEQ , Label ( label ) ) )
case ComparisonType . NotEqual =>
List ( AssemblyLine . relative ( BNE , Label ( label ) ) )
case ComparisonType . LessUnsigned =>
List ( AssemblyLine . relative ( BCC , Label ( label ) ) )
case ComparisonType . GreaterOrEqualUnsigned =>
List ( AssemblyLine . relative ( BCS , Label ( label ) ) )
case ComparisonType . LessOrEqualUnsigned =>
List ( AssemblyLine . relative ( BCC , Label ( label ) ) , AssemblyLine . relative ( BEQ , Label ( label ) ) )
case ComparisonType . GreaterUnsigned =>
2018-07-31 16:16:36 +00:00
val x = ctx . nextLabel ( "co" )
2017-12-06 23:23:30 +00:00
List (
AssemblyLine . relative ( BEQ , x ) ,
AssemblyLine . relative ( BCS , Label ( label ) ) ,
AssemblyLine . label ( x ) )
case ComparisonType . LessSigned =>
2018-08-03 14:21:02 +00:00
if ( comparingAgainstZero ) List ( AssemblyLine . relative ( BMI , label ) ) else {
2019-05-29 12:08:42 +00:00
maybeConstant match {
case Some ( NumericConstant ( n @ ( 1 | 2 | 4 | 8 | 16 | 32 | 64 ) , _ ) ) =>
secondParamCompiled = Nil
List (
AssemblyLine . immediate ( AND , 255 ^ ( n - 1 ) ) ,
AssemblyLine . relative ( BEQ , label ) ,
AssemblyLine . relative ( BMI , label ) )
case _ =>
val fixup = ctx . nextLabel ( "co" )
List (
AssemblyLine . relative ( BVC , fixup ) ,
AssemblyLine . immediate ( EOR , 0x80 ) ,
AssemblyLine . label ( fixup ) ,
AssemblyLine . relative ( BMI , label ) )
}
2018-08-03 14:21:02 +00:00
}
2017-12-06 23:23:30 +00:00
case ComparisonType . GreaterOrEqualSigned =>
2018-08-03 14:21:02 +00:00
if ( comparingAgainstZero ) List ( AssemblyLine . relative ( BPL , label ) ) else {
2019-05-29 12:08:42 +00:00
maybeConstant match {
case Some ( NumericConstant ( n @ ( 1 | 2 | 4 | 8 | 16 | 32 | 64 ) , _ ) ) =>
secondParamCompiled = Nil
val x = ctx . nextLabel ( "co" )
List (
AssemblyLine . immediate ( AND , 255 ^ ( n - 1 ) ) ,
AssemblyLine . relative ( BEQ , x ) ,
AssemblyLine . relative ( BPL , label ) ,
AssemblyLine . label ( x ) )
case _ =>
val fixup = ctx . nextLabel ( "co" )
List (
AssemblyLine . relative ( BVC , fixup ) ,
AssemblyLine . immediate ( EOR , 0x80 ) ,
AssemblyLine . label ( fixup ) , AssemblyLine . relative ( BPL , label ) )
}
2018-08-03 14:21:02 +00:00
}
2017-12-06 23:23:30 +00:00
case ComparisonType . LessOrEqualSigned =>
2018-08-03 14:21:02 +00:00
if ( comparingAgainstZero ) {
List ( AssemblyLine . relative ( BEQ , label ) ,
AssemblyLine . relative ( BMI , label ) )
} else {
2019-05-29 12:08:42 +00:00
maybeConstant match {
case Some ( NumericConstant ( n @ ( 0 | 1 | 3 | 7 | 15 | 31 | 63 ) , _ ) ) =>
secondParamCompiled = Nil
List (
AssemblyLine . immediate ( AND , 255 ^ n ) ,
AssemblyLine . relative ( BEQ , label ) ,
AssemblyLine . relative ( BMI , label ) )
case _ =>
val fixup = ctx . nextLabel ( "co" )
List ( AssemblyLine . relative ( BVC , fixup ) ,
AssemblyLine . immediate ( EOR , 0x80 ) ,
AssemblyLine . label ( fixup ) ,
AssemblyLine . relative ( BMI , label ) ,
AssemblyLine . relative ( BEQ , label ) )
}
2018-08-03 14:21:02 +00:00
}
2017-12-06 23:23:30 +00:00
case ComparisonType . GreaterSigned =>
2018-08-03 14:21:02 +00:00
if ( comparingAgainstZero ) {
val x = ctx . nextLabel ( "co" )
List ( AssemblyLine . relative ( BEQ , x ) ,
AssemblyLine . relative ( BPL , label ) ,
AssemblyLine . label ( x ) )
} else {
2019-05-29 12:08:42 +00:00
maybeConstant match {
case Some ( NumericConstant ( n @ ( 0 | 1 | 3 | 7 | 15 | 31 | 63 ) , _ ) ) =>
secondParamCompiled = Nil
val x = ctx . nextLabel ( "co" )
List (
AssemblyLine . immediate ( AND , 255 ^ n ) ,
AssemblyLine . relative ( BEQ , x ) ,
AssemblyLine . relative ( BPL , label ) ,
AssemblyLine . label ( x ) )
case _ =>
val fixup = ctx . nextLabel ( "co" )
val x = ctx . nextLabel ( "co" )
List (
AssemblyLine . relative ( BVC , fixup ) ,
AssemblyLine . immediate ( EOR , 0x80 ) ,
AssemblyLine . label ( fixup ) ,
AssemblyLine . relative ( BEQ , x ) ,
AssemblyLine . relative ( BPL , label ) ,
AssemblyLine . label ( x ) )
}
2018-08-03 14:21:02 +00:00
}
2017-12-06 23:23:30 +00:00
}
firstParamCompiled ++ secondParamCompiled ++ branchingCompiled
}
def compileWordComparison ( ctx : CompilationContext , compType : ComparisonType . Value , lhs : Expression , rhs : Expression , branches : BranchSpec ) : List [ AssemblyLine ] = {
val env = ctx . env
2017-12-23 23:08:49 +00:00
// TODO: comparing longer variables
2017-12-06 23:23:30 +00:00
val b = env . get [ Type ] ( "byte" )
val w = env . get [ Type ] ( "word" )
val ( effectiveComparisonType , x ) = branches match {
case NoBranching => return Nil
case BranchIfTrue ( label ) => compType -> label
case BranchIfFalse ( label ) => ComparisonType . negate ( compType ) -> label
}
2019-04-15 17:45:26 +00:00
val ( preparations , lh , ll , rh , rl ) = ( lhs , env . eval ( lhs ) , rhs , env . eval ( rhs ) ) match {
2017-12-06 23:23:30 +00:00
case ( _ , Some ( NumericConstant ( lc , _ ) ) , _ , Some ( NumericConstant ( rc , _ ) ) ) =>
return if ( effectiveComparisonType match {
// TODO: those masks are probably wrong
case ComparisonType . Equal =>
( lc & 0xffff ) == ( rc & 0xffff ) // ??
case ComparisonType . NotEqual =>
( lc & 0xffff ) != ( rc & 0xffff ) // ??
case ComparisonType . LessOrEqualUnsigned =>
( lc & 0xffff ) <= ( rc & 0xffff )
case ComparisonType . GreaterOrEqualUnsigned =>
( lc & 0xffff ) >= ( rc & 0xffff )
case ComparisonType . GreaterUnsigned =>
( lc & 0xffff ) > ( rc & 0xffff )
case ComparisonType . LessUnsigned =>
( lc & 0xffff ) < ( rc & 0xffff )
case ComparisonType . LessOrEqualSigned =>
lc . toShort <= rc . toShort
case ComparisonType . GreaterOrEqualSigned =>
lc . toShort >= rc . toShort
case ComparisonType . GreaterSigned =>
lc . toShort > rc . toShort
case ComparisonType . LessSigned =>
lc . toShort < rc . toShort
} ) List ( AssemblyLine . absolute ( JMP , Label ( x ) ) ) else Nil
case ( _ , Some ( lc ) , _ , Some ( rc ) ) =>
// TODO: comparing late-bound constants
???
2018-05-14 00:18:46 +00:00
case ( _ , Some ( lc ) , rv : VariableExpression , None ) =>
2017-12-06 23:23:30 +00:00
return compileWordComparison ( ctx , ComparisonType . flip ( compType ) , rhs , lhs , branches )
case ( v : VariableExpression , None , _ , Some ( rc ) ) =>
2019-07-31 20:32:41 +00:00
val lva = env . get [ Variable ] ( v . name )
2019-04-15 17:45:26 +00:00
( Nil ,
AssemblyLine . variable ( ctx , CMP , lva , 1 ) ,
2018-06-18 22:00:48 +00:00
AssemblyLine . variable ( ctx , CMP , lva , 0 ) ,
List ( AssemblyLine . immediate ( CMP , rc . hiByte . quickSimplify ) ) ,
List ( AssemblyLine . immediate ( CMP , rc . loByte . quickSimplify ) ) )
2017-12-06 23:23:30 +00:00
case ( lv : VariableExpression , None , rv : VariableExpression , None ) =>
2019-07-31 20:32:41 +00:00
val lva = env . get [ Variable ] ( lv . name )
val rva = env . get [ Variable ] ( rv . name )
2019-04-15 17:45:26 +00:00
( Nil ,
AssemblyLine . variable ( ctx , CMP , lva , 1 ) ,
2018-06-18 22:00:48 +00:00
AssemblyLine . variable ( ctx , CMP , lva , 0 ) ,
AssemblyLine . variable ( ctx , CMP , rva , 1 ) ,
AssemblyLine . variable ( ctx , CMP , rva , 0 ) )
2019-04-15 17:45:26 +00:00
case ( expr , None , _ , Some ( constant ) ) if effectiveComparisonType == ComparisonType . Equal =>
val innerLabel = ctx . nextLabel ( "cp" )
return MosExpressionCompiler . compileToAX ( ctx , expr ) ++ List (
AssemblyLine . immediate ( CMP , constant . loByte ) ,
AssemblyLine . relative ( BNE , innerLabel ) ,
AssemblyLine . immediate ( CPX , constant . hiByte ) ,
AssemblyLine . relative ( BEQ , Label ( x ) ) ,
AssemblyLine . label ( innerLabel ) )
case ( _ , Some ( constant ) , expr , None ) if effectiveComparisonType == ComparisonType . Equal =>
val innerLabel = ctx . nextLabel ( "cp" )
return MosExpressionCompiler . compileToAX ( ctx , expr ) ++ List (
AssemblyLine . immediate ( CMP , constant . loByte ) ,
AssemblyLine . relative ( BNE , innerLabel ) ,
AssemblyLine . immediate ( CPX , constant . hiByte ) ,
AssemblyLine . relative ( BEQ , Label ( x ) ) ,
AssemblyLine . label ( innerLabel ) )
case ( expr , None , _ , Some ( constant ) ) if effectiveComparisonType == ComparisonType . NotEqual =>
return MosExpressionCompiler . compileToAX ( ctx , expr ) ++ List (
AssemblyLine . immediate ( CMP , constant . loByte ) ,
AssemblyLine . relative ( BNE , Label ( x ) ) ,
AssemblyLine . immediate ( CPX , constant . hiByte ) ,
AssemblyLine . relative ( BNE , Label ( x ) ) )
case ( _ , Some ( constant ) , expr , None ) if effectiveComparisonType == ComparisonType . NotEqual =>
return MosExpressionCompiler . compileToAX ( ctx , expr ) ++ List (
AssemblyLine . immediate ( CMP , constant . loByte ) ,
AssemblyLine . relative ( BNE , Label ( x ) ) ,
AssemblyLine . immediate ( CPX , constant . hiByte ) ,
AssemblyLine . relative ( BNE , Label ( x ) ) )
2018-03-16 12:19:54 +00:00
case _ =>
2019-07-19 13:48:08 +00:00
val lc = MosExpressionCompiler . compileToAX ( ctx , lhs )
val rc = MosExpressionCompiler . compileToAX ( ctx , rhs )
( lc , rc , effectiveComparisonType ) match {
case (
2019-10-22 11:54:30 +00:00
List ( lcl @AssemblyLine0 ( LDA , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) , lch @AssemblyLine0 ( LDX , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) ) ,
2019-07-19 13:48:08 +00:00
_ ,
ComparisonType . NotEqual
) =>
return rc ++ List (
lcl . copy ( opcode = CMP ) ,
AssemblyLine . relative ( BNE , Label ( x ) ) ,
lch . copy ( opcode = CPX ) ,
AssemblyLine . relative ( BNE , Label ( x ) ) )
case (
_ ,
2019-10-22 11:54:30 +00:00
List ( rcl @AssemblyLine0 ( LDA , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) , rch @AssemblyLine0 ( LDX , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) ) ,
2019-07-19 13:48:08 +00:00
ComparisonType . NotEqual
) =>
return lc ++ List (
rcl . copy ( opcode = CMP ) ,
AssemblyLine . relative ( BNE , Label ( x ) ) ,
rch . copy ( opcode = CPX ) ,
AssemblyLine . relative ( BNE , Label ( x ) ) )
case (
2019-10-22 11:54:30 +00:00
List ( lcl @AssemblyLine0 ( LDA , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) , lch @AssemblyLine0 ( LDX , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) ) ,
2019-07-19 13:48:08 +00:00
_ ,
ComparisonType . Equal
) =>
val skip = ctx . nextLabel ( "cp" )
return rc ++ List (
lcl . copy ( opcode = CMP ) ,
AssemblyLine . relative ( BNE , skip ) ,
lch . copy ( opcode = CPX ) ,
AssemblyLine . relative ( BEQ , Label ( x ) ) ,
AssemblyLine . label ( skip ) )
case (
_ ,
2019-10-22 11:54:30 +00:00
List ( rcl @AssemblyLine0 ( LDA , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) , rch @AssemblyLine0 ( LDX , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) ) ,
2019-07-19 13:48:08 +00:00
ComparisonType . Equal
) =>
val skip = ctx . nextLabel ( "cp" )
return lc ++ List (
rcl . copy ( opcode = CMP ) ,
AssemblyLine . relative ( BNE , Label ( skip ) ) ,
rch . copy ( opcode = CPX ) ,
AssemblyLine . relative ( BEQ , Label ( x ) ) ,
AssemblyLine . label ( skip ) )
2019-10-22 11:54:30 +00:00
case (
List ( lcl @AssemblyLine0 ( LDA , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) , lch @AssemblyLine0 ( LDX , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) ) ,
List ( rcl @AssemblyLine0 ( LDA , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) , rch @AssemblyLine0 ( LDX , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) ) ,
_
) =>
( Nil ,
List ( lch . copy ( opcode = CMP ) ) ,
List ( lcl . copy ( opcode = CMP ) ) ,
List ( rch . copy ( opcode = CMP ) ) ,
List ( rcl . copy ( opcode = CMP ) ) )
case (
_ ,
List ( AssemblyLine0 ( LDA , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) , AssemblyLine0 ( LDX , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) ) ,
_
) =>
if ( ctx . options . zpRegisterSize < 2 ) {
ctx . log . error ( "Too complex expressions in comparison" , lhs . position . orElse ( rhs . position ) )
( Nil , Nil , Nil , Nil , Nil )
} else {
val reg = ctx . env . get [ ThingInMemory ] ( "__reg.loword" )
return lc ++ List ( AssemblyLine . zeropage ( STA , reg ) , AssemblyLine . zeropage ( STX , reg , 1 ) ) ++ compileWordComparison (
ctx , effectiveComparisonType , VariableExpression ( "__reg.loword" ) . pos ( lhs . position ) , rhs , BranchIfTrue ( x ) )
}
case (
List ( AssemblyLine0 ( LDA , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) , AssemblyLine0 ( LDX , Absolute | Immediate | ZeroPage | LongAbsolute , _ ) ) ,
_ ,
_
) =>
if ( ctx . options . zpRegisterSize < 2 ) {
ctx . log . error ( "Too complex expressions in comparison" , lhs . position . orElse ( rhs . position ) )
( Nil , Nil , Nil , Nil , Nil )
} else {
val reg = ctx . env . get [ ThingInMemory ] ( "__reg.loword" )
return rc ++ List ( AssemblyLine . zeropage ( STA , reg ) , AssemblyLine . zeropage ( STX , reg , 1 ) ) ++ compileWordComparison (
ctx , effectiveComparisonType , lhs , VariableExpression ( "__reg.loword" ) . pos ( rhs . position ) , BranchIfTrue ( x ) )
}
2019-07-19 13:48:08 +00:00
case _ =>
// TODO comparing expressions
ctx . log . error ( "Too complex expressions in comparison" , lhs . position . orElse ( rhs . position ) )
( Nil , Nil , Nil , Nil , Nil )
}
2018-03-16 12:19:54 +00:00
}
2018-06-12 20:46:20 +00:00
val lType = MosExpressionCompiler . getExpressionType ( ctx , lhs )
val rType = MosExpressionCompiler . getExpressionType ( ctx , rhs )
2018-12-26 01:05:41 +00:00
def isConstant ( h : List [ AssemblyLine ] , l : List [ AssemblyLine ] , value : Int ) : Boolean = {
( h , l ) match {
case (
List ( AssemblyLine0 ( CMP , Immediate , NumericConstant ( vh , _ ) ) ) ,
List ( AssemblyLine0 ( CMP , Immediate , NumericConstant ( vl , _ ) ) )
) if vh . & ( 0xff ) . << ( 8 ) + vl . & ( 0xff ) == value => true
case _ => false
}
2017-12-06 23:23:30 +00:00
}
2018-12-26 01:05:41 +00:00
val compactEqualityComparison =
if ( isConstant ( rh , rl , 0 ) ) {
Some ( cmpTo ( LDA , ll ) ++ cmpTo ( ORA , lh ) )
} else if ( isConstant ( lh , ll , 0 ) ) {
Some ( cmpTo ( LDA , rl ) ++ cmpTo ( ORA , rh ) )
} else if ( ctx . options . flag ( CompilationFlag . OptimizeForSpeed ) ) {
None
} else if ( isConstant ( rh , rl , 0xffff ) ) {
Some ( cmpTo ( LDA , ll ) ++ cmpTo ( AND , lh ) ++ List ( AssemblyLine . immediate ( CMP , 0xff ) ) )
} else if ( isConstant ( lh , ll , 0xffff ) ) {
Some ( cmpTo ( LDA , rl ) ++ cmpTo ( AND , rh ) ++ List ( AssemblyLine . immediate ( CMP , 0xff ) ) )
} else if ( lType . size == 1 && ! lType . isSigned ) {
Some ( cmpTo ( LDA , ll ) ++ cmpTo ( EOR , rl ) ++ cmpTo ( ORA , rh ) )
} else if ( rType . size == 1 && ! rType . isSigned ) {
Some ( cmpTo ( LDA , rl ) ++ cmpTo ( EOR , ll ) ++ cmpTo ( ORA , lh ) )
} else {
None
}
2017-12-06 23:23:30 +00:00
effectiveComparisonType match {
case ComparisonType . Equal =>
2018-03-16 12:19:54 +00:00
compactEqualityComparison match {
case Some ( code ) => code : + AssemblyLine . relative ( BEQ , Label ( x ) )
case None =>
2018-07-31 16:16:36 +00:00
val innerLabel = ctx . nextLabel ( "cp" )
2018-06-18 22:00:48 +00:00
cmpTo ( LDA , ll ) ++
cmpTo ( CMP , rl ) ++
2018-03-16 12:19:54 +00:00
List ( AssemblyLine . relative ( BNE , innerLabel ) ) ++
2018-06-18 22:00:48 +00:00
cmpTo ( LDA , lh ) ++
cmpTo ( CMP , rh ) ++
2018-03-16 12:19:54 +00:00
List (
AssemblyLine . relative ( BEQ , Label ( x ) ) ,
AssemblyLine . label ( innerLabel ) )
}
2017-12-06 23:23:30 +00:00
case ComparisonType . NotEqual =>
2018-03-16 12:19:54 +00:00
compactEqualityComparison match {
case Some ( code ) => code : + AssemblyLine . relative ( BNE , Label ( x ) )
case None =>
2018-06-18 22:00:48 +00:00
cmpTo ( LDA , ll ) ++
cmpTo ( CMP , rl ) ++
2018-03-16 12:19:54 +00:00
List ( AssemblyLine . relative ( BNE , Label ( x ) ) ) ++
2018-06-18 22:00:48 +00:00
cmpTo ( LDA , lh ) ++
cmpTo ( CMP , rh ) ++
2018-03-16 12:19:54 +00:00
List ( AssemblyLine . relative ( BNE , Label ( x ) ) )
}
2017-12-06 23:23:30 +00:00
case ComparisonType . LessUnsigned =>
2018-07-31 16:16:36 +00:00
val innerLabel = ctx . nextLabel ( "cp" )
2018-06-18 22:00:48 +00:00
cmpTo ( LDA , lh ) ++
cmpTo ( CMP , rh ) ++
2017-12-23 23:08:49 +00:00
List (
AssemblyLine . relative ( BCC , Label ( x ) ) ,
AssemblyLine . relative ( BNE , innerLabel ) ) ++
2018-06-18 22:00:48 +00:00
cmpTo ( LDA , ll ) ++
cmpTo ( CMP , rl ) ++
2017-12-23 23:08:49 +00:00
List (
AssemblyLine . relative ( BCC , Label ( x ) ) ,
AssemblyLine . label ( innerLabel ) )
2017-12-06 23:23:30 +00:00
case ComparisonType . LessOrEqualUnsigned =>
2018-07-31 16:16:36 +00:00
val innerLabel = ctx . nextLabel ( "cp" )
2018-06-18 22:00:48 +00:00
cmpTo ( LDA , rh ) ++
cmpTo ( CMP , lh ) ++
2017-12-23 23:08:49 +00:00
List ( AssemblyLine . relative ( BCC , innerLabel ) ,
AssemblyLine . relative ( BNE , x ) ) ++
2018-06-18 22:00:48 +00:00
cmpTo ( LDA , rl ) ++
cmpTo ( CMP , ll ) ++
2017-12-23 23:08:49 +00:00
List ( AssemblyLine . relative ( BCS , x ) ,
AssemblyLine . label ( innerLabel ) )
2017-12-06 23:23:30 +00:00
case ComparisonType . GreaterUnsigned =>
2018-07-31 16:16:36 +00:00
val innerLabel = ctx . nextLabel ( "cp" )
2018-06-18 22:00:48 +00:00
cmpTo ( LDA , rh ) ++
cmpTo ( CMP , lh ) ++
2017-12-23 23:08:49 +00:00
List ( AssemblyLine . relative ( BCC , Label ( x ) ) ,
AssemblyLine . relative ( BNE , innerLabel ) ) ++
2018-06-18 22:00:48 +00:00
cmpTo ( LDA , rl ) ++
cmpTo ( CMP , ll ) ++
2017-12-23 23:08:49 +00:00
List ( AssemblyLine . relative ( BCC , Label ( x ) ) ,
AssemblyLine . label ( innerLabel ) )
2017-12-06 23:23:30 +00:00
case ComparisonType . GreaterOrEqualUnsigned =>
2018-07-31 16:16:36 +00:00
val innerLabel = ctx . nextLabel ( "cp" )
2018-06-18 22:00:48 +00:00
cmpTo ( LDA , lh ) ++
cmpTo ( CMP , rh ) ++
2017-12-23 23:08:49 +00:00
List ( AssemblyLine . relative ( BCC , innerLabel ) ,
AssemblyLine . relative ( BNE , x ) ) ++
2018-06-18 22:00:48 +00:00
cmpTo ( LDA , ll ) ++
cmpTo ( CMP , rl ) ++
2017-12-23 23:08:49 +00:00
List ( AssemblyLine . relative ( BCS , x ) ,
AssemblyLine . label ( innerLabel ) )
2017-12-06 23:23:30 +00:00
2018-08-03 14:21:02 +00:00
case ComparisonType . LessSigned =>
val fixup = ctx . nextLabel ( "co" )
cmpTo ( LDA , ll ) ++
2020-03-25 22:53:26 +00:00
cmpTo ( CMP , rl ) ++
2018-08-03 14:21:02 +00:00
cmpTo ( LDA , lh ) ++
cmpTo ( SBC , rh ) ++
List (
AssemblyLine . relative ( BVC , fixup ) ,
AssemblyLine . immediate ( EOR , 0x80 ) ,
2020-03-25 22:53:26 +00:00
AssemblyLine . label ( fixup ) ) ++
List ( AssemblyLine . relative ( BMI , x ) )
2018-08-03 14:21:02 +00:00
case ComparisonType . GreaterOrEqualSigned =>
val fixup = ctx . nextLabel ( "co" )
cmpTo ( LDA , ll ) ++
2020-03-25 22:53:26 +00:00
cmpTo ( CMP , rl ) ++
2018-08-03 14:21:02 +00:00
cmpTo ( LDA , lh ) ++
cmpTo ( SBC , rh ) ++
List (
AssemblyLine . relative ( BVC , fixup ) ,
AssemblyLine . immediate ( EOR , 0x80 ) ,
2020-03-25 22:53:26 +00:00
AssemblyLine . label ( fixup ) ) ++
List ( AssemblyLine . relative ( BPL , x ) )
case ComparisonType . GreaterSigned =>
val fixup = ctx . nextLabel ( "co" )
cmpTo ( LDA , rl ) ++
cmpTo ( CMP , ll ) ++
cmpTo ( LDA , rh ) ++
cmpTo ( SBC , lh ) ++
List (
AssemblyLine . relative ( BVC , fixup ) ,
AssemblyLine . immediate ( EOR , 0x80 ) ,
AssemblyLine . label ( fixup ) ) ++
List ( AssemblyLine . relative ( BMI , x ) )
case ComparisonType . LessOrEqualSigned =>
val fixup = ctx . nextLabel ( "co" )
cmpTo ( LDA , rl ) ++
cmpTo ( CMP , ll ) ++
cmpTo ( LDA , rh ) ++
cmpTo ( SBC , lh ) ++
List (
AssemblyLine . relative ( BVC , fixup ) ,
AssemblyLine . immediate ( EOR , 0x80 ) ,
AssemblyLine . label ( fixup ) ) ++
List ( AssemblyLine . relative ( BPL , x ) )
2017-12-06 23:23:30 +00:00
case _ => ???
2018-08-03 14:21:02 +00:00
// TODO: signed word comparisons: <=, >
2017-12-06 23:23:30 +00:00
}
}
2018-03-16 12:19:54 +00:00
def compileLongComparison ( ctx : CompilationContext , compType : ComparisonType . Value , lhs : Expression , rhs : Expression , size : Int , branches : BranchSpec , alreadyFlipped : Boolean = false ) : List [ AssemblyLine ] = {
2018-06-12 20:46:20 +00:00
val rType = MosExpressionCompiler . getExpressionType ( ctx , rhs )
2018-03-16 12:19:54 +00:00
if ( rType . size < size && rType . isSigned ) {
if ( alreadyFlipped ) ???
else return compileLongComparison ( ctx , ComparisonType . flip ( compType ) , rhs , lhs , size , branches , alreadyFlipped = true )
}
val ( effectiveComparisonType , label ) = branches match {
case NoBranching => return Nil
case BranchIfTrue ( x ) => compType -> x
case BranchIfFalse ( x ) => ComparisonType . negate ( compType ) -> x
}
// TODO: check for carry flag clobbering
val l = getLoadForEachByte ( ctx , lhs , size )
val r = getLoadForEachByte ( ctx , rhs , size )
val mask = ( 1L << ( size * 8 ) ) - 1
( ctx . env . eval ( lhs ) , ctx . env . eval ( rhs ) ) match {
case ( Some ( NumericConstant ( lc , _ ) ) , Some ( NumericConstant ( rc , _ ) ) ) =>
return if ( effectiveComparisonType match {
// TODO: those masks are probably wrong
case ComparisonType . Equal =>
( lc & mask ) == ( rc & mask ) // ??
case ComparisonType . NotEqual =>
( lc & mask ) != ( rc & mask ) // ??
case ComparisonType . LessOrEqualUnsigned =>
( lc & mask ) <= ( rc & mask )
case ComparisonType . GreaterOrEqualUnsigned =>
( lc & mask ) >= ( rc & mask )
case ComparisonType . GreaterUnsigned =>
( lc & mask ) > ( rc & mask )
case ComparisonType . LessUnsigned =>
( lc & mask ) < ( rc & mask )
case ComparisonType . LessOrEqualSigned =>
signExtend ( lc , mask ) <= signExtend ( lc , mask )
case ComparisonType . GreaterOrEqualSigned =>
signExtend ( lc , mask ) >= signExtend ( lc , mask )
case ComparisonType . GreaterSigned =>
signExtend ( lc , mask ) > signExtend ( lc , mask )
case ComparisonType . LessSigned =>
signExtend ( lc , mask ) < signExtend ( lc , mask )
} ) List ( AssemblyLine . absolute ( JMP , Label ( label ) ) ) else Nil
case _ =>
effectiveComparisonType match {
case ComparisonType . Equal =>
2018-07-31 16:16:36 +00:00
val innerLabel = ctx . nextLabel ( "cp" )
2018-03-16 12:19:54 +00:00
val bytewise = l . zip ( r ) . map {
2018-06-18 22:00:48 +00:00
case ( cmpL , cmpR ) => cmpTo ( LDA , cmpL ) ++ cmpTo ( CMP , cmpR )
2018-03-16 12:19:54 +00:00
}
bytewise . init . flatMap ( b => b : + AssemblyLine . relative ( BNE , innerLabel ) ) ++ bytewise . last ++ List (
AssemblyLine . relative ( BEQ , Label ( label ) ) ,
AssemblyLine . label ( innerLabel ) )
case ComparisonType . NotEqual =>
l . zip ( r ) . flatMap {
2018-06-18 22:00:48 +00:00
case ( cmpL , cmpR ) => cmpTo ( LDA , cmpL ) ++ cmpTo ( CMP , cmpR ) : + AssemblyLine . relative ( BNE , label )
2018-03-16 12:19:54 +00:00
}
case ComparisonType . LessUnsigned =>
val calculateCarry = AssemblyLine . implied ( SEC ) : : l . zip ( r ) . flatMap {
2018-06-18 22:00:48 +00:00
case ( cmpL , cmpR ) => cmpTo ( LDA , cmpL ) ++ cmpTo ( SBC , cmpR )
2018-03-16 12:19:54 +00:00
}
calculateCarry ++ List ( AssemblyLine . relative ( BCC , Label ( label ) ) )
case ComparisonType . GreaterOrEqualUnsigned =>
val calculateCarry = AssemblyLine . implied ( SEC ) : : l . zip ( r ) . flatMap {
2018-06-18 22:00:48 +00:00
case ( cmpL , cmpR ) => cmpTo ( LDA , cmpL ) ++ cmpTo ( SBC , cmpR )
2018-03-16 12:19:54 +00:00
}
calculateCarry ++ List ( AssemblyLine . relative ( BCS , Label ( label ) ) )
case ComparisonType . GreaterUnsigned | ComparisonType . LessOrEqualUnsigned =>
compileLongComparison ( ctx , ComparisonType . flip ( compType ) , rhs , lhs , size , branches , alreadyFlipped = true )
case _ =>
2018-07-30 16:15:44 +00:00
ctx . log . error ( "Long signed comparisons are not yet supported" , lhs . position )
2018-03-16 12:19:54 +00:00
Nil
}
}
}
private def signExtend ( value : Long , mask : Long ) : Long = {
val masked = value & mask
if ( masked > mask / 2 ) masked | ~ mask
else masked
}
2017-12-06 23:23:30 +00:00
def compileInPlaceByteMultiplication ( ctx : CompilationContext , v : LhsExpression , addend : Expression ) : List [ AssemblyLine ] = {
val b = ctx . env . get [ Type ] ( "byte" )
ctx . env . eval ( addend ) match {
case Some ( NumericConstant ( 0 , _ ) ) =>
2018-06-12 20:46:20 +00:00
MosExpressionCompiler . compile ( ctx , v , None , NoBranching ) ++ ( AssemblyLine . immediate ( LDA , 0 ) : : MosExpressionCompiler . compileByteStorage ( ctx , MosRegister . A , v ) )
2017-12-06 23:23:30 +00:00
case Some ( NumericConstant ( 1 , _ ) ) =>
2018-06-12 20:46:20 +00:00
MosExpressionCompiler . compile ( ctx , v , None , NoBranching )
2017-12-06 23:23:30 +00:00
case Some ( NumericConstant ( x , _ ) ) =>
2018-06-12 20:46:20 +00:00
compileByteMultiplication ( ctx , v , x . toInt ) ++ MosExpressionCompiler . compileByteStorage ( ctx , MosRegister . A , v )
2017-12-06 23:23:30 +00:00
case _ =>
2018-06-12 20:46:20 +00:00
PseudoregisterBuiltIns . compileByteMultiplication ( ctx , Some ( v ) , addend , storeInRegLo = false ) ++ MosExpressionCompiler . compileByteStorage ( ctx , MosRegister . A , v )
2017-12-06 23:23:30 +00:00
}
}
2019-03-18 19:01:40 +00:00
private def isPowerOfTwoUpTo15 ( n : Long ) : Boolean = if ( n <= 0 || n >= 0x8000 ) false else 0 == ( ( n - 1 ) & n )
2018-12-14 21:50:20 +00:00
def compileInPlaceWordMultiplication ( ctx : CompilationContext , v : LhsExpression , addend : Expression ) : List [ AssemblyLine ] = {
2020-01-03 13:52:35 +00:00
v match {
case dx : DerefExpression =>
2021-06-21 12:20:24 +00:00
val el = if ( dx . isVolatile ) Elidability . Volatile else Elidability . Elidable
2020-01-03 13:52:35 +00:00
// this is ugly, needs a rewrite
2020-07-13 20:49:23 +00:00
return handleWordOrLongInPlaceModificationViaDeref ( ctx , dx , addend , fromMsb = false ) { ( ptr , reg , offset , r ) =>
2020-01-03 13:52:35 +00:00
val constR = r match {
case List ( AssemblyLine0 ( LDA , Immediate , l ) , AssemblyLine0 ( LDX , Immediate , h ) ) =>
h . asl ( 8 ) . + ( l ) . quickSimplify match {
case NumericConstant ( n , _ ) => Some ( n . toInt & 0xffff )
case _ => None
}
case _ => None
}
constR match {
case Some ( 1 ) => Nil
case Some ( 0 ) => List (
AssemblyLine . immediate ( LDA , 0 ) ,
AssemblyLine . immediate ( LDY , offset ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( INY ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) )
2020-01-03 13:52:35 +00:00
case Some ( 2 ) => List (
AssemblyLine . immediate ( LDA , 0 ) ,
AssemblyLine . immediate ( LDY , offset ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( ASL ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( INY ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( ROL ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) )
2020-01-03 13:52:35 +00:00
// TODO: other powers of two
case _ if reg . toAddress == ptr => List (
AssemblyLine . implied ( PHA ) ,
AssemblyLine . immediate ( LDY , offset ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , reg ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . zeropage ( STA , reg , 2 ) ,
AssemblyLine . implied ( INY ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , reg ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . zeropage ( STA , reg , 3 ) ,
AssemblyLine . implied ( PLA ) ,
AssemblyLine . implied ( TAY ) ,
AssemblyLine . zeropage ( LDA , reg , 1 ) ,
AssemblyLine . implied ( PHA ) ,
AssemblyLine . zeropage ( LDA , reg ) ,
AssemblyLine . implied ( PHA ) ,
AssemblyLine . zeropage ( STY , reg ) ,
AssemblyLine . zeropage ( STX , reg , 1 ) ,
AssemblyLine . absolute ( JSR , ctx . env . get [ ThingInMemory ] ( "__mul_u16u16u16" ) ) ,
AssemblyLine . zeropage ( STA , reg , 2 ) ,
AssemblyLine . implied ( PLA ) ,
AssemblyLine . zeropage ( STA , reg ) ,
AssemblyLine . implied ( PLA ) ,
AssemblyLine . zeropage ( STA , reg , 1 ) ,
AssemblyLine . immediate ( LDY , offset ) ,
AssemblyLine . zeropage ( LDA , reg , 2 ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , reg ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( INY ) ,
AssemblyLine . implied ( TXA ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , reg ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
)
case _ => List (
AssemblyLine . zeropage ( STA , reg ) ,
AssemblyLine . zeropage ( STX , reg , 1 ) ,
AssemblyLine . immediate ( LDY , offset ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . zeropage ( STA , reg , 2 ) ,
AssemblyLine . implied ( INY ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . zeropage ( STA , reg , 3 ) ,
AssemblyLine . absolute ( JSR , ctx . env . get [ ThingInMemory ] ( "__mul_u16u16u16" ) ) ,
AssemblyLine . immediate ( LDY , offset ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( INY ) ,
AssemblyLine . implied ( TXA ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
)
}
2020-07-13 20:49:23 +00:00
} ( Left ( { size => ??? } ) )
2020-01-03 13:52:35 +00:00
case _ =>
2019-04-15 17:45:26 +00:00
}
2018-12-14 21:50:20 +00:00
val b = ctx . env . get [ Type ] ( "byte" )
val w = ctx . env . get [ Type ] ( "word" )
ctx . env . eval ( addend ) match {
case Some ( NumericConstant ( 0 , _ ) ) =>
MosExpressionCompiler . compile ( ctx , v , None , NoBranching ) ++ MosExpressionCompiler . compileAssignment ( ctx , LiteralExpression ( 0 , 2 ) , v )
case Some ( NumericConstant ( 1 , _ ) ) =>
MosExpressionCompiler . compile ( ctx , v , None , NoBranching )
2019-03-18 19:01:40 +00:00
case Some ( NumericConstant ( n , _ ) ) if isPowerOfTwoUpTo15 ( n ) =>
BuiltIns . compileInPlaceWordOrLongShiftOps ( ctx , v , LiteralExpression ( java . lang . Long . bitCount ( n - 1 ) , 1 ) , aslRatherThanLsr = true )
2018-12-14 21:50:20 +00:00
case _ =>
// TODO: optimize?
PseudoregisterBuiltIns . compileWordMultiplication ( ctx , Some ( v ) , addend , storeInRegLo = true ) ++
MosExpressionCompiler . compileAssignment ( ctx , VariableExpression ( "__reg.loword" ) , v )
}
}
2017-12-06 23:23:30 +00:00
def compileByteMultiplication ( ctx : CompilationContext , v : Expression , c : Int ) : List [ AssemblyLine ] = {
2019-06-24 13:20:39 +00:00
c match {
case 0 =>
2020-09-20 21:39:30 +00:00
if ( ctx . isConstant ( v ) ) return List ( AssemblyLine . immediate ( LDA , 0 ) )
2019-06-24 13:20:39 +00:00
else return MosExpressionCompiler . compileToA ( ctx , v ) ++ List ( AssemblyLine . immediate ( LDA , 0 ) )
case 1 => return MosExpressionCompiler . compileToA ( ctx , v )
case 2 | 4 | 8 | 16 | 32 =>
return MosExpressionCompiler . compileToA ( ctx , v ) ++ List . fill ( Integer . numberOfTrailingZeros ( c ) ) ( AssemblyLine . implied ( ASL ) )
case 128 =>
return MosExpressionCompiler . compileToA ( ctx , v ) ++ List ( AssemblyLine . implied ( ROR ) , AssemblyLine . implied ( ROR ) , AssemblyLine . immediate ( AND , 0x80 ) )
case 64 =>
return MosExpressionCompiler . compileToA ( ctx , v ) ++ List ( AssemblyLine . implied ( ROR ) , AssemblyLine . implied ( ROR ) , AssemblyLine . implied ( ROR ) , AssemblyLine . immediate ( AND , 0xC0 ) )
case _ =>
}
2017-12-06 23:23:30 +00:00
val result = ListBuffer [ AssemblyLine ] ( )
// TODO: optimise
2017-12-16 16:55:08 +00:00
val addingCode = simpleOperation ( ADC , ctx , v , IndexChoice . PreferY , preserveA = false , commutative = false , decimal = false )
2017-12-06 23:23:30 +00:00
val adc = addingCode . last
val indexing = addingCode . init
result ++= indexing
val mult = c & 0xff
var mask = 128
var empty = true
while ( mask > 0 ) {
if ( ! empty ) {
result += AssemblyLine . implied ( ASL )
}
if ( ( mult & mask ) != 0 ) {
2020-07-17 23:14:43 +00:00
if ( empty ) {
result += adc . copy ( opcode = LDA )
} else {
2020-07-17 23:16:31 +00:00
result += AssemblyLine . implied ( CLC )
result += adc
2020-07-17 23:14:43 +00:00
}
2017-12-06 23:23:30 +00:00
empty = false
}
mask >>>= 1
}
2019-06-24 13:20:39 +00:00
val sizeIfCalling = addingCode . map ( _ . sizeInBytes ) . sum + 9
val sizeIfUnrolling = result . map ( _ . sizeInBytes ) . sum
var shouldUnroll = true
if ( ctx . options . zpRegisterSize >= 2 ) {
if ( ctx . options . flag ( CompilationFlag . OptimizeForSize ) ) {
shouldUnroll = sizeIfUnrolling <= sizeIfCalling
} else if ( ! ctx . options . flag ( CompilationFlag . OptimizeForSpeed ) ) {
shouldUnroll = sizeIfUnrolling <= sizeIfCalling + 6
}
}
if ( shouldUnroll ) {
result . toList
} else {
indexing ++ List ( adc . copy ( opcode = LDA ) ) ++ PseudoregisterBuiltIns . compileByteMultiplication ( ctx , None , LiteralExpression ( c , 1 ) , storeInRegLo = false )
}
2017-12-06 23:23:30 +00:00
}
2018-03-05 11:05:37 +00:00
//noinspection ZeroIndexToHead
2017-12-06 23:23:30 +00:00
def compileByteMultiplication ( ctx : CompilationContext , params : List [ Expression ] ) : List [ AssemblyLine ] = {
val ( constants , variables ) = params . map ( p => p -> ctx . env . eval ( p ) ) . partition ( _ . _2 . exists ( _ . isInstanceOf [ NumericConstant ] ) )
val constant = constants . map ( _ . _2 . get . asInstanceOf [ NumericConstant ] . value ) . foldLeft ( 1L ) ( _ * _ ) . toInt
variables . length match {
case 0 => List ( AssemblyLine . immediate ( LDA , constant & 0xff ) )
2018-12-19 00:09:27 +00:00
case 1 =>
2019-06-24 13:20:39 +00:00
if ( constant == 1 ) {
MosExpressionCompiler . compileToA ( ctx , variables . head . _1 )
2018-12-19 00:09:27 +00:00
} else {
2019-06-24 13:20:39 +00:00
val sim = simplicity ( ctx . env , variables . head . _1 )
if ( sim >= 'I' ) {
compileByteMultiplication ( ctx , variables . head . _1 , constant )
} else {
constant match {
case 2 | 4 | 8 | 16 | 32 =>
MosExpressionCompiler . compileToA ( ctx , variables . head . _1 ) ++ List . fill ( Integer . numberOfTrailingZeros ( constant ) ) ( AssemblyLine . implied ( ASL ) )
case 128 =>
MosExpressionCompiler . compileToA ( ctx , variables . head . _1 ) ++ List ( AssemblyLine . implied ( ROR ) , AssemblyLine . implied ( ROR ) , AssemblyLine . immediate ( AND , 0x80 ) )
case 64 =>
MosExpressionCompiler . compileToA ( ctx , variables . head . _1 ) ++ List ( AssemblyLine . implied ( ROR ) , AssemblyLine . implied ( ROR ) , AssemblyLine . implied ( ROR ) , AssemblyLine . immediate ( AND , 0xC0 ) )
case _ =>
MosExpressionCompiler . compileToA ( ctx , variables . head . _1 ) ++
List ( AssemblyLine . zeropage ( STA , ctx . env . get [ ThingInMemory ] ( "__reg.b0" ) ) ) ++
compileByteMultiplication ( ctx , VariableExpression ( "__reg.b0" ) , constant )
}
}
2018-12-19 00:09:27 +00:00
}
2017-12-06 23:23:30 +00:00
case 2 =>
2018-03-05 11:05:37 +00:00
if ( constant == 1 )
PseudoregisterBuiltIns . compileByteMultiplication ( ctx , Some ( variables ( 0 ) . _1 ) , variables ( 1 ) . _1 , storeInRegLo = false )
else
PseudoregisterBuiltIns . compileByteMultiplication ( ctx , Some ( variables ( 0 ) . _1 ) , variables ( 1 ) . _1 , storeInRegLo = true ) ++
2018-08-03 11:06:23 +00:00
compileByteMultiplication ( ctx , VariableExpression ( "__reg.b0" ) , constant )
2018-03-05 11:05:37 +00:00
case _ => ??? // TODO
2017-12-06 23:23:30 +00:00
}
}
2019-06-06 11:06:30 +00:00
def compileUnsignedWordByByteDivision ( ctx : CompilationContext , p : Expression , q : Expression , modulo : Boolean ) : List [ AssemblyLine ] = {
if ( ctx . options . zpRegisterSize < 3 ) {
ctx . log . error ( "Word by byte division requires the zeropage pseudoregister of size at least 3" , p . position )
return Nil
}
PseudoregisterBuiltIns . compileUnsignedWordByByteDivision ( ctx , p , q , modulo )
}
2019-06-05 16:36:39 +00:00
def compileUnsignedByteDivision ( ctx : CompilationContext , p : Expression , q : Expression , modulo : Boolean ) : List [ AssemblyLine ] = {
2019-06-06 11:06:30 +00:00
if ( ctx . options . zpRegisterSize < 2 ) {
2019-06-05 16:36:39 +00:00
ctx . log . error ( "Byte division requires the zeropage pseudoregister" , p . position )
return Nil
}
ctx . env . eval ( q ) match {
case Some ( NumericConstant ( qq , _ ) ) =>
if ( qq < 0 ) {
ctx . log . error ( "Unsigned division by negative constant" , q . position )
Nil
} else if ( qq == 0 ) {
ctx . log . error ( "Unsigned division by zero" , q . position )
Nil
} else if ( qq > 255 ) {
if ( modulo ) MosExpressionCompiler . compileToA ( ctx , p )
2019-06-06 11:35:26 +00:00
else List ( AssemblyLine . immediate ( LDA , 0 ) . position ( q . position ) )
} else if ( qq == 1 ) {
if ( modulo ) List ( AssemblyLine . immediate ( LDA , 0 ) . position ( q . position ) )
else MosExpressionCompiler . compileToA ( ctx , p )
2019-06-24 13:20:39 +00:00
} else if ( qq >= 128 && ! modulo ) {
MosExpressionCompiler . compileToA ( ctx , p ) ++ List ( AssemblyLine . immediate ( CMP , qq ) , AssemblyLine . immediate ( LDA , 0 ) , AssemblyLine . implied ( ROL ) )
2019-06-06 11:35:26 +00:00
} else if ( isPowerOfTwoUpTo15 ( qq ) ) {
if ( modulo ) MosExpressionCompiler . compileToA ( ctx , p ) : + AssemblyLine . immediate ( AND , qq - 1 ) . position ( q . position )
else MosExpressionCompiler . compileToA ( ctx , p ) ++ List . fill ( java . lang . Long . bitCount ( qq - 1 ) ) ( AssemblyLine . implied ( LSR ) . position ( q . position ) )
2019-06-05 16:36:39 +00:00
} else {
compileUnsignedByteDivision ( ctx , p , qq . toInt , modulo )
}
2019-06-05 23:17:34 +00:00
case _ =>
if ( modulo ) {
PseudoregisterBuiltIns . compileUnsignedByteModulo ( ctx , Some ( p ) , q , storeInRegLo = false )
} else {
PseudoregisterBuiltIns . compileUnsignedByteDivision ( ctx , Some ( p ) , q , storeInRegLo = false )
}
2019-06-05 16:36:39 +00:00
}
}
def compileUnsignedByteDivision ( ctx : CompilationContext , p : Expression , q : Int , modulo : Boolean ) : List [ AssemblyLine ] = {
val reg = ctx . env . get [ VariableInMemory ] ( "__reg" )
val initP = MosExpressionCompiler . compileToA ( ctx , p )
val result = ListBuffer [ AssemblyLine ] ( )
if ( ctx . options . flag ( CompilationFlag . EmitCmosOpcodes ) ) {
result ++= initP
result += AssemblyLine . zeropage ( STZ , reg )
} else if ( MosExpressionCompiler . changesZpreg ( initP , 0 ) ) {
result ++= initP
result += AssemblyLine . implied ( PHA )
result += AssemblyLine . immediate ( LDA , 0 )
result += AssemblyLine . zeropage ( STA , reg )
result += AssemblyLine . implied ( PLA )
} else {
result += AssemblyLine . immediate ( LDA , 0 )
result += AssemblyLine . zeropage ( STA , reg )
result ++= initP
}
for ( i <- 7. to ( 0 , - 1 ) ) {
if ( ( q << i ) <= 255 ) {
val lbl = ctx . nextLabel ( "dv" )
result += AssemblyLine . immediate ( CMP , q << i )
result += AssemblyLine . relative ( BCC , lbl )
result += AssemblyLine . immediate ( SBC , q << i )
result += AssemblyLine . label ( lbl )
result += AssemblyLine . zeropage ( ROL , reg )
}
}
if ( ! modulo ) {
result += AssemblyLine . zeropage ( LDA , reg )
}
result . toList
}
2017-12-06 23:23:30 +00:00
def compileInPlaceByteAddition ( ctx : CompilationContext , v : LhsExpression , addend : Expression , subtract : Boolean , decimal : Boolean ) : List [ AssemblyLine ] = {
2018-08-03 11:06:23 +00:00
if ( decimal && ! ctx . options . flag ( CompilationFlag . DecimalMode ) && ctx . options . zpRegisterSize < 4 ) {
ctx . log . error ( "Unsupported decimal operation. Consider increasing the size of the zeropage register." , v . position )
return compileInPlaceByteAddition ( ctx , v , addend , subtract , decimal = false )
2017-12-06 23:23:30 +00:00
}
val env = ctx . env
val b = env . get [ Type ] ( "byte" )
2018-02-24 23:45:25 +00:00
val lhsIsDirectlyIncrementable = v match {
2018-03-07 11:36:21 +00:00
case _ : VariableExpression => true
case IndexedExpression ( pointy , indexExpr ) =>
val indexerSize = getIndexerSize ( ctx , indexExpr )
indexerSize <= 1 && ( env . getPointy ( pointy ) match {
case _ : ConstantPointy => true
case _ : VariablePointy => false
} )
2018-02-24 23:45:25 +00:00
case _ => false
}
2017-12-06 23:23:30 +00:00
env . eval ( addend ) match {
2018-06-12 20:46:20 +00:00
case Some ( NumericConstant ( 0 , _ ) ) => MosExpressionCompiler . compile ( ctx , v , None , NoBranching )
2018-02-24 23:45:25 +00:00
case Some ( NumericConstant ( 1 , _ ) ) if lhsIsDirectlyIncrementable && ! decimal => if ( subtract ) {
2017-12-06 23:23:30 +00:00
simpleOperation ( DEC , ctx , v , IndexChoice . RequireX , preserveA = false , commutative = true )
} else {
simpleOperation ( INC , ctx , v , IndexChoice . RequireX , preserveA = false , commutative = true )
}
// TODO: compile +=2 to two INCs
2018-02-24 23:45:25 +00:00
case Some ( NumericConstant ( - 1 , _ ) ) if lhsIsDirectlyIncrementable && ! decimal => if ( subtract ) {
2017-12-06 23:23:30 +00:00
simpleOperation ( INC , ctx , v , IndexChoice . RequireX , preserveA = false , commutative = true )
} else {
simpleOperation ( DEC , ctx , v , IndexChoice . RequireX , preserveA = false , commutative = true )
}
case _ =>
2018-08-03 11:06:23 +00:00
if ( decimal && ! ctx . options . flag ( CompilationFlag . DecimalMode ) ) {
val reg = ctx . env . get [ MemoryVariable ] ( "__reg" )
val loadRhs = MosExpressionCompiler . compile ( ctx , addend , Some ( b -> ctx . env . genRelativeVariable ( reg . toAddress + 3 , b , zeropage = true ) ) , NoBranching )
val loadLhs = MosExpressionCompiler . compileToA ( ctx , v )
val storeLhs = MosExpressionCompiler . compileByteStorage ( ctx , MosRegister . A , v )
val subroutine = if ( subtract ) "__sub_decimal" else "__adc_decimal"
loadRhs ++ MosExpressionCompiler . preserveZpregIfNeededDestroyingAAndX ( ctx , 3 , loadLhs ) ++ List (
AssemblyLine . zeropage ( STA , reg , 2 ) ) ++ ( if ( subtract ) List (
AssemblyLine . absolute ( JSR , ctx . env . get [ ThingInMemory ] ( "__sub_decimal" ) )
) else List (
AssemblyLine . implied ( CLC ) ,
AssemblyLine . absolute ( JSR , ctx . env . get [ ThingInMemory ] ( "__adc_decimal" ) )
) ) ++ storeLhs
2020-07-18 22:54:23 +00:00
} else if ( ! subtract && simplicity ( env , v ) >= simplicity ( env , addend ) ) {
2018-06-12 20:46:20 +00:00
val loadRhs = MosExpressionCompiler . compile ( ctx , addend , Some ( b -> RegisterVariable ( MosRegister . A , b ) ) , NoBranching )
2018-01-07 22:30:43 +00:00
val modifyAcc = insertBeforeLast ( AssemblyLine . implied ( CLC ) , simpleOperation ( ADC , ctx , v , IndexChoice . PreferY , preserveA = true , commutative = true , decimal = decimal ) )
2018-06-12 20:46:20 +00:00
val storeLhs = MosExpressionCompiler . compileByteStorage ( ctx , MosRegister . A , v )
2018-01-07 22:30:43 +00:00
loadRhs ++ modifyAcc ++ storeLhs
2017-12-06 23:23:30 +00:00
} else {
2018-06-12 20:46:20 +00:00
val loadLhs = MosExpressionCompiler . compile ( ctx , v , Some ( b -> RegisterVariable ( MosRegister . A , b ) ) , NoBranching )
2018-01-07 22:30:43 +00:00
val modifyLhs = if ( subtract ) {
insertBeforeLast ( AssemblyLine . implied ( SEC ) , simpleOperation ( SBC , ctx , addend , IndexChoice . PreferY , preserveA = true , commutative = false , decimal = decimal ) )
} else {
insertBeforeLast ( AssemblyLine . implied ( CLC ) , simpleOperation ( ADC , ctx , addend , IndexChoice . PreferY , preserveA = true , commutative = true , decimal = decimal ) )
}
2018-06-12 20:46:20 +00:00
val storeLhs = MosExpressionCompiler . compileByteStorage ( ctx , MosRegister . A , v )
2018-01-07 22:30:43 +00:00
loadLhs ++ modifyLhs ++ storeLhs
2017-12-06 23:23:30 +00:00
}
}
}
2018-07-20 20:46:53 +00:00
private def getIndexerSize ( ctx : CompilationContext , indexExpr : Expression ) : Int = {
ctx . env . evalVariableAndConstantSubParts ( indexExpr ) . _1 . map ( v => MosExpressionCompiler . getExpressionType ( ctx , v ) . size ) . sum
2018-03-07 11:36:21 +00:00
}
2020-01-03 13:52:35 +00:00
@inline
2020-07-13 20:49:23 +00:00
private def handleWordOrLongInPlaceModificationViaDeref ( ctx : CompilationContext , lhs : DerefExpression , rhs : Expression , fromMsb : Boolean )
( wordFooter : ( Constant , VariableInMemory , Int , List [ AssemblyLine ] ) => List [ AssemblyLine ] )
( eitherDoWholeThingOrMergeByte : Either [ ( Int ) => List [ AssemblyLine ] , ( Int , List [ AssemblyLine ] , Boolean ) => List [ AssemblyLine ] ] ) : List [ AssemblyLine ] = {
2020-01-03 13:52:35 +00:00
if ( ctx . options . zpRegisterSize < 2 ) {
ctx . log . error ( "Unsupported operation. Consider increasing the size of the zeropage register." , lhs . position )
return MosExpressionCompiler . compileToAX ( ctx , lhs ) ++ MosExpressionCompiler . compileToAX ( ctx , rhs )
}
val env = ctx . env
val targetType = MosExpressionCompiler . getExpressionType ( ctx , lhs )
2020-07-13 20:49:23 +00:00
val reg = env . get [ VariableInMemory ] ( "__reg" )
2020-01-03 13:52:35 +00:00
if ( targetType . size == 2 ) {
var l = MosExpressionCompiler . compileToZReg ( ctx , lhs . inner )
val ptr = l match {
case List ( AssemblyLine0 ( LDA , ZeroPage , p ) , AssemblyLine0 ( STA , ZeroPage , _ ) , AssemblyLine0 ( LDA , ZeroPage , q ) , AssemblyLine0 ( STA , ZeroPage , _ ) ) if p . succ == q =>
l = Nil
p
case List ( AssemblyLine0 ( LDA , ZeroPage , p ) , AssemblyLine0 ( LDX , ZeroPage , q ) , AssemblyLine0 ( STA , ZeroPage , _ ) , AssemblyLine0 ( STX , ZeroPage , _ ) ) if p . succ == q =>
l = Nil
p
case _ =>
reg . toAddress
}
val r = MosExpressionCompiler . compileToAX ( ctx , rhs )
2020-07-13 20:49:23 +00:00
val s = wordFooter ( ptr , reg , lhs . offset , r )
2020-01-03 13:52:35 +00:00
if ( MosExpressionCompiler . changesZpreg ( r , 0 ) || MosExpressionCompiler . changesZpreg ( r , 1 ) ) {
r ++ MosExpressionCompiler . preserveRegisterIfNeeded ( ctx , MosRegister . AX , l ) ++ s
} else {
l ++ r ++ s
}
} else {
2020-07-13 20:49:23 +00:00
val result = ListBuffer [ AssemblyLine ] ( )
result ++= MosExpressionCompiler . compileToZReg ( ctx , lhs . inner # + # lhs . offset )
eitherDoWholeThingOrMergeByte match {
case Left ( wholeThing ) => result ++= wholeThing ( targetType . size )
case Right ( mergeByte ) =>
val loads = getLoadForEachByte ( ctx , rhs , targetType . size ) . map ( x => MosExpressionCompiler . preserve2ZpregIfNeededDestroyingAAndX ( ctx , 0 , 1 , cmpTo ( LDA , x ) ) )
if ( fromMsb ) {
for ( i <- targetType . size . - ( 1 ) . to ( 0 , - 1 ) ) {
result ++= mergeByte ( i , loads ( i ) , i == targetType . size - 1 )
}
} else {
for ( i <- 0 until targetType . size ) {
result ++= mergeByte ( i , loads ( i ) , i == targetType . size - 1 )
}
}
}
result . toList
2020-01-03 13:52:35 +00:00
}
}
2017-12-06 23:23:30 +00:00
def compileInPlaceWordOrLongAddition ( ctx : CompilationContext , lhs : LhsExpression , addend : Expression , subtract : Boolean , decimal : Boolean ) : List [ AssemblyLine ] = {
2018-08-03 11:06:23 +00:00
if ( decimal && ! ctx . options . flag ( CompilationFlag . DecimalMode ) && ctx . options . zpRegisterSize < 4 ) {
ctx . log . error ( "Unsupported decimal operation. Consider increasing the size of the zeropage register." , lhs . position )
return compileInPlaceWordOrLongAddition ( ctx , lhs , addend , subtract , decimal = false )
2017-12-06 23:23:30 +00:00
}
2020-01-03 13:52:35 +00:00
lhs match {
case dx : DerefExpression =>
2021-06-21 12:20:24 +00:00
val el = if ( dx . isVolatile ) Elidability . Volatile else Elidability . Elidable
2020-01-03 13:52:35 +00:00
if ( subtract && ctx . options . zpRegisterSize < 3 ) {
ctx . log . error ( "Too complex left hand side. Consider increasing the size of the zeropage register." , lhs . position )
return compileInPlaceWordOrLongAddition ( ctx , lhs , addend , subtract = false , decimal = false )
}
2020-07-13 20:49:23 +00:00
return if ( subtract ) handleWordOrLongInPlaceModificationViaDeref ( ctx , dx , addend , fromMsb = false ) ( ( ptr , reg , offset , _ ) => wrapInSedCldIfNeeded ( decimal , List (
2020-01-03 13:52:35 +00:00
AssemblyLine . immediate ( LDY , offset ) ,
AssemblyLine . implied ( SEC ) ,
AssemblyLine . zeropage ( STA , reg , 2 ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . zeropage ( SBC , reg , 2 ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( INY ) ,
AssemblyLine . zeropage ( STX , reg , 2 ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( LDA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . zeropage ( SBC , reg , 2 ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-07-13 20:49:23 +00:00
) ) ) ( Right ( { ( iy , r , last ) =>
val reg = ctx . env . get [ VariableInMemory ] ( "__reg" )
val result = ListBuffer [ AssemblyLine ] ( )
if ( iy != 0 ) {
result ++= MosExpressionCompiler . preserveCarryIfNeeded ( ctx , r )
} else {
result ++= r
}
result += AssemblyLine . immediate ( LDY , iy )
result += AssemblyLine . zeropage ( STA , reg , 2 )
2021-06-21 12:20:24 +00:00
result += AssemblyLine . indexedY ( LDA , reg ) . copy ( elidability = el )
2020-07-13 20:49:23 +00:00
if ( iy == 0 ) {
result += AssemblyLine . implied ( SEC )
}
if ( decimal ) {
result += AssemblyLine . implied ( SED )
}
result += AssemblyLine . zeropage ( SBC , reg , 2 )
if ( decimal ) {
result += AssemblyLine . implied ( CLD )
}
result += AssemblyLine . indexedY ( STA , reg )
result . toList
} ) ) else handleWordOrLongInPlaceModificationViaDeref ( ctx , dx , addend , fromMsb = false ) ( ( ptr , _ , offset , _ ) => wrapInSedCldIfNeeded ( decimal , List (
2020-01-03 13:52:35 +00:00
AssemblyLine . immediate ( LDY , offset ) ,
AssemblyLine . implied ( CLC ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( ADC , ptr ) . copy ( elidability = el ) ,
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( TXA ) ,
AssemblyLine . implied ( INY ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( ADC , ptr ) . copy ( elidability = el ) ,
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-07-13 20:49:23 +00:00
) ) ) ( Right ( { ( iy , r , last ) =>
val reg = ctx . env . get [ VariableInMemory ] ( "__reg" )
val result = ListBuffer [ AssemblyLine ] ( )
if ( iy != 0 ) {
result ++= MosExpressionCompiler . preserveCarryIfNeeded ( ctx , r )
} else {
result ++= r
}
result += AssemblyLine . immediate ( LDY , iy )
if ( iy == 0 ) {
result += AssemblyLine . implied ( CLC )
}
if ( decimal ) {
result += AssemblyLine . implied ( SED )
}
result += AssemblyLine . indexedY ( ADC , reg )
if ( decimal ) {
result += AssemblyLine . implied ( CLD )
}
result += AssemblyLine . indexedY ( STA , reg )
result . toList
} ) )
2020-01-03 13:52:35 +00:00
case _ =>
2019-04-15 17:45:26 +00:00
}
2017-12-06 23:23:30 +00:00
val env = ctx . env
val b = env . get [ Type ] ( "byte" )
val w = env . get [ Type ] ( "word" )
2018-01-07 22:30:43 +00:00
val targetBytes : List [ List [ AssemblyLine ] ] = getStorageForEachByte ( ctx , lhs )
2017-12-06 23:23:30 +00:00
val lhsIsStack = targetBytes . head . head . opcode == TSX
val targetSize = targetBytes . size
2018-06-12 20:46:20 +00:00
val addendType = MosExpressionCompiler . getExpressionType ( ctx , addend )
2017-12-06 23:23:30 +00:00
var addendSize = addendType . size
def isRhsComplex ( xs : List [ AssemblyLine ] ) : Boolean = xs match {
2018-12-14 14:42:31 +00:00
case AssemblyLine0 ( LDA , _ , _ ) : : Nil => false
case AssemblyLine0 ( LDA , _ , _ ) : : AssemblyLine0 ( LDX , _ , _ ) : : Nil => false
2017-12-06 23:23:30 +00:00
case _ => true
}
def isRhsStack ( xs : List [ AssemblyLine ] ) : Boolean = xs . exists ( _ . opcode == TSX )
2017-12-18 16:51:48 +00:00
val canUseIncDec = ! decimal && targetBytes . forall ( _ . forall ( l => l . opcode != STA || ( l . addrMode match {
case AddrMode . Absolute => true
case AddrMode . AbsoluteX => true
case AddrMode . ZeroPage => true
case AddrMode . ZeroPageX => true
case _ => false
} ) ) )
def doDec ( lines : List [ List [ AssemblyLine ] ] ) : List [ AssemblyLine ] = lines match {
case Nil => Nil
case x : : Nil => staTo ( DEC , x )
case x : : xs =>
2018-07-31 16:16:36 +00:00
val label = ctx . nextLabel ( "de" )
2017-12-18 16:51:48 +00:00
staTo ( LDA , x ) ++
List ( AssemblyLine . relative ( BNE , label ) ) ++
doDec ( xs ) ++
List ( AssemblyLine . label ( label ) ) ++
staTo ( DEC , x )
}
2017-12-06 23:23:30 +00:00
val ( calculateRhs , addendByteRead0 ) : ( List [ AssemblyLine ] , List [ List [ AssemblyLine ] ] ) = env . eval ( addend ) match {
2017-12-18 16:51:48 +00:00
case Some ( NumericConstant ( 0 , _ ) ) =>
2018-06-12 20:46:20 +00:00
return MosExpressionCompiler . compile ( ctx , lhs , None , NoBranching )
2017-12-18 16:51:48 +00:00
case Some ( NumericConstant ( 1 , _ ) ) if canUseIncDec && ! subtract =>
2018-03-03 00:21:57 +00:00
if ( ctx . options . flags ( CompilationFlag . Emit65CE02Opcodes ) ) {
targetBytes match {
2018-12-14 14:42:31 +00:00
case List ( List ( AssemblyLine0 ( STA , ZeroPage , l ) ) , List ( AssemblyLine0 ( STA , ZeroPage , h ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( l . + ( 1 ) . quickSimplify == h ) {
return List ( AssemblyLine . zeropage ( INC_W , l ) )
}
2018-07-24 21:14:09 +00:00
case _ =>
2018-03-03 00:21:57 +00:00
}
}
if ( ctx . options . flags ( CompilationFlag . EmitNative65816Opcodes ) ) {
targetBytes match {
2018-12-14 14:42:31 +00:00
case List ( List ( AssemblyLine0 ( STA , a1 @ ( ZeroPage | Absolute | ZeroPageX | AbsoluteX ) , l ) ) , List ( AssemblyLine0 ( STA , a2 , h ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( a1 == a2 && l . + ( 1 ) . quickSimplify == h ) {
return List ( AssemblyLine . accu16 , AssemblyLine ( INC_W , a1 , l ) , AssemblyLine . accu8 )
}
}
}
2018-07-31 16:16:36 +00:00
val label = ctx . nextLabel ( "in" )
2017-12-18 16:51:48 +00:00
return staTo ( INC , targetBytes . head ) ++ targetBytes . tail . flatMap ( l => AssemblyLine . relative ( BNE , label ) : : staTo ( INC , l ) ) : + AssemblyLine . label ( label )
case Some ( NumericConstant ( - 1 , _ ) ) if canUseIncDec && subtract =>
2018-03-03 00:21:57 +00:00
if ( ctx . options . flags ( CompilationFlag . Emit65CE02Opcodes ) ) {
targetBytes match {
2018-12-14 14:42:31 +00:00
case List ( List ( AssemblyLine0 ( STA , ZeroPage , l ) ) , List ( AssemblyLine0 ( STA , ZeroPage , h ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( l . + ( 1 ) . quickSimplify == h ) {
return List ( AssemblyLine . zeropage ( INC_W , l ) )
}
}
}
if ( ctx . options . flags ( CompilationFlag . EmitNative65816Opcodes ) ) {
targetBytes match {
2018-12-14 14:42:31 +00:00
case List ( List ( AssemblyLine0 ( STA , a1 @ ( ZeroPage | Absolute | ZeroPageX | AbsoluteX ) , l ) ) , List ( AssemblyLine0 ( STA , a2 , h ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( a1 == a2 && l . + ( 1 ) . quickSimplify == h ) {
return List ( AssemblyLine . accu16 , AssemblyLine ( INC_W , a1 , l ) , AssemblyLine . accu8 )
}
}
}
2018-07-31 16:16:36 +00:00
val label = ctx . nextLabel ( "in" )
2017-12-18 16:51:48 +00:00
return staTo ( INC , targetBytes . head ) ++ targetBytes . tail . flatMap ( l => AssemblyLine . relative ( BNE , label ) : : staTo ( INC , l ) ) : + AssemblyLine . label ( label )
case Some ( NumericConstant ( 1 , _ ) ) if canUseIncDec && subtract =>
2018-03-03 00:21:57 +00:00
if ( ctx . options . flags ( CompilationFlag . Emit65CE02Opcodes ) ) {
targetBytes match {
2018-12-14 14:42:31 +00:00
case List ( List ( AssemblyLine0 ( STA , ZeroPage , l ) ) , List ( AssemblyLine0 ( STA , ZeroPage , h ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( l . + ( 1 ) . quickSimplify == h ) {
return List ( AssemblyLine . zeropage ( DEC_W , l ) )
}
}
}
if ( ctx . options . flags ( CompilationFlag . EmitNative65816Opcodes ) ) {
targetBytes match {
2018-12-14 14:42:31 +00:00
case List ( List ( AssemblyLine0 ( STA , a1 @ ( ZeroPage | Absolute | ZeroPageX | AbsoluteX ) , l ) ) , List ( AssemblyLine0 ( STA , a2 , h ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( a1 == a2 && l . + ( 1 ) . quickSimplify == h ) {
return List ( AssemblyLine . accu16 , AssemblyLine ( DEC_W , a1 , l ) , AssemblyLine . accu8 )
}
}
}
2017-12-18 16:51:48 +00:00
return doDec ( targetBytes )
case Some ( NumericConstant ( - 1 , _ ) ) if canUseIncDec && ! subtract =>
2018-03-03 00:21:57 +00:00
if ( ctx . options . flags ( CompilationFlag . Emit65CE02Opcodes ) ) {
targetBytes match {
2018-12-14 14:42:31 +00:00
case List ( List ( AssemblyLine0 ( STA , ZeroPage , l ) ) , List ( AssemblyLine0 ( STA , ZeroPage , h ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( l . + ( 1 ) . quickSimplify == h ) {
return List ( AssemblyLine . zeropage ( DEC_W , l ) )
}
}
}
if ( ctx . options . flags ( CompilationFlag . EmitNative65816Opcodes ) ) {
targetBytes match {
2018-12-14 14:42:31 +00:00
case List ( List ( AssemblyLine0 ( STA , a1 @ ( ZeroPage | Absolute | ZeroPageX | AbsoluteX ) , l ) ) , List ( AssemblyLine0 ( STA , a2 , h ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( a1 == a2 && l . + ( 1 ) . quickSimplify == h ) {
return List ( AssemblyLine . accu16 , AssemblyLine ( DEC_W , a1 , l ) , AssemblyLine . accu8 )
}
}
}
2018-07-31 16:16:36 +00:00
val label = ctx . nextLabel ( "de" )
2017-12-18 16:51:48 +00:00
return doDec ( targetBytes )
2017-12-06 23:23:30 +00:00
case Some ( constant ) =>
addendSize = targetSize
2019-09-20 16:33:41 +00:00
Nil -> List . tabulate ( targetSize ) ( i => List ( AssemblyLine . immediate ( LDA ,
if ( i >= addendType . size && constant . isProvablyNegative ( addendType ) ) NumericConstant ( - 1 , 1 ) else constant . subbyte ( i )
) ) )
2017-12-06 23:23:30 +00:00
case None =>
addendSize match {
case 1 =>
2018-06-12 20:46:20 +00:00
val base = MosExpressionCompiler . compile ( ctx , addend , Some ( b -> RegisterVariable ( MosRegister . A , b ) ) , NoBranching )
2017-12-06 23:23:30 +00:00
if ( subtract ) {
if ( isRhsComplex ( base ) ) {
if ( isRhsStack ( base ) ) {
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "Subtracting a stack-based value" , addend . position )
2017-12-06 23:23:30 +00:00
???
}
( base ++ List ( AssemblyLine . implied ( PHA ) ) ) -> List ( List ( AssemblyLine . implied ( TSX ) , AssemblyLine . absoluteX ( LDA , 0x101 ) ) )
} else {
2018-08-03 11:06:23 +00:00
Nil -> base . map ( l => l . copy ( opcode = LDA ) : : Nil )
2017-12-06 23:23:30 +00:00
}
} else {
base -> List ( Nil )
}
case 2 =>
2018-06-12 20:46:20 +00:00
val base = MosExpressionCompiler . compile ( ctx , addend , Some ( MosExpressionCompiler . getExpressionType ( ctx , addend ) -> RegisterVariable ( MosRegister . AX , w ) ) , NoBranching )
2017-12-06 23:23:30 +00:00
if ( isRhsStack ( base ) ) {
2018-06-12 20:46:20 +00:00
val fixedBase = MosExpressionCompiler . compile ( ctx , addend , Some ( MosExpressionCompiler . getExpressionType ( ctx , addend ) -> RegisterVariable ( MosRegister . AY , w ) ) , NoBranching )
2017-12-06 23:23:30 +00:00
if ( subtract ) {
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "Subtracting a stack-based value" , addend . position )
2017-12-06 23:23:30 +00:00
if ( isRhsComplex ( base ) ) {
???
} else {
2018-08-03 11:06:23 +00:00
Nil -> fixedBase . map ( l => l . copy ( opcode = LDA ) : : Nil )
2017-12-06 23:23:30 +00:00
???
}
} else {
fixedBase -> List ( Nil , List ( AssemblyLine . implied ( TYA ) ) )
}
} else {
if ( subtract ) {
if ( isRhsComplex ( base ) ) {
( base ++ List (
AssemblyLine . implied ( PHA ) ,
AssemblyLine . implied ( TXA ) ,
AssemblyLine . implied ( PHA ) )
) -> List (
List ( AssemblyLine . implied ( TSX ) , AssemblyLine . absoluteX ( LDA , 0x102 ) ) ,
List ( AssemblyLine . implied ( TSX ) , AssemblyLine . absoluteX ( LDA , 0x101 ) ) )
} else {
2018-08-03 11:06:23 +00:00
Nil -> base . map ( l => l . copy ( opcode = LDA ) : : Nil )
2017-12-06 23:23:30 +00:00
}
} else {
if ( lhsIsStack ) {
2018-06-12 20:46:20 +00:00
val fixedBase = MosExpressionCompiler . compile ( ctx , addend , Some ( MosExpressionCompiler . getExpressionType ( ctx , addend ) -> RegisterVariable ( MosRegister . AY , w ) ) , NoBranching )
2017-12-06 23:23:30 +00:00
fixedBase -> List ( Nil , List ( AssemblyLine . implied ( TYA ) ) )
} else {
base -> List ( Nil , List ( AssemblyLine . implied ( TXA ) ) )
}
}
}
2018-08-01 13:28:47 +00:00
case _ => addend match {
2017-12-06 23:23:30 +00:00
case vv : VariableExpression =>
val source = env . get [ Variable ] ( vv . name )
2019-09-20 16:33:41 +00:00
Nil -> List . tabulate ( targetSize ) ( i => AssemblyLine . variable ( ctx , LDA , source , i ) )
2018-08-01 13:28:47 +00:00
case f : FunctionCallExpression =>
val jsr = MosExpressionCompiler . compile ( ctx , addend , None , BranchSpec . None )
val result = ctx . env . get [ VariableInMemory ] ( f . functionName + ".return" )
2019-09-20 16:33:41 +00:00
jsr -> List . tabulate ( targetSize ) ( i => AssemblyLine . variable ( ctx , LDA , result , i ) )
2018-08-01 13:28:47 +00:00
}
2017-12-06 23:23:30 +00:00
}
}
val addendByteRead = addendByteRead0 ++ List . fill ( ( targetSize - addendByteRead0 . size ) max 0 ) ( List ( AssemblyLine . immediate ( LDA , 0 ) ) )
2018-03-03 00:21:57 +00:00
2019-09-20 16:33:41 +00:00
if ( ctx . options . flags ( CompilationFlag . EmitNative65816Opcodes ) && ! addendType . isSigned ) {
2018-03-05 23:21:43 +00:00
( removeTsx ( targetBytes ) , calculateRhs , removeTsx ( addendByteRead ) ) match {
case (
2018-12-14 14:42:31 +00:00
List ( List ( AssemblyLine0 ( STA , ta1 , tl ) ) , List ( AssemblyLine0 ( STA , ta2 , th ) ) ) ,
2018-03-05 23:21:43 +00:00
Nil ,
2018-12-14 14:42:31 +00:00
List ( List ( AssemblyLine0 ( LDA , Immediate , al ) ) , List ( AssemblyLine0 ( LDA , Immediate , ah ) ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( ta1 == ta2 && tl . + ( 1 ) . quickSimplify == th ) {
return wrapInSedCldIfNeeded ( decimal , List (
AssemblyLine . implied ( if ( subtract ) SEC else CLC ) ,
AssemblyLine . accu16 ,
AssemblyLine ( LDA_W , ta1 , tl ) ,
AssemblyLine ( if ( subtract ) SBC_W else ADC_W , WordImmediate , ah . asl ( 8 ) . + ( al ) . quickSimplify ) ,
AssemblyLine ( STA_W , ta1 , tl ) ,
AssemblyLine . accu8 ) )
}
2018-03-05 23:21:43 +00:00
case (
2018-12-14 14:42:31 +00:00
List ( List ( AssemblyLine0 ( STA , ta1 , tl ) ) , List ( AssemblyLine0 ( STA , ta2 , th ) ) ) ,
2018-03-05 23:21:43 +00:00
Nil ,
2018-12-14 14:42:31 +00:00
List ( List ( AssemblyLine0 ( LDA , aa1 , al ) ) , List ( AssemblyLine0 ( LDA , aa2 , ah ) ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( ta1 == ta2 && aa1 == aa2 && tl . + ( 1 ) . quickSimplify == th && al . + ( 1 ) . quickSimplify == ah ) {
return wrapInSedCldIfNeeded ( decimal , List (
AssemblyLine . accu16 ,
AssemblyLine . implied ( if ( subtract ) SEC else CLC ) ,
AssemblyLine ( LDA_W , ta1 , tl ) ,
AssemblyLine ( if ( subtract ) SBC_W else ADC_W , aa1 , al ) ,
AssemblyLine ( STA_W , ta1 , tl ) ,
AssemblyLine . accu8 ) )
}
2018-03-05 23:21:43 +00:00
case (
2018-12-14 14:42:31 +00:00
List ( List ( AssemblyLine0 ( STA , ta1 , tl ) ) , List ( AssemblyLine0 ( STA , ta2 , th ) ) ) ,
List ( AssemblyLine0 ( TSX , _ , _ ) , AssemblyLine0 ( LDA , AbsoluteX , NumericConstant ( al , _ ) ) , AssemblyLine0 ( LDY , AbsoluteX , NumericConstant ( ah , _ ) ) ) ,
List ( Nil , List ( AssemblyLine0 ( TYA , _ , _ ) ) ) ) =>
2018-03-05 23:21:43 +00:00
if ( ta1 == ta2 && tl . + ( 1 ) . quickSimplify == th && al + 1 == ah ) {
return wrapInSedCldIfNeeded ( decimal , List (
AssemblyLine . accu16 ,
AssemblyLine . implied ( if ( subtract ) SEC else CLC ) ,
AssemblyLine ( LDA_W , ta1 , tl ) ,
AssemblyLine ( if ( subtract ) SBC_W else ADC_W , Stack , NumericConstant ( al & 0xff , 1 ) ) ,
AssemblyLine ( STA_W , ta1 , tl ) ,
AssemblyLine . accu8 ) )
}
2018-03-03 00:21:57 +00:00
case _ =>
}
}
2017-12-06 23:23:30 +00:00
val buffer = mutable . ListBuffer [ AssemblyLine ] ( )
buffer ++= calculateRhs
buffer += AssemblyLine . implied ( if ( subtract ) SEC else CLC )
val extendMultipleBytes = targetSize > addendSize + 1
val extendAtLeastOneByte = targetSize > addendSize
for ( i <- 0 until targetSize ) {
2018-08-03 11:06:23 +00:00
if ( decimal && ! ctx . options . flag ( CompilationFlag . DecimalMode ) ) {
val reg = ctx . env . get [ VariableInMemory ] ( "__reg" )
buffer ++= staTo ( LDA , targetBytes ( i ) )
if ( targetBytes ( i ) . isEmpty ) {
buffer += AssemblyLine . immediate ( LDA , 0 )
}
buffer += AssemblyLine . zeropage ( STA , reg , 2 )
// TODO: AX?
buffer ++= MosExpressionCompiler . preserveZpregIfNeededDestroyingAAndX ( ctx , 2 , addendByteRead ( i ) )
buffer += AssemblyLine . zeropage ( STA , reg , 3 )
if ( subtract ) {
if ( i == 0 ) {
buffer += AssemblyLine . absolute ( JSR , env . get [ ThingInMemory ] ( "__sub_decimal" ) )
} else {
buffer += AssemblyLine . absolute ( JSR , env . get [ ThingInMemory ] ( "__sbc_decimal" ) )
}
} else {
if ( i == 0 ) {
buffer += AssemblyLine . implied ( CLC )
}
buffer += AssemblyLine . absolute ( JSR , env . get [ ThingInMemory ] ( "__adc_decimal" ) )
}
buffer ++= targetBytes ( i )
} else if ( subtract ) {
2019-09-20 16:33:41 +00:00
if ( i >= addendSize ) {
if ( addendType . isSigned && ! decimal ) {
buffer += AssemblyLine . implied ( TXA )
buffer ++= staTo ( ADC , targetBytes ( i ) )
} else {
buffer ++= staTo ( LDA , targetBytes ( i ) )
buffer ++= wrapInSedCldIfNeeded ( decimal , ldTo ( SBC , addendByteRead ( i ) ) )
}
} else {
if ( addendType . isSigned && i == addendSize - 1 && extendAtLeastOneByte && ! decimal ) {
val label = ctx . nextLabel ( "sx" )
buffer ++= addendByteRead ( i )
buffer += AssemblyLine . immediate ( EOR , 0xff )
buffer += AssemblyLine . implied ( PHA )
buffer += AssemblyLine . immediate ( ORA , 0x7f )
buffer += AssemblyLine . relative ( BMI , label )
buffer += AssemblyLine . immediate ( LDA , 0 )
buffer += AssemblyLine . label ( label )
buffer += AssemblyLine . implied ( TAX )
buffer += AssemblyLine . implied ( PLA )
buffer ++= staTo ( ADC , targetBytes ( i ) )
} else {
buffer ++= staTo ( LDA , targetBytes ( i ) )
buffer ++= wrapInSedCldIfNeeded ( decimal , ldTo ( SBC , addendByteRead ( i ) ) )
}
2017-12-06 23:23:30 +00:00
}
buffer ++= targetBytes ( i )
} else {
if ( i >= addendSize ) {
if ( addendType . isSigned ) {
2018-07-31 16:16:36 +00:00
val label = ctx . nextLabel ( "sx" )
2017-12-06 23:23:30 +00:00
buffer += AssemblyLine . implied ( TXA )
if ( i == addendSize ) {
buffer += AssemblyLine . immediate ( ORA , 0x7f )
buffer += AssemblyLine . relative ( BMI , label )
buffer += AssemblyLine . immediate ( LDA , 0 )
buffer += AssemblyLine . label ( label )
if ( extendMultipleBytes ) buffer += AssemblyLine . implied ( TAX )
}
} else {
buffer += AssemblyLine . immediate ( LDA , 0 )
}
} else {
buffer ++= addendByteRead ( i )
if ( addendType . isSigned && i == addendSize - 1 && extendAtLeastOneByte ) {
buffer += AssemblyLine . implied ( TAX )
}
}
2017-12-16 16:55:08 +00:00
buffer ++= wrapInSedCldIfNeeded ( decimal , staTo ( ADC , targetBytes ( i ) ) )
2017-12-06 23:23:30 +00:00
buffer ++= targetBytes ( i )
}
}
for ( i <- 0 until calculateRhs . count ( a => a . opcode == PHA ) - calculateRhs . count ( a => a . opcode == PLA ) ) {
buffer += AssemblyLine . implied ( PLA )
}
2017-12-16 16:55:08 +00:00
buffer . toList
2017-12-06 23:23:30 +00:00
}
def compileInPlaceByteBitOp ( ctx : CompilationContext , v : LhsExpression , param : Expression , operation : Opcode . Value ) : List [ AssemblyLine ] = {
val env = ctx . env
val b = env . get [ Type ] ( "byte" )
( operation , env . eval ( param ) ) match {
case ( EOR , Some ( NumericConstant ( 0 , _ ) ) )
| ( ORA , Some ( NumericConstant ( 0 , _ ) ) )
| ( AND , Some ( NumericConstant ( 0xff , _ ) ) )
| ( AND , Some ( NumericConstant ( - 1 , _ ) ) ) =>
2018-08-06 17:17:57 +00:00
MosExpressionCompiler . compile ( ctx , v , None , NoBranching )
2017-12-06 23:23:30 +00:00
case _ =>
2018-01-07 22:30:43 +00:00
if ( simplicity ( env , v ) > simplicity ( env , param ) ) {
2018-06-12 20:46:20 +00:00
val loadRhs = MosExpressionCompiler . compile ( ctx , param , Some ( b -> RegisterVariable ( MosRegister . A , b ) ) , NoBranching )
2018-01-07 22:30:43 +00:00
val modifyAcc = simpleOperation ( operation , ctx , v , IndexChoice . PreferY , preserveA = true , commutative = true )
2018-06-12 20:46:20 +00:00
val storeLhs = MosExpressionCompiler . compileByteStorage ( ctx , MosRegister . A , v )
2018-01-07 22:30:43 +00:00
loadRhs ++ modifyAcc ++ storeLhs
} else {
2018-06-12 20:46:20 +00:00
val loadLhs = MosExpressionCompiler . compile ( ctx , v , Some ( b -> RegisterVariable ( MosRegister . A , b ) ) , NoBranching )
2018-01-07 22:30:43 +00:00
val modifyLhs = simpleOperation ( operation , ctx , param , IndexChoice . PreferY , preserveA = true , commutative = true )
2018-06-12 20:46:20 +00:00
val storeLhs = MosExpressionCompiler . compileByteStorage ( ctx , MosRegister . A , v )
2018-01-07 22:30:43 +00:00
loadLhs ++ modifyLhs ++ storeLhs
}
2017-12-06 23:23:30 +00:00
}
}
def compileInPlaceWordOrLongBitOp ( ctx : CompilationContext , lhs : LhsExpression , param : Expression , operation : Opcode . Value ) : List [ AssemblyLine ] = {
2020-01-03 13:52:35 +00:00
lhs match {
2021-06-21 12:20:24 +00:00
case dx : DerefExpression =>
val el = if ( dx . isVolatile ) Elidability . Volatile else Elidability . Elidable
return handleWordOrLongInPlaceModificationViaDeref ( ctx , dx , param , fromMsb = false ) ( ( ptr , _ , offset , _ ) => List (
2020-01-03 13:52:35 +00:00
AssemblyLine . immediate ( LDY , offset ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( operation , ptr ) . copy ( elidability = el ) ,
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-01-03 13:52:35 +00:00
AssemblyLine . implied ( TXA ) ,
AssemblyLine . implied ( INY ) ,
2021-06-21 12:20:24 +00:00
AssemblyLine . indexedY ( operation , ptr ) . copy ( elidability = el ) ,
AssemblyLine . indexedY ( STA , ptr ) . copy ( elidability = el ) ,
2020-07-13 20:49:23 +00:00
) ) ( Right ( { ( iy , r , last ) =>
val reg = ctx . env . get [ VariableInMemory ] ( "__reg" )
r ++ List (
AssemblyLine . immediate ( LDY , iy ) ,
AssemblyLine . indexedY ( operation , reg ) ,
AssemblyLine . indexedY ( STA , reg ) )
} ) )
2020-01-03 13:52:35 +00:00
case _ =>
2019-04-15 17:45:26 +00:00
}
2017-12-06 23:23:30 +00:00
val env = ctx . env
val b = env . get [ Type ] ( "byte" )
val w = env . get [ Type ] ( "word" )
2018-01-07 22:30:43 +00:00
val targetBytes : List [ List [ AssemblyLine ] ] = getStorageForEachByte ( ctx , lhs )
2017-12-06 23:23:30 +00:00
val lo = targetBytes . head
val targetSize = targetBytes . size
2018-06-12 20:46:20 +00:00
val paramType = MosExpressionCompiler . getExpressionType ( ctx , param )
2017-12-06 23:23:30 +00:00
var paramSize = paramType . size
val extendMultipleBytes = targetSize > paramSize + 1
val extendAtLeastOneByte = targetSize > paramSize
val ( calculateRhs , addendByteRead ) = env . eval ( param ) match {
case Some ( constant ) =>
paramSize = targetSize
Nil -> List . tabulate ( targetSize ) ( i => List ( AssemblyLine . immediate ( LDA , constant . subbyte ( i ) ) ) )
case None =>
paramSize match {
case 1 =>
2018-06-12 20:46:20 +00:00
val base = MosExpressionCompiler . compile ( ctx , param , Some ( b -> RegisterVariable ( MosRegister . A , b ) ) , NoBranching )
2017-12-06 23:23:30 +00:00
base -> List ( Nil )
case 2 =>
2018-06-12 20:46:20 +00:00
val base = MosExpressionCompiler . compile ( ctx , param , Some ( MosExpressionCompiler . getExpressionType ( ctx , param ) -> RegisterVariable ( MosRegister . AX , w ) ) , NoBranching )
2017-12-06 23:23:30 +00:00
base -> List ( Nil , List ( AssemblyLine . implied ( TXA ) ) )
2018-08-01 13:28:47 +00:00
case _ => param match {
2017-12-06 23:23:30 +00:00
case vv : VariableExpression =>
val source = env . get [ Variable ] ( vv . name )
2018-08-01 13:28:47 +00:00
Nil -> List . tabulate ( paramSize ) ( i => AssemblyLine . variable ( ctx , LDA , source , i ) )
case f : FunctionCallExpression =>
val jsr = MosExpressionCompiler . compile ( ctx , param , None , BranchSpec . None )
val result = ctx . env . get [ VariableInMemory ] ( f . functionName + ".return" )
jsr -> List . tabulate ( paramSize ) ( i => AssemblyLine . variable ( ctx , LDA , result , i ) )
}
2017-12-06 23:23:30 +00:00
}
}
2018-03-03 00:21:57 +00:00
if ( ctx . options . flags ( CompilationFlag . EmitNative65816Opcodes ) ) {
( removeTsx ( targetBytes ) , removeTsx ( addendByteRead ) ) match {
2018-12-14 14:42:31 +00:00
case ( List ( List ( AssemblyLine0 ( STA , ta1 , tl ) ) , List ( AssemblyLine0 ( STA , ta2 , th ) ) ) , List ( List ( AssemblyLine0 ( LDA , Immediate , al ) ) , List ( AssemblyLine0 ( LDA , Immediate , ah ) ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( ta1 == ta2 && tl . + ( 1 ) . quickSimplify == th ) {
return List (
AssemblyLine . accu16 ,
AssemblyLine ( LDA_W , ta1 , tl ) ,
AssemblyLine ( Opcode . widen ( operation ) . get , WordImmediate , ah . asl ( 8 ) . + ( al ) . quickSimplify ) ,
AssemblyLine ( STA_W , ta1 , tl ) ,
AssemblyLine . accu8 )
}
2018-12-14 14:42:31 +00:00
case ( List ( List ( AssemblyLine0 ( STA , ta1 , tl ) ) , List ( AssemblyLine0 ( STA , ta2 , th ) ) ) , List ( List ( AssemblyLine0 ( LDA , aa1 , al ) ) , List ( AssemblyLine0 ( LDA , aa2 , ah ) ) ) ) =>
2018-03-03 00:21:57 +00:00
if ( ta1 == ta2 && aa1 == aa2 && tl . + ( 1 ) . quickSimplify == th && al . + ( 1 ) . quickSimplify == ah ) {
return List (
AssemblyLine . accu16 ,
AssemblyLine ( LDA_W , ta1 , tl ) ,
AssemblyLine ( Opcode . widen ( operation ) . get , aa1 , al ) ,
AssemblyLine ( STA_W , ta1 , tl ) ,
AssemblyLine . accu8 )
}
case _ =>
}
}
2017-12-06 23:23:30 +00:00
val AllOnes = ( 1L << ( 8 * targetSize ) ) - 1
( operation , env . eval ( param ) ) match {
case ( EOR , Some ( NumericConstant ( 0 , _ ) ) )
| ( ORA , Some ( NumericConstant ( 0 , _ ) ) )
| ( AND , Some ( NumericConstant ( AllOnes , _ ) ) ) =>
2018-06-12 20:46:20 +00:00
MosExpressionCompiler . compile ( ctx , lhs , None , NoBranching )
2017-12-06 23:23:30 +00:00
case _ =>
val buffer = mutable . ListBuffer [ AssemblyLine ] ( )
buffer ++= calculateRhs
for ( i <- 0 until targetSize ) {
if ( i < paramSize ) {
buffer ++= addendByteRead ( i )
if ( paramType . isSigned && i == paramSize - 1 && extendAtLeastOneByte ) {
buffer += AssemblyLine . implied ( TAX )
}
} else {
if ( paramType . isSigned ) {
2018-07-31 16:16:36 +00:00
val label = ctx . nextLabel ( "sx" )
2017-12-06 23:23:30 +00:00
buffer += AssemblyLine . implied ( TXA )
if ( i == paramSize ) {
buffer += AssemblyLine . immediate ( ORA , 0x7f )
buffer += AssemblyLine . relative ( BMI , label )
buffer += AssemblyLine . immediate ( LDA , 0 )
buffer += AssemblyLine . label ( label )
if ( extendMultipleBytes ) buffer += AssemblyLine . implied ( TAX )
}
} else {
buffer += AssemblyLine . immediate ( LDA , 0 )
}
}
buffer ++= staTo ( operation , targetBytes ( i ) )
buffer ++= targetBytes ( i )
}
for ( i <- 0 until calculateRhs . count ( a => a . opcode == PHA ) - calculateRhs . count ( a => a . opcode == PLA ) ) {
buffer += AssemblyLine . implied ( PLA )
}
buffer . toList
}
}
2018-08-03 11:06:23 +00:00
def getStorageForEachByte ( ctx : CompilationContext , lhs : LhsExpression ) : List [ List [ AssemblyLine ] ] = {
2018-01-07 22:30:43 +00:00
val env = ctx . env
lhs match {
case v : VariableExpression =>
val variable = env . get [ Variable ] ( v . name )
List . tabulate ( variable . typ . size ) { i => AssemblyLine . variable ( ctx , STA , variable , i ) }
case IndexedExpression ( variable , index ) =>
2018-06-12 20:46:20 +00:00
List ( MosExpressionCompiler . compileByteStorage ( ctx , MosRegister . A , lhs ) )
2018-01-07 22:30:43 +00:00
case SeparateBytesExpression ( h : LhsExpression , l : LhsExpression ) =>
2018-01-08 00:03:08 +00:00
if ( simplicity ( ctx . env , h ) < 'J' || simplicity ( ctx . env , l ) < 'J' ) {
// a[b]:c[d] is the most complex expression that doesn't cause the following warning
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "Too complex expression given to the `:` operator, generated code might be wrong" , lhs . position )
2018-01-08 00:03:08 +00:00
}
2018-01-07 22:30:43 +00:00
List (
getStorageForEachByte ( ctx , l ) . head ,
2018-06-12 20:46:20 +00:00
MosExpressionCompiler . preserveRegisterIfNeeded ( ctx , MosRegister . A , getStorageForEachByte ( ctx , h ) . head ) )
2018-01-07 22:30:43 +00:00
case _ =>
???
}
}
2018-03-16 12:19:54 +00:00
private def getLoadForEachByte ( ctx : CompilationContext , expr : Expression , size : Int ) : List [ List [ AssemblyLine ] ] = {
val env = ctx . env
env . eval ( expr ) match {
case Some ( c ) =>
2018-06-18 22:00:48 +00:00
List . tabulate ( size ) { i => List ( AssemblyLine . immediate ( CMP , c . subbyte ( i ) ) ) }
2018-03-16 12:19:54 +00:00
case None =>
expr match {
case v : VariableExpression =>
val variable = env . get [ Variable ] ( v . name )
List . tabulate ( size ) { i =>
if ( i < variable . typ . size ) {
2018-06-18 22:00:48 +00:00
AssemblyLine . variable ( ctx , CMP , variable , i )
2018-03-16 12:19:54 +00:00
} else if ( variable . typ . isSigned ) {
2018-07-31 16:16:36 +00:00
val label = ctx . nextLabel ( "sx" )
2018-06-18 22:00:48 +00:00
AssemblyLine . variable ( ctx , LDA , variable , variable . typ . size - 1 ) ++ List (
2018-03-16 12:19:54 +00:00
AssemblyLine . immediate ( ORA , 0x7F ) ,
AssemblyLine . relative ( BMI , label ) ,
2018-06-18 22:00:48 +00:00
AssemblyLine . immediate ( CMP , 0 ) ,
2018-03-16 12:19:54 +00:00
AssemblyLine . label ( label ) )
2018-06-18 22:00:48 +00:00
} else List ( AssemblyLine . immediate ( CMP , 0 ) )
2018-03-16 12:19:54 +00:00
}
case expr @IndexedExpression ( variable , index ) =>
List . tabulate ( size ) { i =>
2018-06-12 20:46:20 +00:00
if ( i == 0 ) MosExpressionCompiler . compileByteStorage ( ctx , MosRegister . A , expr )
2018-06-18 22:00:48 +00:00
else List ( AssemblyLine . immediate ( CMP , 0 ) )
2018-03-16 12:19:54 +00:00
}
case SeparateBytesExpression ( h : LhsExpression , l : LhsExpression ) =>
if ( simplicity ( ctx . env , h ) < 'J' || simplicity ( ctx . env , l ) < 'J' ) {
// a[b]:c[d] is the most complex expression that doesn't cause the following warning
2018-07-30 16:15:44 +00:00
ctx . log . warn ( "Too complex expression given to the `:` operator, generated code might be wrong" , expr . position )
2018-03-16 12:19:54 +00:00
}
List . tabulate ( size ) { i =>
if ( i == 0 ) getStorageForEachByte ( ctx , l ) . head
2018-06-12 20:46:20 +00:00
else if ( i == 1 ) MosExpressionCompiler . preserveRegisterIfNeeded ( ctx , MosRegister . A , getStorageForEachByte ( ctx , h ) . head )
2018-06-18 22:00:48 +00:00
else List ( AssemblyLine . immediate ( CMP , 0 ) )
2018-03-16 12:19:54 +00:00
}
case _ =>
???
}
}
}
2018-03-03 00:21:57 +00:00
private def removeTsx ( codes : List [ List [ AssemblyLine ] ] ) : List [ List [ AssemblyLine ] ] = codes . map {
2018-12-14 14:42:31 +00:00
case List ( AssemblyLine0 ( TSX , _ , _ ) , AssemblyLine0 ( op , AbsoluteX , NumericConstant ( nn , _ ) ) ) if nn >= 0x100 && nn <= 0x1ff =>
2018-03-03 00:21:57 +00:00
List ( AssemblyLine ( op , Stack , NumericConstant ( nn & 0xff , 1 ) ) )
case x => x
}
2017-12-06 23:23:30 +00:00
}