mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-09 09:41:23 +00:00
Fixed zero-extending for bytes; improved adding bytes to constant words
This commit is contained in:
parent
986252db5b
commit
9ff888c0d9
@ -67,6 +67,7 @@ If and only if both `h` and `l` are assignable expressions, then `h:l` is also a
|
||||
`byte + byte`
|
||||
`constant word + constant word`
|
||||
`constant long + constant long`
|
||||
`constant word + byte`
|
||||
`word + word` (zpreg)
|
||||
|
||||
* `*`: multiplication; the size of the result is the same as the size of the arguments
|
||||
|
@ -903,9 +903,9 @@ object BuiltIns {
|
||||
base -> List(Nil)
|
||||
}
|
||||
case 2 =>
|
||||
val base = ExpressionCompiler.compile(ctx, addend, Some(w -> RegisterVariable(Register.AX, w)), NoBranching)
|
||||
val base = ExpressionCompiler.compile(ctx, addend, Some(ExpressionCompiler.getExpressionType(ctx, addend) -> RegisterVariable(Register.AX, w)), NoBranching)
|
||||
if (isRhsStack(base)) {
|
||||
val fixedBase = ExpressionCompiler.compile(ctx, addend, Some(w -> RegisterVariable(Register.AY, w)), NoBranching)
|
||||
val fixedBase = ExpressionCompiler.compile(ctx, addend, Some(ExpressionCompiler.getExpressionType(ctx, addend) -> RegisterVariable(Register.AY, w)), NoBranching)
|
||||
if (subtract) {
|
||||
ErrorReporting.warn("Subtracting a stack-based value", ctx.options)
|
||||
if (isRhsComplex(base)) {
|
||||
@ -932,7 +932,7 @@ object BuiltIns {
|
||||
}
|
||||
} else {
|
||||
if (lhsIsStack) {
|
||||
val fixedBase = ExpressionCompiler.compile(ctx, addend, Some(w -> RegisterVariable(Register.AY, w)), NoBranching)
|
||||
val fixedBase = ExpressionCompiler.compile(ctx, addend, Some(ExpressionCompiler.getExpressionType(ctx, addend) -> RegisterVariable(Register.AY, w)), NoBranching)
|
||||
fixedBase -> List(Nil, List(AssemblyLine.implied(TYA)))
|
||||
} else {
|
||||
base -> List(Nil, List(AssemblyLine.implied(TXA)))
|
||||
@ -1083,7 +1083,7 @@ object BuiltIns {
|
||||
val base = ExpressionCompiler.compile(ctx, param, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
|
||||
base -> List(Nil)
|
||||
case 2 =>
|
||||
val base = ExpressionCompiler.compile(ctx, param, Some(w -> RegisterVariable(Register.AX, w)), NoBranching)
|
||||
val base = ExpressionCompiler.compile(ctx, param, Some(ExpressionCompiler.getExpressionType(ctx, param) -> RegisterVariable(Register.AX, w)), NoBranching)
|
||||
base -> List(Nil, List(AssemblyLine.implied(TXA)))
|
||||
case _ => Nil -> (param match {
|
||||
case vv: VariableExpression =>
|
||||
@ -1190,7 +1190,7 @@ object BuiltIns {
|
||||
AssemblyLine.variable(ctx, STA, variable, i)
|
||||
} else if (variable.typ.isSigned) {
|
||||
val label = MfCompiler.nextLabel("sx")
|
||||
AssemblyLine.variable(ctx, STA, variable, i) ++ List(
|
||||
AssemblyLine.variable(ctx, STA, variable, variable.typ.size - 1) ++ List(
|
||||
AssemblyLine.immediate(ORA, 0x7F),
|
||||
AssemblyLine.relative(BMI, label),
|
||||
AssemblyLine.immediate(STA, 0),
|
||||
|
@ -219,11 +219,10 @@ object ExpressionCompiler {
|
||||
val w = ctx.env.get[Type]("word")
|
||||
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
ErrorReporting.error("16-bit indexing requires a zeropage pseudoregister")
|
||||
compile(ctx, indexExpression, Some(w -> RegisterVariable(Register.YA, w)), BranchSpec.None)
|
||||
return Nil
|
||||
}
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val compileIndex = compile(ctx, indexExpression, Some(w -> RegisterVariable(Register.YA, w)), BranchSpec.None)
|
||||
val compileIndex = compile(ctx, indexExpression, Some(ExpressionCompiler.getExpressionType(ctx, indexExpression) -> RegisterVariable(Register.YA, w)), BranchSpec.None)
|
||||
val prepareRegister = pointy match {
|
||||
case ConstantPointy(addr, _) =>
|
||||
List(
|
||||
@ -407,9 +406,20 @@ object ExpressionCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
def assertBool(ctx: CompilationContext, params: List[Expression], expectedParamCount: Int): Unit = {
|
||||
def assertBool(ctx: CompilationContext, fname: String, params: List[Expression], expectedParamCount: Int): Unit = {
|
||||
if (params.length != expectedParamCount) {
|
||||
ErrorReporting.error("Invalid number of parameters", params.headOption.flatMap(_.position))
|
||||
ErrorReporting.error("Invalid number of parameters for " + fname, params.headOption.flatMap(_.position))
|
||||
return
|
||||
}
|
||||
params.foreach { param =>
|
||||
if (!getExpressionType(ctx, param).isInstanceOf[BooleanType])
|
||||
ErrorReporting.fatal("Parameter should be boolean", param.position)
|
||||
}
|
||||
}
|
||||
|
||||
def assertBool(ctx: CompilationContext, fname: String, params: List[Expression]): Unit = {
|
||||
if (params.length < 2) {
|
||||
ErrorReporting.error("Invalid number of parameters for " + fname, params.headOption.flatMap(_.position))
|
||||
return
|
||||
}
|
||||
params.foreach { param =>
|
||||
@ -743,7 +753,11 @@ object ExpressionCompiler {
|
||||
case 1 =>
|
||||
val calculate = BuiltIns.compileAddition(ctx, params, decimal = decimal)
|
||||
val store = expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
calculate ++ store
|
||||
if (exprTypeAndVariable.exists(_._1.size >= 2)) {
|
||||
calculate ++ List(AssemblyLine.immediate(LDX, 0)) ++ store
|
||||
} else {
|
||||
calculate ++ store
|
||||
}
|
||||
case 2 =>
|
||||
val calculate = PseudoregisterBuiltIns.compileWordAdditionToAX(ctx, params, decimal = decimal)
|
||||
val store = expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
@ -793,11 +807,13 @@ object ExpressionCompiler {
|
||||
}
|
||||
|
||||
case f@FunctionCallExpression(name, params) =>
|
||||
val calculate = name match {
|
||||
var zeroExtend = false
|
||||
val calculate: List[AssemblyLine] = name match {
|
||||
case "not" =>
|
||||
assertBool(ctx, params, 1)
|
||||
assertBool(ctx, "not", params, 1)
|
||||
compile(ctx, params.head, exprTypeAndVariable, branches.flip)
|
||||
case "hi" | "lo" =>
|
||||
zeroExtend = true
|
||||
val hi = name == "hi"
|
||||
if (params.length != 1) {
|
||||
ErrorReporting.error("Too many parameters for hi/lo", f.position)
|
||||
@ -809,7 +825,7 @@ object ExpressionCompiler {
|
||||
ErrorReporting.error("Invalid parameter type for hi/lo", param.position)
|
||||
compile(ctx, param, None, BranchSpec.None)
|
||||
} else {
|
||||
val compilation = compile(ctx, param, Some(w -> RegisterVariable(Register.AX, w)), BranchSpec.None)
|
||||
val compilation = compile(ctx, param, Some(ExpressionCompiler.getExpressionType(ctx, param) -> RegisterVariable(Register.AX, w)), BranchSpec.None)
|
||||
if (hi) {
|
||||
if (typ.size == 2) compilation :+ AssemblyLine.implied(TXA)
|
||||
else if (typ.isSigned) compilation ++ signExtendA()
|
||||
@ -844,60 +860,65 @@ object ExpressionCompiler {
|
||||
)
|
||||
}
|
||||
case "&&" =>
|
||||
assertBool(ctx, params, 2)
|
||||
val a = params.head
|
||||
val b = params(1)
|
||||
assertBool(ctx, "&&", params)
|
||||
branches match {
|
||||
case BranchIfFalse(_) =>
|
||||
compile(ctx, a, exprTypeAndVariable, branches) ++ compile(ctx, b, exprTypeAndVariable, branches)
|
||||
params.flatMap(compile(ctx, _, exprTypeAndVariable, branches))
|
||||
case _ =>
|
||||
val skip = MfCompiler.nextLabel("an")
|
||||
compile(ctx, a, exprTypeAndVariable, BranchIfFalse(skip)) ++
|
||||
compile(ctx, b, exprTypeAndVariable, branches) ++
|
||||
params.init.flatMap(compile(ctx, _, exprTypeAndVariable, BranchIfFalse(skip))) ++
|
||||
compile(ctx, params.last, exprTypeAndVariable, branches) ++
|
||||
List(AssemblyLine.label(skip))
|
||||
}
|
||||
case "||" =>
|
||||
assertBool(ctx, params, 2)
|
||||
val a = params.head
|
||||
val b = params(1)
|
||||
assertBool(ctx, "||", params)
|
||||
branches match {
|
||||
case BranchIfTrue(_) =>
|
||||
compile(ctx, a, exprTypeAndVariable, branches) ++ compile(ctx, b, exprTypeAndVariable, branches)
|
||||
params.flatMap(compile(ctx, _, exprTypeAndVariable, branches))
|
||||
case _ =>
|
||||
val skip = MfCompiler.nextLabel("or")
|
||||
compile(ctx, a, exprTypeAndVariable, BranchIfTrue(skip)) ++
|
||||
compile(ctx, b, exprTypeAndVariable, branches) ++
|
||||
params.init.flatMap(compile(ctx, _, exprTypeAndVariable, BranchIfTrue(skip))) ++
|
||||
compile(ctx, params.last, exprTypeAndVariable, branches) ++
|
||||
List(AssemblyLine.label(skip))
|
||||
}
|
||||
case "^^" => ???
|
||||
case "&" =>
|
||||
getParamMaxSize(ctx, params) match {
|
||||
case 1 => BuiltIns.compileBitOps(AND, ctx, params)
|
||||
case 1 =>
|
||||
zeroExtend = true
|
||||
BuiltIns.compileBitOps(AND, ctx, params)
|
||||
case 2 => PseudoregisterBuiltIns.compileWordBitOpsToAX(ctx, params, AND)
|
||||
}
|
||||
case "*" =>
|
||||
zeroExtend = true
|
||||
assertAllBytes("Long multiplication not supported", ctx, params)
|
||||
BuiltIns.compileByteMultiplication(ctx, params)
|
||||
case "|" =>
|
||||
getParamMaxSize(ctx, params) match {
|
||||
case 1 => BuiltIns.compileBitOps(ORA, ctx, params)
|
||||
case 1 =>
|
||||
zeroExtend = true
|
||||
BuiltIns.compileBitOps(ORA, ctx, params)
|
||||
case 2 => PseudoregisterBuiltIns.compileWordBitOpsToAX(ctx, params, ORA)
|
||||
}
|
||||
case "^" =>
|
||||
getParamMaxSize(ctx, params) match {
|
||||
case 1 => BuiltIns.compileBitOps(EOR, ctx, params)
|
||||
case 1 =>
|
||||
zeroExtend = true
|
||||
BuiltIns.compileBitOps(EOR, ctx, params)
|
||||
case 2 => PseudoregisterBuiltIns.compileWordBitOpsToAX(ctx, params, EOR)
|
||||
}
|
||||
case ">>>>" =>
|
||||
val (l, r, 2) = assertBinary(ctx, params)
|
||||
l match {
|
||||
case v: LhsExpression =>
|
||||
zeroExtend = true
|
||||
BuiltIns.compileNonetOps(ctx, v, r)
|
||||
}
|
||||
case "<<" =>
|
||||
val (l, r, size) = assertBinary(ctx, params)
|
||||
size match {
|
||||
case 1 =>
|
||||
zeroExtend = true
|
||||
BuiltIns.compileShiftOps(ASL, ctx, l, r)
|
||||
case 2 =>
|
||||
PseudoregisterBuiltIns.compileWordShiftOps(left = true, ctx, l, r)
|
||||
@ -909,6 +930,7 @@ object ExpressionCompiler {
|
||||
val (l, r, size) = assertBinary(ctx, params)
|
||||
size match {
|
||||
case 1 =>
|
||||
zeroExtend = true
|
||||
BuiltIns.compileShiftOps(LSR, ctx, l, r)
|
||||
case 2 =>
|
||||
PseudoregisterBuiltIns.compileWordShiftOps(left = false, ctx, l, r)
|
||||
@ -917,10 +939,12 @@ object ExpressionCompiler {
|
||||
Nil
|
||||
}
|
||||
case "<<'" =>
|
||||
zeroExtend = true
|
||||
assertAllBytes("Long shift ops not supported", ctx, params)
|
||||
val (l, r, 1) = assertBinary(ctx, params)
|
||||
DecimalBuiltIns.compileByteShiftLeft(ctx, l, r, rotate = false)
|
||||
case ">>'" =>
|
||||
zeroExtend = true
|
||||
assertAllBytes("Long shift ops not supported", ctx, params)
|
||||
val (l, r, 1) = assertBinary(ctx, params)
|
||||
DecimalBuiltIns.compileByteShiftRight(ctx, l, r, rotate = false)
|
||||
@ -1198,8 +1222,12 @@ object ExpressionCompiler {
|
||||
result
|
||||
}
|
||||
}
|
||||
val store = expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
calculate ++ store
|
||||
val store: List[AssemblyLine] = expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
if (zeroExtend && exprTypeAndVariable.exists(_._1.size >= 2)) {
|
||||
calculate ++ List(AssemblyLine.immediate(LDX, 0)) ++ store
|
||||
} else {
|
||||
calculate ++ store
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,29 @@ import millfork.assembly.AddrMode._
|
||||
object PseudoregisterBuiltIns {
|
||||
|
||||
def compileWordAdditionToAX(ctx: CompilationContext, params: List[(Boolean, Expression)], decimal: Boolean): List[AssemblyLine] = {
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
val w = ctx.env.get[Type]("word")
|
||||
if (!decimal) {
|
||||
val (variablePart, constPart) = ctx.env.evalVariableAndConstantSubParts(SumExpression(params, decimal = false))
|
||||
variablePart match {
|
||||
case None =>
|
||||
return ExpressionCompiler.compileConstant(ctx, constPart, RegisterVariable(Register.AX, w))
|
||||
case Some(v) =>
|
||||
val typ = ExpressionCompiler.getExpressionType(ctx, v)
|
||||
if (typ.size == 1 && !typ.isSigned) {
|
||||
val bytePart = ExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(Register.A, b)), BranchSpec.None)
|
||||
val label = MfCompiler.nextLabel("ah")
|
||||
return bytePart ++ List(
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.immediate(ADC, constPart.loByte),
|
||||
AssemblyLine.immediate(LDX, constPart.hiByte),
|
||||
AssemblyLine.relative(BCC, label),
|
||||
AssemblyLine.implied(INX),
|
||||
AssemblyLine.label(label)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
ErrorReporting.error("Word addition or subtraction requires the zeropage pseudoregister", params.headOption.flatMap(_._2.position))
|
||||
return Nil
|
||||
@ -22,11 +45,9 @@ object PseudoregisterBuiltIns {
|
||||
if (params.isEmpty) {
|
||||
return List(AssemblyLine.immediate(LDA, 0), AssemblyLine.immediate(LDX, 0))
|
||||
}
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
val w = ctx.env.get[Type]("word")
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val head = params.head match {
|
||||
case (false, e) => ExpressionCompiler.compile(ctx, e, Some(w -> reg), BranchSpec.None)
|
||||
case (false, e) => ExpressionCompiler.compile(ctx, e, Some(ExpressionCompiler.getExpressionType(ctx, e) -> reg), BranchSpec.None)
|
||||
case (true, e) => ???
|
||||
}
|
||||
params.tail.foldLeft[List[AssemblyLine]](head){case (code, (sub, param)) => code ++ addToReg(ctx, param, sub, decimal)} ++ List(
|
||||
@ -44,7 +65,7 @@ object PseudoregisterBuiltIns {
|
||||
val w = ctx.env.get[Type]("word")
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
// TODO: smarter on 65816
|
||||
val compileRight = ExpressionCompiler.compile(ctx, r, Some(w -> reg), BranchSpec.None)
|
||||
val compileRight = ExpressionCompiler.compile(ctx, r, Some(ExpressionCompiler.getExpressionType(ctx, r) -> reg), BranchSpec.None)
|
||||
val op = if (subtract) SBC else ADC
|
||||
val prepareCarry = AssemblyLine.implied(if (subtract) SEC else CLC)
|
||||
compileRight match {
|
||||
@ -97,7 +118,7 @@ object PseudoregisterBuiltIns {
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
val w = ctx.env.get[Type]("word")
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val head = ExpressionCompiler.compile(ctx, params.head, Some(w -> reg), BranchSpec.None)
|
||||
val head = ExpressionCompiler.compile(ctx, params.head, Some(ExpressionCompiler.getExpressionType(ctx, params.head) -> reg), BranchSpec.None)
|
||||
params.tail.foldLeft[List[AssemblyLine]](head){case (code, param) => code ++ bitOpReg(ctx, param, op)} ++ List(
|
||||
AssemblyLine.zeropage(LDA, reg),
|
||||
AssemblyLine.zeropage(LDX, reg, 1),
|
||||
@ -113,7 +134,7 @@ object PseudoregisterBuiltIns {
|
||||
val w = ctx.env.get[Type]("word")
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
// TODO: smarter on 65816
|
||||
val compileRight = ExpressionCompiler.compile(ctx, r, Some(w -> reg), BranchSpec.None)
|
||||
val compileRight = ExpressionCompiler.compile(ctx, r, Some(ExpressionCompiler.getExpressionType(ctx, r) -> reg), BranchSpec.None)
|
||||
compileRight match {
|
||||
case List(
|
||||
AssemblyLine(LDA, Immediate, NumericConstant(0, _), _),
|
||||
@ -166,7 +187,7 @@ object PseudoregisterBuiltIns {
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
val w = ctx.env.get[Type]("word")
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val firstParamCompiled = ExpressionCompiler.compile(ctx, l, Some(w -> reg), NoBranching)
|
||||
val firstParamCompiled = ExpressionCompiler.compile(ctx, l, Some(ExpressionCompiler.getExpressionType(ctx, l) -> reg), NoBranching)
|
||||
ctx.env.eval(r) match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
Nil
|
||||
|
@ -149,6 +149,22 @@ class WordMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
}
|
||||
|
||||
test("Word addition 3") {
|
||||
EmuBenchmarkRun("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| byte c
|
||||
| c = b($82)
|
||||
| output = $482 + c
|
||||
| }
|
||||
| noinline byte b(byte b) {
|
||||
| return b
|
||||
| }
|
||||
""".stripMargin){ m =>
|
||||
m.readWord(0xc000) should equal(0x504)
|
||||
}
|
||||
}
|
||||
|
||||
test("Word bit ops 2") {
|
||||
EmuBenchmarkRun("""
|
||||
| word output @$c000
|
||||
|
Loading…
x
Reference in New Issue
Block a user