Merge remote-tracking branch 'origin/master'

This commit is contained in:
Irmen de Jong 2018-10-29 10:24:30 +01:00
commit 1e776b1f53
20 changed files with 1058 additions and 1010 deletions

2
.gitignore vendored
View File

@ -23,3 +23,5 @@ __pycache__/
parser.out parser.out
parsetab.py parsetab.py
.pytest_cache/ .pytest_cache/
compiler/src/prog8_kotlin.jar
compiler/src/compiled_java

View File

@ -7,10 +7,10 @@
sub start() { sub start() {
ubyte pixely = 255
ubyte ub = 0
byte b = 99 byte b = 99
byte b2 = 100 byte b2 = 100
ubyte ub = 255
ubyte ub2 = 0
word w = 999 word w = 999
word w2 = 3 word w2 = 3
uword uw = 40000 uword uw = 40000
@ -33,7 +33,6 @@ sub start() {
ubyte[2,3] ubmatrix1 ubyte[2,3] ubmatrix1
ubyte[2,3] ubmatrix2 ubyte[2,3] ubmatrix2
memory byte mbyte = $c000 memory byte mbyte = $c000
memory byte mbyte2 = $d000 memory byte mbyte2 = $d000
memory ubyte mubyte = $c001 memory ubyte mubyte = $c001
@ -44,156 +43,138 @@ sub start() {
memory uword muword2 = $d004 memory uword muword2 = $d004
memory float mfloat = $c006 memory float mfloat = $c006
memory float mfloat2 = $d006 memory float mfloat2 = $d006
memory byte[3] mbarr1 = $e000
memory ubyte[3] mubarr1 = $e100
memory word[3] mwarr1 = $e100
memory uword[3] muwarr1 = $e100
;label: str string = "hello"
str_p stringp = "hello"
; all possible assignments to a BYTE VARIABLE (not array)
byte_assignment_to_register:
A = 42
A = X
A = ub
A = mubyte
A = AY[4]
A = ubarr1[2]
A = string[4]
A = string[X]
A = string[b]
A = string[ub]
A = ubarr1[X]
A = ubarr1[b]
A = ubarr1[ub]
A = AY[Y]
A = AY[b]
A = AY[ub]
A = ubmatrix1[1,2]
;A = ubmatrix1[1,Y] ; todo via evaluation
A = ubmatrix1[X,2] ; todo via evaluation TODO fix error constant y dimension of index should have been const-folded with x into one value
;A = ubmatrix1[X,Y] ; todo via evaluation
;A = ubmatrix1[1,b2] ; todo via evaluation
;A = ubmatrix1[X,b2] ; todo via evaluation
A = ubmatrix1[b2,2] ; todo FIX ERROR constant y dimension of index should have been const-folded with x into one value
;A = ubmatrix1[b2,X] ; todo via evaluation
;A = ubmatrix1[b,b2] ; todo via evaluation
;A = ubmatrix1[ub,ub2] ; todo via evaluation
;byte_assignment_to_bytevar:
; b = 42
; b = b2
; b = mbyte
; b = barr1[2]
; b = bmatrix1[1,2]
; ;
; while A>99 { ; ub = 42
; X=22 ; ub = X
; } ; ub = ub2
; ub = mubyte
; ub = ubarr1[2]
; ub = ubmatrix1[1,2]
; ub = string[4]
; ub = AY[4]
; ;
; repeat {
; X=22
; } until A>99
; ;
; for X in 0 to 99 { ;; all possible assignments to a WORD VARIABLE (not array)
; Y=33
; }
; ;
; for ubyte derp in 2 to 44 { ;word_assignment_to_registerpair:
; X=44 ; AY = 42
; } ; AY = 42.w
; AY = 42555
; AY = X
; AY = XY
; AY = ub
; AY = mubyte
; AY = ubarr1[2]
; AY = ubmatrix1[1,2]
; AY = string[4]
; AY = uw
; AY = muword
; AY = uwarr1[2]
; AY = string[4]
; AY = XY[4]
; ;
; if A<22 goto label ;;word_assignment_to_wordvar:
; w = -42
; w = -42.w
; w = -12345
; w = X
; w = b2
; w = ub2
; w = w2
; w = mbyte
; w = mubyte
; w = mword
; w = barr1[2]
; w = ubarr1[2]
; w = warr1[2]
; w = bmatrix1[1,2]
; w = ubmatrix1[1,2]
; w = string[4]
; w = AY[4]
; ;
; if X<22 { ; uw = 42
; A=99 ; uw = 42.w
; } else { ; uw = 42555
; Y=42 ; uw = X
; } ; uw = AY
; uw = ub2
Y=42 ; uw = uw2
AY=42 ; uw = mubyte
AY=42555 ; uw = muword
Y = ub ; uw = ubarr1[2]
AY= ub ; uw = uwarr1[2]
AY= uw ; uw = ubmatrix1[1,2]
; uw = string[4]
Y = mubyte ; uw = AY[4]
AY = mubyte ;
AY = muword ;
;; all possible assignments to a FLOAT VARIABLE
Y = ubarr1[2] ;float_assignment_to_floatvar:
AY = ubarr1[2] ; fl1 = 34
AY = uwarr1[2] ; fl1 = 34555.w
; fl1 = 3.33e22
barr1[2]=42 ; fl1 = X
ubarr1[2]=42 ; fl1 = AY
warr1[2]=12555 ; fl1 = b2
uwarr1[2]=42555 ; fl1 = ub2
farr1[2]=42.5678 ; fl1 = w2
; fl1 = uw2
ubarr1[2]=X ; fl1 = mbyte
uwarr1[2]=XY ; fl1 = mubyte
; farr1[2]=XY ; @todo ; fl1 = mword
; fl1 = muword
barr1[2] = b ; fl1 = barr1[2]
ubarr1[2] = ub ; fl1 = ubarr1[2]
warr1[2] = w ; fl1 = warr1[2]
uwarr1[2] = uw ; fl1 = uwarr1[2]
farr1[2] = fl1 ; fl1 = bmatrix1[1,2]
; fl1 = ubmatrix1[1,2]
barr1[2] = mbyte ; fl1 = string[4]
ubarr1[2] = mubyte
warr1[2] = mword
uwarr1[2] = muword
farr1[2] = mfloat
b= barr1[2]
ub = ubarr1[2]
w = warr1[2]
uw = uwarr1[2]
; fl1 = farr1[2] ; @todo
mbyte= barr1[2]
mubyte = ubarr1[2]
mword = warr1[2]
muword = uwarr1[2]
; mfloat = farr1[2] ; @todo
barr1[2] = barr2[3]
ubarr1[2] = ubarr2[3]
warr1[2] = warr2[3]
uwarr1[2] = uwarr2[3]
; farr1[2] = farr2[3] ; @todo
XY[2]=42
XY[2] = ub
XY[2] = mubyte
ub = XY[2]
uw = XY[2]
;fl1 = XY[2] ; @todo
mubyte = XY[2]
muword = XY[2]
;mfloat = XY[2] ; @todo
XY[2] = AY[3] ; @todo wat is de output hiervan???
b = 1
ub = 1
w = 1
uw = 1
fl1 = 2.345
b = b2
ub = pixely
w = b2
w = w2
w = ub
uw = ub
uw = uw2
;fl1 = ub ; @todo
;fl1 = b2 ; @todo
;fl1 = uw2 ; @todo
;fl1 = w2 ; @todo
fl1 = fl2
b = mbyte
ub = mubyte
w = mword
w = mbyte
w = mubyte
uw = mubyte
uw = muword
fl1 = mfloat
;fl1 = mbyte ; @todo
;fl1 = mword ; @todo
;fl1 = mubyte ; @todo
;fl1 = muword ; @todo
mbyte = 1
mubyte = 1
mword = 1
muword = 1
mfloat = 3.456
%breakpoint
mbyte = b
mubyte = ub
mword = w
muword = uw
mfloat = fl2
%breakpoint
mbyte = mbyte2
mubyte = mubyte2
mword = mword2
muword = muword2
mfloat = mfloat2
return return
} }

View File

@ -1,6 +1,6 @@
mkdir compiled_java mkdir compiled_java
java -jar ../antlr/lib/antlr-4.7.1-complete.jar -o ./prog8/parser -no-listener -no-visitor -package prog8.parser ../antlr/prog8.g4 java -jar ../antlr/lib/antlr-4.7.1-complete.jar -o ./prog8/parser -Xexact-output-dir -no-listener -no-visitor -package prog8.parser ../antlr/prog8.g4
@dir /b /S src *.java > sources.txt @dir /b /S src *.java > sources.txt
javac -verbose -d compiled_java -cp ../antlr/lib/antlr-runtime-4.7.1.jar @sources.txt javac -verbose -d compiled_java -cp ../antlr/lib/antlr-runtime-4.7.1.jar @sources.txt

View File

@ -1,5 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
java -jar ../antlr/lib/antlr-4.7.1-complete.jar -o ./prog8/parser -Xexact-output-dir -no-listener -no-visitor -package prog8.parser ../antlr/prog8.g4
find prog8 -name \*.java > javasources.txt find prog8 -name \*.java > javasources.txt
mkdir -p compiled_java mkdir -p compiled_java
javac -verbose -d compiled_java -cp ../antlr/lib/antlr-runtime-4.7.1.jar @javasources.txt javac -verbose -d compiled_java -cp ../antlr/lib/antlr-runtime-4.7.1.jar @javasources.txt

View File

@ -67,7 +67,7 @@ enum class BranchCondition {
} }
val IterableDatatypes = setOf( val IterableDatatypes = setOf(
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.STR, DataType.STR_S, // note: the STR_P/STR_PS types aren't iterable because they store their length as the first byte
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_F, DataType.MATRIX_UB, DataType.MATRIX_B) DataType.ARRAY_F, DataType.MATRIX_UB, DataType.MATRIX_B)
@ -231,7 +231,7 @@ interface IAstProcessor {
fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression { fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
arrayIndexedExpression.identifier?.process(this) arrayIndexedExpression.identifier?.process(this)
arrayIndexedExpression.array.process(this) arrayIndexedExpression.arrayspec.process(this)
return arrayIndexedExpression return arrayIndexedExpression
} }
@ -622,7 +622,7 @@ class VarDecl(val type: VarDeclType,
DataType.WORD -> DataType.ARRAY_W DataType.WORD -> DataType.ARRAY_W
DataType.FLOAT -> DataType.ARRAY_F DataType.FLOAT -> DataType.ARRAY_F
else -> { else -> {
datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats", position)) datatypeErrors.add(SyntaxError("arrayspec can only contain bytes/words/floats", position))
DataType.UBYTE DataType.UBYTE
} }
} }
@ -898,13 +898,13 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
class ArrayIndexedExpression(val identifier: IdentifierReference?, class ArrayIndexedExpression(val identifier: IdentifierReference?,
val register: Register?, val register: Register?,
var array: ArraySpec, var arrayspec: ArraySpec,
override val position: Position) : IExpression { override val position: Position) : IExpression {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
identifier?.linkParents(this) identifier?.linkParents(this)
array.linkParents(this) arrayspec.linkParents(this)
} }
override fun isIterable(namespace: INameScope, heap: HeapValues) = false override fun isIterable(namespace: INameScope, heap: HeapValues) = false
@ -930,6 +930,10 @@ class ArrayIndexedExpression(val identifier: IdentifierReference?,
} }
throw FatalAstException("cannot get indexed element on $target") throw FatalAstException("cannot get indexed element on $target")
} }
override fun toString(): String {
return "ArrayIndexed(ident=$identifier, reg=$register, arrayspec=$arrayspec; pos=$position)"
}
} }
@ -1046,8 +1050,8 @@ class LiteralValue(val type: DataType,
else "str:$strvalue" else "str:$strvalue"
} }
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> {
if(heapId!=null) "array:#$heapId" if(heapId!=null) "arrayspec:#$heapId"
else "array:$arrayvalue" else "arrayspec:$arrayvalue"
} }
DataType.MATRIX_UB, DataType.MATRIX_B -> { DataType.MATRIX_UB, DataType.MATRIX_B -> {
if(heapId!=null) "matrix:#$heapId" if(heapId!=null) "matrix:#$heapId"
@ -1936,7 +1940,7 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
litval.charliteral()!=null -> LiteralValue(DataType.UBYTE, bytevalue = Petscii.encodePetscii(litval.charliteral().text.unescape(), true)[0], position = litval.toPosition()) litval.charliteral()!=null -> LiteralValue(DataType.UBYTE, bytevalue = Petscii.encodePetscii(litval.charliteral().text.unescape(), true)[0], position = litval.toPosition())
litval.arrayliteral()!=null -> { litval.arrayliteral()!=null -> {
val array = litval.arrayliteral()?.toAst() val array = litval.arrayliteral()?.toAst()
// the actual type of the array can not yet be determined here (missing namespace & heap) // the actual type of the arrayspec can not yet be determined here (missing namespace & heap)
// the ConstantFolder takes care of that and converts the type if needed. // the ConstantFolder takes care of that and converts the type if needed.
LiteralValue(DataType.ARRAY_UB, arrayvalue = array, position = litval.toPosition()) LiteralValue(DataType.ARRAY_UB, arrayvalue = array, position = litval.toPosition())
} }

View File

@ -247,7 +247,7 @@ class AstChecker(private val namespace: INameScope,
if(param.second.register==Register.AX || param.second.register==Register.AY || if(param.second.register==Register.AX || param.second.register==Register.AY ||
param.second.register==Register.XY) { param.second.register==Register.XY) {
if(param.first.type!=DataType.UWORD && param.first.type !in StringDatatypes && param.first.type !in ArrayDatatypes) if(param.first.type!=DataType.UWORD && param.first.type !in StringDatatypes && param.first.type !in ArrayDatatypes)
err("parameter '${param.first.name}' should be uword/str/array") err("parameter '${param.first.name}' should be uword/str/arrayspec")
} }
} }
for(ret in subroutine.returntypes.withIndex().zip(subroutine.asmReturnvaluesRegisters)) { for(ret in subroutine.returntypes.withIndex().zip(subroutine.asmReturnvaluesRegisters)) {
@ -260,7 +260,7 @@ class AstChecker(private val namespace: INameScope,
ret.second.register==Register.XY) { ret.second.register==Register.XY) {
if(ret.first.value!=DataType.UWORD && ret.first.value != DataType.UBYTE && if(ret.first.value!=DataType.UWORD && ret.first.value != DataType.UBYTE &&
ret.first.value !in StringDatatypes && ret.first.value !in ArrayDatatypes) ret.first.value !in StringDatatypes && ret.first.value !in ArrayDatatypes)
err("return value #${ret.first.index+1} should be uword/ubyte/string/array") err("return value #${ret.first.index+1} should be uword/ubyte/string/arrayspec")
} }
} }
@ -330,11 +330,17 @@ class AstChecker(private val namespace: INameScope,
return super.process(assignment) return super.process(assignment)
} }
} }
} else if(assignment.target.arrayindexed!=null) {
if(assignment.target.arrayindexed!!.register!=null) {
val value = assignment.value
if (value is ArrayIndexedExpression && value.register in setOf(Register.AX, Register.AY, Register.XY))
checkResult.add(SyntaxError("reading AND writing from registerpair arrays not supported due to register overlap", assignment.position))
}
} }
// it is not possible to assign a new array to something. // it is not possible to assign a new arrayspec to something.
if(assignment.value.resultingDatatype(namespace, heap) in ArrayDatatypes) if(assignment.value.resultingDatatype(namespace, heap) in ArrayDatatypes)
checkResult.add(SyntaxError("it's not possible to assign an array literal value to something, use it as a variable decl initializer instead", assignment.position)) 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))
if(assignment.aug_op!=null) { if(assignment.aug_op!=null) {
// check augmented assignment: // check augmented assignment:
@ -440,6 +446,25 @@ class AstChecker(private val namespace: INameScope,
} }
} }
VarDeclType.MEMORY -> { VarDeclType.MEMORY -> {
if(decl.arrayspec!=null) {
val arraySize = decl.arrayspec.size() ?: 1
when(decl.datatype) {
DataType.ARRAY_B, DataType.ARRAY_UB ->
if(arraySize > 256)
err("byte arrayspec length must be 1-256")
DataType.ARRAY_W, DataType.ARRAY_UW ->
if(arraySize > 128)
err("word arrayspec length must be 1-128")
DataType.ARRAY_F ->
if(arraySize > 51)
err("float arrayspec length must be 1-51")
DataType.MATRIX_B, DataType.MATRIX_UB ->
if(arraySize > 32768)
err("invalid matrix size, must be 1-32768")
else -> {}
}
}
if(decl.value !is LiteralValue) { if(decl.value !is LiteralValue) {
err("value of memory var decl is not a literal (it is a ${decl.value!!::class.simpleName}).", decl.value?.position) err("value of memory var decl is not a literal (it is a ${decl.value!!::class.simpleName}).", decl.value?.position)
} else { } else {
@ -545,7 +570,7 @@ class AstChecker(private val namespace: INameScope,
} }
in ArrayDatatypes -> { in ArrayDatatypes -> {
if(lv.heapId==null) if(lv.heapId==null)
throw FatalAstException("array/matrix should have been moved to heap at ${lv.position}") throw FatalAstException("arrayspec/matrix should have been moved to heap at ${lv.position}")
} }
else -> {} else -> {}
} }
@ -677,7 +702,7 @@ class AstChecker(private val namespace: INameScope,
val indexedRegister = postIncrDecr.target.arrayindexed?.register val indexedRegister = postIncrDecr.target.arrayindexed?.register
if(indexedRegister!=null) { if(indexedRegister!=null) {
if(indexedRegister==Register.A || indexedRegister==Register.X || indexedRegister==Register.Y) if(indexedRegister==Register.A || indexedRegister==Register.X || indexedRegister==Register.Y)
checkResult.add(SyntaxError("array indexing on registers requires register pair variable", postIncrDecr.position)) checkResult.add(SyntaxError("indexing on registers requires register pair variable", postIncrDecr.position))
} else { } else {
val target = postIncrDecr.target.arrayindexed?.identifier?.targetStatement(namespace) val target = postIncrDecr.target.arrayindexed?.identifier?.targetStatement(namespace)
if(target==null) { if(target==null) {
@ -699,25 +724,45 @@ class AstChecker(private val namespace: INameScope,
val target = arrayIndexedExpression.identifier!!.targetStatement(namespace) val target = arrayIndexedExpression.identifier!!.targetStatement(namespace)
if(target is VarDecl) { if(target is VarDecl) {
if(target.datatype !in IterableDatatypes) if(target.datatype !in IterableDatatypes)
checkResult.add(SyntaxError("array indexing requires an iterable variable", arrayIndexedExpression.position)) checkResult.add(SyntaxError("indexing requires an iterable variable", arrayIndexedExpression.position))
val arraysize = target.arrayspec?.size() val arraysize = target.arrayspec?.size()
if(arraysize!=null) { if(arraysize!=null) {
// check out of bounds // check out of bounds
if((arrayIndexedExpression.array.y as? LiteralValue)?.asIntegerValue != null) { if((arrayIndexedExpression.arrayspec.y as? LiteralValue)?.asIntegerValue != null) {
throw FatalAstException("constant y dimension of index should have been const-folded with x into one value") throw FatalAstException("constant y dimension of index should have been const-folded with x into one value ${arrayIndexedExpression.arrayspec.position}")
} }
val index = (arrayIndexedExpression.array.x as? LiteralValue)?.asIntegerValue val index = (arrayIndexedExpression.arrayspec.x as? LiteralValue)?.asIntegerValue
if(index!=null && (index<0 || index>=arraysize)) if(index!=null && (index<0 || index>=arraysize))
checkResult.add(ExpressionError("array index out of bounds", arrayIndexedExpression.array.position)) checkResult.add(ExpressionError("arrayspec index out of bounds", arrayIndexedExpression.arrayspec.position))
} else if(target.datatype in StringDatatypes) {
// check string lengths
(arrayIndexedExpression.arrayspec.y as? LiteralValue)?.asIntegerValue
val heapId = (target.value as LiteralValue).heapId!!
val stringLen = heap.get(heapId).str!!.length
val index = (arrayIndexedExpression.arrayspec.x as? LiteralValue)?.asIntegerValue
if(index!=null && (index<0 || index>=stringLen))
checkResult.add(ExpressionError("index out of bounds", arrayIndexedExpression.arrayspec.position))
} }
} else } else
checkResult.add(SyntaxError("array indexing requires a variable to act upon", arrayIndexedExpression.position)) checkResult.add(SyntaxError("indexing requires a variable to act upon", arrayIndexedExpression.position))
} else if(reg==Register.A || reg==Register.X || reg==Register.Y) { } else if(reg==Register.A || reg==Register.X || reg==Register.Y) {
checkResult.add(SyntaxError("array indexing on registers requires register pair variable", arrayIndexedExpression.position)) checkResult.add(SyntaxError("indexing on registers requires register pair variable", arrayIndexedExpression.position))
} else if(arrayIndexedExpression.array.y!=null) { } else if(arrayIndexedExpression.arrayspec.y!=null) {
checkResult.add(SyntaxError("array indexing on registers can only use one index dimension", arrayIndexedExpression.position)) checkResult.add(SyntaxError("indexing on registers can only use one index dimension", arrayIndexedExpression.position))
} }
// check index value 0..255
val regx = (arrayIndexedExpression.arrayspec.x as? RegisterExpr)?.register
val regy = (arrayIndexedExpression.arrayspec.y as? RegisterExpr)?.register
if((regx in setOf(Register.AX, Register.AY, Register.XY)) ||
(regy in setOf(Register.AX, Register.AY, Register.XY))) {
checkResult.add(SyntaxError("array indexing is limited to byte size 0..255", arrayIndexedExpression.position))
}
val dtx = arrayIndexedExpression.arrayspec.x.resultingDatatype(namespace, heap)
val dty = arrayIndexedExpression.arrayspec.y?.resultingDatatype(namespace, heap)
if(dtx!=DataType.UBYTE && dtx!=DataType.BYTE || (dty!=null && dty != DataType.UBYTE && dty != DataType.BYTE))
checkResult.add(SyntaxError("array indexing is limited to byte size 0..255", arrayIndexedExpression.position))
return super.process(arrayIndexedExpression) return super.process(arrayIndexedExpression)
} }
@ -760,7 +805,7 @@ class AstChecker(private val namespace: INameScope,
val expectedSize = arrayspec!!.size() val expectedSize = arrayspec!!.size()
val rangeSize=range.size() val rangeSize=range.size()
if(rangeSize!=null && rangeSize != expectedSize) { if(rangeSize!=null && rangeSize != expectedSize) {
checkResult.add(ExpressionError("range size doesn't match array/matrix size, expected $expectedSize found $rangeSize", range.position)) checkResult.add(ExpressionError("range size doesn't match arrayspec/matrix size, expected $expectedSize found $rangeSize", range.position))
return false return false
} }
return true return true
@ -825,61 +870,61 @@ class AstChecker(private val namespace: INameScope,
return err("string length must be 0-255") return err("string length must be 0-255")
} }
DataType.ARRAY_UB, DataType.ARRAY_B -> { DataType.ARRAY_UB, DataType.ARRAY_B -> {
// value may be either a single byte, or a byte array (of all constant values) // value may be either a single byte, or a byte arrayspec (of all constant values)
if(value.type==targetDt) { if(value.type==targetDt) {
val arraySpecSize = arrayspec.size() val arraySpecSize = arrayspec.size()
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>256) if(arraySpecSize<1 || arraySpecSize>256)
return err("byte array length must be 1-256") return err("byte arrayspec length must be 1-256")
val constX = arrayspec.x.constValue(namespace, heap) val constX = arrayspec.x.constValue(namespace, heap)
if(constX?.asIntegerValue==null) if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value") return err("arrayspec size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue val expectedSize = constX.asIntegerValue
if (arraySize != expectedSize) if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") return err("initializer arrayspec size mismatch (expecting $expectedSize, got $arraySize)")
return true return true
} }
return err("invalid byte array size, must be 1-256") return err("invalid byte arrayspec size, must be 1-256")
} }
return err("invalid byte array initialization value ${value.type}, expected $targetDt") return err("invalid byte arrayspec initialization value ${value.type}, expected $targetDt")
} }
DataType.ARRAY_UW, DataType.ARRAY_W -> { DataType.ARRAY_UW, DataType.ARRAY_W -> {
// value may be either a single word, or a word array // value may be either a single word, or a word arrayspec
if(value.type==targetDt) { if(value.type==targetDt) {
val arraySpecSize = arrayspec.size() val arraySpecSize = arrayspec.size()
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>128) if(arraySpecSize<1 || arraySpecSize>128)
return err("word array length must be 1-128") return err("word arrayspec length must be 1-128")
val constX = arrayspec.x.constValue(namespace, heap) val constX = arrayspec.x.constValue(namespace, heap)
if(constX?.asIntegerValue==null) if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value") return err("arrayspec size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue val expectedSize = constX.asIntegerValue
if (arraySize != expectedSize) if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") return err("initializer arrayspec size mismatch (expecting $expectedSize, got $arraySize)")
return true return true
} }
return err("invalid word array size, must be 1-128") return err("invalid word arrayspec size, must be 1-128")
} }
return err("invalid word array initialization value ${value.type}, expected $targetDt") return err("invalid word arrayspec initialization value ${value.type}, expected $targetDt")
} }
DataType.ARRAY_F -> { DataType.ARRAY_F -> {
// value may be either a single float, or a float array // value may be either a single float, or a float arrayspec
if(value.type==targetDt) { if(value.type==targetDt) {
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).doubleArray!!.size val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).doubleArray!!.size
val arraySpecSize = arrayspec.size() val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize < 1 || arraySpecSize>51) if(arraySpecSize < 1 || arraySpecSize>51)
return err("float array length must be 1-51") return err("float arrayspec length must be 1-51")
val constX = arrayspec.x.constValue(namespace, heap) val constX = arrayspec.x.constValue(namespace, heap)
if(constX?.asIntegerValue==null) if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value") return err("arrayspec size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue val expectedSize = constX.asIntegerValue
if (arraySize != expectedSize) if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") return err("initializer arrayspec size mismatch (expecting $expectedSize, got $arraySize)")
} else } else
return err("invalid float array size, must be 1-51") return err("invalid float arrayspec size, must be 1-51")
// check if the floating point values are all within range // check if the floating point values are all within range
val doubles = if(value.arrayvalue!=null) val doubles = if(value.arrayvalue!=null)
@ -890,17 +935,17 @@ class AstChecker(private val namespace: INameScope,
return err("floating point value overflow") return err("floating point value overflow")
return true return true
} }
return err("invalid float array initialization value ${value.type}, expected $targetDt") return err("invalid float arrayspec initialization value ${value.type}, expected $targetDt")
} }
DataType.MATRIX_UB, DataType.MATRIX_B -> { DataType.MATRIX_UB, DataType.MATRIX_B -> {
// value can only be a single byte, or a byte array (which represents the matrix) // value can only be a single byte, or a byte arrayspec (which represents the matrix)
if(value.type==targetDt || if(value.type==targetDt ||
(targetDt==DataType.MATRIX_UB && value.type==DataType.ARRAY_UB) || (targetDt==DataType.MATRIX_UB && value.type==DataType.ARRAY_UB) ||
(targetDt==DataType.MATRIX_B && value.type==DataType.ARRAY_B)) { (targetDt==DataType.MATRIX_B && value.type==DataType.ARRAY_B)) {
val arraySpecSize = arrayspec.size() val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>256) if(arraySpecSize<1 || arraySpecSize>32768)
return err("invalid matrix size, must be 1-256") return err("invalid matrix size, must be 1-32768")
val constX = arrayspec.x.constValue(namespace, heap) val constX = arrayspec.x.constValue(namespace, heap)
val constY = arrayspec.y?.constValue(namespace, heap) val constY = arrayspec.y?.constValue(namespace, heap)
if (constX?.asIntegerValue == null || (constY!=null && constY.asIntegerValue == null)) if (constX?.asIntegerValue == null || (constY!=null && constY.asIntegerValue == null))
@ -912,9 +957,9 @@ class AstChecker(private val namespace: INameScope,
return err("initializer matrix size mismatch (expecting $expectedSize, got ${matrix.size} elements)") return err("initializer matrix size mismatch (expecting $expectedSize, got ${matrix.size} elements)")
return true return true
} }
return err("invalid matrix size, must be 1-256") return err("invalid matrix size, must be 1-32768")
} }
return err("invalid matrix initialization value of type ${value.type} - expecting byte array") return err("invalid matrix initialization value of type ${value.type} - expecting byte arrayspec")
} }
} }
return true return true

