mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-01 06:29:53 +00:00
Allow taking addresses of stack variables
This commit is contained in:
parent
f22b62e57f
commit
0205520bf9
@ -30,6 +30,8 @@
|
||||
|
||||
* Added `nullptr`.
|
||||
|
||||
* You can now take a pointer to a stack variable.
|
||||
|
||||
* Arrays can now have elements of types other than `byte` (still limited in size to 1 byte though) and be built out of struct literals.
|
||||
|
||||
* Arrays can now be constant.
|
||||
@ -64,6 +66,8 @@
|
||||
|
||||
* 8080 and LR35902: fixed large stack variables.
|
||||
|
||||
* Other bug fixes.
|
||||
|
||||
* Optimization improvements.
|
||||
|
||||
## 0.3.2
|
||||
|
@ -42,7 +42,7 @@ but the main disadvantages are:
|
||||
|
||||
* increased stack usage
|
||||
|
||||
* cannot take their addresses
|
||||
* their addresses are not considered constants and it's slower to get them
|
||||
|
||||
* cannot use them in inline assembly code blocks
|
||||
|
||||
|
@ -1001,7 +1001,7 @@ object AlwaysGoodOptimizations {
|
||||
XContainsHardwareStackPointer & HasOpcodeIn(INC, DEC, ASL, LSR) & MatchAddrMode(0) & MatchParameter(1) |
|
||||
Linear & XContainsHardwareStackPointer & HasAddrMode(AbsoluteX) & Not(MatchParameter(1)) & Not(HasOpcodeIn(OpcodeClasses.AccessesWordInMemory)) |
|
||||
Linear & HasAddrMode(AbsoluteX) & Not(MatchParameter(1) & XContainsHardwareStackPointer) & Not(ChangesMemory) |
|
||||
Linear & Not(ChangesS) & DoesntChangeMemoryAt(0, 1) & Not(HasAddrMode(AbsoluteX))).* ~
|
||||
Linear & Not(ChangesS) & DoesntChangeMemoryAt(0, 1) & Not(HasAddrMode(AbsoluteX)) & Not(HasOpcodeIn(TXA, TXY, HuSAX, SXY, STX, STX_W, PHX, PHX_W, SAX, XAA, SAX, AHX, SHX, TAS))).* ~
|
||||
(Elidable & XContainsHardwareStackPointer & HasOpcodeIn(LDA, LDX, LDY, ADC, SBC, ORA, EOR, AND, CMP) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
|
||||
val oldA = ctx.get[Int](2)
|
||||
val ADDR = ctx.get[Constant](1)
|
||||
|
@ -28,6 +28,13 @@ object CoarseFlowAnalyzer {
|
||||
val preservesE: Set[String] = Set("__divmod_u16u8u16u8")
|
||||
val preservesH: Set[String] = Set("__mul_u8u8u8")
|
||||
val preservesL: Set[String] = Set("__mul_u8u8u8")
|
||||
// does the code preserve the stack variables apart from direct writes, assuming Z80 and base pointer in IX?
|
||||
val preservesStackVariables = code.forall {
|
||||
case ZLine0(ZOpcode.ADD_16, TwoRegisters(ZRegister.HL, ZRegister.SP), _) => false
|
||||
case ZLine0(ZOpcode.ADD_16, TwoRegisters(ZRegister.IY, ZRegister.SP), _) => false
|
||||
case ZLine0(ZOpcode.ADD_16, TwoRegisters(ZRegister.IX, ZRegister.SP), _) => compilationOptions.flag(CompilationFlag.UseIxForStack)
|
||||
case _ => true
|
||||
}
|
||||
|
||||
var changed = true
|
||||
while (changed) {
|
||||
@ -56,7 +63,7 @@ object CoarseFlowAnalyzer {
|
||||
case _ => true
|
||||
}
|
||||
if (mayBeCalled) {
|
||||
val result = initialStatus.copy(memIx = currentStatus.memIx)
|
||||
val result = if (preservesStackVariables) initialStatus.copy(memIx = currentStatus.memIx) else initialStatus
|
||||
currentStatus = result.copy(
|
||||
b = if (preservesB(n)) currentStatus.b else result.b,
|
||||
c = if (preservesC(n)) currentStatus.c else result.c,
|
||||
@ -144,19 +151,21 @@ object CoarseFlowAnalyzer {
|
||||
val newV = currentStatus.getRegister(r).map(i => i.+(1).&(0xff))
|
||||
currentStatus = currentStatus.
|
||||
copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus).
|
||||
setRegister(r, newV)
|
||||
setRegister(r, newV).
|
||||
cleanMemIxIfNeeded(preservesStackVariables, r)
|
||||
case ZLine0(DEC, OneRegister(r), _) =>
|
||||
val newV = currentStatus.getRegister(r).map(i => i.-(1).&(0xff))
|
||||
currentStatus = currentStatus.
|
||||
copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus).
|
||||
setRegister(r, newV)
|
||||
setRegister(r, newV).
|
||||
cleanMemIxIfNeeded(preservesStackVariables, r)
|
||||
case ZLine0(INC, OneRegisterOffset(r, o), _) =>
|
||||
val newV = currentStatus.getRegister(r, o).map(i => i.+(1).&(0xff))
|
||||
val newV = currentStatus.cleanMemIxIfNeeded(preservesStackVariables, r).getRegister(r, o).map(i => i.+(1).&(0xff))
|
||||
currentStatus = currentStatus.
|
||||
copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus).
|
||||
setRegister(r, newV, o)
|
||||
case ZLine0(DEC, OneRegisterOffset(r, o), _) =>
|
||||
val newV = currentStatus.getRegister(r, o).map(i => i.-(1).&(0xff))
|
||||
val newV = currentStatus.cleanMemIxIfNeeded(preservesStackVariables, r).getRegister(r, o).map(i => i.-(1).&(0xff))
|
||||
currentStatus = currentStatus.
|
||||
copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus).
|
||||
setRegister(r, newV, o)
|
||||
@ -174,21 +183,27 @@ object CoarseFlowAnalyzer {
|
||||
|
||||
case ZLine0(LD_AHLI, _, _) =>
|
||||
val newHL = currentStatus.getRegister(ZRegister.HL).map(i => i.+(1).&(0xffff))
|
||||
currentStatus = currentStatus.copy(a = AnyStatus).setRegister(ZRegister.HL, newHL)
|
||||
currentStatus = currentStatus.copy(a = AnyStatus).setRegister(ZRegister.HL, newHL).
|
||||
cleanMemIxIfNeeded(preservesStackVariables, ZRegister.MEM_HL)
|
||||
case ZLine0(LD_HLIA, _, _) =>
|
||||
val newHL = currentStatus.getRegister(ZRegister.HL).map(i => i.+(1).&(0xffff))
|
||||
currentStatus = currentStatus.setRegister(ZRegister.HL, newHL)
|
||||
currentStatus = currentStatus.setRegister(ZRegister.HL, newHL).
|
||||
cleanMemIxIfNeeded(preservesStackVariables, ZRegister.MEM_HL)
|
||||
case ZLine0(LD_AHLD, _, _) =>
|
||||
val newHL = currentStatus.getRegister(ZRegister.HL).map(i => i.-(1).&(0xffff))
|
||||
currentStatus = currentStatus.copy(a = AnyStatus).setRegister(ZRegister.HL, newHL)
|
||||
currentStatus = currentStatus.copy(a = AnyStatus).setRegister(ZRegister.HL, newHL).
|
||||
cleanMemIxIfNeeded(preservesStackVariables, ZRegister.MEM_HL)
|
||||
case ZLine0(LD_HLDA, _, _) =>
|
||||
val newHL = currentStatus.getRegister(ZRegister.HL).map(i => i.-(1).&(0xffff))
|
||||
currentStatus = currentStatus.setRegister(ZRegister.HL, newHL)
|
||||
currentStatus = currentStatus.setRegister(ZRegister.HL, newHL).
|
||||
cleanMemIxIfNeeded(preservesStackVariables, ZRegister.MEM_HL)
|
||||
|
||||
case ZLine0(op, OneRegister(r), _) if ZOpcodeClasses.SET(op) =>
|
||||
currentStatus = currentStatus.setRegister(r, currentStatus.getRegister(r).map(i => i | 1.<<(ZOpcodeClasses.SET_seq.indexOf(op))))
|
||||
currentStatus = currentStatus.setRegister(r, currentStatus.getRegister(r).map(i => i | 1.<<(ZOpcodeClasses.SET_seq.indexOf(op)))).
|
||||
cleanMemIxIfNeeded(preservesStackVariables, r)
|
||||
case ZLine0(op, OneRegister(r), _) if ZOpcodeClasses.RES(op) =>
|
||||
currentStatus = currentStatus.setRegister(r, currentStatus.getRegister(r).map(i => i & ~1.<<(ZOpcodeClasses.RES_seq.indexOf(op))))
|
||||
currentStatus = currentStatus.setRegister(r, currentStatus.getRegister(r).map(i => i & ~1.<<(ZOpcodeClasses.RES_seq.indexOf(op)))).
|
||||
cleanMemIxIfNeeded(preservesStackVariables, r)
|
||||
|
||||
case l@ZLine0(ADD, OneRegisterOffset(s, o), _) =>
|
||||
val (newA, newC) = currentStatus.a.adc(currentStatus.getRegister(s, o), Status.SingleFalse)
|
||||
@ -221,11 +236,12 @@ object CoarseFlowAnalyzer {
|
||||
case ZLine0(LD, TwoRegisters(ZRegister.A, ZRegister.I | ZRegister.R), _) =>
|
||||
currentStatus = currentStatus.copy(a = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus, nf = AnyStatus)
|
||||
case ZLine0(LD, TwoRegisters(t, ZRegister.IMM_8), NumericConstant(value, _)) =>
|
||||
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt))
|
||||
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt)).
|
||||
cleanMemIxIfNeeded(preservesStackVariables, t)
|
||||
case ZLine0(LD, TwoRegistersOffset(t, ZRegister.IMM_8, o), NumericConstant(value, _)) =>
|
||||
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt), o)
|
||||
currentStatus = currentStatus.cleanMemIxIfNeeded(preservesStackVariables, t).setRegister(t, SingleStatus(value.toInt), o)
|
||||
case ZLine0(LD | LD_16, TwoRegisters(t, s), _) =>
|
||||
currentStatus = currentStatus.setRegister(t, currentStatus.getRegister(s))
|
||||
currentStatus = currentStatus.setRegister(t, currentStatus.getRegister(s)).cleanMemIxIfNeeded(preservesStackVariables, t)
|
||||
case ZLine0(LD | LD_16, TwoRegistersOffset(t, s, o), _) =>
|
||||
currentStatus = currentStatus.setRegister(t, currentStatus.getRegister(s, o), o)
|
||||
case ZLine0(EX_DE_HL, _, _) =>
|
||||
@ -237,9 +253,11 @@ object CoarseFlowAnalyzer {
|
||||
case ZLine0(SLA, OneRegister(r), _) =>
|
||||
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
.setRegister(r, currentStatus.getRegister(r).map(_.<<(1).&(0xff)))
|
||||
.cleanMemIxIfNeeded(preservesStackVariables, r)
|
||||
case ZLine0(SRL, OneRegister(r), _) =>
|
||||
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
.setRegister(r, currentStatus.getRegister(r).map(_.>>(1).&(0x7f)))
|
||||
.cleanMemIxIfNeeded(preservesStackVariables, r)
|
||||
|
||||
|
||||
case ZLine0(RLA | RRA | RLCA | RRCA, _, _) =>
|
||||
|
@ -144,6 +144,12 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
case UnknownStatus => this.copy(l = UnknownStatus, h = UnknownStatus, hl = UnknownStatus)
|
||||
}
|
||||
|
||||
def cleanMemIxIfNeeded(preservesStackVariables: Boolean, r: ZRegister.Value): CpuStatus = if (preservesStackVariables) this else {
|
||||
r match {
|
||||
case ZRegister.MEM_HL | ZRegister.MEM_BC | ZRegister.MEM_DE => copy(memIx = Map())
|
||||
case _ => this
|
||||
}
|
||||
}
|
||||
|
||||
override def toString: String = {
|
||||
val memRepr = if (memIx.isEmpty) "" else (0 to memIx.keys.max).map(i => memIx.getOrElse(i, UnknownStatus)).mkString("")
|
||||
|
@ -41,7 +41,7 @@ object BuiltIns {
|
||||
val parts: (List[AssemblyLine], List[AssemblyLine]) = env.eval(source).fold {
|
||||
val b = env.get[Type]("byte")
|
||||
source match {
|
||||
case VariableExpression(name) =>
|
||||
case VariableExpression(name) if env.maybeGet[Variable](name).isDefined =>
|
||||
val v = env.get[Variable](name)
|
||||
if (v.typ.size > 1) {
|
||||
ctx.log.error(s"Variable `$name` is too big for a built-in operation", source.position)
|
||||
|
@ -447,6 +447,67 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
compileToZReg(ctx, pointerExpression) -> ctx.env.get[ThingInMemory]("__reg.loword")
|
||||
}
|
||||
|
||||
def compileStackOffset(ctx: CompilationContext, target: Variable, offset: Int, subbyte: Option[Int]): List[AssemblyLine] = {
|
||||
val hi = if (ctx.options.flag(CompilationFlag.SoftwareStack)) {
|
||||
MemoryAddressConstant(ctx.env.get[ThingInMemory]("__stack")).hiByte
|
||||
} else {
|
||||
Constant.One
|
||||
}
|
||||
val tsx = if (ctx.options.flag(CompilationFlag.SoftwareStack)) {
|
||||
AssemblyLine.absolute(LDX, MemoryAddressConstant(ctx.env.get[ThingInMemory]("__sp")))
|
||||
} else {
|
||||
AssemblyLine.implied(TSX)
|
||||
}
|
||||
val actualOffset = if (ctx.options.flag(CompilationFlag.SoftwareStack)) {
|
||||
offset & 0xff
|
||||
} else {
|
||||
(offset + ctx.extraStackOffset) & 0xff
|
||||
}
|
||||
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
subbyte match {
|
||||
case Some(1) => compile(ctx, GeneratedConstantExpression(hi, b), Some(b -> target), BranchSpec.None)
|
||||
case Some(0) => target match {
|
||||
case RegisterVariable(MosRegister.A, _) => actualOffset match {
|
||||
case 0 => List(tsx, AssemblyLine.implied(TXA))
|
||||
case 1 => List(tsx, AssemblyLine.implied(INX), AssemblyLine.implied(TXA))
|
||||
case 2 => List(tsx, AssemblyLine.implied(INX), AssemblyLine.implied(INX), AssemblyLine.implied(TXA))
|
||||
case _ => List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(CLC), AssemblyLine.immediate(ADC, actualOffset))
|
||||
}
|
||||
|
||||
case RegisterVariable(MosRegister.X, _) => actualOffset match {
|
||||
case 0 => List(tsx)
|
||||
case 1 => List(tsx, AssemblyLine.implied(INX))
|
||||
case 2 => List(tsx, AssemblyLine.implied(INX), AssemblyLine.implied(INX))
|
||||
case 3 => List(tsx, AssemblyLine.implied(INX), AssemblyLine.implied(INX), AssemblyLine.implied(INX))
|
||||
case _ => List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(CLC), AssemblyLine.immediate(ADC, actualOffset), AssemblyLine.implied(TAX))
|
||||
}
|
||||
case RegisterVariable(MosRegister.Y, _) => actualOffset match {
|
||||
case 0 => List(tsx)
|
||||
case 1 => List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(TAY), AssemblyLine.implied(INY))
|
||||
case _ => List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(CLC), AssemblyLine.immediate(ADC, actualOffset), AssemblyLine.implied(TAY))
|
||||
}
|
||||
case _ =>
|
||||
val loadA = actualOffset match {
|
||||
case 0 => List(tsx, AssemblyLine.implied(TXA))
|
||||
case 1 => List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(INX))
|
||||
case 2 => List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(INX), AssemblyLine.implied(INX))
|
||||
case _ => List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(CLC), AssemblyLine.immediate(ADC, actualOffset))
|
||||
}
|
||||
loadA ++ expressionStorageFromA(ctx, Some(b -> target), None)
|
||||
}
|
||||
case None =>
|
||||
val loadA = actualOffset match {
|
||||
case 0 => List(tsx, AssemblyLine.implied(TXA))
|
||||
case 1 => List(tsx, AssemblyLine.implied(INX), AssemblyLine.implied(TXA))
|
||||
case 2 => List(tsx, AssemblyLine.implied(INX), AssemblyLine.implied(INX), AssemblyLine.implied(TXA))
|
||||
case _ => List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(CLC), AssemblyLine.immediate(ADC, actualOffset))
|
||||
}
|
||||
loadA ++ List(AssemblyLine.immediate(LDX, hi)) ++ expressionStorageFromAX(ctx, Some(ctx.env.get[Type]("pointer") -> target), None)
|
||||
case _ => throw new IllegalArgumentException
|
||||
}
|
||||
}
|
||||
|
||||
def compile(ctx: CompilationContext, expr: Expression, exprTypeAndVariable: Option[(Type, Variable)], branches: BranchSpec): List[AssemblyLine] = {
|
||||
val env = ctx.env
|
||||
env.eval(expr) match {
|
||||
@ -476,6 +537,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
assertCompatible(exprType, target.typ)
|
||||
env.eval(expr).map(c => compileConstant(ctx, c, target)).getOrElse {
|
||||
env.get[TypedThing](name) match {
|
||||
case source: StackOffsetThing => compileStackOffset(ctx, target, source.offset, source.subbyte)
|
||||
case source: VariableInMemory =>
|
||||
target match {
|
||||
case RegisterVariable(MosRegister.A, _) => AssemblyLine.variable(ctx, LDA, source)
|
||||
|
@ -253,7 +253,14 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case LiteralExpression(value, _) => ???
|
||||
case GeneratedConstantExpression(_, _) => ???
|
||||
case VariableExpression(name) =>
|
||||
env.get[Variable](name) match {
|
||||
env.get[VariableLikeThing](name) match {
|
||||
case s: StackOffsetThing =>
|
||||
s.subbyte match {
|
||||
case None => targetifyHL(ctx, target, calculateStackAddressToHL(ctx, s.offset))
|
||||
case Some(0) => targetifyA(ctx, target, calculateStackAddressToHL(ctx, s.offset) :+ ZLine.ld8(ZRegister.A, ZRegister.L), isSigned = false)
|
||||
case Some(1) => targetifyA(ctx, target, calculateStackAddressToHL(ctx, s.offset) :+ ZLine.ld8(ZRegister.A, ZRegister.H), isSigned = false)
|
||||
case _ => throw new IllegalArgumentException
|
||||
}
|
||||
case v: VariableInMemory =>
|
||||
import ZRegister._
|
||||
v.typ.size match {
|
||||
@ -1139,7 +1146,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
|
||||
def calculateStackAddressToHL(ctx: CompilationContext, baseOffset: Int): List[ZLine] = {
|
||||
if (ctx.options.flag(CompilationFlag.UseIxForStack) || ctx.options.flag(CompilationFlag.UseIyForStack)) {
|
||||
???
|
||||
// TODO: is this correct?
|
||||
List(ZLine.ldImm16(ZRegister.HL, baseOffset + ctx.extraStackOffset), ZLine.registers(ZOpcode.ADD_16, ZRegister.HL, ZRegister.SP))
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitSharpOpcodes)) {
|
||||
List(ZLine.imm8(ZOpcode.LD_HLSP, baseOffset + ctx.extraStackOffset))
|
||||
} else {
|
||||
@ -1592,7 +1600,22 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case None =>
|
||||
rhs match {
|
||||
case VariableExpression(vname) =>
|
||||
env.get[Variable](vname) match {
|
||||
env.get[VariableLikeThing](vname) match {
|
||||
case s: StackOffsetThing =>
|
||||
s.subbyte match {
|
||||
case None =>
|
||||
val list = List(
|
||||
calculateStackAddressToHL(ctx, s.offset) :+ ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
calculateStackAddressToHL(ctx, s.offset) :+ ZLine.ld8(ZRegister.A, ZRegister.H)) ++ List.fill(size)(List(ZLine.ldImm8(ZRegister.A, 0)))
|
||||
list.take(size)
|
||||
case Some(0) =>
|
||||
val list = (calculateStackAddressToHL(ctx, s.offset) :+ ZLine.ld8(ZRegister.A, ZRegister.L)) :: List.fill(size)(List(ZLine.ldImm8(ZRegister.A, 0)))
|
||||
list.take(size)
|
||||
case Some(1) =>
|
||||
val list = (calculateStackAddressToHL(ctx, s.offset) :+ ZLine.ld8(ZRegister.A, ZRegister.H)) :: List.fill(size)(List(ZLine.ldImm8(ZRegister.A, 0)))
|
||||
list.take(size)
|
||||
case _ => throw new IllegalArgumentException
|
||||
}
|
||||
case v: VariableInMemory =>
|
||||
List.tabulate(size) { i =>
|
||||
if (i < v.typ.size) {
|
||||
|
@ -1471,6 +1471,12 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (stmt.stack) {
|
||||
val v = StackVariable(prefix + name, typ, this.baseStackOffset)
|
||||
addVariable(options, name, v, stmt.position)
|
||||
addThing(StackOffsetThing(v.name + ".addr", this.baseStackOffset, get[Type]("pointer"), None), stmt.position)
|
||||
addThing(StackOffsetThing(v.name + ".addr.lo", this.baseStackOffset, b, Some(0)), stmt.position)
|
||||
addThing(StackOffsetThing(v.name + ".addr.hi", this.baseStackOffset, b, Some(1)), stmt.position)
|
||||
addThing(StackOffsetThing(v.name + ".pointer", this.baseStackOffset, PointerType("pointer."+v.typ.name, v.typ.name, Some(v.typ)), None), stmt.position)
|
||||
addThing(StackOffsetThing(v.name + ".pointer.lo", this.baseStackOffset, b, Some(0)), stmt.position)
|
||||
addThing(StackOffsetThing(v.name + ".pointer.hi", this.baseStackOffset, b, Some(1)), stmt.position)
|
||||
baseStackOffset += typ.size
|
||||
} else {
|
||||
val (v, addr) = stmt.address.fold[(VariableInMemory, Constant)]({
|
||||
@ -1736,7 +1742,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (options.flag(CompilationFlag.SoftwareStack)) {
|
||||
if (!things.contains("__sp")) {
|
||||
things("__sp") = UninitializedMemoryVariable("__sp", b, VariableAllocationMethod.Auto, None, NoAlignment, isVolatile = false)
|
||||
things("__stack") = UninitializedArray("__stack", 256, None, b, b, readOnly = false, WithinPageAlignment)
|
||||
things("__stack") = UninitializedArray("__stack", 256, None, b, b, readOnly = false, DivisibleAlignment(256))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
4
src/main/scala/millfork/env/Thing.scala
vendored
4
src/main/scala/millfork/env/Thing.scala
vendored
@ -383,6 +383,10 @@ case class ConstantThing(name: String, value: Constant, typ: Type) extends Typed
|
||||
def map(f: Constant => Constant) = ConstantThing("", f(value), typ)
|
||||
}
|
||||
|
||||
case class StackOffsetThing(name: String, offset: Int, typ: Type, subbyte: Option[Int]) extends TypedThing with VariableLikeThing {
|
||||
|
||||
}
|
||||
|
||||
trait ParamSignature {
|
||||
def types: List[Type]
|
||||
|
||||
|
@ -290,4 +290,105 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
m.readLong(0xc000) should equal(400)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test("Pointers to stack variables 1") {
|
||||
val code =
|
||||
"""
|
||||
| noinline void inc(pointer.byte p) {
|
||||
| p[0] += 1
|
||||
| test1 = word(p)
|
||||
| }
|
||||
| byte output @$c000
|
||||
| word test1 @$c002
|
||||
| word stkptr @$c004
|
||||
| void main () {
|
||||
| stack byte a
|
||||
| a = 0
|
||||
| inc(a.pointer) // ↑
|
||||
| inc(a.pointer) // ↑
|
||||
| inc(a.pointer) // ↑
|
||||
| inc(a.pointer) // ↑
|
||||
| inc(a.pointer) // ↑
|
||||
| inc(a.pointer) // ↑
|
||||
| inc(a.pointer) // ↑
|
||||
| inc(a.pointer) // ↑
|
||||
| inc(a.pointer) // ↑
|
||||
| output = a
|
||||
| #if ARCH_6502
|
||||
| asm {
|
||||
| tsx
|
||||
| stx stkptr
|
||||
| ldx #0
|
||||
| stx stkptr+1
|
||||
| }
|
||||
| #endif
|
||||
| #if ARCH_I80 && CPUFEATURE_8080
|
||||
| #pragma zilog_syntax
|
||||
| asm {
|
||||
| ld hl,0
|
||||
| add hl,sp
|
||||
| ld (stkptr),hl
|
||||
| }
|
||||
| #endif
|
||||
| #if CPUFEATURE_GAMEBOY
|
||||
| #pragma zilog_syntax
|
||||
| asm {
|
||||
| ld hl,0
|
||||
| add hl,sp
|
||||
| ld a,l
|
||||
| ld (stkptr),a
|
||||
| ld a,h
|
||||
| ld (stkptr+1),a
|
||||
| }
|
||||
| #endif
|
||||
| }
|
||||
""".stripMargin
|
||||
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Cmos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(code) { m =>
|
||||
println("$" + m.readWord(0xc002).toHexString)
|
||||
// (0x1f0 until 0x200).foreach { a =>
|
||||
// printf(f"$$$a%04x = $$${m.readByte(a)}%02x%n");
|
||||
// }
|
||||
// (0xc000 until 0xc010).foreach { a =>
|
||||
// printf(f"$$$a%04x = $$${m.readByte(a)}%02x%n");
|
||||
// }
|
||||
println("$" + m.readWord(0xc004).toHexString)
|
||||
m.readByte(0xc000) should equal(code.count(_ == '↑'))
|
||||
}
|
||||
EmuSoftwareStackBenchmarkRun(code) { m =>
|
||||
println("$" + m.readWord(0xc002).toHexString)
|
||||
println("$" + m.readWord(0xc004).toHexString)
|
||||
m.readByte(0xc000) should equal(code.count(_ == '↑'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test("Pointers to stack variables 2") {
|
||||
val code =
|
||||
"""
|
||||
| import zp_reg
|
||||
| void main () {
|
||||
| stack byte a
|
||||
| a = 0
|
||||
| byte b
|
||||
| word w
|
||||
| b += a.addr.lo
|
||||
| w += a.addr
|
||||
| w <<= a.addr.hi
|
||||
| b *= a.addr.lo
|
||||
| b <<= a.addr.lo
|
||||
| w = nonet(b << a.addr.lo)
|
||||
| w &= a.addr
|
||||
| w = w | a.addr
|
||||
|
|
||||
| }
|
||||
""".stripMargin
|
||||
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80)(code) { m =>
|
||||
}
|
||||
EmuSoftwareStackBenchmarkRun(code) { m =>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user