1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-11 15:29:34 +00:00

Fix sign extension when using pointers

This commit is contained in:
Karol Stasiak 2021-04-24 01:18:21 +02:00
parent 510f85960c
commit 1bcb6d5010
5 changed files with 182 additions and 75 deletions

View File

@ -1002,7 +1002,7 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
else if (i == 1) List(
MLine.tfr(M6809Register.A, M6809Register.B),
MLine.inherent(SEX),
MLine.tfr(M6809Register.B, M6809Register.A)
MLine.tfr(M6809Register.A, M6809Register.B)
)
else List(MLine.tfr(M6809Register.A, M6809Register.B))
} else {

View File

@ -1029,7 +1029,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
if (target.typ.size == 1) {
AssemblyLine.variable(ctx, STA, target)
}
else if (target.typ.isSigned) {
else if (pointy.elementType.isSigned) {
AssemblyLine.variable(ctx, STA, target) ++ signExtendA(ctx) ++
List.tabulate(target.typ.size - 1)(i => AssemblyLine.variable(ctx, STA, target, i + 1)).flatten
} else {
@ -1119,10 +1119,30 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
Nil
}
register match {
case MosRegister.A | MosRegister.X | MosRegister.Y => result ++ suffix
case MosRegister.AX | MosRegister.YX => result :+ AssemblyLine.immediate(LDX, 0) // TODO: signedness?
case MosRegister.AY | MosRegister.XY => result :+ AssemblyLine.immediate(LDY, 0)
case MosRegister.AW => result ++ List(AssemblyLine.implied(XBA), AssemblyLine.immediate(LDA, 0), AssemblyLine.implied(XBA))
case MosRegister.A | MosRegister.X | MosRegister.Y =>
result ++ suffix
case MosRegister.AX | MosRegister.YX if !pointy.elementType.isSigned =>
result :+ AssemblyLine.immediate(LDX, 0)
case MosRegister.AY | MosRegister.XY if !pointy.elementType.isSigned =>
result :+ AssemblyLine.immediate(LDY, 0)
case MosRegister.XA | MosRegister.YA if !pointy.elementType.isSigned =>
result :+ AssemblyLine.immediate(LDA, 0)
case MosRegister.AX =>
result ++ List(AssemblyLine.implied(PHA)) ++ signExtendA(ctx) ++ List(AssemblyLine.implied(TAX), AssemblyLine.implied(PLA))
case MosRegister.AY =>
result ++ List(AssemblyLine.implied(PHA)) ++ signExtendA(ctx) ++ List(AssemblyLine.implied(TAY), AssemblyLine.implied(PLA))
case MosRegister.XY =>
result ++ List(AssemblyLine.implied(TAX)) ++ signExtendA(ctx) ++ List(AssemblyLine.implied(TAY))
case MosRegister.YX =>
result ++ List(AssemblyLine.implied(TAY)) ++ signExtendA(ctx) ++ List(AssemblyLine.implied(TAX))
case MosRegister.XA =>
result ++ List(AssemblyLine.implied(TAX)) ++ signExtendA(ctx)
case MosRegister.YA =>
result ++ List(AssemblyLine.implied(TAY)) ++ signExtendA(ctx)
case MosRegister.AW if !pointy.elementType.isSigned =>
result ++ List(AssemblyLine.implied(XBA), AssemblyLine.immediate(LDA, 0), AssemblyLine.implied(XBA))
case MosRegister.AW =>
result ++ List(AssemblyLine.implied(PHA), AssemblyLine.implied(XBA), AssemblyLine.implied(PLA)) ++ signExtendA(ctx) ++ List(AssemblyLine.implied(XBA))
}
}
case DerefExpression(inner, offset, targetType) =>
@ -1168,7 +1188,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
case Some((variableType, variable)) =>
prepare ++ (0 until variableType.size).flatMap { i =>
val load =
if (i >= targetType.size) List(AssemblyLine.immediate(LDA, 0))
if (i == targetType.size) signExtendA(ctx)
else if (i > targetType.size) Nil
else if (am == AbsoluteY) List(AssemblyLine.absolute(LDA, addr + offset + i))
else if (i == 0) List(AssemblyLine.immediate(LDY, offset), AssemblyLine(LDA, am, addr))
else List(AssemblyLine.implied(INY), AssemblyLine(LDA, am, addr))
@ -2249,7 +2270,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
AssemblyLine(STA, am, addr))
case (_, AbsoluteY) =>
prepare ++ (0 until targetType.size).flatMap { i =>
val load = if (i >= sourceType.size) List(AssemblyLine.immediate(LDA, 0)) else AssemblyLine.variable(ctx, LDA, variable, i)
val load = loadSingleByteAssumingAPreserved(ctx, sourceType, variable, i)
load ++ List(
AssemblyLine.absolute(STA, addr + offset + i))
}
@ -2257,14 +2278,14 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
fastTarget match {
case Some((constAddr, fastAddrMode, initializeY)) =>
initializeY(offset) ++ (0 until targetType.size).flatMap { i =>
val load = if (i >= sourceType.size) List(AssemblyLine.immediate(LDA, 0)) else AssemblyLine.variable(ctx, LDA, variable, i)
val load = loadSingleByteAssumingAPreserved(ctx, sourceType, variable, i)
load ++ (if (i == 0) List(AssemblyLine(STA, fastAddrMode, constAddr)) else List(
AssemblyLine.implied(INY),
AssemblyLine(STA, fastAddrMode, constAddr)))
}
case _ =>
prepare ++ (0 until targetType.size).flatMap { i =>
val load = if (i >= sourceType.size) List(AssemblyLine.immediate(LDA, 0)) else AssemblyLine.variable(ctx, LDA, variable, i)
val load = loadSingleByteAssumingAPreserved(ctx, sourceType, variable, i)
load ++ List(
if (i == 0) AssemblyLine.immediate(LDY, offset) else AssemblyLine.implied(INY),
AssemblyLine(STA, am, addr))
@ -2279,32 +2300,17 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
(am, amSource) match {
case (AbsoluteY, AbsoluteY) =>
prepare ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
if (i >= sourceType.size) List(
AssemblyLine.immediate(LDA, 0),
AssemblyLine.absolute(STA, addr + offset + i))
else List(
AssemblyLine.absolute(LDA, addrSource + sourceOffset + i),
AssemblyLine.absolute(STA, addr + offset + i))
loadSingleByteAssumingAPreserved(ctx, sourceType, addrSource + sourceOffset, i) :+ AssemblyLine.absolute(STA, addr + offset + i)
}
case (AbsoluteY, _) =>
prepare ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
if (i >= sourceType.size) List(
AssemblyLine.immediate(LDA, 0),
AssemblyLine.absolute(STA, addr + offset + i))
else List(
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
AssemblyLine(LDA, amSource, addrSource),
AssemblyLine.absolute(STA, addr + offset + i))
loadSingleByteIndexedCountingYAssumingAPreserved(ctx, sourceType, amSource, addrSource, sourceOffset, i) :+
AssemblyLine.absolute(STA, addr + offset + i)
}
case (_, AbsoluteY) =>
prepare ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
if (i >= sourceType.size) List(
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
AssemblyLine.immediate(LDA, 0),
AssemblyLine(STA, am, addr))
else List(
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
AssemblyLine.absolute(LDA, addrSource + sourceOffset + i),
(if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY)) ::
(loadSingleByteAssumingAPreserved(ctx, sourceType, addrSource + sourceOffset, i) :+
AssemblyLine(STA, am, addr))
}
case (IndexedY, IndexedY) =>
@ -2320,13 +2326,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
AssemblyLine.zeropage(STA, reg, 2),
AssemblyLine.zeropage(LDA, reg, 1),
AssemblyLine.zeropage(STA, reg, 3)) ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
if (i >= sourceType.size) List(
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
AssemblyLine.immediate(LDA, 0),
AssemblyLine.indexedY(STA, reg, 2))
else List(
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
AssemblyLine.indexedY(LDA, reg),
(if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY)) ::
(loadSingleByteIndexedAssumingAPreserved(ctx, sourceType, IndexedY, reg.toAddress, i) :+
AssemblyLine.indexedY(STA, reg, 2))
}
case (false, true) =>
@ -2335,13 +2336,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
AssemblyLine.zeropage(STA, reg, 2),
AssemblyLine.zeropage(LDA, reg, 1),
AssemblyLine.zeropage(STA, reg, 3)) ++ prepare ++ (0 until targetType.size).flatMap { i =>
if (i >= sourceType.size) List(
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
AssemblyLine.immediate(LDA, 0),
AssemblyLine.indexedY(STA, reg))
else List(
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
AssemblyLine.indexedY(LDA, reg, 2),
(if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY)) ::
(loadSingleByteIndexedAssumingAPreserved(ctx, sourceType, IndexedY, reg.toAddress + 2, i) :+
AssemblyLine.indexedY(STA, reg))
}
case _ =>
@ -2354,25 +2350,15 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
AssemblyLine.zeropage(STA, reg, 2),
AssemblyLine.implied(PLA),
AssemblyLine.zeropage(STA, reg, 3)) ++ (0 until targetType.size).flatMap { i =>
if (i >= sourceType.size) List(
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
AssemblyLine.immediate(LDA, 0),
AssemblyLine.indexedY(STA, reg, 2))
else List(
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
AssemblyLine.indexedY(LDA, reg),
(if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY)) ::
(loadSingleByteIndexedAssumingAPreserved(ctx, sourceType, IndexedY, reg.toAddress, i) :+
AssemblyLine.indexedY(STA, reg, 2))
}
}
case (MemoryAddressConstant(thT: MemoryVariable), MemoryAddressConstant(thS: MemoryVariable)) if thT.name != thS.name =>
prepare ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
if (i >= sourceType.size) List(
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
AssemblyLine.immediate(LDA, 0),
AssemblyLine.indexedY(STA, thT))
else List(
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
AssemblyLine.indexedY(LDA, thS),
(if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY)) ::
(loadSingleByteIndexedAssumingAPreserved(ctx, sourceType, IndexedY, thS.toAddress, i) :+
AssemblyLine.indexedY(STA, thT))
}
case _ =>
@ -2450,6 +2436,34 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
}
}
private def loadSingleByteAssumingAPreserved(ctx: CompilationContext, sourceType: Type, variable: Variable, i: Int) = {
if (i == sourceType.size) {
if (sourceType.isSigned) signExtendA(ctx) else List(AssemblyLine.immediate(LDA, 0))
} else if (i > sourceType.size) Nil
else AssemblyLine.variable(ctx, LDA, variable, i)
}
private def loadSingleByteAssumingAPreserved(ctx: CompilationContext, sourceType: Type, varAddr: Constant, i: Int) = {
if (i == sourceType.size) {
if (sourceType.isSigned) signExtendA(ctx) else List(AssemblyLine.immediate(LDA, 0))
} else if (i > sourceType.size) Nil
else List(AssemblyLine.absolute(LDA, varAddr + i))
}
private def loadSingleByteIndexedAssumingAPreserved(ctx: CompilationContext, sourceType: Type, addrMode: AddrMode.Value, baseAddr: Constant, i: Int) = {
if (i == sourceType.size) {
if (sourceType.isSigned) signExtendA(ctx) else List(AssemblyLine.immediate(LDA, 0))
} else if (i > sourceType.size) Nil
else List(AssemblyLine(LDA, addrMode, baseAddr))
}
private def loadSingleByteIndexedCountingYAssumingAPreserved(ctx: CompilationContext, sourceType: Type, addrMode: AddrMode.Value, baseAddr: Constant, sourceOffset:Int, i: Int) = {
if (i == sourceType.size) {
if (sourceType.isSigned) signExtendA(ctx) else List(AssemblyLine.immediate(LDA, 0))
} else if (i > sourceType.size) Nil
else List(if(i==0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY), AssemblyLine(LDA, addrMode, baseAddr))
}
def arrayBoundsCheck(ctx: CompilationContext, pointy: Pointy, register: MosRegister.Value, index: Expression): List[AssemblyLine] = {
if (!ctx.options.flags(CompilationFlag.CheckIndexOutOfBounds)) return Nil
val arrayLength:Int = pointy match {

View File

@ -1505,31 +1505,28 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
case ZExpressionTarget.HL =>
if (signExtend) {
List(ldAbs8(A, sourceAddr, elidability), ld8(L, A)) ++
signExtendHighestByte(ctx, A, signExtend) ++
List(ld8(H, A))
signExtendHighestByte(ctx, A, H, signedSource = signExtend)
} else {
List(ldAbs8(A, sourceAddr, elidability), ld8(L, A), ldImm8(H, 0))
}
case ZExpressionTarget.BC =>
if (signExtend) {
List(ldAbs8(A, sourceAddr, elidability), ld8(C, A)) ++
signExtendHighestByte(ctx, A, signExtend) ++
List(ld8(B, A))
signExtendHighestByte(ctx, A, B, signedSource = signExtend)
} 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))
signExtendHighestByte(ctx, A, D, signedSource = signExtend)
} 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) ++
signExtendHighestByte(ctx, A, A, signedSource = signExtend) ++
List(ld8(H, A), ld8(E, A))
} else {
List(ldAbs8(ZRegister.A, sourceAddr, elidability), ld8(L, A), ldImm8(H, 0), ldImm8(E, 0))
@ -1537,7 +1534,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
case ZExpressionTarget.DEHL =>
if (signExtend) {
List(ldAbs8(A, sourceAddr, elidability), ld8(L, A)) ++
signExtendHighestByte(ctx, A, signExtend) ++
signExtendHighestByte(ctx, A, 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))
@ -1593,13 +1590,13 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
prepareA ++ fillUpperBytes
}
private def signExtendHighestByte(ctx: CompilationContext, hiRegister: ZRegister.Value, signedSource: Boolean = true): List[ZLine] = {
private def signExtendHighestByte(ctx: CompilationContext, sourceHiRegister: ZRegister.Value, targetHiRegister: ZRegister.Value = ZRegister.A, signedSource: Boolean = true): List[ZLine] = {
if (!signedSource) {
return List(ZLine.ldImm8(ZRegister.A, 0))
return List(ZLine.ldImm8(targetHiRegister, 0))
}
val prefix = if (hiRegister == ZRegister.A) Nil else List(ZLine.ld8(ZRegister.A, hiRegister))
val prefix = if (sourceHiRegister == ZRegister.A) Nil else List(ZLine.ld8(ZRegister.A, sourceHiRegister))
val label = ctx.nextLabel("sx")
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
val resultInA = if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
prefix ++ List(
ZLine.imm8(OR, 0x7f),
ZLine.jump(label, IfFlagSet(ZFlag.S)),
@ -1616,6 +1613,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
} else {
throw new IllegalStateException()
}
if (targetHiRegister == ZRegister.A) resultInA else resultInA :+ ZLine.ld8(targetHiRegister, ZRegister.A)
}
def signExtendViaIX(ctx: CompilationContext, targetOffset: Int, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
@ -1874,10 +1872,11 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
val lo = stashAFIfChanged(ctx, compileDerefPointer(ctx, e)) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
if (targetType.size == 1) lo
else if (targetType.size == 2) {
lo ++ List(ZLine.register(INC_16, ZRegister.HL)) ++ signExtendHighestByte(ctx, ZRegister.MEM_HL)
lo
lo ++ List(ZLine.register(INC_16, ZRegister.HL)) ++
signExtendHighestByte(ctx, ZRegister.A, signedSource = signedSource) ++
List(ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
} else {
lo ++ signExtendHighestByte(ctx, ZRegister.A) ++ List.tabulate(targetType.size - 1)(_ => List(
lo ++ signExtendHighestByte(ctx, ZRegister.A, signedSource = signedSource) ++ List.tabulate(targetType.size - 1)(_ => List(
ZLine.register(INC_16, ZRegister.HL),
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
)).flatten
@ -2020,7 +2019,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
ZLine.ld8(MEM_HL, B),
ZLine.register(INC_16, HL),
ZLine.ld8(MEM_HL, E)) ++ (
if (targetType.size > 3) signExtendHighestByte(ctx, E, signedSource) ++ List.fill(targetType.size - 3)(List(ZLine.register(INC_16, HL), ZLine.ld8(MEM_HL, A))).flatten
if (targetType.size > 3) signExtendHighestByte(ctx, E, A, signedSource) ++ List.fill(targetType.size - 3)(List(ZLine.register(INC_16, HL), ZLine.ld8(MEM_HL, A))).flatten
else Nil)
}
@ -2054,7 +2053,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
ZLine.ld8(MEM_HL, E),
ZLine.register(INC_16, HL),
ZLine.ld8(MEM_HL, D)) ++ (
if (targetType.size > 4) signExtendHighestByte(ctx, D, signedSource) ++ List.fill(targetType.size - 4)(List(ZLine.register(INC_16, HL), ZLine.ld8(MEM_HL, A))).flatten
if (targetType.size > 4) signExtendHighestByte(ctx, D, A, signedSource) ++ List.fill(targetType.size - 4)(List(ZLine.register(INC_16, HL), ZLine.ld8(MEM_HL, A))).flatten
else Nil)
}

View File

@ -936,4 +936,30 @@ class AssemblyOptimizationSuite extends FunSuite with Matchers {
|}
|""".stripMargin)
}
test("Sign extension") {
EmuBenchmarkRun(
"""
|array(word) output [5]@$c000
|
|noinline void set(byte i, sbyte s) {
| word w
| output[i] = s
|}
|
|void main() {
| set(0, 0)
| set(1, -1)
| set(2, 1)
| set(3, 0)
| set(4, 10)
|}
|""".stripMargin) { m =>
m.readWord(0xc000) should equal(0)
m.readWord(0xc002) should equal(0xffff)
m.readWord(0xc004) should equal(1)
m.readWord(0xc006) should equal(0)
m.readWord(0xc008) should equal(10)
}
}
}

View File

@ -207,4 +207,72 @@ class SignExtensionSuite extends FunSuite with Matchers {
}
}
test("Derefs and sign extension") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)(
"""
|pointer.word op
|pointer.sbyte ip
|array(sbyte) input[1]
|array(word) output [4] @$c000
|word output2 @$c010
|
|noinline byte id(byte x) = x
|
|noinline void init() {
| input[0] = -2
| ip = input[0].pointer
| op = output[0].pointer
|}
|
|void main() {
| sbyte sb
| sb = -2
| init()
| op[id(0)] = sb
| output[id(1)] = sb
| op[id(2)] = ip[id(0)]
| output[id(3)] = ip[id(0)]
| output2 = ip[id(0)]
|}
|""".stripMargin){ m =>
m.readWord(0xc000) should equal(0xfffe)
m.readWord(0xc002) should equal(0xfffe)
m.readWord(0xc004) should equal(0xfffe)
m.readWord(0xc006) should equal(0xfffe)
m.readWord(0xc010) should equal(0xfffe)
}
}
test("Derefs and sign extension 2") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)(
"""
|pointer.long op
|pointer.sbyte ip
|array(sbyte) input[1]
|array(long) output [4] @$c000
|long output2 @$c010
|
|noinline byte id(byte x) = x
|
|noinline void init() {
| input[0] = -2
| ip = input[0].pointer
| op = output[0].pointer
|}
|
|void main() {
| sbyte sb
| sb = -2
| init()
| op[id(0)] = sb
| output[id(1)] = sb
| output2 = ip[id(0)]
|}
|""".stripMargin){ m =>
m.readLong(0xc000) should equal(-2)
m.readLong(0xc004) should equal(-2)
m.readLong(0xc010) should equal(-2)
}
}
}