View File

@ -126,7 +126,7 @@ class AstIdentifiersChecker : IAstProcessor {
if(forLoop.decltype!=null) if(forLoop.decltype!=null)
checkResult.add(SyntaxError("register loop variables cannot be explicitly declared with a datatype", forLoop.position)) checkResult.add(SyntaxError("register loop variables cannot be explicitly declared with a datatype", forLoop.position))
if(forLoop.loopRegister == Register.X || forLoop.loopRegister==Register.XY || forLoop.loopRegister==Register.AX) if(forLoop.loopRegister == Register.X || forLoop.loopRegister==Register.XY || forLoop.loopRegister==Register.AX)
checkResult.add(SyntaxError("it's not possible to write to the X register because it's used as an internal pointer", forLoop.position)) printWarning("possible problem writing to the X register, because it's used as an internal pointer", forLoop.position)
} else if(forLoop.loopVar!=null) { } else if(forLoop.loopVar!=null) {
val varName = forLoop.loopVar.nameInSource.last() val varName = forLoop.loopVar.nameInSource.last()
when (forLoop.decltype) { when (forLoop.decltype) {
@ -148,7 +148,7 @@ class AstIdentifiersChecker : IAstProcessor {
override fun process(assignTarget: AssignTarget): AssignTarget { override fun process(assignTarget: AssignTarget): AssignTarget {
if(assignTarget.register==Register.X || assignTarget.register==Register.AX || assignTarget.register==Register.XY) if(assignTarget.register==Register.X || assignTarget.register==Register.AX || assignTarget.register==Register.XY)
checkResult.add(SyntaxError("it's not possible to write to the X register because it's used as an internal pointer", assignTarget.position)) printWarning("possible problem writing to the X register, because it's used as an internal pointer", assignTarget.position)
return super.process(assignTarget) return super.process(assignTarget)
} }
} }

View File

@ -312,7 +312,9 @@ private class StatementTranslator(private val prog: IntermediateProgram,
DataType.ARRAY_UW, DataType.ARRAY_W -> Opcode.READ_INDEXED_VAR_WORD DataType.ARRAY_UW, DataType.ARRAY_W -> Opcode.READ_INDEXED_VAR_WORD
DataType.ARRAY_F -> Opcode.READ_INDEXED_VAR_FLOAT DataType.ARRAY_F -> Opcode.READ_INDEXED_VAR_FLOAT
DataType.MATRIX_UB, DataType.MATRIX_B -> Opcode.READ_INDEXED_VAR_BYTE DataType.MATRIX_UB, DataType.MATRIX_B -> Opcode.READ_INDEXED_VAR_BYTE
else -> throw CompilerException("invalid dt for indexed $dt") DataType.STR, DataType.STR_S -> Opcode.READ_INDEXED_VAR_BYTE
DataType.STR_P, DataType.STR_PS -> throw CompilerException("cannot access pascal-string type $dt with index")
else -> throw CompilerException("invalid dt for indexed access $dt")
} }
} }
@ -322,7 +324,9 @@ private class StatementTranslator(private val prog: IntermediateProgram,
DataType.ARRAY_UW, DataType.ARRAY_W -> Opcode.WRITE_INDEXED_VAR_WORD DataType.ARRAY_UW, DataType.ARRAY_W -> Opcode.WRITE_INDEXED_VAR_WORD
DataType.ARRAY_F -> Opcode.WRITE_INDEXED_VAR_FLOAT DataType.ARRAY_F -> Opcode.WRITE_INDEXED_VAR_FLOAT
DataType.MATRIX_UB, DataType.MATRIX_B -> Opcode.WRITE_INDEXED_VAR_BYTE DataType.MATRIX_UB, DataType.MATRIX_B -> Opcode.WRITE_INDEXED_VAR_BYTE
else -> throw CompilerException("invalid dt for indexed $dt") DataType.STR, DataType.STR_S -> Opcode.WRITE_INDEXED_VAR_BYTE
DataType.STR_P, DataType.STR_PS -> throw CompilerException("cannot access pascal-string type $dt with index")
else -> throw CompilerException("invalid dt for indexed access $dt")
} }
} }
@ -350,14 +354,12 @@ private class StatementTranslator(private val prog: IntermediateProgram,
private fun opcodePopmem(dt: DataType): Opcode { private fun opcodePopmem(dt: DataType): Opcode {
return when (dt) { return when (dt) {
DataType.UBYTE -> Opcode.POP_MEM_UB DataType.UBYTE, DataType.BYTE -> Opcode.POP_MEM_BYTE
DataType.BYTE -> Opcode.POP_MEM_B DataType.UWORD, DataType.WORD -> Opcode.POP_MEM_WORD
DataType.UWORD -> Opcode.POP_MEM_UW
DataType.WORD -> Opcode.POP_MEM_W
DataType.FLOAT -> Opcode.POP_MEM_FLOAT DataType.FLOAT -> Opcode.POP_MEM_FLOAT
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS,
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F, DataType.MATRIX_UB, DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F, DataType.MATRIX_UB,
DataType.ARRAY_B, DataType.ARRAY_W, DataType.MATRIX_B -> Opcode.POP_MEM_UW DataType.ARRAY_B, DataType.ARRAY_W, DataType.MATRIX_B -> Opcode.POP_MEM_WORD
} }
} }
@ -628,7 +630,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F, DataType.MATRIX_UB, DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F, DataType.MATRIX_UB,
DataType.ARRAY_B, DataType.ARRAY_W, DataType.MATRIX_B -> { DataType.ARRAY_B, DataType.ARRAY_W, DataType.MATRIX_B -> {
if(lv.heapId==null) if(lv.heapId==null)
throw CompilerException("array/matrix should have been moved into heap ${lv.position}") throw CompilerException("arrayspec/matrix should have been moved into heap ${lv.position}")
prog.instr(Opcode.PUSH_WORD, Value(lv.type, lv.heapId)) prog.instr(Opcode.PUSH_WORD, Value(lv.type, lv.heapId))
} }
} }
@ -1046,14 +1048,14 @@ private class StatementTranslator(private val prog: IntermediateProgram,
val reg=arrayindexed.register val reg=arrayindexed.register
if(reg==Register.A || reg==Register.X || reg==Register.Y) if(reg==Register.A || reg==Register.X || reg==Register.Y)
throw CompilerException("requires register pair") throw CompilerException("requires register pair")
if(arrayindexed.array.y!=null) if(arrayindexed.arrayspec.y!=null)
throw CompilerException("when using an address, can only use one index dimension") throw CompilerException("when using an address, can only use one index dimension")
reg.toString() reg.toString()
} else { } else {
variable!!.scopedname variable!!.scopedname
} }
translate(arrayindexed.array.x) translate(arrayindexed.arrayspec.x)
val y = arrayindexed.array.y val y = arrayindexed.arrayspec.y
if(y!=null) { if(y!=null) {
// calc matrix index i=y*columns+x // calc matrix index i=y*columns+x
// (the const-folding will have removed this for us when both x and y are constants) // (the const-folding will have removed this for us when both x and y are constants)
@ -1167,12 +1169,12 @@ private class StatementTranslator(private val prog: IntermediateProgram,
when (valueDt) { when (valueDt) {
DataType.UBYTE -> prog.instr(Opcode.UB2FLOAT) DataType.UBYTE -> prog.instr(Opcode.UB2FLOAT)
DataType.BYTE -> prog.instr(Opcode.B2FLOAT) DataType.BYTE -> prog.instr(Opcode.B2FLOAT)
DataType.UWORD -> prog.instr(Opcode.W2FLOAT) DataType.UWORD -> prog.instr(Opcode.UW2FLOAT)
DataType.WORD -> prog.instr(Opcode.UW2FLOAT) DataType.WORD -> prog.instr(Opcode.W2FLOAT)
else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
} }
} }
// todo: maybe if you assign byte or word to array/matrix, clear it with that value? // todo: maybe if you assign byte or word to arrayspec/matrix, clear it with that value?
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_F, DataType.MATRIX_UB, DataType.MATRIX_B -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") DataType.ARRAY_F, DataType.MATRIX_UB, DataType.MATRIX_B -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")

