mirror of
https://github.com/KarolS/millfork.git
synced 2024-10-31 14:04:58 +00:00
Fix signed constants and word-sbyte subtraction
This commit is contained in:
parent
1347be51ae
commit
d38405f467
@ -24,7 +24,7 @@
|
||||
|
||||
* Added `vectrex`, `msx_br` and `koi7n2` text encodings.
|
||||
|
||||
* 6502: Fixed arithmetic promotion bugs for function return values.
|
||||
* Fixed arithmetic promotion bugs for signed values.
|
||||
|
||||
* Fixed parsing of `zp_bytes` in platform definitions.
|
||||
|
||||
|
@ -116,13 +116,7 @@ pointer source
|
||||
}
|
||||
else {
|
||||
// or just move it
|
||||
// FIXME: word-subbyte doesn't work yet
|
||||
if input_dy==$ff {
|
||||
demosp.ypos+=1
|
||||
}
|
||||
if input_dy==$01 {
|
||||
demosp.ypos-=1
|
||||
}
|
||||
demosp.ypos -= input_dy
|
||||
demosp.xpos += input_dx
|
||||
}
|
||||
}
|
||||
|
@ -1355,7 +1355,6 @@ object BuiltIns {
|
||||
}
|
||||
}
|
||||
}
|
||||
val label = ctx.nextLabel("de")
|
||||
return doDec(targetBytes)
|
||||
case Some(NumericConstant(-1, _)) if canUseIncDec && !subtract =>
|
||||
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
|
||||
@ -1378,7 +1377,9 @@ object BuiltIns {
|
||||
return doDec(targetBytes)
|
||||
case Some(constant) =>
|
||||
addendSize = targetSize
|
||||
Nil -> List.tabulate(targetSize)(i => List(AssemblyLine.immediate(LDA, constant.subbyte(i))))
|
||||
Nil -> List.tabulate(targetSize)(i => List(AssemblyLine.immediate(LDA,
|
||||
if (i >= addendType.size && constant.isProvablyNegative(addendType)) NumericConstant(-1, 1) else constant.subbyte(i)
|
||||
)))
|
||||
case None =>
|
||||
addendSize match {
|
||||
case 1 =>
|
||||
@ -1436,17 +1437,17 @@ object BuiltIns {
|
||||
case _ => addend match {
|
||||
case vv: VariableExpression =>
|
||||
val source = env.get[Variable](vv.name)
|
||||
Nil -> List.tabulate(addendSize)(i => AssemblyLine.variable(ctx, LDA, source, i))
|
||||
Nil -> List.tabulate(targetSize)(i => AssemblyLine.variable(ctx, LDA, source, i))
|
||||
case f: FunctionCallExpression =>
|
||||
val jsr = MosExpressionCompiler.compile(ctx, addend, None, BranchSpec.None)
|
||||
val result = ctx.env.get[VariableInMemory](f.functionName + ".return")
|
||||
jsr -> List.tabulate(addendSize)(i => AssemblyLine.variable(ctx, LDA, result, i))
|
||||
jsr -> List.tabulate(targetSize)(i => AssemblyLine.variable(ctx, LDA, result, i))
|
||||
}
|
||||
}
|
||||
}
|
||||
val addendByteRead = addendByteRead0 ++ List.fill((targetSize - addendByteRead0.size) max 0)(List(AssemblyLine.immediate(LDA, 0)))
|
||||
|
||||
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes) && !addendType.isSigned) {
|
||||
(removeTsx(targetBytes), calculateRhs, removeTsx(addendByteRead)) match {
|
||||
case (
|
||||
List(List(AssemblyLine0(STA, ta1, tl)), List(AssemblyLine0(STA, ta2, th))),
|
||||
@ -1520,12 +1521,32 @@ object BuiltIns {
|
||||
}
|
||||
buffer ++= targetBytes(i)
|
||||
} else if (subtract) {
|
||||
if (addendSize < targetSize && addendType.isSigned) {
|
||||
// TODO: sign extension
|
||||
???
|
||||
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)))
|
||||
}
|
||||
}
|
||||
buffer ++= staTo(LDA, targetBytes(i))
|
||||
buffer ++= wrapInSedCldIfNeeded(decimal, ldTo(SBC, addendByteRead(i)))
|
||||
buffer ++= targetBytes(i)
|
||||
} else {
|
||||
if (i >= addendSize) {
|
||||
|
@ -16,7 +16,7 @@ import millfork.output.NoAlignment
|
||||
*/
|
||||
object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
|
||||
def compileConstant(ctx: CompilationContext, expr: Constant, target: Variable): List[AssemblyLine] = {
|
||||
def compileConstant(ctx: CompilationContext, expr: Constant, exprType: Type, target: Variable): List[AssemblyLine] = {
|
||||
target match {
|
||||
case RegisterVariable(MosRegister.A, _) => List(AssemblyLine(LDA, Immediate, expr))
|
||||
case RegisterVariable(MosRegister.AW, _) =>
|
||||
@ -617,7 +617,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case Some(value) =>
|
||||
return exprTypeAndVariable.fold(noop) { case (exprType, target) =>
|
||||
assertCompatible(exprType, target.typ)
|
||||
compileConstant(ctx, value, target)
|
||||
compileConstant(ctx, value, exprType, target)
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
@ -627,17 +627,17 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case LiteralExpression(value, size) =>
|
||||
exprTypeAndVariable.fold(noop) { case (exprType, target) =>
|
||||
assertCompatible(exprType, target.typ)
|
||||
compileConstant(ctx, NumericConstant(value, size), target)
|
||||
compileConstant(ctx, NumericConstant(value, size), exprType, target)
|
||||
}
|
||||
case GeneratedConstantExpression(value, _) =>
|
||||
exprTypeAndVariable.fold(noop) { case (exprType, target) =>
|
||||
assertCompatible(exprType, target.typ)
|
||||
compileConstant(ctx, value, target)
|
||||
compileConstant(ctx, value, exprType, target)
|
||||
}
|
||||
case VariableExpression(name) =>
|
||||
exprTypeAndVariable.fold(noop) { case (exprType, target) =>
|
||||
assertCompatible(exprType, target.typ)
|
||||
env.eval(expr).map(c => compileConstant(ctx, c, target)).getOrElse {
|
||||
env.eval(expr).map(c => compileConstant(ctx, c, exprType, target)).getOrElse {
|
||||
env.get[TypedThing](name) match {
|
||||
case source: StackOffsetThing => compileStackOffset(ctx, target, source.offset, source.subbyte)
|
||||
case source: VariableInMemory =>
|
||||
@ -902,7 +902,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
}
|
||||
}
|
||||
case source@ConstantThing(_, value, _) =>
|
||||
compileConstant(ctx, value, target)
|
||||
compileConstant(ctx, value, exprType, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1080,7 +1080,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
if (neg) MathOperator.Minus else MathOperator.Plus
|
||||
}, c, v).quickSimplify
|
||||
}
|
||||
exprTypeAndVariable.map(x => compileConstant(ctx, value.quickSimplify, x._2)).getOrElse(Nil)
|
||||
exprTypeAndVariable.map(x => compileConstant(ctx, value.quickSimplify, exprType, x._2)).getOrElse(Nil)
|
||||
} else {
|
||||
getSumSize(ctx, params) match {
|
||||
case 1 =>
|
||||
@ -1244,7 +1244,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case Some(c) =>
|
||||
exprTypeAndVariable match {
|
||||
case Some((t, v)) =>
|
||||
compileConstant(ctx, c, v)
|
||||
compileConstant(ctx, c, w, v)
|
||||
case _ =>
|
||||
Nil
|
||||
}
|
||||
@ -1259,7 +1259,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case Some(c) =>
|
||||
exprTypeAndVariable match {
|
||||
case Some((t, v)) =>
|
||||
compileConstant(ctx, c, v)
|
||||
compileConstant(ctx, c, w, v)
|
||||
case _ =>
|
||||
Nil
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ object PseudoregisterBuiltIns {
|
||||
val result = ListBuffer[AssemblyLine]()
|
||||
var hard = Option.empty[List[AssemblyLine]]
|
||||
val niceReads = mutable.ListBuffer[(List[AssemblyLine], List[AssemblyLine])]()
|
||||
var constant = Constant.Zero
|
||||
var constant: Constant = NumericConstant(0, 2)
|
||||
var counter = 0
|
||||
for ((subtract, read) <- reads) {
|
||||
read match {
|
||||
@ -99,7 +99,7 @@ object PseudoregisterBuiltIns {
|
||||
val (variablePart, constPart) = ctx.env.evalVariableAndConstantSubParts(SumExpression(params, decimal = false))
|
||||
variablePart match {
|
||||
case None =>
|
||||
return MosExpressionCompiler.compileConstant(ctx, constPart, RegisterVariable(MosRegister.AX, w))
|
||||
return MosExpressionCompiler.compileConstant(ctx, constPart, w, RegisterVariable(MosRegister.AX, w))
|
||||
case Some(v) =>
|
||||
val typ = MosExpressionCompiler.getExpressionType(ctx, v)
|
||||
if (typ.size == 1 && !typ.isSigned) {
|
||||
@ -146,7 +146,7 @@ object PseudoregisterBuiltIns {
|
||||
val (variablePart, constPart) = ctx.env.evalVariableAndConstantSubParts(SumExpression(params, decimal = false))
|
||||
variablePart match {
|
||||
case None =>
|
||||
return MosExpressionCompiler.compileConstant(ctx, constPart, RegisterVariable(MosRegister.AW, w))
|
||||
return MosExpressionCompiler.compileConstant(ctx, constPart, w, RegisterVariable(MosRegister.AW, w))
|
||||
case Some(v) =>
|
||||
}
|
||||
}
|
||||
|
@ -354,7 +354,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
import ZRegister._
|
||||
v.typ.size match {
|
||||
case 0 => ???
|
||||
case 1 => loadByte(v.toAddress, target, v.isVolatile)
|
||||
case 1 => loadByte(ctx, v.toAddress, target, v.isVolatile, v.typ.isSigned)
|
||||
case 2 => target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.HL =>
|
||||
@ -548,7 +548,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
case i: IndexedExpression =>
|
||||
calculateAddressToHL(ctx, i, forWriting = false) match {
|
||||
case List(ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr)) => loadByte(addr, target, volatile = false)
|
||||
case List(ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr)) => loadByte(ctx, addr, target, volatile = false, signExtend = false)
|
||||
case code => code ++ loadByteViaHL(target)
|
||||
}
|
||||
case SumExpression(params, decimal) =>
|
||||
@ -1447,16 +1447,53 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def loadByte(sourceAddr: Constant, target: ZExpressionTarget.Value, volatile: Boolean): List[ZLine] = {
|
||||
def loadByte(ctx: CompilationContext, sourceAddr: Constant, target: ZExpressionTarget.Value, volatile: Boolean, signExtend: Boolean): List[ZLine] = {
|
||||
import ZRegister._
|
||||
import ZLine.{ld8, ldImm8, ldAbs8, ldImm16}
|
||||
val elidability = if (volatile) Elidability.Volatile else Elidability.Elidable
|
||||
target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.A => List(ZLine.ldAbs8(ZRegister.A, sourceAddr, elidability))
|
||||
case ZExpressionTarget.HL => List(ZLine.ldAbs8(ZRegister.A, sourceAddr, elidability), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0))
|
||||
case ZExpressionTarget.BC => List(ZLine.ldAbs8(ZRegister.A, sourceAddr, elidability), ZLine.ld8(ZRegister.C, ZRegister.A), ZLine.ldImm8(ZRegister.B, 0))
|
||||
case ZExpressionTarget.DE => List(ZLine.ldAbs8(ZRegister.A, sourceAddr, elidability), ZLine.ld8(ZRegister.E, ZRegister.A), ZLine.ldImm8(ZRegister.D, 0))
|
||||
case ZExpressionTarget.EHL => List(ZLine.ldAbs8(ZRegister.A, sourceAddr, elidability), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0), ZLine.ldImm8(ZRegister.E, 0))
|
||||
case ZExpressionTarget.DEHL => List(ZLine.ldAbs8(ZRegister.A, sourceAddr, elidability), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0), ZLine.ldImm16(ZRegister.DE, 0))
|
||||
case ZExpressionTarget.HL =>
|
||||
if (signExtend) {
|
||||
List(ldAbs8(A, sourceAddr, elidability), ld8(L, A)) ++
|
||||
signExtendHighestByte(ctx, A, signExtend) ++
|
||||
List(ld8(H, A))
|
||||
} else {
|
||||
List(ldAbs8(A, sourceAddr, elidability), ld8(L, A), ldImm8(H, 0))
|
||||
}
|
||||
case ZExpressionTarget.BC =>
|
||||
if (signExtend) {
|
||||
List(ldAbs8(A, sourceAddr, elidability), ld8(L, A)) ++
|
||||
signExtendHighestByte(ctx, A, signExtend) ++
|
||||
List(ld8(H, A))
|
||||
} else {
|
||||
List(ldAbs8(A, sourceAddr, elidability), ld8(C, A), ldImm8(B, 0))
|
||||
}
|
||||
case ZExpressionTarget.DE =>
|
||||
if (signExtend) {
|
||||
List(ldAbs8(A, sourceAddr, elidability), ld8(E, A)) ++
|
||||
signExtendHighestByte(ctx, A, signExtend) ++
|
||||
List(ld8(D, A))
|
||||
} else {
|
||||
List(ldAbs8(A, sourceAddr, elidability), ld8(E, A), ldImm8(D, 0))
|
||||
}
|
||||
case ZExpressionTarget.EHL =>
|
||||
if (signExtend) {
|
||||
List(ldAbs8(A, sourceAddr, elidability), ld8(L, A)) ++
|
||||
signExtendHighestByte(ctx, A, signExtend) ++
|
||||
List(ld8(H, A), ld8(E, A))
|
||||
} else {
|
||||
List(ldAbs8(ZRegister.A, sourceAddr, elidability), ld8(L, A), ldImm8(H, 0), ldImm8(E, 0))
|
||||
}
|
||||
case ZExpressionTarget.DEHL =>
|
||||
if (signExtend) {
|
||||
List(ldAbs8(A, sourceAddr, elidability), ld8(L, A)) ++
|
||||
signExtendHighestByte(ctx, A, signExtend) ++
|
||||
List(ld8(H, A), ld8(E, A), ld8(D, A))
|
||||
} else {
|
||||
List(ldAbs8(A, sourceAddr, elidability), ld8(L, A), ldImm8(H, 0), ldImm16(DE, 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
29
src/main/scala/millfork/env/Constant.scala
vendored
29
src/main/scala/millfork/env/Constant.scala
vendored
@ -25,6 +25,7 @@ sealed trait Constant {
|
||||
def isProvably(value: Int): Boolean = false
|
||||
def isProvablyInRange(startInclusive: Int, endInclusive: Int): Boolean = false
|
||||
def isProvablyNonnegative: Boolean = false
|
||||
def isProvablyNegative(asType: Type): Boolean = false
|
||||
final def isProvablyGreaterOrEqualThan(other: Int): Boolean = isProvablyGreaterOrEqualThan(Constant(other))
|
||||
def isProvablyGreaterOrEqualThan(other: Constant): Boolean = other match {
|
||||
case NumericConstant(0, _) => true
|
||||
@ -114,6 +115,15 @@ sealed trait Constant {
|
||||
|
||||
def fitsInto(typ: Type): Boolean = true // TODO
|
||||
|
||||
def fitInto(typ: Type): Constant = {
|
||||
// TODO:
|
||||
typ.size match {
|
||||
case 1 => loByte
|
||||
case 2 => subword(0)
|
||||
case _ => this
|
||||
}
|
||||
}
|
||||
|
||||
final def succ: Constant = (this + 1).quickSimplify
|
||||
}
|
||||
|
||||
@ -123,6 +133,7 @@ case class AssertByte(c: Constant) extends Constant {
|
||||
override def isProvablyZero: Boolean = c.isProvablyZero
|
||||
override def isProvably(i: Int): Boolean = c.isProvably(i)
|
||||
override def isProvablyNonnegative: Boolean = c.isProvablyNonnegative
|
||||
override def isProvablyNegative(asType: Type): Boolean = c.isProvablyNegative(asType)
|
||||
override def isProvablyInRange(startInclusive: Int, endInclusive: Int): Boolean = c.isProvablyInRange(startInclusive, endInclusive)
|
||||
override def fitsProvablyIntoByte: Boolean = true
|
||||
|
||||
@ -204,6 +215,11 @@ case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
|
||||
override def isProvablyZero: Boolean = value == 0
|
||||
override def isProvably(i: Int): Boolean = value == i
|
||||
override def isProvablyNonnegative: Boolean = value >= 0
|
||||
override def isProvablyNegative(asType: Type): Boolean = {
|
||||
if (!asType.isSigned) return false
|
||||
if (asType.size >= 8) return value < 0
|
||||
value.&(0x1L.<<(8 * asType.size - 1)) != 0
|
||||
}
|
||||
override def fitsProvablyIntoByte: Boolean = requiredSize == 1
|
||||
override def isProvablyDivisibleBy256: Boolean = (value & 0xff) == 0
|
||||
|
||||
@ -251,6 +267,19 @@ case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def fitInto(typ: Type): Constant = {
|
||||
if (typ.size >= 8) {
|
||||
return NumericConstant(value, typ.size)
|
||||
}
|
||||
val actualBits = 1L.<<(8 * typ.size).-(1).&(value)
|
||||
if (isProvablyNegative(typ)) {
|
||||
val sx = (-1L).<<(8 * typ.size)
|
||||
NumericConstant(sx | actualBits, typ.size)
|
||||
} else {
|
||||
NumericConstant(actualBits, typ.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class MemoryAddressConstant(var thing: ThingInMemory) extends Constant {
|
||||
|
45
src/main/scala/millfork/env/Environment.scala
vendored
45
src/main/scala/millfork/env/Environment.scala
vendored
@ -573,7 +573,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
None -> Constant.Zero
|
||||
case Some(t: Type) =>
|
||||
if (t.isSigned) Some(e) -> Constant.Zero
|
||||
else variable -> constant
|
||||
else variable -> constant.fitInto(t)
|
||||
case _ =>
|
||||
// dunno what to do
|
||||
Some(e) -> Constant.Zero
|
||||
@ -768,18 +768,17 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
maybeGet[Type](name) match {
|
||||
case Some(t: StructType) =>
|
||||
if (params.size == t.fields.size) {
|
||||
sequence(params.map(eval)).map(fields => StructureConstant(t, fields))
|
||||
sequence(params.map(eval)).map(fields => StructureConstant(t, fields.zip(t.fields).map{
|
||||
case (fieldConst, fieldDesc) =>
|
||||
fieldConst.fitInto(get[Type](fieldDesc.typeName))
|
||||
}))
|
||||
} else None
|
||||
case Some(_: UnionType) =>
|
||||
None
|
||||
case Some(t) =>
|
||||
if (params.size == 1) {
|
||||
eval(params.head).map{ c =>
|
||||
(t.size, t.isSigned) match {
|
||||
case (1, false) => c.loByte
|
||||
case (2, false) => c.subword(0)
|
||||
case _ => c // TODO
|
||||
}
|
||||
c.fitInto(t)
|
||||
}
|
||||
} else None
|
||||
case _ => None
|
||||
@ -892,15 +891,15 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
value = eval(v).getOrElse(errorConstant(s"Enum constant `${stmt.name}.$name` is not a constant", stmt.position))
|
||||
case _ =>
|
||||
}
|
||||
addThing(ConstantThing(name, value, t), stmt.position)
|
||||
addThing(ConstantThing(name, value.fitInto(t), t), stmt.position)
|
||||
value += 1
|
||||
}
|
||||
}
|
||||
|
||||
def registerStruct(stmt: StructDefinitionStatement): Unit = {
|
||||
stmt.fields.foreach{ f =>
|
||||
if (Environment.invalidFieldNames.contains(f._2)) {
|
||||
log.error(s"Invalid field name: `${f._2}`", stmt.position)
|
||||
if (Environment.invalidFieldNames.contains(f.fieldName)) {
|
||||
log.error(s"Invalid field name: `${f.fieldName}`", stmt.position)
|
||||
}
|
||||
}
|
||||
addThing(StructType(stmt.name, stmt.fields), stmt.position)
|
||||
@ -908,8 +907,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
|
||||
def registerUnion(stmt: UnionDefinitionStatement): Unit = {
|
||||
stmt.fields.foreach{ f =>
|
||||
if (Environment.invalidFieldNames.contains(f._2)) {
|
||||
log.error(s"Invalid field name: `${f._2}`", stmt.position)
|
||||
if (Environment.invalidFieldNames.contains(f.fieldName)) {
|
||||
log.error(s"Invalid field name: `${f.fieldName}`", stmt.position)
|
||||
}
|
||||
}
|
||||
addThing(UnionType(stmt.name, stmt.fields), stmt.position)
|
||||
@ -924,7 +923,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
else {
|
||||
val newPath = path + name
|
||||
var sum = 0
|
||||
for( (fieldType, _) <- s.fields) {
|
||||
for( FieldDesc(fieldType, _) <- s.fields) {
|
||||
val fieldSize = getTypeSize(fieldType, newPath)
|
||||
if (fieldSize < 0) return -1
|
||||
sum += fieldSize
|
||||
@ -935,7 +934,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
}
|
||||
val b = get[Type]("byte")
|
||||
var offset = 0
|
||||
for( (fieldType, fieldName) <- s.fields) {
|
||||
for( FieldDesc(fieldType, fieldName) <- s.fields) {
|
||||
addThing(ConstantThing(s"$name.$fieldName.offset", NumericConstant(offset, 1), b), None)
|
||||
offset += getTypeSize(fieldType, newPath)
|
||||
}
|
||||
@ -946,7 +945,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
else {
|
||||
val newPath = path + name
|
||||
var max = 0
|
||||
for( (fieldType, _) <- s.fields) {
|
||||
for( FieldDesc(fieldType, _) <- s.fields) {
|
||||
val fieldSize = getTypeSize(fieldType, newPath)
|
||||
if (fieldSize < 0) return -1
|
||||
max = max max fieldSize
|
||||
@ -956,7 +955,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
log.error(s"Union `$name` is larger than 255 bytes")
|
||||
}
|
||||
val b = get[Type]("byte")
|
||||
for ((fieldType, fieldName) <- s.fields) {
|
||||
for (FieldDesc(fieldType, fieldName) <- s.fields) {
|
||||
addThing(ConstantThing(s"$name.$fieldName.offset", NumericConstant(0, 1), b), None)
|
||||
}
|
||||
max
|
||||
@ -1358,7 +1357,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
List.fill(tt.size)(LiteralExpression(0, 1))
|
||||
} else {
|
||||
tt.fields.zip(fieldValues).flatMap {
|
||||
case ((fieldTypeName, _), expr) => extractStructArrayContents(expr, Some(get[Type](fieldTypeName)))
|
||||
case (FieldDesc(fieldTypeName, _), expr) => extractStructArrayContents(expr, Some(get[Type](fieldTypeName)))
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
@ -1392,7 +1391,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
List.fill(tt.size)(LiteralExpression(0, 1))
|
||||
} else {
|
||||
tt.fields.zip(fieldValues).flatMap {
|
||||
case ((fieldTypeName, _), expr) => extractStructArrayContents(expr, Some(get[Type](fieldTypeName)))
|
||||
case (FieldDesc(fieldTypeName, _), expr) => extractStructArrayContents(expr, Some(get[Type](fieldTypeName)))
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
@ -1649,7 +1648,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (stmt.register) log.error(s"`$name` is a constant and cannot be in a register", position)
|
||||
if (stmt.address.isDefined) log.error(s"`$name` is a constant and cannot have an address", position)
|
||||
if (stmt.initialValue.isEmpty) log.error(s"`$name` is a constant and requires a value", position)
|
||||
val constantValue: Constant = stmt.initialValue.flatMap(eval).getOrElse(errorConstant(s"`$name` has a non-constant value", position))
|
||||
val constantValue: Constant = stmt.initialValue.flatMap(eval).getOrElse(errorConstant(s"`$name` has a non-constant value", position)).fitInto(typ)
|
||||
if (constantValue.requiredSize > typ.size) log.error(s"`$name` is has an invalid value: not in the range of `$typ`", position)
|
||||
addThing(ConstantThing(prefix + name, constantValue, typ), stmt.position)
|
||||
for((suffix, offset, t) <- getSubvariables(typ)) {
|
||||
@ -1839,7 +1838,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
case s: StructType =>
|
||||
val builder = new ListBuffer[(String, Int, VariableType)]
|
||||
var offset = 0
|
||||
for((typeName, fieldName) <- s.fields) {
|
||||
for(FieldDesc(typeName, fieldName) <- s.fields) {
|
||||
val typ = get[VariableType](typeName)
|
||||
val suffix = "." + fieldName
|
||||
builder += ((suffix, offset, typ))
|
||||
@ -1851,7 +1850,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
builder.toList
|
||||
case s: UnionType =>
|
||||
val builder = new ListBuffer[(String, Int, VariableType)]
|
||||
for((typeName, fieldName) <- s.fields) {
|
||||
for(FieldDesc(typeName, fieldName) <- s.fields) {
|
||||
val typ = get[VariableType](typeName)
|
||||
val suffix = "." + fieldName
|
||||
builder += ((suffix, 0, typ))
|
||||
@ -1937,11 +1936,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
things.values.foreach {
|
||||
case st@StructType(_, fields) =>
|
||||
st.mutableFieldsWithTypes = fields.map {
|
||||
case (tn, name) => get[Type](tn) -> name
|
||||
case FieldDesc(tn, name) => get[Type](tn) -> name
|
||||
}
|
||||
case ut@UnionType(_, fields) =>
|
||||
ut.mutableFieldsWithTypes = fields.map {
|
||||
case (tn, name) => get[Type](tn) -> name
|
||||
case FieldDesc(tn, name) => get[Type](tn) -> name
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
|
4
src/main/scala/millfork/env/Thing.scala
vendored
4
src/main/scala/millfork/env/Thing.scala
vendored
@ -112,14 +112,14 @@ case class EnumType(name: String, count: Option[Int]) extends VariableType {
|
||||
|
||||
sealed trait CompoundVariableType extends VariableType
|
||||
|
||||
case class StructType(name: String, fields: List[(String, String)]) extends CompoundVariableType {
|
||||
case class StructType(name: String, fields: List[FieldDesc]) extends CompoundVariableType {
|
||||
override def size: Int = mutableSize
|
||||
var mutableSize: Int = -1
|
||||
var mutableFieldsWithTypes: List[(Type, String)] = Nil
|
||||
override def isSigned: Boolean = false
|
||||
}
|
||||
|
||||
case class UnionType(name: String, fields: List[(String, String)]) extends CompoundVariableType {
|
||||
case class UnionType(name: String, fields: List[FieldDesc]) extends CompoundVariableType {
|
||||
override def size: Int = mutableSize
|
||||
var mutableSize: Int = -1
|
||||
var mutableFieldsWithTypes: List[(Type, String)] = Nil
|
||||
|
@ -10,6 +10,8 @@ import millfork.output.MemoryAlignment
|
||||
|
||||
case class Position(moduleName: String, line: Int, column: Int, cursor: Int)
|
||||
|
||||
case class FieldDesc(typeName:String, fieldName: String)
|
||||
|
||||
sealed trait Node {
|
||||
var position: Option[Position] = None
|
||||
}
|
||||
@ -455,11 +457,11 @@ case class EnumDefinitionStatement(name: String, variants: List[(String, Option[
|
||||
override def getAllExpressions: List[Expression] = variants.flatMap(_._2)
|
||||
}
|
||||
|
||||
case class StructDefinitionStatement(name: String, fields: List[(String, String)]) extends DeclarationStatement {
|
||||
case class StructDefinitionStatement(name: String, fields: List[FieldDesc]) extends DeclarationStatement {
|
||||
override def getAllExpressions: List[Expression] = Nil
|
||||
}
|
||||
|
||||
case class UnionDefinitionStatement(name: String, fields: List[(String, String)]) extends DeclarationStatement {
|
||||
case class UnionDefinitionStatement(name: String, fields: List[FieldDesc]) extends DeclarationStatement {
|
||||
override def getAllExpressions: List[Expression] = Nil
|
||||
}
|
||||
|
||||
|
@ -538,12 +538,12 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
variants <- enumVariants ~/ Pass
|
||||
} yield Seq(EnumDefinitionStatement(name, variants).pos(p))
|
||||
|
||||
val compoundTypeField: P[(String, String)] = for {
|
||||
val compoundTypeField: P[FieldDesc] = for {
|
||||
typ <- identifier ~/ HWS
|
||||
name <- identifier ~ HWS
|
||||
} yield typ -> name
|
||||
} yield FieldDesc(typ, name)
|
||||
|
||||
val compoundTypeFields: P[List[(String, String)]] =
|
||||
val compoundTypeFields: P[List[FieldDesc]] =
|
||||
("{" ~/ AWS ~ compoundTypeField.rep(sep = NoCut(EOLOrComma) ~ !"}" ~/ Pass) ~/ AWS ~/ "}" ~/ Pass).map(_.toList)
|
||||
|
||||
val structDefinition: P[Seq[StructDefinitionStatement]] = for {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.Cpu
|
||||
import millfork.env.{BasicPlainType, DerivedPlainType, NumericConstant}
|
||||
import millfork.test.emu.EmuUnoptimizedCrossPlatformRun
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
@ -21,4 +22,15 @@ class ConstantSuite extends FunSuite with Matchers {
|
||||
| }
|
||||
""".stripMargin){m => }
|
||||
}
|
||||
|
||||
test("Constants should be negative when needed") {
|
||||
def signed(size: Int) = DerivedPlainType("", BasicPlainType("", size), isSigned = true, isPointy = false)
|
||||
NumericConstant(0xff, 1).isProvablyNegative(signed(1)) should be(true)
|
||||
NumericConstant(0x7f, 1).isProvablyNegative(signed(1)) should be(false)
|
||||
NumericConstant(0xff, 2).isProvablyNegative(signed(2)) should be(false)
|
||||
NumericConstant(0xff0f, 2).isProvablyNegative(signed(1)) should be(false)
|
||||
NumericConstant(-0x4000, 8).isProvablyNegative(signed(1)) should be(false)
|
||||
NumericConstant(0x7f, 2).isProvablyNegative(signed(2)) should be(false)
|
||||
NumericConstant(-1, 8).isProvablyNegative(signed(8)) should be(true)
|
||||
}
|
||||
}
|
@ -700,4 +700,37 @@ class WordMathSuite extends FunSuite with Matchers with AppendedClues {
|
||||
m.readWord(0xc002) should equal((x % y) & 0xffff) withClue s"= $x %% $y (c002)"
|
||||
}
|
||||
}
|
||||
|
||||
test("Sign extension in subtraction") {
|
||||
for {
|
||||
i <- Seq(5324, 6453, 1500)
|
||||
j <- Seq(0, 1, -1, -3, -7, -128, 127)
|
||||
// i <- Seq(5324)
|
||||
// j <- Seq(-1)
|
||||
} {
|
||||
EmuUnoptimizedCrossPlatformRun(/*Cpu.Mos, */Cpu.Z80)(
|
||||
s"""
|
||||
| word output0 @$$c000
|
||||
| word output1 @$$c002
|
||||
| word output2 @$$c004
|
||||
| void main () {
|
||||
| sbyte tmp
|
||||
| output0 = $i
|
||||
| output2 = $i
|
||||
| tmp = $j
|
||||
| memory_barrier()
|
||||
| output1 = output0 - sbyte(${j&0xff})
|
||||
| memory_barrier()
|
||||
| output0 -= sbyte(${j&0xff})
|
||||
| output2 -= tmp
|
||||
| }
|
||||
| noinline word id(word w) = w
|
||||
""".
|
||||
stripMargin){m =>
|
||||
m.readWord(0xc000) should equal((i - j) & 0xffff) withClue s"= $i - $j (c000)"
|
||||
m.readWord(0xc002) should equal((i - j) & 0xffff) withClue s"= $i - $j (c002)"
|
||||
m.readWord(0xc004) should equal((i - j) & 0xffff) withClue s"= $i - $j (c004)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user