removed requirement of virtual regs R0-R15 to be at start of subroutine params

This commit is contained in:
Irmen de Jong 2020-12-25 15:43:48 +01:00
parent 9002c67639
commit 4c1c595f14
4 changed files with 26 additions and 128 deletions

View File

@ -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 {{

View File

@ -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).

View File

@ -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()}

View File

@ -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???
}
}