mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-23 23:30:22 +00:00
Improve passing of register parameters to assembly functions
This commit is contained in:
parent
c5135423f8
commit
a29b2a994b
@ -125,9 +125,31 @@ and call `increase(score, 10)`, the entire call will compile into:
|
||||
|
||||
Non-macro functions can only have their parameters passed via registers:
|
||||
|
||||
* `byte a`, `byte x`, `byte y`: a single byte passed via the given CPU register
|
||||
* `byte a`, `byte x`, `byte y`: a single byte passed via the given CPU register; any 1-byte type can be used
|
||||
|
||||
* `word xa`, `word ax`, `word ay`, `word ya`, `word xy`, `word yx`: a 2-byte word byte passed via given two CPU registers, with the high byte passed through the first register and the low byte passed through the second register
|
||||
* `word xa`, `word ax`, `word ay`, `word ya`, `word xy`, `word yx`: a 2-byte word byte passed via given two CPU registers,
|
||||
with the high byte passed through the first register and the low byte passed through the second register; any 2-byte type can be used
|
||||
|
||||
For example, this piece of code:
|
||||
|
||||
asm void f(word ax) @F_ADDR extern
|
||||
|
||||
f(5)
|
||||
|
||||
will compile to
|
||||
|
||||
LDA #5
|
||||
LDX #0
|
||||
JSR F_ADDR
|
||||
|
||||
**Work in progress**:
|
||||
Only the following combinations of register parameters work reliably:
|
||||
|
||||
* zero or one register parameters
|
||||
|
||||
* two register parameters where at least one of them is an 8-bit parameter passed via A
|
||||
|
||||
Other combinations are guaranteed to work only with constant arguments.
|
||||
|
||||
Macro assembly functions can have maximum one parameter passed via a register.
|
||||
|
||||
|
@ -154,20 +154,20 @@ and call `increase(score, 10)`, the entire call will compile into:
|
||||
|
||||
Non-macro functions can only have their parameters passed via registers:
|
||||
|
||||
* `byte a`, `byte b`, etc.: a single byte passed via the given CPU register
|
||||
* `byte a`, `byte b`, `byte c`, `byte d`, `byte e`, `byte h`, `byte l`: a single byte passed via the given CPU register; any 1-byte type can be used
|
||||
|
||||
* `word hl`, `word bc`, `word de`: a 2-byte word byte passed via given 16-bit register
|
||||
* `word hl`, `word bc`, `word de`: a 2-byte word byte passed via given 16-bit register; any 2-byte type can be used
|
||||
|
||||
Parameters passed via other registers (`I`, `IX`, `IY`, `IXH` etc.) or combinations of registers do not work yet.
|
||||
|
||||
**Work in progress**:
|
||||
Currently, only few parameter signatures are supported for non-macro assembly functions:
|
||||
Only the following combinations of register parameters work reliably:
|
||||
|
||||
* `()`
|
||||
* zero or one register parameters
|
||||
|
||||
* `(byte a)`, `(byte b)`, `(byte c)`, `(byte d)`, `(byte e)`, `(byte h)`, `(byte l)` ("byte" may be any other 2-byte type)
|
||||
* two register parameters where at least one of them is a 16-bit parameter
|
||||
|
||||
* `(word hl)`, `(word bc)`, `(word de)` ("word" may be any other 2-byte type)
|
||||
|
||||
More parameters or parameters passed via other registers do not work yet.
|
||||
Other combinations are guaranteed to work only with constant arguments.
|
||||
|
||||
Macro assembly functions cannot have any parameter passed via registers.
|
||||
|
||||
|
@ -41,6 +41,7 @@ Syntax:
|
||||
* `<return_type>` is a valid return type, see [Types](./types.md)
|
||||
|
||||
* `<params>` is a comma-separated list of parameters, in form `type name`. Allowed types are the same as for local variables.
|
||||
For assembly functions, certain parameter names are interpreted as CPU registers.
|
||||
|
||||
* `<alignment>` is either a numeric literal that is a power of 2, or keyword `fast`.
|
||||
The function will be allocated at the address divisible by alignment.
|
||||
|
@ -1500,7 +1500,9 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case Seq((_, param)) => param
|
||||
case Seq((MosRegister.A, pa), (_, pxy)) => pa ++ preserveRegisterIfNeeded(ctx, MosRegister.A, pxy)
|
||||
case Seq((_, pxy), (MosRegister.A, pa)) => pa ++ preserveRegisterIfNeeded(ctx, MosRegister.A, pxy)
|
||||
case other => other.flatMap(_._2) // TODO : make sure all registers are passed in correctly
|
||||
case other =>
|
||||
ctx.log.warn("Unsupported register parameter combination: " + other.map(_._1.toString).mkString("(", ",", ")"), expr.position)
|
||||
other.flatMap(_._2) // TODO : make sure all registers are passed in correctly
|
||||
}
|
||||
secondViaMemory ++ thirdViaRegisters :+ AssemblyLine.absoluteOrLongAbsolute(JSR, function, ctx.options)
|
||||
case NormalParamSignature(List(MemoryVariable(_, typ, _))) if typ.size == 1 =>
|
||||
|
@ -30,6 +30,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
toA.init
|
||||
case ZLine0(ZOpcode.LD, TwoRegisters(ZRegister.A, source@(ZRegister.B | ZRegister.C | ZRegister.D | ZRegister.E | ZRegister.MEM_HL)), _) =>
|
||||
toA.init :+ ZLine.ld8(register, source)
|
||||
case ZLine0(ZOpcode.LD, TwoRegisters(ZRegister.A, ZRegister.IMM_8), param) if toA.size == 1 =>
|
||||
List(ZLine.ldImm8(register, param))
|
||||
case ZLine0(ZOpcode.LD, TwoRegistersOffset(ZRegister.A, ZRegister.MEM_IX_D, offset), _) =>
|
||||
toA.init :+ ZLine.ldViaIx(register, offset)
|
||||
case ZLine0(ZOpcode.LD, TwoRegistersOffset(ZRegister.A, ZRegister.MEM_IY_D, offset), _) =>
|
||||
@ -1058,9 +1060,45 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
compileToBC(ctx, params.head) :+ ZLine(CALL, NoRegisters, function.toAddress)
|
||||
case AssemblyParamSignature(Nil) =>
|
||||
List(ZLine(CALL, NoRegisters, function.toAddress))
|
||||
case AssemblyParamSignature(paramConvs) =>
|
||||
// TODO: stop being lazy and implement this
|
||||
???
|
||||
case AssemblyParamSignature(paramConvs) =>val pairs = params.zip(paramConvs)
|
||||
val viaMemory = pairs.flatMap {
|
||||
case (paramExpr, AssemblyParam(typ, paramVar: VariableInMemory, AssemblyParameterPassingBehaviour.Copy)) =>
|
||||
ctx.log.error("Variable parameters to assembly functions are not supported", expression.position)
|
||||
Nil
|
||||
case _ => Nil
|
||||
}
|
||||
val viaRegisters = pairs.flatMap {
|
||||
case (paramExpr, AssemblyParam(typ, paramVar@ZRegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy)) =>
|
||||
if (typ.size != ZRegister.registerSize(register)) {
|
||||
ctx.log.error(s"Type ${typ.name} and register $register are of different sizes", expression.position)
|
||||
}
|
||||
val compi = ZRegister.registerSize(register) match {
|
||||
case 1 => compile8BitTo(ctx, paramExpr, register)
|
||||
case 2 => register match {
|
||||
case ZRegister.HL => compileToHL(ctx, paramExpr)
|
||||
case ZRegister.BC => compileToBC(ctx, paramExpr)
|
||||
case ZRegister.DE => compileToDE(ctx, paramExpr)
|
||||
case _ =>
|
||||
ctx.log.error(s"Unsupported register $register", expression.position)
|
||||
Nil
|
||||
}
|
||||
}
|
||||
Some(register -> compi)
|
||||
case _ => Nil
|
||||
} match {
|
||||
case Seq() => Nil
|
||||
case Seq((_, param)) => param
|
||||
case Seq((ZRegister.HL, phl), (_, pxx)) => phl ++ stashHLIfChanged(ctx, pxx)
|
||||
case Seq((_, pxx), (ZRegister.HL, phl)) => phl ++ stashHLIfChanged(ctx, pxx)
|
||||
case Seq((ZRegister.DE, pde), (_, pxx)) => pde ++ stashDEIfChanged(ctx, pxx)
|
||||
case Seq((_, pxx), (ZRegister.DE, pde)) => pde ++ stashDEIfChanged(ctx, pxx)
|
||||
case Seq((ZRegister.BC, pbc), (_, pxx)) => pbc ++ stashBCIfChanged(ctx, pxx)
|
||||
case Seq((_, pxx), (ZRegister.BC, pbc)) => pbc ++ stashBCIfChanged(ctx, pxx)
|
||||
case other =>
|
||||
ctx.log.warn("Unsupported register parameter combination: " + other.map(_._1.toString).mkString("(", ",", ")"), expression.position)
|
||||
other.flatMap(_._2) // TODO : make sure all registers are passed in correctly
|
||||
}
|
||||
viaMemory ++ viaRegisters :+ ZLine(CALL, NoRegisters, function.toAddress)
|
||||
case NormalParamSignature(List(param)) if param.typ.size == 1 =>
|
||||
compileToA(ctx, params.head) :+ ZLine(CALL, NoRegisters, function.toAddress)
|
||||
case NormalParamSignature(List(param)) if param.typ.size == 2 =>
|
||||
|
Loading…
Reference in New Issue
Block a user