taking the address of strings and arrays

This commit is contained in:
Irmen de Jong 2018-11-23 00:31:21 +01:00
parent 069f6ea372
commit 6c8354aef0
8 changed files with 152 additions and 40 deletions

View File

@ -14,6 +14,7 @@ sub start() {
memory uword memaddr = $c000
uword[2] wordarray
byte b1
memory byte mb1 = $c991
str stringvar = "??????????\n\n\nnext line\r\r\rnext line after carriagereturn"
@ -34,29 +35,38 @@ sub start() {
secretnumber = '@'
secretnumber = '\r'
testword = stringvar ; @todo fix str address assignment
testword = "stringstring" ; @todo fix str address assignment
freadstr_arg = stringvar ; @todo fix str address assignment
freadstr_arg = "stringstring" ; @todo fix str address assignment
secretnumber = "stringstring2222" ; @todo fix str address assignment
testword = stringvar
testword = wordarray
freadstr_arg = stringvar
freadstr_arg = wordarray
wordarray[1] = stringvar
wordarray[1] = wordarray
wordarray[b1] = stringvar
wordarray[b1] = wordarray
wordarray[mb1] = stringvar
wordarray[mb1] = wordarray
; testword = "stringstring" ; @todo asmgen for this
; freadstr_arg = "stringstring" ; @todo asmgen for this
; freadstr_arg = "stringstring2222" ; @todo asmgen for this
; wordarray[1] = "stringstring" ; @todo asmgen for this
address =c64.MEMBOT(1, 40000.w) ; ok!
address =c64.MEMBOT(1, address) ; ok!
address =c64.MEMBOT(1, memaddr) ; ok!
A, Y =c64.GETADR() ; ok!
Y, A =c64.GETADR() ; ok!
address = c64flt.GETADRAY() ; ok!
memaddr = c64flt.GETADRAY() ; ok!
wordarray[1] = c64flt.GETADRAY() ; ok!
v1, v2 =c64.GETADR() ; ok!
address =c64.IOBASE() ; ok!
A = c64.CHRIN() ; ok !
X = c64.CHRIN() ; ok !
Y = c64.CHRIN() ; ok!
v1 = c64.CHRIN() ; ok !
; address =c64.MEMBOT(1, 40000.w) ; ok!
; address =c64.MEMBOT(1, address) ; ok!
; address =c64.MEMBOT(1, memaddr) ; ok!
;
; A, Y =c64.GETADR() ; ok!
; Y, A =c64.GETADR() ; ok!
; address = c64flt.GETADRAY() ; ok!
; memaddr = c64flt.GETADRAY() ; ok!
; wordarray[1] = c64flt.GETADRAY() ; ok!
; v1, v2 =c64.GETADR() ; ok!
; address =c64.IOBASE() ; ok!
; A = c64.CHRIN() ; ok !
; X = c64.CHRIN() ; ok !
; Y = c64.CHRIN() ; ok!
; v1 = c64.CHRIN() ; ok !
return
}

View File

@ -338,26 +338,36 @@ class AstChecker(private val namespace: INameScope,
if(target.identifier!=null) {
val targetName = target.identifier.nameInSource
val targetSymbol = namespace.lookup(targetName, assignment)
when {
targetSymbol == null -> {
when (targetSymbol) {
null -> {
checkResult.add(ExpressionError("undefined symbol: ${targetName.joinToString(".")}", assignment.position))
return assignment
}
targetSymbol !is VarDecl -> {
!is VarDecl -> {
checkResult.add(SyntaxError("assignment LHS must be register or variable", assignment.position))
return assignment
}
targetSymbol.type == VarDeclType.CONST -> {
checkResult.add(ExpressionError("cannot assign new value to a constant", assignment.position))
return assignment
else -> {
if(targetSymbol.type == VarDeclType.CONST) {
checkResult.add(ExpressionError("cannot assign new value to a constant", assignment.position))
return assignment
}
if(assignment.value.resultingDatatype(namespace, heap) in ArrayDatatypes) {
if(targetSymbol.datatype==DataType.UWORD)
return assignment // array can be assigned to UWORD (it's address should be taken as the value then)
}
}
else -> {}
}
}
// it is not possible to assign a new arrayspec to something.
if(assignment.value.resultingDatatype(namespace, heap) in ArrayDatatypes)
checkResult.add(SyntaxError("it's not possible to assign an arrayspec literal value to something, use it as a variable decl initializer instead", assignment.position))
// it is only possible to assign an array to something that is an UWORD or UWORD array (in which case the address of the array value is used as the value)
if(assignment.value.resultingDatatype(namespace, heap) in ArrayDatatypes) {
// the UWORD case has been handled above already, check for UWORD array
val arrayVar = target.arrayindexed?.identifier?.targetStatement(namespace)
if(arrayVar is VarDecl && arrayVar.datatype==DataType.ARRAY_UW)
return assignment
checkResult.add(SyntaxError("it's not possible to assign an array to something other than an UWORD, use it as a variable decl initializer instead", assignment.position))
}
if(assignment.aug_op!=null) {
// check augmented assignment:
@ -844,10 +854,12 @@ class AstChecker(private val namespace: INameScope,
return err("value '$number' out of range for byte")
}
DataType.UWORD -> {
if(value.isString || value.isArray) // string or array are assignable to uword; their memory address is used.
return true
val number = value.asIntegerValue ?: return if (value.floatvalue!=null)
err("unsigned word value expected instead of float; possible loss of precision")
else
err("unsigned word value expected")
err("unsigned word value or address expected")
if (number < 0 || number > 65535)
return err("value '$number' out of range for unsigned word")
}
@ -950,7 +962,7 @@ class AstChecker(private val namespace: INameScope,
DataType.BYTE -> sourceDatatype==DataType.BYTE
DataType.UBYTE -> sourceDatatype==DataType.UBYTE
DataType.WORD -> sourceDatatype==DataType.BYTE || sourceDatatype==DataType.UBYTE || sourceDatatype==DataType.WORD
DataType.UWORD -> sourceDatatype==DataType.UBYTE || sourceDatatype==DataType.UWORD
DataType.UWORD -> sourceDatatype in setOf(DataType.UBYTE, DataType.UWORD, DataType.STR, DataType.STR_S) || sourceDatatype in ArrayDatatypes
DataType.FLOAT -> sourceDatatype in NumericDatatypes
DataType.STR -> sourceDatatype==DataType.STR
DataType.STR_S -> sourceDatatype==DataType.STR_S

View File

@ -1180,13 +1180,44 @@ private class StatementTranslator(private val prog: IntermediateProgram,
when(targetDt) {
DataType.UBYTE, DataType.BYTE ->
throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
DataType.UWORD, DataType.WORD -> {
DataType.WORD -> {
when (valueDt) {
DataType.UBYTE -> prog.instr(Opcode.UB2UWORD)
DataType.BYTE -> prog.instr(Opcode.B2WORD)
else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
}
}
DataType.UWORD -> {
when (valueDt) {
DataType.UBYTE -> prog.instr(Opcode.UB2UWORD)
DataType.BYTE -> prog.instr(Opcode.B2WORD)
DataType.STR, DataType.STR_S -> {
when(stmt.value) {
is LiteralValue -> {
val lv = stmt.value as LiteralValue
prog.removeLastInstruction()
prog.instr(Opcode.PUSH_ADDR_STR, Value(lv.type, lv.heapId!!))
}
is IdentifierReference -> {
val vardecl = (stmt.value as IdentifierReference).targetStatement(namespace) as VarDecl
prog.removeLastInstruction()
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
}
else -> throw CompilerException("can only take address of a literal string value or a string/array variable")
}
}
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.ARRAY_W, DataType.ARRAY_UW, DataType.ARRAY_F -> {
if (stmt.value is IdentifierReference) {
val vardecl = (stmt.value as IdentifierReference).targetStatement(namespace) as VarDecl
prog.removeLastInstruction()
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
}
else
throw CompilerException("can only take address of a literal string value or a string/array variable")
}
else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
}
}
DataType.FLOAT -> {
when (valueDt) {
DataType.UBYTE -> prog.instr(Opcode.UB2FLOAT)

View File

@ -295,6 +295,10 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
currentBlock.instructions.add(Instruction(Opcode.LINE, callLabel = "${position.line} ${position.file}"))
}
fun removeLastInstruction() {
currentBlock.instructions.removeAt(currentBlock.instructions.lastIndex)
}
fun symbolDef(name: String, value: Int) {
currentBlock.integerConstants[name] = value
}

View File

@ -17,6 +17,8 @@ enum class Opcode {
PUSH_REGAX_WORD, // push registers A/X as a 16-bit word
PUSH_REGAY_WORD, // push registers A/Y as a 16-bit word
PUSH_REGXY_WORD, // push registers X/Y as a 16-bit word
PUSH_ADDR_STR, // push the address of the string value (literal)
PUSH_ADDR_HEAPVAR, // push the address of the variable that's on the heap (string or array)
// popping values off the (evaluation) stack, possibly storing them in another location
DISCARD_BYTE, // discard top byte value
@ -227,7 +229,7 @@ val opcodesWithVarArgument = setOf(
Opcode.ROL_VAR_BYTE, Opcode.ROL_VAR_WORD, Opcode.ROR_VAR_BYTE, Opcode.ROR_VAR_WORD,
Opcode.ROL2_VAR_BYTE, Opcode.ROL2_VAR_WORD, Opcode.ROR2_VAR_BYTE, Opcode.ROR2_VAR_WORD,
Opcode.POP_VAR_BYTE, Opcode.POP_VAR_WORD, Opcode.POP_VAR_FLOAT,
Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_FLOAT,
Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_FLOAT, Opcode.PUSH_ADDR_HEAPVAR,
Opcode.READ_INDEXED_VAR_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.READ_INDEXED_VAR_FLOAT,
Opcode.WRITE_INDEXED_VAR_BYTE, Opcode.WRITE_INDEXED_VAR_WORD, Opcode.WRITE_INDEXED_VAR_FLOAT
)

View File

@ -1298,6 +1298,15 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
sty ${segment[1].callLabel}+1
"""
},
// var = address-of other var
AsmPattern(listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.POP_VAR_WORD)) { segment ->
"""
lda #<${segment[0].callLabel}
ldy #>${segment[0].callLabel}
sta ${segment[1].callLabel}
sty ${segment[1].callLabel}+1
"""
},
// var = mem ubyte
AsmPattern(listOf(Opcode.PUSH_MEM_UB, Opcode.UB2UWORD, Opcode.POP_VAR_WORD)) { segment ->
" lda ${hexVal(segment[0])} | sta ${segment[2].callLabel} | lda #0 | sta ${segment[2].callLabel}+1"
@ -1393,6 +1402,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_MEM_WORD)) { segment ->
" lda ${segment[0].callLabel} || sta ${hexVal(segment[1])} | lda ${segment[0].callLabel}+1 | sta ${hexValPlusOne(segment[1])}"
},
// mem uword = address-of var
AsmPattern(listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.POP_MEM_WORD)) { segment ->
" lda #<${segment[0].callLabel} || sta ${hexVal(segment[1])} | lda #>${segment[0].callLabel} | sta ${hexValPlusOne(segment[1])}"
},
// mem (u)word = mem (u)word
AsmPattern(
listOf(Opcode.PUSH_MEM_UW, Opcode.POP_MEM_WORD),
@ -1607,6 +1620,20 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
sta ${segment[2].callLabel}+1,y
"""
},
// uwordarray[index mem] = address-of var
AsmPattern(
listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.PUSH_MEM_B, Opcode.WRITE_INDEXED_VAR_WORD),
listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.PUSH_MEM_UB, Opcode.WRITE_INDEXED_VAR_WORD)) { segment ->
"""
lda ${hexVal(segment[1])}
asl a
tay
lda #<${segment[0].callLabel}
sta ${segment[2].callLabel},y
lda #>${segment[0].callLabel}
sta ${segment[2].callLabel}+1,y
"""
},
// uwordarray[index] = ubytevar
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.UB2UWORD, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment ->
val index = segment[2].arg!!.integerValue()*2
@ -1898,6 +1925,11 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
val index = segment[1].arg!!.integerValue()*2
" lda ${segment[0].callLabel} | sta ${segment[2].callLabel}+$index | lda ${segment[0].callLabel}+1 | sta ${segment[2].callLabel}+${index+1}"
},
// uwordarray[index] = address-of var
AsmPattern(listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment ->
val index = segment[1].arg!!.integerValue()*2
" lda #<${segment[0].callLabel} | sta ${segment[2].callLabel}+$index | lda #>${segment[0].callLabel} | sta ${segment[2].callLabel}+${index+1}"
},
// uwordarray[index] = mem uword
AsmPattern(listOf(Opcode.PUSH_MEM_UW, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment ->
val index = segment[1].arg!!.integerValue()*2
@ -1967,6 +1999,16 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
}
" $loadIndexY | lda ${segment[0].callLabel} | sta ${segment[2].callLabel},y | lda ${segment[0].callLabel}+1 | sta ${segment[2].callLabel}+1,y"
},
// uwordarray[indexvar] = address-of var
AsmPattern(listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.PUSH_VAR_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment ->
val loadIndexY = when(segment[1].callLabel) {
"A" -> " asl a | tay"
"X" -> " txa | asl a | tay"
"Y" -> " tya | asl a | tay"
else -> " lda ${segment[1].callLabel} | asl a | tay"
}
" $loadIndexY | lda #<${segment[0].callLabel} | sta ${segment[2].callLabel},y | lda #>${segment[0].callLabel} | sta ${segment[2].callLabel}+1,y"
},
// uwordarray[indexvar] = mem ubyte
AsmPattern(listOf(Opcode.PUSH_MEM_UB, Opcode.UB2UWORD, Opcode.PUSH_VAR_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment ->
val loadIndexY = when(segment[2].callLabel) {
@ -2505,15 +2547,15 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
},
// AX register pair = word var
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_REGAX_WORD)) { segment ->
" lda #<${segment[0].callLabel} | ldx #>${segment[0].callLabel}"
" lda ${segment[0].callLabel} | ldx ${segment[0].callLabel}+1"
},
// AY register pair = word var
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_REGAY_WORD)) { segment ->
" lda #<${segment[0].callLabel} | ldy #>${segment[0].callLabel}"
" lda ${segment[0].callLabel} | ldy ${segment[0].callLabel}+1"
},
// XY register pair = word var
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_REGXY_WORD)) { segment ->
" ldx #<${segment[0].callLabel} | ldy #>${segment[0].callLabel}"
" ldx ${segment[0].callLabel} | ldy ${segment[0].callLabel}+1"
},
// AX register pair = mem word
AsmPattern(

View File

@ -1323,6 +1323,19 @@ class StackVm(private var traceOutputFile: String?) {
P_irqd = evalstack.pop().asBooleanValue
}
Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code")
Opcode.PUSH_ADDR_HEAPVAR -> {
val heapId = variables[ins.callLabel]!!.heapId
if(heapId<=0)
throw VmExecutionException("expected variable on heap")
evalstack.push(Value(DataType.UWORD, heapId)) // push the "address" of the string or array variable
}
Opcode.PUSH_ADDR_STR -> {
val heapId = ins.arg!!.heapId
if(heapId<=0)
throw VmExecutionException("expected string to be on heap")
evalstack.push(Value(DataType.UWORD, heapId)) // push the "address" of the string
}
//else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}")
}

View File

@ -207,8 +207,6 @@ Array types are also supported. They can be made of bytes, words and floats::
Right now, the array should be small enough to be indexable by a single byte index.
This means byte arrays should be <= 256 elements, word arrays <= 128 elements, and float
arrays <= 51 elements. This limit may or may not be lifted in a future version.
Matrixes can be indexed in each dimension only by a byte as well, this also means
their maximum size is 65536 elements (bytes).
Note that the various keywords for the data type and variable type (``byte``, ``word``, ``const``, etc.)
@ -300,7 +298,7 @@ When declaring values with an initial value, this value will be set into the var
the program reaches the declaration again. This can be in loops, multiple subroutine calls,
or even multiple invocations of the entire program.
This only works for simple types, *and not for string variables, arrays and matrices*.
This only works for simple types, *and not for string variables and arrays*.
It is assumed these are left unchanged by the program.
If you do modify them in-place, you should take care yourself that they work as
expected when the program is restarted.