various code generation fixes, slight optimization of 16-bit word additions

This commit is contained in:
Irmen de Jong 2019-01-02 00:38:11 +01:00
parent 72d58d5856
commit aea1292f92
7 changed files with 185 additions and 72 deletions

View File

@ -1,20 +1,63 @@
%import c64utils
%option enable_floats
~ main {
ubyte[3] balloonsprite = [ %00000000,%01111111,%00000000 ]
sub start() {
ubyte i=0
A= @($d020)
A= @($d020+i)
@($d020) = 0
@($d020+i) = 0
@($d020+i) = 1
@($d020+i) = 2
@($d020) = @($d020+i) + 1
@($d020+i) = @($d020+i) + 1
c64scr.print_ub(X)
uword uw1 = 0
uword uw2 = $77ff
uword uw3 = $55aa
word w1 = 0
word w2 = $22ff
word w3 = $55aa
memory uword muw1 = $2000
memory uword muw2 = $3000
memory uword muw3 = $4000
memory word mw1 = $4100
memory word mw2 = $4200
memory word mw3 = $4300
uword[3] uwarr = $55aa
word[3] warr = $55aa
memory uword[3] muwarr = $4400
memory word[3] mwarr = $4500
muw3 = $55aa
uwarr[0] = $55aa
uwarr[1] = $55aa
uwarr[2] = $55aa
muwarr[0] = $55aa
muwarr[1] = $55aa
muwarr[2] = $55aa
mwarr[0] = $55aa
mwarr[1] = $55aa
mwarr[2] = $55aa
uw1 = uw2 + $55aa ;52649
c64scr.print_uw(uw1)
c64.CHROUT('\n')
uw1 = uw2 + uw3 ;52649
c64scr.print_uw(uw1)
c64.CHROUT('\n')
uw1 = uw2 + muw3 ;52649
c64scr.print_uw(uw1)
c64.CHROUT('\n')
; uw1 = uw2 + uwarr[2] ;52649
; c64scr.print_uw(uw1)
; c64.CHROUT('\n')
; w1 = w2 + $55aa ; 30889
; c64scr.print_w(w1)
; c64.CHROUT('\n')
; w1 = w2 + w3 ; 30889
; c64scr.print_w(w1)
; c64.CHROUT('\n')
;
; uwarr[2] = uwarr[1] + $55aa
; uwarr[2] = uwarr[1] + uw3
; uwarr[2] = uwarr[1] + uwarr[1]
}
}

View File

@ -524,7 +524,7 @@ class Block(override val name: String,
return "Block(name=$name, address=$address, ${statements.size} statements)"
}
val options = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.name!!}.toSet()
fun options() = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.name!!}.toSet()
}

View File

