mirror of
https://github.com/irmen/prog8.git
synced 2024-06-28 20:29:36 +00:00
removed requirement of virtual regs R0-R15 to be at start of subroutine params
This commit is contained in:
parent
9002c67639
commit
4c1c595f14
|
@ -279,30 +279,33 @@ asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
|
|||
}
|
||||
|
||||
|
||||
asmsub vaddr(uword address @R0, ubyte bank @R1, ubyte addrsel @A, byte incrdecr @Y) {
|
||||
asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte incrdecr @Y) {
|
||||
; -- setup the VERA's address register 0 or 1
|
||||
%asm {{
|
||||
and #1
|
||||
pha
|
||||
lda cx16.r1
|
||||
and #1
|
||||
sta cx16.VERA_CTRL
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda cx16.r1
|
||||
and #1
|
||||
pla
|
||||
cpy #0
|
||||
bmi _decr
|
||||
beq _seth
|
||||
bmi ++
|
||||
beq +
|
||||
ora #%00010000
|
||||
_seth sta cx16.VERA_ADDR_H
|
||||
+ sta cx16.VERA_ADDR_H
|
||||
rts
|
||||
+ ora #%00011000
|
||||
sta cx16.VERA_ADDR_H
|
||||
rts
|
||||
_decr ora #%00011000
|
||||
bra _seth
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub vpoke(uword address @R0, ubyte bank @A, ubyte value @Y) {
|
||||
asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) {
|
||||
; -- write a single byte to VERA's video memory
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
|
@ -318,7 +321,7 @@ asmsub vpoke(uword address @R0, ubyte bank @A, ubyte value @Y) {
|
|||
}}
|
||||
}
|
||||
|
||||
asmsub vpoke_or(uword address @R0, ubyte bank @A, ubyte value @Y) {
|
||||
asmsub vpoke_or(ubyte bank @A, uword address @R0, ubyte value @Y) {
|
||||
; -- or a single byte to the value already in the VERA's video memory at that location
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
|
@ -336,7 +339,7 @@ asmsub vpoke_or(uword address @R0, ubyte bank @A, ubyte value @Y) {
|
|||
}}
|
||||
}
|
||||
|
||||
asmsub vpoke_and(uword address @R0, ubyte bank @A, ubyte value @Y) {
|
||||
asmsub vpoke_and(ubyte bank @A, uword address @R0, ubyte value @Y) {
|
||||
; -- and a single byte to the value already in the VERA's video memory at that location
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
|
@ -354,7 +357,7 @@ asmsub vpoke_and(uword address @R0, ubyte bank @A, ubyte value @Y) {
|
|||
}}
|
||||
}
|
||||
|
||||
asmsub vpoke_xor(uword address @R0, ubyte bank @A, ubyte value @Y) {
|
||||
asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) {
|
||||
; -- xor a single byte to the value already in the VERA's video memory at that location
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
|
|
|
@ -339,14 +339,6 @@ internal class AstChecker(private val program: Program,
|
|||
if(carryParameter!=null && carryParameter !== subroutine.asmParameterRegisters.last())
|
||||
err("carry parameter has to come last")
|
||||
|
||||
val cx16virtualRegParameters = subroutine.asmParameterRegisters.withIndex().filter { it.value.registerOrPair in Cx16VirtualRegisters }
|
||||
if(cx16virtualRegParameters.isNotEmpty()) {
|
||||
for(elt in cx16virtualRegParameters.withIndex()) {
|
||||
if(elt.index != elt.value.index)
|
||||
err("cx16 virtual register parameters have to be specified first")
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
||||
// Instead, their reference (address) should be passed (as an UWORD).
|
||||
|
|
|
@ -54,13 +54,15 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||
it is ArrayLiteralValue ||
|
||||
it is IdentifierReference} -> {
|
||||
// There's no risk of clobbering for these simple argument types. Optimize the register loading directly from these values.
|
||||
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||
argumentViaRegister(sub, arg.first, arg.second)
|
||||
}
|
||||
val argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters)
|
||||
val (vregsArgsInfo, otherRegsArgsInfo) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters }
|
||||
for(arg in vregsArgsInfo)
|
||||
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||
for(arg in otherRegsArgsInfo)
|
||||
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||
}
|
||||
else -> {
|
||||
// Risk of clobbering due to complex expression args. Evaluate first, then assign registers.
|
||||
// TODO not used yet because code is larger: registerArgsViaVirtualRegistersEvaluation(stmt, sub)
|
||||
registerArgsViaStackEvaluation(stmt, sub)
|
||||
}
|
||||
}
|
||||
|
@ -76,96 +78,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||
}
|
||||
}
|
||||
|
||||
private fun registerArgsViaVirtualRegistersEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||
// This is called when one or more of the arguments are 'complex' and
|
||||
// cannot be assigned to a cpu register easily or risk clobbering other cpu registers.
|
||||
// To solve this, the expressions are first evaluated into the 'virtual registers and then loaded from there.
|
||||
|
||||
// TODO not used yet; code generated here is bigger than the eval-stack based code because it always treats the virtual regs as words and also sometimes uses the stack for evaluation and then copies it to a virtual register.
|
||||
|
||||
if(sub.parameters.isEmpty())
|
||||
return
|
||||
|
||||
// 1. load all arguments left-to-right into the R0..R15 registers
|
||||
for (vrarg in stmt.args.zip(Cx16VirtualRegisters)) {
|
||||
asmgen.assignExpressionToRegister(vrarg.first, vrarg.second)
|
||||
}
|
||||
|
||||
// 2. Gather up the arguments in the correct registers (in specific order to not clobber earlier values)
|
||||
var argForCarry: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||
var argForXregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||
var argForAregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||
|
||||
for(argi in stmt.args.zip(sub.asmParameterRegisters).withIndex()) {
|
||||
val valueIsInVirtualReg = Cx16VirtualRegisters[argi.index]
|
||||
val valueIsInVirtualRegAsmString = valueIsInVirtualReg.toString().toLowerCase()
|
||||
when {
|
||||
argi.value.second.statusflag == Statusflag.Pc -> {
|
||||
require(argForCarry == null)
|
||||
argForCarry = argi
|
||||
}
|
||||
argi.value.second.statusflag != null -> throw AssemblyError("can only use Carry as status flag parameter")
|
||||
argi.value.second.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) -> {
|
||||
require(argForXregister==null)
|
||||
argForXregister = argi
|
||||
}
|
||||
argi.value.second.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.AY) -> {
|
||||
require(argForAregister == null)
|
||||
argForAregister = argi
|
||||
}
|
||||
argi.value.second.registerOrPair == RegisterOrPair.Y -> {
|
||||
asmgen.out(" ldy cx16.$valueIsInVirtualRegAsmString")
|
||||
}
|
||||
argi.value.second.registerOrPair in Cx16VirtualRegisters -> {
|
||||
if(argi.value.second.registerOrPair != valueIsInVirtualReg) {
|
||||
asmgen.out("""
|
||||
lda cx16.$valueIsInVirtualRegAsmString
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
||||
lda cx16.$valueIsInVirtualRegAsmString+1
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1
|
||||
""")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird argument")
|
||||
}
|
||||
}
|
||||
|
||||
if(argForCarry!=null) {
|
||||
asmgen.out("""
|
||||
lda cx16.${Cx16VirtualRegisters[argForCarry.index].toString().toLowerCase()}
|
||||
beq +
|
||||
sec
|
||||
bcs ++
|
||||
+ clc
|
||||
+ php""") // push the status flags
|
||||
}
|
||||
|
||||
if(argForAregister!=null) {
|
||||
when(argForAregister.value.second.registerOrPair) {
|
||||
RegisterOrPair.A -> asmgen.out(" lda cx16.${Cx16VirtualRegisters[argForAregister.index].toString().toLowerCase()}")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda cx16.${Cx16VirtualRegisters[argForAregister.index].toString().toLowerCase()} | ldy cx16.${Cx16VirtualRegisters[argForAregister.index].toString().toLowerCase()}+1")
|
||||
else -> throw AssemblyError("weird arg")
|
||||
}
|
||||
}
|
||||
|
||||
if(argForXregister!=null) {
|
||||
if(argForAregister!=null)
|
||||
asmgen.out(" pha")
|
||||
when(argForXregister.value.second.registerOrPair) {
|
||||
RegisterOrPair.X -> asmgen.out(" ldx cx16.${Cx16VirtualRegisters[argForXregister.index].toString().toLowerCase()}")
|
||||
RegisterOrPair.AX -> asmgen.out(" lda cx16.${Cx16VirtualRegisters[argForXregister.index].toString().toLowerCase()} | ldx cx16.${Cx16VirtualRegisters[argForXregister.index].toString().toLowerCase()}+1")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx cx16.${Cx16VirtualRegisters[argForXregister.index].toString().toLowerCase()} | ldy cx16.${Cx16VirtualRegisters[argForXregister.index].toString().toLowerCase()}+1")
|
||||
else -> throw AssemblyError("weird arg")
|
||||
}
|
||||
if(argForAregister!=null)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
|
||||
if(argForCarry!=null)
|
||||
asmgen.out(" plp") // set the carry flag back to correct value
|
||||
}
|
||||
|
||||
|
||||
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||
// this is called when one or more of the arguments are 'complex' and
|
||||
// cannot be assigned to a register easily or risk clobbering other registers.
|
||||
|
@ -202,6 +114,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||
asmgen.out(" ldy P8ESTACK_LO+${argi.index},x")
|
||||
}
|
||||
argi.value.second.registerOrPair in Cx16VirtualRegisters -> {
|
||||
// immediately output code to load the virtual register, to avoid clobbering the A register later
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+${argi.index},x
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
||||
|
|
|
@ -8,31 +8,21 @@
|
|||
|
||||
main {
|
||||
|
||||
|
||||
sub vpoke(ubyte bank, uword address, ubyte value) {
|
||||
%asm {{
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub vpokeasm(uword address @R0, ubyte bank @A, ubyte value @Y) {
|
||||
asmsub derp(ubyte value @A, uword address @R0) {
|
||||
%asm {{
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub start () {
|
||||
txt.chrout('!')
|
||||
uword bank = 1
|
||||
uword address = 1000
|
||||
ubyte value = 123
|
||||
bank++
|
||||
|
||||
derp(value, address)
|
||||
cx16.vpoke(lsb(bank), address, value)
|
||||
|
||||
test_stack.test()
|
||||
vpoke(lsb(bank), address, value)
|
||||
test_stack.test()
|
||||
vpokeasm(address, lsb(bank), value) ; TODO generates params on stack if expression is used such as lsb(bank). CHECK STACK UNWINDING!!!
|
||||
test_stack.test()
|
||||
; TODO also see if we can do this via R0-R15 temp registers rather than using the estack???
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user