1
0
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:
Karol Stasiak 2018-03-18 23:54:02 +01:00
parent 986252db5b
commit 9ff888c0d9
5 changed files with 103 additions and 37 deletions

View File

@ -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

View File

@ -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),

View File

@ -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
}
}
}

View File

@ -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

View File

@ -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