@ -617,17 +617,17 @@ class AstChecker(private val namespace: INameScope,
err("invalid import directive, cannot import itself")
}
"%breakpoint" -> {
if(directive.parent !is Block) err("this directive may only occur in a block")
if(directive.parent !is INameScope || directive.parent is Module) err("this directive may only occur in a block")
if(directive.args.isNotEmpty())
err("invalid breakpoint directive, expected no arguments")
}
"%asminclude" -> {
if(directive.parent !is Block) err("this directive may only occur in a block")
if(directive.parent !is INameScope || directive.parent is Module) err("this directive may only occur in a block")
if(directive.args.size!=2 || directive.args[0].str==null || directive.args[1].name==null)
err("invalid asminclude directive, expected arguments: \"filename\", scopelabel")
}
"%asmbinary" -> {
if(directive.parent !is Block) err("this directive may only occur in a block")
if(directive.parent !is INameScope || directive.parent is Module) err("this directive may only occur in a block")
val errormsg = "invalid asmbinary directive, expected arguments: \"filename\" [, offset [, length ] ]"
if(directive.args.isEmpty()) err(errormsg)
if(directive.args.isNotEmpty() && directive.args[0].str==null) err(errormsg)

View File

@ -31,6 +31,7 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He
val varDecls = module.statements.filterIsInstance<VarDecl>()
module.statements.removeAll(varDecls)
module.statements.addAll(0, varDecls)
val directives = module.statements.filter {it is Directive && it.directive in directivesToMove}
module.statements.removeAll(directives)
module.statements.addAll(0, directives)

View File

@ -151,7 +151,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
val continueStmtLabelStack : Stack<String> = Stack()
override fun process(block: Block): IStatement {
prog.newBlock(block.scopedname, block.name, block.address, block.options)
prog.newBlock(block.scopedname, block.name, block.address, block.options())
processVariables(block) // @todo optimize initializations with same value: load the value only once (sort on initalization value, datatype ?)
prog.label("block."+block.scopedname)
prog.line(block.position)
@ -182,18 +182,6 @@ private class StatementTranslator(private val prog: IntermediateProgram,
return super.process(subroutine)
}
override fun process(directive: Directive): IStatement {
when(directive.directive) {
"%asminclude" -> throw CompilerException("can't use %asminclude in stackvm")
"%asmbinary" -> throw CompilerException("can't use %asmbinary in stackvm")
"%breakpoint" -> {
prog.line(directive.position)
prog.instr(Opcode.BREAKPOINT)
}
}
return super.process(directive)
}
private fun translate(statements: List<IStatement>) {
for (stmt: IStatement in statements) {
generatedLabelSequenceNumber++
@ -214,7 +202,17 @@ private class StatementTranslator(private val prog: IntermediateProgram,
is AnonymousScope -> translate(stmt)
is ReturnFromIrq -> translate(stmt)
is Return -> translate(stmt)
is Directive, is VarDecl, is Subroutine -> {} // skip this, already processed these.
is Directive -> {
when(stmt.directive) {
"%asminclude" -> throw CompilerException("can't use %asminclude in stackvm")
"%asmbinary" -> throw CompilerException("can't use %asmbinary in stackvm")
"%breakpoint" -> {
prog.line(stmt.position)
prog.instr(Opcode.BREAKPOINT)
}
}
}
is VarDecl, is Subroutine -> {} // skip this, already processed these.
is InlineAssembly -> translate(stmt)
else -> TODO("translate statement $stmt to stackvm")
}

View File

@ -502,7 +502,7 @@ _prog8_irq_handler_excl
}
}
Opcode.PUSH_VAR_WORD -> {
" lda ${ins.callLabel} | ldy ${ins.callLabel}+1 | sta ${ESTACK_LO.toHex()},x | pha | tya | sta ${ESTACK_HI.toHex()},x | pla | dex"
" lda ${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | lda ${ins.callLabel}+1 | sta ${ESTACK_HI.toHex()},x | dex"
}
Opcode.PUSH_VAR_FLOAT -> " lda #<${ins.callLabel} | ldy #>${ins.callLabel}| jsr prog8_lib.push_float"
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB -> {
@ -782,7 +782,7 @@ _prog8_irq_handler_excl
Opcode.CAST_B_TO_UW, Opcode.CAST_B_TO_W -> " lda ${(ESTACK_LO+1)},x | ${signExtendA("${(ESTACK_HI+1).toHex()},x")}" // sign extend the lsb
Opcode.MSB -> " lda ${(ESTACK_HI+1).toHex()},x | sta ${(ESTACK_LO+1).toHex()},x"
Opcode.ADD_UB, Opcode.ADD_B -> {
Opcode.ADD_UB, Opcode.ADD_B -> { // TODO inline better?
"""
lda ${(ESTACK_LO + 2).toHex()},x
clc
@ -791,7 +791,7 @@ _prog8_irq_handler_excl
sta ${(ESTACK_LO + 1).toHex()},x
"""
}
Opcode.SUB_UB, Opcode.SUB_B -> {
Opcode.SUB_UB, Opcode.SUB_B -> { // TODO inline better?
"""
lda ${(ESTACK_LO + 2).toHex()},x
sec
@ -2921,6 +2921,46 @@ _prog8_irq_handler_excl
AsmPattern(listOf(Opcode.PUSH_MEM_B, Opcode.PUSH_BYTE, Opcode.BITXOR_BYTE),
listOf(Opcode.PUSH_MEM_UB, Opcode.PUSH_BYTE, Opcode.BITXOR_BYTE)) { segment ->
" lda ${hexVal(segment[0])} | eor #${hexVal(segment[1])} | sta ${ESTACK_LO.toHex()},x | dex "
},
// 16 bit addition avoiding excessive stack usage
// @todo optimize this even more with longer asmpatterns (avoid stack use altogether on most common operations)
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.ADD_UW),
listOf(Opcode.PUSH_VAR_WORD, Opcode.ADD_W)) { segment ->
"""
clc
lda ${segment[0].callLabel}
adc ${(ESTACK_LO+1).toHex()},x
sta ${(ESTACK_LO+1).toHex()},x
lda ${segment[0].callLabel}+1
adc ${(ESTACK_HI+1).toHex()},x
sta ${(ESTACK_HI+1).toHex()},x
"""
},
AsmPattern(listOf(Opcode.PUSH_MEM_UW, Opcode.ADD_UW),
listOf(Opcode.PUSH_MEM_W, Opcode.ADD_W)) { segment ->
"""
clc
lda ${hexVal(segment[0])}
adc ${(ESTACK_LO + 1).toHex()},x
sta ${(ESTACK_LO + 1).toHex()},x
lda ${hexValPlusOne(segment[0])}
adc ${(ESTACK_HI + 1).toHex()},x
sta ${(ESTACK_HI + 1).toHex()},x
"""
},
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.ADD_UW),
listOf(Opcode.PUSH_WORD, Opcode.ADD_W)) { segment ->
"""
clc
lda #<${hexVal(segment[0])}
adc ${(ESTACK_LO+1).toHex()},x
sta ${(ESTACK_LO+1).toHex()},x
lda #>${hexVal(segment[0])}
adc ${(ESTACK_HI+1).toHex()},x
sta ${(ESTACK_HI+1).toHex()},x
"""
}
)

View File

@ -243,8 +243,9 @@ class StackVm(private var traceOutputFile: String?) {
val result = variables[name]
if(result!=null)
return result
if(name in memoryPointers)
if(name in memoryPointers) {
throw VmExecutionException("variable is memory-mapped: $name = ${memoryPointers[name]}")
}
throw VmExecutionException("unknown variable: $name")
}
@ -1205,24 +1206,44 @@ class StackVm(private var traceOutputFile: String?) {
Opcode.READ_INDEXED_VAR_BYTE -> {
// put the byte value of variable[index] onto the stack
val index = evalstack.pop().integerValue()
if(ins.callLabel in memoryPointers) {
val variable = memoryPointers[ins.callLabel]!!
val address = variable.first + index
when(variable.second) {
DataType.ARRAY_UB -> evalstack.push(Value(DataType.UBYTE, mem.getUByte(address)))
DataType.ARRAY_B -> evalstack.push(Value(DataType.BYTE, mem.getSByte(address)))
else -> throw VmExecutionException("not a proper array/string variable with byte elements")
}
} else {
val variable = getVar(ins.callLabel!!)
if(variable.type==DataType.UWORD) {
if (variable.type == DataType.UWORD) {
// assume the variable is a pointer (address) and get the ubyte value from that memory location
evalstack.push(Value(DataType.UBYTE, mem.getUByte(variable.integerValue())))
} else {
// get indexed byte element from the arrayspec
val array = heap.get(variable.heapId)
when(array.type) {
DataType.ARRAY_UB-> evalstack.push(Value(DataType.UBYTE, array.array!![index]))
when (array.type) {
DataType.ARRAY_UB -> evalstack.push(Value(DataType.UBYTE, array.array!![index]))
DataType.ARRAY_B -> evalstack.push(Value(DataType.BYTE, array.array!![index]))
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> evalstack.push(Value(DataType.UBYTE, Petscii.encodePetscii(array.str!![index].toString(), true)[0]))
else -> throw VmExecutionException("not a proper array/string variable with byte elements")
}
}
}
}
Opcode.READ_INDEXED_VAR_WORD -> {
// put the word value of variable[index] onto the stack
val index = evalstack.pop().integerValue()
if(ins.callLabel in memoryPointers) {
val variable = memoryPointers[ins.callLabel]!!
val address = variable.first + index*2
when(variable.second) {
DataType.ARRAY_UW -> evalstack.push(Value(DataType.UWORD, mem.getUWord(address)))
DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, mem.getSWord(address)))
else -> throw VmExecutionException("not a proper arrayspec var with word elements")
}
} else {
// normal variable
val variable = getVar(ins.callLabel!!)
if(variable.type==DataType.UWORD) {
// assume the variable is a pointer (address) and get the word value from that memory location
@ -1237,21 +1258,31 @@ class StackVm(private var traceOutputFile: String?) {
}
}
}
}
Opcode.READ_INDEXED_VAR_FLOAT -> {
// put the f;pat value of variable[index] onto the stack
// put the float value of variable[index] onto the stack
val index = evalstack.pop().integerValue()
if(ins.callLabel in memoryPointers) {
val variable = memoryPointers[ins.callLabel]!!
val address = variable.first + index*5
if(variable.second==DataType.ARRAY_F)
evalstack.push(Value(DataType.FLOAT, mem.getFloat(address)))
else
throw VmExecutionException("not a proper arrayspec var with float elements")
} else {
val variable = getVar(ins.callLabel!!)
if(variable.type==DataType.UWORD) {
if (variable.type == DataType.UWORD) {
// assume the variable is a pointer (address) and get the float value from that memory location
evalstack.push(Value(DataType.UWORD, mem.getFloat(variable.integerValue())))
} else {
// get indexed float element from the arrayspec
val array = heap.get(variable.heapId)
if(array.type!=DataType.ARRAY_F)
if (array.type != DataType.ARRAY_F)
throw VmExecutionException("not a proper arrayspec var with float elements")
evalstack.push(Value(DataType.FLOAT, array.doubleArray!![index]))
}
}
}
Opcode.WRITE_INDEXED_VAR_BYTE -> {
// store byte value on the stack in variable[index] (index is on the stack as well)
val index = evalstack.pop().integerValue()
@ -1310,21 +1341,21 @@ class StackVm(private var traceOutputFile: String?) {
if(value.type==DataType.UWORD) {
if(memloc.second!=DataType.ARRAY_UW)
throw VmExecutionException("invalid memory pointer type $memloc")
mem.setUWord(memloc.first, value.integerValue())
mem.setUWord(memloc.first+index*2, value.integerValue())
}
else {
if(memloc.second!=DataType.ARRAY_W)
throw VmExecutionException("invalid memory pointer type $memloc")
mem.setSWord(memloc.first, value.integerValue())
mem.setSWord(memloc.first+index*2, value.integerValue())
}
} else {
val variable = getVar(varname)
if (variable.type == DataType.UWORD) {
// assume the variable is a pointer (address) and write the word value to that memory location
if(value.type==DataType.UWORD)
mem.setUWord(variable.integerValue(), value.integerValue())
mem.setUWord(variable.integerValue()+index*2, value.integerValue())
else
mem.setSWord(variable.integerValue(), value.integerValue())
mem.setSWord(variable.integerValue()+index*2, value.integerValue())
} else {
// set indexed word element in the arrayspec
val array = heap.get(variable.heapId)
@ -1347,12 +1378,12 @@ class StackVm(private var traceOutputFile: String?) {
// variable is the name of a pointer, write the float value to that memory location
if(memloc.second!=DataType.ARRAY_F)
throw VmExecutionException("invalid memory pointer type $memloc")
mem.setFloat(memloc.first, value.numericValue().toDouble())
mem.setFloat(memloc.first+index*5, value.numericValue().toDouble())
} else {
val variable = getVar(varname)
if (variable.type == DataType.UWORD) {
// assume the variable is a pointer (address) and write the float value to that memory location
mem.setFloat(variable.integerValue(), value.numericValue().toDouble())
mem.setFloat(variable.integerValue()+index*5, value.numericValue().toDouble())
} else {
// set indexed float element in the arrayspec
val array = heap.get(variable.heapId)