View File

@ -16,15 +16,15 @@ abstract class Zeropage(private val options: CompilationOptions) {
val size = val size =
if(vardecl.arrayspec!=null) { if(vardecl.arrayspec!=null) {
printWarning("allocating a large value (array) in zeropage", vardecl.position) printWarning("allocating a large value (arrayspec) in zeropage", vardecl.position)
val y = (vardecl.arrayspec.y as? LiteralValue)?.asIntegerValue val y = (vardecl.arrayspec.y as? LiteralValue)?.asIntegerValue
if(y==null) { if(y==null) {
// 1 dimensional array // 1 dimensional arrayspec
when(vardecl.datatype) { when(vardecl.datatype) {
DataType.UBYTE -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! DataType.UBYTE -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!!
DataType.UWORD -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 2 DataType.UWORD -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 2
DataType.FLOAT -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 5 DataType.FLOAT -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 5
else -> throw CompilerException("array can only be of byte, word, float") else -> throw CompilerException("arrayspec can only be of byte, word, float")
} }
} else { } else {
// 2 dimensional matrix (only bytes for now) // 2 dimensional matrix (only bytes for now)

View File

@ -25,10 +25,6 @@ open class Instruction(val opcode: Opcode,
// opcodes that manipulate a variable // opcodes that manipulate a variable
"${opcode.toString().toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd() "${opcode.toString().toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd()
} }
opcode in setOf(Opcode.COPY_MEM_BYTE, Opcode.COPY_MEM_WORD, Opcode.COPY_MEM_FLOAT) -> {
// opcodes with two (address) args
"${opcode.toString().toLowerCase()} $arg $arg2"
}
callLabel==null -> "${opcode.toString().toLowerCase()} $argStr" callLabel==null -> "${opcode.toString().toLowerCase()} $argStr"
else -> "${opcode.toString().toLowerCase()} $callLabel $argStr" else -> "${opcode.toString().toLowerCase()} $callLabel $argStr"
} }

View File

@ -79,54 +79,47 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
when (it[0].value.opcode) { when (it[0].value.opcode) {
Opcode.PUSH_VAR_BYTE -> Opcode.PUSH_VAR_BYTE ->
if (it[1].value.opcode == Opcode.POP_VAR_BYTE) { if (it[1].value.opcode == Opcode.POP_VAR_BYTE) {
if (it[0].value.callLabel != it[1].value.callLabel) if (it[0].value.callLabel == it[1].value.callLabel) {
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_VAR_BYTE, null, null, it[0].value.callLabel, it[1].value.callLabel)
else
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
} }
Opcode.PUSH_VAR_WORD -> Opcode.PUSH_VAR_WORD ->
if (it[1].value.opcode == Opcode.POP_VAR_WORD) { if (it[1].value.opcode == Opcode.POP_VAR_WORD) {
if (it[0].value.callLabel != it[1].value.callLabel) if (it[0].value.callLabel == it[1].value.callLabel) {
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_VAR_WORD, null, null, it[0].value.callLabel, it[1].value.callLabel)
else
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
} }
Opcode.PUSH_VAR_FLOAT -> Opcode.PUSH_VAR_FLOAT ->
if (it[1].value.opcode == Opcode.POP_VAR_FLOAT) { if (it[1].value.opcode == Opcode.POP_VAR_FLOAT) {
if (it[0].value.callLabel != it[1].value.callLabel) if (it[0].value.callLabel == it[1].value.callLabel) {
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_VAR_FLOAT, null, null, it[0].value.callLabel, it[1].value.callLabel)
else
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
} }
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB -> Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB ->
if(it[1].value.opcode == Opcode.POP_MEM_B || it[1].value.opcode == Opcode.POP_MEM_UB) { if(it[1].value.opcode == Opcode.POP_MEM_BYTE) {
if(it[0].value.arg != it[1].value.arg) if(it[0].value.arg == it[1].value.arg) {
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_MEM_BYTE, it[0].value.arg, it[1].value.arg)
else
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
} }
Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW -> Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW ->
if(it[1].value.opcode == Opcode.POP_MEM_W || it[1].value.opcode == Opcode.POP_MEM_UW) { if(it[1].value.opcode == Opcode.POP_MEM_WORD) {
if(it[0].value.arg != it[1].value.arg) if(it[0].value.arg == it[1].value.arg) {
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_MEM_WORD, it[0].value.arg, it[1].value.arg)
else
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
} }
Opcode.PUSH_MEM_FLOAT -> Opcode.PUSH_MEM_FLOAT ->
if(it[1].value.opcode == Opcode.POP_MEM_FLOAT) { if(it[1].value.opcode == Opcode.POP_MEM_FLOAT) {
if(it[0].value.arg != it[1].value.arg) if(it[0].value.arg == it[1].value.arg) {
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_MEM_FLOAT, it[0].value.arg, it[1].value.arg)
else
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
} }
else -> { else -> {}
}
} }
} }
@ -289,7 +282,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F -> { DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F -> {
val litval = (decl.value as LiteralValue) val litval = (decl.value as LiteralValue)
if(litval.heapId==null) if(litval.heapId==null)
throw CompilerException("array/matrix should already be in the heap") throw CompilerException("arrayspec/matrix should already be in the heap")
Value(decl.datatype, litval.heapId) Value(decl.datatype, litval.heapId)
} }
} }

