mirror of
https://github.com/irmen/prog8.git
synced 2024-06-30 18:29:48 +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
|
; -- setup the VERA's address register 0 or 1
|
||||||
%asm {{
|
%asm {{
|
||||||
|
and #1
|
||||||
|
pha
|
||||||
|
lda cx16.r1
|
||||||
and #1
|
and #1
|
||||||
sta cx16.VERA_CTRL
|
sta cx16.VERA_CTRL
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
lda cx16.r1
|
pla
|
||||||
and #1
|
|
||||||
cpy #0
|
cpy #0
|
||||||
bmi _decr
|
bmi ++
|
||||||
beq _seth
|
beq +
|
||||||
ora #%00010000
|
ora #%00010000
|
||||||
_seth sta cx16.VERA_ADDR_H
|
+ sta cx16.VERA_ADDR_H
|
||||||
|
rts
|
||||||
|
+ ora #%00011000
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
rts
|
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
|
; -- write a single byte to VERA's video memory
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%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
|
; -- or a single byte to the value already in the VERA's video memory at that location
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%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
|
; -- and a single byte to the value already in the VERA's video memory at that location
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%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
|
; -- xor a single byte to the value already in the VERA's video memory at that location
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%asm {{
|
||||||
|
|
|
@ -339,14 +339,6 @@ internal class AstChecker(private val program: Program,
|
||||||
if(carryParameter!=null && carryParameter !== subroutine.asmParameterRegisters.last())
|
if(carryParameter!=null && carryParameter !== subroutine.asmParameterRegisters.last())
|
||||||
err("carry parameter has to come 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 {
|
} else {
|
||||||
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
||||||
// Instead, their reference (address) should be passed (as an UWORD).
|
// 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 ArrayLiteralValue ||
|
||||||
it is IdentifierReference} -> {
|
it is IdentifierReference} -> {
|
||||||
// There's no risk of clobbering for these simple argument types. Optimize the register loading directly from these values.
|
// 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)) {
|
val argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters)
|
||||||
argumentViaRegister(sub, arg.first, arg.second)
|
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 -> {
|
else -> {
|
||||||
// Risk of clobbering due to complex expression args. Evaluate first, then assign registers.
|
// 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)
|
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) {
|
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||||
// this is called when one or more of the arguments are 'complex' and
|
// 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.
|
// 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")
|
asmgen.out(" ldy P8ESTACK_LO+${argi.index},x")
|
||||||
}
|
}
|
||||||
argi.value.second.registerOrPair in Cx16VirtualRegisters -> {
|
argi.value.second.registerOrPair in Cx16VirtualRegisters -> {
|
||||||
|
// immediately output code to load the virtual register, to avoid clobbering the A register later
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda P8ESTACK_LO+${argi.index},x
|
lda P8ESTACK_LO+${argi.index},x
|
||||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
||||||
|
|
|
@ -8,31 +8,21 @@
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
|
asmsub derp(ubyte value @A, uword address @R0) {
|
||||||
sub vpoke(ubyte bank, uword address, ubyte value) {
|
|
||||||
%asm {{
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub vpokeasm(uword address @R0, ubyte bank @A, ubyte value @Y) {
|
|
||||||
%asm {{
|
%asm {{
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub start () {
|
sub start () {
|
||||||
txt.chrout('!')
|
|
||||||
uword bank = 1
|
uword bank = 1
|
||||||
uword address = 1000
|
uword address = 1000
|
||||||
ubyte value = 123
|
ubyte value = 123
|
||||||
bank++
|
bank++
|
||||||
|
|
||||||
|
derp(value, address)
|
||||||
|
cx16.vpoke(lsb(bank), address, value)
|
||||||
|
|
||||||
test_stack.test()
|
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