View File

@ -4,7 +4,7 @@ enum class Opcode {
// pushing values on the (evaluation) stack // pushing values on the (evaluation) stack
PUSH_BYTE, // push byte value PUSH_BYTE, // push byte value
PUSH_WORD, // push word value (or 'address' of string / array / matrix) PUSH_WORD, // push word value (or 'address' of string / arrayspec / matrix)
PUSH_FLOAT, // push float value PUSH_FLOAT, // push float value
PUSH_MEM_B, // push byte value from memory to stack PUSH_MEM_B, // push byte value from memory to stack
PUSH_MEM_UB, // push unsigned byte value from memory to stack PUSH_MEM_UB, // push unsigned byte value from memory to stack
@ -19,23 +19,13 @@ enum class Opcode {
DISCARD_BYTE, // discard top byte value DISCARD_BYTE, // discard top byte value
DISCARD_WORD, // discard top word value DISCARD_WORD, // discard top word value
DISCARD_FLOAT, // discard top float value DISCARD_FLOAT, // discard top float value
POP_MEM_B, // pop byte value into destination memory address POP_MEM_BYTE, // pop (u)byte value into destination memory address
POP_MEM_UB, // pop byte value into destination memory address POP_MEM_WORD, // pop (u)word value into destination memory address
POP_MEM_W, // pop word value into destination memory address
POP_MEM_UW, // pop word value into destination memory address
POP_MEM_FLOAT, // pop float value into destination memory address POP_MEM_FLOAT, // pop float value into destination memory address
POP_VAR_BYTE, // pop byte value into variable (byte, ubyte) POP_VAR_BYTE, // pop (u)byte value into variable
POP_VAR_WORD, // pop word value into variable (word, uword) POP_VAR_WORD, // pop (u)word value into variable
POP_VAR_FLOAT, // pop float value into variable POP_VAR_FLOAT, // pop float value into variable
// optimized copying of one var to another (replaces push+pop)
COPY_VAR_BYTE,
COPY_VAR_WORD,
COPY_VAR_FLOAT,
COPY_MEM_BYTE,
COPY_MEM_WORD,
COPY_MEM_FLOAT,
// numeric arithmetic // numeric arithmetic
ADD_UB, ADD_UB,
ADD_B, ADD_B,
@ -187,7 +177,7 @@ enum class Opcode {
NOTEQUAL_WORD, NOTEQUAL_WORD,
NOTEQUAL_F, NOTEQUAL_F,
// array access // arrayspec access
READ_INDEXED_VAR_BYTE, READ_INDEXED_VAR_BYTE,
READ_INDEXED_VAR_WORD, READ_INDEXED_VAR_WORD,
READ_INDEXED_VAR_FLOAT, READ_INDEXED_VAR_FLOAT,
@ -233,32 +223,6 @@ val opcodesWithVarArgument = setOf(
Opcode.ROL2_VAR_BYTE, Opcode.ROL2_VAR_WORD, Opcode.ROR2_VAR_BYTE, Opcode.ROR2_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.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.COPY_VAR_BYTE, Opcode.COPY_VAR_WORD, Opcode.COPY_VAR_FLOAT,
Opcode.READ_INDEXED_VAR_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.READ_INDEXED_VAR_FLOAT, 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 Opcode.WRITE_INDEXED_VAR_BYTE, Opcode.WRITE_INDEXED_VAR_WORD, Opcode.WRITE_INDEXED_VAR_FLOAT
) )
val pushOpcodes = setOf(
Opcode.PUSH_BYTE,
Opcode.PUSH_WORD,
Opcode.PUSH_FLOAT,
Opcode.PUSH_MEM_B,
Opcode.PUSH_MEM_UB,
Opcode.PUSH_MEM_W,
Opcode.PUSH_MEM_UW,
Opcode.PUSH_MEM_FLOAT,
Opcode.PUSH_VAR_BYTE,
Opcode.PUSH_VAR_WORD,
Opcode.PUSH_VAR_FLOAT
)
val popOpcodes = setOf(
Opcode.POP_MEM_B,
Opcode.POP_MEM_UB,
Opcode.POP_MEM_W,
Opcode.POP_MEM_UW,
Opcode.POP_MEM_FLOAT,
Opcode.POP_VAR_BYTE,
Opcode.POP_VAR_WORD,
Opcode.POP_VAR_FLOAT
)

File diff suppressed because it is too large Load Diff

View File

@ -100,7 +100,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
if(arglist.type==DataType.ARRAY_UB || arglist.type==DataType.ARRAY_UW || arglist.type==DataType.ARRAY_F || arglist.type==DataType.MATRIX_UB) { if(arglist.type==DataType.ARRAY_UB || arglist.type==DataType.ARRAY_UW || arglist.type==DataType.ARRAY_F || arglist.type==DataType.MATRIX_UB) {
val dt = arglist.arrayvalue!!.map {it.resultingDatatype(namespace, heap)} val dt = arglist.arrayvalue!!.map {it.resultingDatatype(namespace, heap)}
if(dt.any { it!=DataType.UBYTE && it!=DataType.UWORD && it!=DataType.FLOAT}) { if(dt.any { it!=DataType.UBYTE && it!=DataType.UWORD && it!=DataType.FLOAT}) {
throw FatalAstException("fuction $function only accepts array of numeric values") throw FatalAstException("fuction $function only accepts arrayspec of numeric values")
} }
if(dt.any { it==DataType.FLOAT }) return DataType.FLOAT if(dt.any { it==DataType.FLOAT }) return DataType.FLOAT
if(dt.any { it==DataType.UWORD }) return DataType.UWORD if(dt.any { it==DataType.UWORD }) return DataType.UWORD
@ -119,10 +119,10 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
DataType.ARRAY_F -> DataType.FLOAT DataType.ARRAY_F -> DataType.FLOAT
DataType.MATRIX_UB -> DataType.UBYTE DataType.MATRIX_UB -> DataType.UBYTE
DataType.MATRIX_B -> DataType.BYTE DataType.MATRIX_B -> DataType.BYTE
null -> throw FatalAstException("function requires one argument which is an array $function") null -> throw FatalAstException("function requires one argument which is an arrayspec $function")
} }
} }
throw FatalAstException("function requires one argument which is an array $function") throw FatalAstException("function requires one argument which is an arrayspec $function")
} }
val func = BuiltinFunctions[function]!! val func = BuiltinFunctions[function]!!
@ -237,7 +237,7 @@ private fun collectionArgOutputBoolean(args: List<IExpression>, position: Positi
throw NotConstArgumentException() throw NotConstArgumentException()
function(constants.map { it!!.toDouble() }) function(constants.map { it!!.toDouble() })
} else { } else {
val array = heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires array/matrix argument", position) val array = heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires arrayspec/matrix argument", position)
function(array.map { it.toDouble() }) function(array.map { it.toDouble() })
} }
return LiteralValue.fromBoolean(result, position) return LiteralValue.fromBoolean(result, position)
@ -313,7 +313,7 @@ private fun builtinAbs(args: List<IExpression>, position: Position, namespace:IN
private fun builtinAvg(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue { private fun builtinAvg(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
if(args.size!=1) if(args.size!=1)
throw SyntaxError("avg requires array/matrix argument", position) throw SyntaxError("avg requires arrayspec/matrix argument", position)
val iterable = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException() val iterable = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
val result = if(iterable.arrayvalue!=null) { val result = if(iterable.arrayvalue!=null) {
@ -323,7 +323,7 @@ private fun builtinAvg(args: List<IExpression>, position: Position, namespace:IN
(constants.map { it!!.toDouble() }).average() (constants.map { it!!.toDouble() }).average()
} }
else { else {
val array = heap.get(iterable.heapId!!).array ?: throw SyntaxError("avg requires array/matrix argument", position) val array = heap.get(iterable.heapId!!).array ?: throw SyntaxError("avg requires arrayspec/matrix argument", position)
array.average() array.average()
} }
return numericLiteral(result, args[0].position) return numericLiteral(result, args[0].position)

View File

@ -56,10 +56,10 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.MATRIX_UB, DataType.MATRIX_B -> { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.MATRIX_UB, DataType.MATRIX_B -> {
val litval = decl.value as? LiteralValue val litval = decl.value as? LiteralValue
if(litval?.type==DataType.FLOAT) if(litval?.type==DataType.FLOAT)
errors.add(ExpressionError("array requires only integers here", litval.position)) errors.add(ExpressionError("arrayspec requires only integers here", litval.position))
val size = decl.arrayspec!!.size() val size = decl.arrayspec!!.size()
if(litval!=null && litval.isArray) { if(litval!=null && litval.isArray) {
// array initializer value is an array already, keep as-is (or convert to WORDs if needed) // arrayspec initializer value is an arrayspec already, keep as-is (or convert to WORDs if needed)
if(litval.heapId!=null) { if(litval.heapId!=null) {
if (decl.datatype == DataType.MATRIX_UB && litval.type != DataType.MATRIX_UB) { if (decl.datatype == DataType.MATRIX_UB && litval.type != DataType.MATRIX_UB) {
val array = heap.get(litval.heapId).copy(type = DataType.MATRIX_UB) val array = heap.get(litval.heapId).copy(type = DataType.MATRIX_UB)
@ -79,7 +79,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
} }
} }
} else if (size != null) { } else if (size != null) {
// array initializer is empty or a single int, and we know the size; create the array. // arrayspec initializer is empty or a single int, and we know the size; create the arrayspec.
val fillvalue = if (litval == null) 0 else litval.asIntegerValue ?: 0 val fillvalue = if (litval == null) 0 else litval.asIntegerValue ?: 0
when(decl.datatype){ when(decl.datatype){
DataType.ARRAY_UB, DataType.MATRIX_UB -> { DataType.ARRAY_UB, DataType.MATRIX_UB -> {
@ -109,7 +109,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
val litval = decl.value as? LiteralValue val litval = decl.value as? LiteralValue
val size = decl.arrayspec!!.size() val size = decl.arrayspec!!.size()
if(litval!=null && litval.isArray) { if(litval!=null && litval.isArray) {
// array initializer value is an array already, make sure to convert to floats // arrayspec initializer value is an arrayspec already, make sure to convert to floats
if(litval.heapId!=null) { if(litval.heapId!=null) {
val array = heap.get(litval.heapId) val array = heap.get(litval.heapId)
if (array.doubleArray == null) { if (array.doubleArray == null) {
@ -119,7 +119,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
} }
} }
} else if (size != null) { } else if (size != null) {
// array initializer is empty or a single int, and we know the size; create the array. // arrayspec initializer is empty or a single int, and we know the size; create the arrayspec.
val fillvalue = if (litval == null) 0.0 else litval.asNumericValue?.toDouble() ?: 0.0 val fillvalue = if (litval == null) 0.0 else litval.asNumericValue?.toDouble() ?: 0.0
if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE) if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE)
errors.add(ExpressionError("float value overflow", litval?.position ?: decl.position)) errors.add(ExpressionError("float value overflow", litval?.position ?: decl.position))
@ -492,7 +492,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
val array: Array<IExpression> = arraylit.arrayvalue!!.map { it.process(this) }.toTypedArray() val array: Array<IExpression> = arraylit.arrayvalue!!.map { it.process(this) }.toTypedArray()
val allElementsAreConstant = array.fold(true) { c, expr-> c and (expr is LiteralValue)} val allElementsAreConstant = array.fold(true) { c, expr-> c and (expr is LiteralValue)}
if(!allElementsAreConstant) { if(!allElementsAreConstant) {
addError(ExpressionError("array/matrix literal can contain only constant values", arraylit.position)) addError(ExpressionError("arrayspec/matrix literal can contain only constant values", arraylit.position))
return arraylit return arraylit
} else { } else {
val valuesInArray = array.map { it.constValue(namespace, heap)!!.asNumericValue!! } val valuesInArray = array.map { it.constValue(namespace, heap)!!.asNumericValue!! }
@ -533,24 +533,24 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
DataType.MATRIX_UB, DataType.MATRIX_UB,
DataType.MATRIX_B -> heap.add(arrayDt, integerArray) DataType.MATRIX_B -> heap.add(arrayDt, integerArray)
DataType.ARRAY_F -> heap.add(arrayDt, doubleArray) DataType.ARRAY_F -> heap.add(arrayDt, doubleArray)
else -> throw CompilerException("invalid array type") else -> throw CompilerException("invalid arrayspec type")
} }
return LiteralValue(arrayDt, heapId = heapId, position = arraylit.position) return LiteralValue(arrayDt, heapId = heapId, position = arraylit.position)
} }
} }
override fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression { override fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
if(arrayIndexedExpression.array.y!=null) { if(arrayIndexedExpression.arrayspec.y!=null) {
if(arrayIndexedExpression.array.size()!=null) { if(arrayIndexedExpression.arrayspec.size()!=null) {
// both x and y are known // both x and y are known
// calculate the 2-dimension index i = y*columns + x // calculate the 2-dimension index i = y*columns + x
if(arrayIndexedExpression.identifier!=null) { if(arrayIndexedExpression.identifier!=null) {
val x = (arrayIndexedExpression.array.x as LiteralValue).asIntegerValue!! val x = (arrayIndexedExpression.arrayspec.x as LiteralValue).asIntegerValue!!
val y = (arrayIndexedExpression.array.y as LiteralValue).asIntegerValue!! val y = (arrayIndexedExpression.arrayspec.y as LiteralValue).asIntegerValue!!
val variable = arrayIndexedExpression.identifier.targetStatement(namespace) as? VarDecl val variable = arrayIndexedExpression.identifier.targetStatement(namespace) as? VarDecl
if(variable!=null) { if(variable!=null) {
val index = x + y * (variable.arrayspec!!.x as LiteralValue).asIntegerValue!! val index = x + y * (variable.arrayspec!!.x as LiteralValue).asIntegerValue!!
arrayIndexedExpression.array = ArraySpec(LiteralValue.optimalInteger(index, arrayIndexedExpression.array.position), null, arrayIndexedExpression.array.position) arrayIndexedExpression.arrayspec = ArraySpec(LiteralValue.optimalInteger(index, arrayIndexedExpression.arrayspec.position), null, arrayIndexedExpression.arrayspec.position)
} }
} }
} }

View File

@ -124,16 +124,6 @@ class Program (val name: String,
val args = if(parts.size==2) parts[1] else null val args = if(parts.size==2) parts[1] else null
val instruction = when(opcode) { val instruction = when(opcode) {
Opcode.LINE -> Instruction(opcode, null, callLabel = args) Opcode.LINE -> Instruction(opcode, null, callLabel = args)
Opcode.COPY_VAR_BYTE, Opcode.COPY_VAR_WORD, Opcode.COPY_VAR_FLOAT -> {
val (v1, v2) = args!!.split(splitpattern, limit = 2)
Instruction(opcode, null, null, v1, v2)
}
Opcode.COPY_MEM_BYTE, Opcode.COPY_MEM_WORD, Opcode.COPY_MEM_FLOAT -> {
val (v1, v2) = args!!.split(splitpattern, limit = 2)
val address1 = getArgValue(v1, heap)
val address2 = getArgValue(v2, heap)
Instruction(opcode, address1, address2)
}
Opcode.JUMP, Opcode.CALL, Opcode.BNEG, Opcode.BPOS, Opcode.JUMP, Opcode.CALL, Opcode.BNEG, Opcode.BPOS,
Opcode.BZ, Opcode.BNZ, Opcode.BCS, Opcode.BCC -> { Opcode.BZ, Opcode.BNZ, Opcode.BCS, Opcode.BCC -> {
if(args!!.startsWith('$')) { if(args!!.startsWith('$')) {
@ -222,7 +212,7 @@ class Program (val name: String,
DataType.MATRIX_UB, DataType.MATRIX_UB,
DataType.MATRIX_B -> { DataType.MATRIX_B -> {
if(!valueStr.startsWith("heap:")) if(!valueStr.startsWith("heap:"))
throw VmExecutionException("invalid array/matrix value, should be a heap reference") throw VmExecutionException("invalid arrayspec/matrix value, should be a heap reference")
else { else {
val heapId = valueStr.substring(5).toInt() val heapId = valueStr.substring(5).toInt()
Value(type, heapId) Value(type, heapId)
@ -303,4 +293,4 @@ class Program (val name: String,
} }
} }
} }
} }

View File

@ -218,30 +218,24 @@ class StackVm(private var traceOutputFile: String?) {
} }
} }
private fun checkDt(value: Value?, expected: DataType) { private fun checkDt(value: Value?, vararg expected: DataType) {
if(value==null)
throw VmExecutionException("expected value")
if(value.type!=expected)
throw VmExecutionException("expected $expected value, found ${value.type}")
}
private fun checkDt(value: Value?, expected: Set<DataType>) {
if(value==null) if(value==null)
throw VmExecutionException("expected value") throw VmExecutionException("expected value")
if(value.type !in expected) if(value.type !in expected)
throw VmExecutionException("incompatible type found ${value.type}") throw VmExecutionException("incompatible type ${value.type}")
} }
private fun dispatch(ins: Instruction) : Instruction { private fun dispatch(ins: Instruction) : Instruction {
traceOutput?.println("\n$ins") traceOutput?.println("\n$ins")
when (ins.opcode) { when (ins.opcode) {
Opcode.NOP -> {} Opcode.NOP -> {}
Opcode.PUSH_BYTE -> { Opcode.PUSH_BYTE -> {
checkDt(ins.arg, setOf(DataType.UBYTE, DataType.BYTE)) checkDt(ins.arg, DataType.UBYTE, DataType.BYTE)
evalstack.push(ins.arg) evalstack.push(ins.arg)
} }
Opcode.PUSH_WORD -> { Opcode.PUSH_WORD -> {
checkDt(ins.arg, setOf(DataType.UWORD, DataType.WORD) + IterableDatatypes) checkDt(ins.arg, *(setOf(DataType.UWORD, DataType.WORD) + IterableDatatypes).toTypedArray())
evalstack.push(ins.arg) evalstack.push(ins.arg)
} }
Opcode.PUSH_FLOAT -> { Opcode.PUSH_FLOAT -> {
@ -280,29 +274,23 @@ class StackVm(private var traceOutputFile: String?) {
val value = evalstack.pop() val value = evalstack.pop()
checkDt(value, DataType.FLOAT) checkDt(value, DataType.FLOAT)
} }
Opcode.POP_MEM_UB -> { Opcode.POP_MEM_BYTE -> {
val value = evalstack.pop() val value = evalstack.pop()
checkDt(value, DataType.UBYTE) checkDt(value, DataType.BYTE, DataType.UBYTE)
val address = ins.arg!!.integerValue() val address = ins.arg!!.integerValue()
mem.setUByte(address, value.integerValue().toShort()) if(value.type==DataType.BYTE)
mem.setSByte(address, value.integerValue().toShort())
else
mem.setUByte(address, value.integerValue().toShort())
} }
Opcode.POP_MEM_B -> { Opcode.POP_MEM_WORD -> {
val value = evalstack.pop() val value = evalstack.pop()
checkDt(value, DataType.BYTE) checkDt(value, DataType.WORD, DataType.UWORD)
val address = ins.arg!!.integerValue() val address = ins.arg!!.integerValue()
mem.setSByte(address, value.integerValue().toShort()) if(value.type==DataType.WORD)
} mem.setSWord(address, value.integerValue())
Opcode.POP_MEM_UW -> { else
val value = evalstack.pop() mem.setUWord(address, value.integerValue())
checkDt(value, DataType.UWORD)
val address = ins.arg!!.integerValue()
mem.setUWord(address, value.integerValue())
}
Opcode.POP_MEM_W -> {
val value = evalstack.pop()
checkDt(value, DataType.WORD)
val address = ins.arg!!.integerValue()
mem.setSWord(address, value.integerValue())
} }
Opcode.POP_MEM_FLOAT -> { Opcode.POP_MEM_FLOAT -> {
val value = evalstack.pop() val value = evalstack.pop()
@ -765,12 +753,12 @@ class StackVm(private var traceOutputFile: String?) {
} }
Opcode.PUSH_VAR_BYTE -> { Opcode.PUSH_VAR_BYTE -> {
val value = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") val value = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE)) checkDt(value, DataType.UBYTE, DataType.BYTE)
evalstack.push(value) evalstack.push(value)
} }
Opcode.PUSH_VAR_WORD -> { Opcode.PUSH_VAR_WORD -> {
val value = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") val value = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
checkDt(value, setOf(DataType.UWORD, DataType.WORD, DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.MATRIX_UB)) checkDt(value, *(setOf(DataType.UWORD, DataType.WORD) + IterableDatatypes).toTypedArray())
evalstack.push(value) evalstack.push(value)
} }
Opcode.PUSH_VAR_FLOAT -> { Opcode.PUSH_VAR_FLOAT -> {
@ -780,62 +768,22 @@ class StackVm(private var traceOutputFile: String?) {
} }
Opcode.POP_VAR_BYTE -> { Opcode.POP_VAR_BYTE -> {
val value = evalstack.pop() val value = evalstack.pop()
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE)) checkDt(value, DataType.UBYTE, DataType.BYTE)
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
checkDt(variable, setOf(DataType.UBYTE, DataType.BYTE)) checkDt(variable, DataType.UBYTE, DataType.BYTE)
if(value.type!=variable.type) if(value.type!=variable.type)
throw VmExecutionException("datatype mismatch") throw VmExecutionException("datatype mismatch")
variables[ins.callLabel!!] = value variables[ins.callLabel!!] = value
} }
Opcode.POP_VAR_WORD -> { Opcode.POP_VAR_WORD -> {
val value = evalstack.pop() val value = evalstack.pop()
checkDt(value, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) checkDt(value, DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
checkDt(variable, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) checkDt(variable, DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)
if(value.type!=variable.type) if(value.type!=variable.type)
throw VmExecutionException("datatype mismatch") throw VmExecutionException("datatype mismatch")
variables[ins.callLabel!!] = value variables[ins.callLabel!!] = value
} }
Opcode.COPY_VAR_BYTE -> {
val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}")
checkDt(source, setOf(DataType.UBYTE, DataType.BYTE))
checkDt(dest, setOf(DataType.UBYTE, DataType.BYTE))
if(dest.type!=source.type)
throw VmExecutionException("datatype mismatch")
variables[ins.callLabel2!!] = source
}
Opcode.COPY_VAR_WORD -> {
val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}")
checkDt(source, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
checkDt(dest, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
if(dest.type!=source.type)
throw VmExecutionException("datatype mismatch")
variables[ins.callLabel2!!] = source
}
Opcode.COPY_VAR_FLOAT -> {
val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}")
checkDt(source, DataType.FLOAT)
checkDt(dest, DataType.FLOAT)
variables[ins.callLabel2!!] = source
}
Opcode.COPY_MEM_BYTE -> {
val sourceAddr = ins.arg!!.integerValue()
val destAddr = ins.arg2!!.integerValue()
mem.setUByte(destAddr, mem.getUByte(sourceAddr))
}
Opcode.COPY_MEM_WORD -> {
val sourceAddr = ins.arg!!.integerValue()
val destAddr = ins.arg2!!.integerValue()
mem.setUWord(destAddr, mem.getUWord(sourceAddr))
}
Opcode.COPY_MEM_FLOAT -> {
val sourceAddr = ins.arg!!.integerValue()
val destAddr = ins.arg2!!.integerValue()
mem.setFloat(destAddr, mem.getFloat(sourceAddr))
}
Opcode.POP_VAR_FLOAT -> { Opcode.POP_VAR_FLOAT -> {
val value = evalstack.pop() val value = evalstack.pop()
checkDt(value, DataType.FLOAT) checkDt(value, DataType.FLOAT)
@ -1229,13 +1177,13 @@ class StackVm(private var traceOutputFile: String?) {
// assume the variable is a pointer (address) and get the ubyte value from that memory location // 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()))) evalstack.push(Value(DataType.UBYTE, mem.getUByte(variable.integerValue())))
} else { } else {
// get indexed byte element from the array // get indexed byte element from the arrayspec
val array = heap.get(variable.heapId) val array = heap.get(variable.heapId)
when(array.type) { when(array.type) {
DataType.ARRAY_UB, DataType.MATRIX_UB -> evalstack.push(Value(DataType.UBYTE, array.array!![index])) DataType.ARRAY_UB, DataType.MATRIX_UB -> evalstack.push(Value(DataType.UBYTE, array.array!![index]))
DataType.ARRAY_B, DataType.MATRIX_B -> evalstack.push(Value(DataType.BYTE, array.array!![index])) DataType.ARRAY_B, DataType.MATRIX_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])) 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/matrix/string variable with byte elements") else -> throw VmExecutionException("not a proper arrayspec/matrix/string variable with byte elements")
} }
} }
} }
@ -1247,12 +1195,12 @@ class StackVm(private var traceOutputFile: String?) {
// assume the variable is a pointer (address) and get the word value from that memory location // assume the variable is a pointer (address) and get the word value from that memory location
evalstack.push(Value(DataType.UWORD, mem.getUWord(variable.integerValue()))) evalstack.push(Value(DataType.UWORD, mem.getUWord(variable.integerValue())))
} else { } else {
// get indexed word element from the array // get indexed word element from the arrayspec
val array = heap.get(variable.heapId) val array = heap.get(variable.heapId)
when(array.type){ when(array.type){
DataType.ARRAY_UW -> evalstack.push(Value(DataType.UWORD, array.array!![index])) DataType.ARRAY_UW -> evalstack.push(Value(DataType.UWORD, array.array!![index]))
DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, array.array!![index])) DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, array.array!![index]))
else -> throw VmExecutionException("not a proper array var with word elements") else -> throw VmExecutionException("not a proper arrayspec var with word elements")
} }
} }
} }
@ -1264,10 +1212,10 @@ class StackVm(private var traceOutputFile: String?) {
// assume the variable is a pointer (address) and get the float value from that memory location // 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()))) evalstack.push(Value(DataType.UWORD, mem.getFloat(variable.integerValue())))
} else { } else {
// get indexed float element from the array // get indexed float element from the arrayspec
val array = heap.get(variable.heapId) val array = heap.get(variable.heapId)
if(array.type!=DataType.ARRAY_F) if(array.type!=DataType.ARRAY_F)
throw VmExecutionException("not a proper array var with float elements") throw VmExecutionException("not a proper arrayspec var with float elements")
evalstack.push(Value(DataType.FLOAT, array.doubleArray!![index])) evalstack.push(Value(DataType.FLOAT, array.doubleArray!![index]))
} }
} }
@ -1275,13 +1223,13 @@ class StackVm(private var traceOutputFile: String?) {
// store byte value on the stack in variable[index] (index is on the stack as well) // store byte value on the stack in variable[index] (index is on the stack as well)
val index = evalstack.pop().integerValue() val index = evalstack.pop().integerValue()
val value = evalstack.pop() val value = evalstack.pop()
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE)) checkDt(value, DataType.UBYTE, DataType.BYTE)
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
if(variable.type==DataType.UWORD) { if(variable.type==DataType.UWORD) {
// assume the variable is a pointer (address) and write the byte value to that memory location // assume the variable is a pointer (address) and write the byte value to that memory location
mem.setUByte(variable.integerValue(), value.integerValue().toShort()) mem.setUByte(variable.integerValue(), value.integerValue().toShort())
} else { } else {
// set indexed byte element in the array // set indexed byte element in the arrayspec
val array = heap.get(variable.heapId) val array = heap.get(variable.heapId)
when(array.type) { when(array.type) {
DataType.ARRAY_UB, DataType.MATRIX_UB -> array.array!![index] = value.integerValue() DataType.ARRAY_UB, DataType.MATRIX_UB -> array.array!![index] = value.integerValue()
@ -1294,7 +1242,7 @@ class StackVm(private var traceOutputFile: String?) {
chars[index] = Petscii.decodePetscii(listOf(value.integerValue().toShort()), true)[0] chars[index] = Petscii.decodePetscii(listOf(value.integerValue().toShort()), true)[0]
heap.update(variable.heapId, chars.joinToString("")) heap.update(variable.heapId, chars.joinToString(""))
} }
else -> throw VmExecutionException("not a proper array/matrix/string var with byte elements") else -> throw VmExecutionException("not a proper arrayspec/matrix/string var with byte elements")
} }
} }
} }
@ -1302,18 +1250,18 @@ class StackVm(private var traceOutputFile: String?) {
// store word value on the stack in variable[index] (index is on the stack as well) // store word value on the stack in variable[index] (index is on the stack as well)
val index = evalstack.pop().integerValue() val index = evalstack.pop().integerValue()
val value = evalstack.pop() val value = evalstack.pop()
checkDt(value, setOf(DataType.UWORD, DataType.WORD)) checkDt(value, DataType.UWORD, DataType.WORD)
val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}")
if(variable.type==DataType.UWORD) { if(variable.type==DataType.UWORD) {
// assume the variable is a pointer (address) and write the word value to that memory location // assume the variable is a pointer (address) and write the word value to that memory location
mem.setUWord(variable.integerValue(), value.integerValue()) mem.setUWord(variable.integerValue(), value.integerValue())
} else { } else {
// set indexed word element in the array // set indexed word element in the arrayspec
val array = heap.get(variable.heapId) val array = heap.get(variable.heapId)
when(array.type) { when(array.type) {
DataType.ARRAY_UW -> array.array!![index] = value.integerValue() DataType.ARRAY_UW -> array.array!![index] = value.integerValue()
DataType.ARRAY_W -> array.array!![index] = value.integerValue() DataType.ARRAY_W -> array.array!![index] = value.integerValue()
else -> throw VmExecutionException("not a proper array var with word elements") else -> throw VmExecutionException("not a proper arrayspec var with word elements")
} }
} }
@ -1328,10 +1276,10 @@ class StackVm(private var traceOutputFile: String?) {
// assume the variable is a pointer (address) and write the float value to that memory location // 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(), value.numericValue().toDouble())
} else { } else {
// set indexed float element in the array // set indexed float element in the arrayspec
val array = heap.get(variable.heapId) val array = heap.get(variable.heapId)
if(array.type!=DataType.ARRAY_F) if(array.type!=DataType.ARRAY_F)
throw VmExecutionException("not a proper array var with float elements") throw VmExecutionException("not a proper arrayspec var with float elements")
array.doubleArray!![index] = value.numericValue().toDouble() array.doubleArray!![index] = value.numericValue().toDouble()
} }
} }
@ -1570,7 +1518,7 @@ class StackVm(private var traceOutputFile: String?) {
} }
Syscall.FUNC_WRD -> { Syscall.FUNC_WRD -> {
val value = evalstack.pop() val value = evalstack.pop()
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD)) checkDt(value, DataType.UBYTE, DataType.BYTE, DataType.UWORD)
when(value.type) { when(value.type) {
DataType.UBYTE, DataType.BYTE -> evalstack.push(Value(DataType.WORD, value.integerValue())) DataType.UBYTE, DataType.BYTE -> evalstack.push(Value(DataType.WORD, value.integerValue()))
DataType.UWORD -> { DataType.UWORD -> {
@ -1587,7 +1535,7 @@ class StackVm(private var traceOutputFile: String?) {
} }
Syscall.FUNC_UWRD -> { Syscall.FUNC_UWRD -> {
val value = evalstack.pop() val value = evalstack.pop()
checkDt(value, setOf(DataType.UBYTE, DataType.BYTE, DataType.WORD)) checkDt(value, DataType.UBYTE, DataType.BYTE, DataType.WORD)
when(value.type) { when(value.type) {
DataType.UBYTE -> evalstack.push(Value(DataType.UWORD, value.integerValue())) DataType.UBYTE -> evalstack.push(Value(DataType.UWORD, value.integerValue()))
DataType.UWORD -> evalstack.push(value) DataType.UWORD -> evalstack.push(value)

View File

@ -222,10 +222,10 @@ class TestStackVmOpcodes {
Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, -23456)), Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, -23456)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 177)), Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 177)),
Instruction(Opcode.PUSH_BYTE, Value(DataType.BYTE, -55)), Instruction(Opcode.PUSH_BYTE, Value(DataType.BYTE, -55)),
Instruction(Opcode.POP_MEM_B, Value(DataType.UWORD, 0x2000)), Instruction(Opcode.POP_MEM_BYTE, Value(DataType.UWORD, 0x2000)),
Instruction(Opcode.POP_MEM_UB, Value(DataType.UWORD, 0x2001)), Instruction(Opcode.POP_MEM_BYTE, Value(DataType.UWORD, 0x2001)),
Instruction(Opcode.POP_MEM_W, Value(DataType.UWORD, 0x3000)), Instruction(Opcode.POP_MEM_WORD, Value(DataType.UWORD, 0x3000)),
Instruction(Opcode.POP_MEM_UW, Value(DataType.UWORD, 0x3002)), Instruction(Opcode.POP_MEM_WORD, Value(DataType.UWORD, 0x3002)),
Instruction(Opcode.POP_MEM_FLOAT, Value(DataType.UWORD, 0x4000))) Instruction(Opcode.POP_MEM_FLOAT, Value(DataType.UWORD, 0x4000)))
vm.load(makeProg(ins), null) vm.load(makeProg(ins), null)
assertEquals(0, vm.mem.getUWord(0x2000)) assertEquals(0, vm.mem.getUWord(0x2000))
@ -281,51 +281,6 @@ class TestStackVmOpcodes {
} }
} }
@Test
fun testCopyVar() {
val ins = mutableListOf(
Instruction(Opcode.COPY_VAR_BYTE, null, null, "bvar1", "bvar2"),
Instruction(Opcode.COPY_VAR_WORD, null, null, "wvar1", "wvar2"),
Instruction(Opcode.COPY_VAR_FLOAT, null, null, "fvar1", "fvar2"),
Instruction(Opcode.COPY_VAR_WORD, null, null, "wvar1", "bvar2"))
val vars = mapOf(
"bvar1" to Value(DataType.UBYTE, 1),
"bvar2" to Value(DataType.UBYTE, 2),
"wvar1" to Value(DataType.UWORD, 1111),
"wvar2" to Value(DataType.UWORD, 2222),
"fvar1" to Value(DataType.FLOAT, 11.11),
"fvar2" to Value(DataType.FLOAT, 22.22)
)
vm.load(makeProg(ins, vars), null)
assertEquals(12, vm.variables.size)
vm.step(3)
assertEquals(Value(DataType.UBYTE, 1), vm.variables["bvar2"])
assertEquals(Value(DataType.UWORD, 1111), vm.variables["wvar2"])
assertEquals(Value(DataType.FLOAT, 11.11), vm.variables["fvar2"])
assertFailsWith<VmExecutionException> {
vm.step(1)
}
}
@Test
fun testCopyMem() {
val ins = mutableListOf(
Instruction(Opcode.COPY_MEM_BYTE, Value(DataType.UWORD, 0xc000), Value(DataType.UWORD, 0xc001)),
Instruction(Opcode.COPY_MEM_WORD, Value(DataType.UWORD, 0xc100), Value(DataType.UWORD, 0xc102)),
Instruction(Opcode.COPY_MEM_FLOAT, Value(DataType.UWORD, 0xc200), Value(DataType.UWORD, 0xc300)))
val mem=mapOf(0xc000 to listOf(Value(DataType.UBYTE, 0x45)),
0xc100 to listOf(Value(DataType.UWORD, 0xc2ca)),
0xc200 to listOf(Value(DataType.FLOAT, 42.25)))
vm.load(makeProg(ins, mem=mem), null)
assertEquals(0, vm.mem.getUByte(0xc001))
assertEquals(0, vm.mem.getUWord(0xc102))
assertEquals(0.0, vm.mem.getFloat(0xc300))
vm.step(3)
assertEquals(0x45, vm.mem.getUByte(0xc001))
assertEquals(0xc2ca, vm.mem.getUWord(0xc102))
assertEquals(42.25, vm.mem.getFloat(0xc300))
}
@Test @Test
fun testAdd() { fun testAdd() {
testBinaryOperator(Value(DataType.UBYTE, 140), Opcode.ADD_UB, Value(DataType.UBYTE, 222), Value(DataType.UBYTE, 106)) testBinaryOperator(Value(DataType.UBYTE, 140), Opcode.ADD_UB, Value(DataType.UBYTE, 222), Value(DataType.UBYTE, 106))

View File

@ -200,17 +200,19 @@ Arrays can be made of bytes, words and floats. Matrixes can oly be made of bytes
byte[4] array = [1, 2, 3, 4] ; initialize the array byte[4] array = [1, 2, 3, 4] ; initialize the array
byte[99] array = 255 ; initialize array with all 255's [255, 255, 255, 255, ...] byte[99] array = 255 ; initialize array with all 255's [255, 255, 255, 255, ...]
byte[100] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199] byte[100] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
byte[2,3] matrix = 1 ; a matrix of 2*3=6 bytes all with value 1
byte[2,3] matrix = [1,2,3,4,5,6] ; a 2*3 matrix with value |(1,2) (3,4) (5,6)| byte[2,3] matrix = [1,2,3,4,5,6] ; a 2*3 matrix with value |(1,2) (3,4) (5,6)|
ubyte[2,3] matrix = 255 ; a matrix of 2*3=6 unsigned bytes all with value 255
value = array[3] ; the fourth value in the array (index is 0-based) value = array[3] ; the fourth value in the array (index is 0-based)
value = matrix[4,2] ; the byte at the 5th column and 3rd row in the matrix value = matrix[4,2] ; the byte at the 5th column and 3rd row in the matrix
char = string[4] ; the fifth character (=byte) in the string char = string[4] ; the fifth character (=byte) in the string
.. note:: .. note::
Right now, the array and matrix size should be small enough to be indexable by a single byte index. Right now, the array should be small enough to be indexable by a single byte index.
This means byte arrays and matrixes should be <= 256 elements, word arrays <= 128 elements, and float This means byte arrays should be <= 256 elements, word arrays <= 128 elements, and float
arrays <= 51 elements. This limit may be lifted in a future version. 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.) Note that the various keywords for the data type and variable type (``byte``, ``word``, ``const``, etc.)

View File

@ -34,9 +34,15 @@ ror2_word
ub2float ub2float
rts rts
b2float
rts
uw2float uw2float
rts rts
w2float
rts
push_float push_float
rts rts