fix logical expressions on arbitrary values, for now with boolean() around the operands

This commit is contained in:
Irmen de Jong 2022-06-27 22:26:40 +02:00
parent 06184bdcb1
commit ef92451d1a
12 changed files with 336 additions and 203 deletions

View File

@ -323,6 +323,7 @@ internal class AssignmentAsmGen(private val program: Program,
return false return false
// optimized code for logical expressions // optimized code for logical expressions
// note: due to boolean() wrapping of operands, we can use simple bitwise and/or/xor
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this... // TODO assume (hope) cx16.r9 isn't used for anything else during the use of this...
if(expr.operator=="and") { if(expr.operator=="and") {
val iDt = expr.left.inferType(program) val iDt = expr.left.inferType(program)
@ -2150,10 +2151,7 @@ internal class AssignmentAsmGen(private val program: Program,
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0") RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float") RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
in Cx16VirtualRegisters -> { in Cx16VirtualRegisters -> {
asmgen.out( asmgen.out(" stz cx16.${target.register.toString().lowercase()} | stz cx16.${target.register.toString().lowercase()}+1")
" stz cx16.${
target.register.toString().lowercase()
} | stz cx16.${target.register.toString().lowercase()}+1")
} }
else -> throw AssemblyError("weird register") else -> throw AssemblyError("weird register")
} }

View File

@ -1,5 +1,6 @@
package prog8.compiler.astprocessing package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.SyntaxError import prog8.ast.base.SyntaxError
@ -7,10 +8,7 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.Encoding import prog8.code.core.*
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
import prog8.code.core.NumericDatatypes
class AstPreprocessor(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() { class AstPreprocessor(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() {
@ -98,6 +96,30 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
val containment = ContainmentCheck(expr.left, expr.right, expr.position) val containment = ContainmentCheck(expr.left, expr.right, expr.position)
return listOf(IAstModification.ReplaceNode(expr, containment, parent)) return listOf(IAstModification.ReplaceNode(expr, containment, parent))
} }
// To enable simple bitwise and/or/xor/not instructions in the codegen for the logical and/or/xor/not,
// we wrap the operands in a call to boolean() if required so that they are 0 or 1 as needed.
// Making the codegen more generic to do this by itself all the time will generate much larger
// code because it is hard to decide there if the value conversion to 0 or 1 is needed or not,
// so a lot of useless checks and conversions are added. Here we can be smarter so the codegen
// can just rely on the correct value of the operands (0 or 1) if they're boolean, and just use bitwise instructions.
if(expr.operator in LogicalOperators) {
fun wrapped(expr: Expression): Expression {
return if(expr is IFunctionCall && expr.target.nameInSource==listOf("boolean"))
expr
else if(expr is BinaryExpression && expr.operator in LogicalOperators+ComparisonOperators)
expr
else
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
}
return listOf(
IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr),
IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr)
)
}
return noModifications return noModifications
} }

View File

@ -190,7 +190,6 @@ internal class StatementReorderer(val program: Program,
} }
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> { override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue // ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
// (this should be done by the ExpressionSimplifier when optimizing is enabled, // (this should be done by the ExpressionSimplifier when optimizing is enabled,
// but the current assembly code generator for IF statements now also depends on it, so we do it here regardless of optimization.) // but the current assembly code generator for IF statements now also depends on it, so we do it here regardless of optimization.)
@ -247,38 +246,6 @@ internal class StatementReorderer(val program: Program,
else -> return noModifications else -> return noModifications
} }
} }
else if(expr.operator in LogicalOperators) {
fun wrapped(expr: Expression): Expression {
return if(expr is IFunctionCall && expr.target.nameInSource==listOf("boolean"))
expr
else
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
}
fun isLogicalExpr(expr: Expression?): Boolean {
if(expr is BinaryExpression && expr.operator in (LogicalOperators + ComparisonOperators))
return true
if(expr is PrefixExpression && expr.operator in LogicalOperators)
return true
return false
}
return if(isLogicalExpr(expr.left)) {
if(isLogicalExpr(expr.right))
noModifications
else
listOf(IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr))
} else {
if(isLogicalExpr(expr.right))
listOf(IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr))
else {
listOf(
IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr),
IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr)
)
}
}
}
return noModifications return noModifications
} }

View File

@ -102,6 +102,24 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
return noModifications return noModifications
} }
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator=="not") {
val logical = parent as? BinaryExpression
if(logical!=null && logical.operator in LogicalOperators) {
// not x as operand in a logical expression --> cast it to ubyte
val cast = TypecastExpression(expr, DataType.UBYTE, true, expr.position)
return listOf(IAstModification.ReplaceNode(expr, cast, parent))
}
val boolCast = parent as? IFunctionCall
if(boolCast!=null && boolCast.target.nameInSource==listOf("boolean")) {
// boolean(not x) --> (not x) as ubyte
val cast = TypecastExpression(expr, DataType.UBYTE, true, expr.position)
return listOf(IAstModification.ReplaceNode(boolCast as Node, cast, boolCast.parent))
}
}
return noModifications
}
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> { override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
// see if a typecast is needed to convert the value's type into the proper target type // see if a typecast is needed to convert the value's type into the proper target type
val valueItype = assignment.value.inferType(program) val valueItype = assignment.value.inferType(program)

View File

@ -12,6 +12,7 @@ import prog8.ast.statements.FunctionCallStatement
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.VMTarget
internal class VariousCleanups(val program: Program, val errors: IErrorReporter, val options: CompilationOptions): AstWalker() { internal class VariousCleanups(val program: Program, val errors: IErrorReporter, val options: CompilationOptions): AstWalker() {
@ -233,5 +234,38 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> { override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options) return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
} }
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
if(functionCallExpr.target.nameInSource==listOf("boolean")) {
// boolean(expr) can be removed if expr is a logical expression or comparison expression itself, or boolean()
val binexpr = functionCallExpr.args.single() as? BinaryExpression
if(binexpr!=null && binexpr.operator in LogicalOperators + ComparisonOperators) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, binexpr, parent))
}
val prefixExpression = functionCallExpr.args.single() as? PrefixExpression
if(prefixExpression!=null && prefixExpression.operator in LogicalOperators) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, prefixExpression, parent))
}
val fcall = functionCallExpr.args.single() as? IFunctionCall
if(fcall!=null && fcall.target.nameInSource==listOf("boolean")) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, fcall as Node, parent))
}
if(options.compTarget.name == VMTarget.NAME) {
// if target is Virtual, remove ALL boolean() conversions of operands to logical expressions
// we can do this because the logical and/or/xor/not in the virtual machine behave correctly
// with operand values other than 0 and 1 (0 = false, everything else=true, result is always 0 or 1)
val logicalExpr = functionCallExpr.parent as? BinaryExpression
if(logicalExpr!=null && logicalExpr.operator in LogicalOperators + ComparisonOperators) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args.single(), parent))
}
val prefixExpr = functionCallExpr.parent as? PrefixExpression
if(prefixExpr!=null && prefixExpr.operator in LogicalOperators) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args.single(), parent))
}
}
}
return noModifications
}
} }

View File

@ -285,14 +285,14 @@ class TestOptimization: FunSpec({
""" """
val result = compileText(C64Target(), false, src, writeAssembly = false)!! val result = compileText(C64Target(), false, src, writeAssembly = false)!!
// bb = (( not bb as uword) or not ww) // bb = (not bb or (not ww as ubyte) )
val bbAssign = result.program.entrypoint.statements.last() as Assignment val bbAssign = result.program.entrypoint.statements.last() as Assignment
val expr = bbAssign.value as BinaryExpression val expr = bbAssign.value as BinaryExpression
expr.operator shouldBe "or" expr.operator shouldBe "or"
expr.left shouldBe instanceOf<TypecastExpression>() // casted to word expr.left shouldBe instanceOf<PrefixExpression>()
expr.right shouldBe instanceOf<PrefixExpression>() expr.right shouldBe instanceOf<TypecastExpression>() // not of word typecast into ubyte
expr.left.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UWORD expr.left.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UWORD expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
val options = CompilationOptions(OutputType.PRG, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(), val options = CompilationOptions(OutputType.PRG, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(),
@ -301,10 +301,11 @@ class TestOptimization: FunSpec({
compTarget = C64Target(), compTarget = C64Target(),
loadAddress = 0u, outputDir= outputDir) loadAddress = 0u, outputDir= outputDir)
result.program.processAstBeforeAsmGeneration(options, ErrorReporterForTests()) result.program.processAstBeforeAsmGeneration(options, ErrorReporterForTests())
printProgram(result.program)
// assignment is now split into: // assignment is now split into:
// bb = not bb // bb = not bb
// bb = (bb or not ww) // bb = (bb or (not ww as ubyte)
val assigns = result.program.entrypoint.statements.filterIsInstance<Assignment>() val assigns = result.program.entrypoint.statements.filterIsInstance<Assignment>()
val bbAssigns = assigns.filter { it.value !is NumericLiteral } val bbAssigns = assigns.filter { it.value !is NumericLiteral }
@ -320,9 +321,10 @@ class TestOptimization: FunSpec({
val bbAssigns1expr = bbAssigns[1].value as BinaryExpression val bbAssigns1expr = bbAssigns[1].value as BinaryExpression
bbAssigns1expr.operator shouldBe "or" bbAssigns1expr.operator shouldBe "or"
(bbAssigns1expr.left as? IdentifierReference)?.nameInSource shouldBe listOf("bb") (bbAssigns1expr.left as? IdentifierReference)?.nameInSource shouldBe listOf("bb")
bbAssigns1expr.right shouldBe instanceOf<PrefixExpression>() bbAssigns1expr.right shouldBe instanceOf<TypecastExpression>()
(bbAssigns1expr.right as PrefixExpression).operator shouldBe "not" val castedValue = (bbAssigns1expr.right as TypecastExpression).expression as PrefixExpression
((bbAssigns1expr.right as PrefixExpression).expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww") castedValue.operator shouldBe "not"
(castedValue.expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww")
bbAssigns1expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE bbAssigns1expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
val asm = generateAssembly(result.program, options) val asm = generateAssembly(result.program, options)

View File

@ -0,0 +1,174 @@
%import textio
%zeropage basicsafe
main {
sub ftrue(ubyte arg) -> ubyte {
arg++
return 128
}
sub ffalse(ubyte arg) -> ubyte {
arg++
return 0
}
sub start() {
ubyte ub1 = 2
ubyte ub2 = 4
ubyte ub3 = 8
ubyte ub4 = 0
ubyte bvalue
txt.print("bitwise or 14: ")
txt.print_ub(ub1 | ub2 | ub3 | ub4)
txt.nl()
txt.print("bitwise or 142: ")
txt.print_ub(ub1 | ub2 | ub3 | ub4 | 128)
txt.nl()
txt.print("bitwise and 0: ")
txt.print_ub(ub1 & ub2 & ub3 & ub4)
txt.nl()
txt.print("bitwise and 8: ")
txt.print_ub(ub3 & ub3 & 127)
txt.nl()
txt.print("bitwise xor 14: ")
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ ub4)
txt.nl()
txt.print("bitwise xor 6: ")
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ 8)
txt.nl()
txt.print("bitwise not 247: ")
txt.print_ub(~ub3)
txt.nl()
txt.print("bitwise not 255: ")
txt.print_ub(~ub4)
txt.nl()
txt.print("not 0: ")
bvalue = not ub3
txt.print_ub(bvalue)
if not ub1
txt.print(" / fail")
else
txt.print(" / ok")
txt.nl()
txt.print("not 1: ")
bvalue = not ub4
txt.print_ub(bvalue)
if not ub4
txt.print(" / ok")
else
txt.print(" / fail")
txt.nl()
bvalue = bvalue and 128
txt.print("bvl 1: ")
txt.print_ub(bvalue)
if bvalue and 128
txt.print(" / ok")
else
txt.print(" / fail")
txt.nl()
txt.print("and 1: ")
bvalue = ub1 and ub2 and ub3
txt.print_ub(bvalue)
if ub1 and ub2 and ub3
txt.print(" / ok")
else
txt.print(" / fail")
txt.nl()
txt.print("and 1: ")
bvalue = ub1 and ub2 and ub3 and 64
txt.print_ub(bvalue)
if ub1 and ub2 and ub3 and 64
txt.print(" / ok")
else
txt.print(" / fail")
txt.nl()
txt.print("and 1: ")
bvalue = ub1 and ub2 and ub3 and ftrue(99)
txt.print_ub(bvalue)
if ub1 and ub2 and ub3 and ftrue(99)
txt.print(" / ok")
else
txt.print(" / fail")
txt.nl()
txt.print("and 0: ")
bvalue = ub1 and ub2 and ub3 and ub4
txt.print_ub(bvalue)
if ub1 and ub2 and ub3 and ub4
txt.print(" / fail")
else
txt.print(" / ok")
txt.nl()
txt.print("and 0: ")
bvalue = ub1 and ub2 and ub3 and ffalse(99)
txt.print_ub(bvalue)
if ub1 and ub2 and ub3 and ffalse(99)
txt.print(" / fail")
else
txt.print(" / ok")
txt.nl()
txt.print(" or 1: ")
bvalue = ub1 or ub2 or ub3 or ub4
txt.print_ub(bvalue)
if ub1 or ub2 or ub3 or ub4
txt.print(" / ok")
else
txt.print(" / fail")
txt.nl()
txt.print(" or 1: ")
bvalue = ub4 or ub4 or 64
txt.print_ub(bvalue)
if ub4 or ub4 or 64
txt.print(" / ok")
else
txt.print(" / fail")
txt.nl()
txt.print(" or 1: ")
bvalue = ub1 or ub2 or ub3 or ftrue(99)
txt.print_ub(bvalue)
if ub1 or ub2 or ub3 or ftrue(99)
txt.print(" / ok")
else
txt.print(" / fail")
txt.nl()
txt.print("xor 1: ")
bvalue = ub1 xor ub2 xor ub3 xor ub4
txt.print_ub(bvalue)
if ub1 xor ub2 xor ub3 xor ub4
txt.print(" / ok")
else
txt.print(" / fail")
txt.nl()
txt.print("xor 1: ")
bvalue = ub1 xor ub2 xor ub3 xor ffalse(99)
txt.print_ub(bvalue)
if ub1 xor ub2 xor ub3 xor ffalse(99)
txt.print(" / ok")
else
txt.print(" / fail")
txt.nl()
txt.print("xor 0: ")
bvalue = ub1 xor ub2 xor ub3 xor ub4 xor true
txt.print_ub(bvalue)
if ub1 xor ub2 xor ub3 xor ub4 xor true
txt.print(" / fail")
else
txt.print(" / ok")
txt.nl()
txt.print("xor 0: ")
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)
txt.print_ub(bvalue)
if ub1 xor ub2 xor ub3 xor ftrue(99)
txt.print(" / fail")
else
txt.print(" / ok")
txt.nl()
}
}

View File

@ -124,6 +124,10 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
return when(operator) { return when(operator) {
"+" -> inferred "+" -> inferred
"~", "not" -> { "~", "not" -> {
// note: "not" should ideally result in UBYTE (boolean) but the way the type system
// currently works means the result of an operator is the same type as the operand(s).
// So not(byte)->byte, not(word)->word. This is taken care of via a cast to ubyte later.
// If we give not a BYTE type here, the asmassignment validation will sometimes crash.
when(inferred.getOr(DataType.UNDEFINED)) { when(inferred.getOr(DataType.UNDEFINED)) {
in ByteDatatypes -> InferredTypes.knownFor(DataType.UBYTE) in ByteDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
in WordDatatypes -> InferredTypes.knownFor(DataType.UWORD) in WordDatatypes -> InferredTypes.knownFor(DataType.UWORD)

View File

@ -3,7 +3,11 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- can we optimize redundant calls to boolean() away? imageviewer.prg got larger because of them - vm: fix and/or/xor to be bitwise again (need to remove optimization from VariousCleanups too?)
- vm: fix not to be bitwise not instead of boolean not
- 6502: fix not codegen to be bitwise not instead of boolean not (maybe need to change boolean() wrapping / variouscleanups)
all these testable with compiler/test/arithmetic/logical.p8
- add some more optimizations in vmPeepholeOptimizer - add some more optimizations in vmPeepholeOptimizer
- vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is. - vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is.
this info is needed for more advanced optimizations and later code generation steps. this info is needed for more advanced optimizations and later code generation steps.

View File

@ -5,147 +5,23 @@
main { main {
sub funcFalse() -> ubyte {
txt.print("false() ")
return false
}
sub funcFalseWord() -> uword {
txt.print("falseword() ")
return 0
}
sub funcTrue() -> ubyte {
txt.print("ftrue() ")
return true
}
sub func1(ubyte a1) -> ubyte {
txt.print("func1() ")
return a1>1
}
sub func2(ubyte a1) -> ubyte {
txt.print("func2() ")
return a1>2
}
sub func3(ubyte a1) -> ubyte {
txt.print("func3() ")
return a1>3
}
sub func4(ubyte a1) -> ubyte {
txt.print("func4() ")
return a1>4
}
sub funcw() -> uword {
txt.print("funcw() ")
return 9999
}
sub start() { sub start() {
ubyte value
uword wvalue
ubyte ub1 = 11
ubyte ub2 = 22
ubyte ub3 = 33
ubyte ub4 = 44
ubyte ub5 = 55
ub4 = 0
txt.print("and with bytes: ")
ub5 = ub1 and ub2 and ub3 and ub4 and ub5
txt.print_ub(ub5)
txt.nl()
ub4 = 0
txt.print("or with bytes: ")
ub5 = ub1 or ub2 or ub3 or ub4 or ub5
txt.print_ub(ub5)
txt.nl()
txt.print("short and with false (word): ")
wvalue = funcw() and funcFalseWord() and funcw() and funcw()
txt.print_uw(wvalue)
txt.nl()
txt.print("short and with false: ")
value = func1(25) and funcFalse()
txt.print_ub(value)
txt.nl()
txt.print("short or with true: ")
value = func1(25) or funcTrue()
txt.print_ub(value)
txt.nl()
txt.print("short xor with false: ")
value = func1(25) xor funcFalse()
txt.print_ub(value)
txt.nl()
txt.print("and with false: ")
value = func1(25) and func2(25) and funcFalse() and func3(25) and func4(25)
txt.print_ub(value)
txt.nl()
txt.print("and with true: ")
value = func1(25) and func2(25) and funcTrue() and func3(25) and func4(25)
txt.print_ub(value)
txt.nl()
txt.print("or with false: ")
value = func1(0) or func2(0) or funcFalse() or func3(25) or func4(25)
txt.print_ub(value)
txt.nl()
txt.print("or with true: ")
value = func1(0) or func2(0) or funcTrue() or func3(25) or func4(25)
txt.print_ub(value)
txt.nl()
txt.print("xor with false: ")
value = func1(25) xor func2(25) xor funcFalse() xor func3(25) xor func4(25)
txt.print_ub(value)
txt.nl()
txt.print("xor with true: ")
value = func1(25) xor func2(25) xor funcTrue() xor func3(25) xor func4(25)
txt.print_ub(value)
txt.nl()
txt.print("\nif and with false: [nothing]: ")
if func1(25) and func2(25) and funcFalse() and func3(25) and func4(25)
txt.print("failure!")
txt.print("\nif and with true: [ok]: ")
if func1(25) and func2(25) and funcTrue() and func3(25) and func4(25)
txt.print("ok!")
txt.print("\nif or with false: [ok]: ")
if func1(0) or func2(0) or funcFalse() or func3(25) or func4(25)
txt.print("ok!")
txt.print("\nif or with true: [ok]: ")
if func1(0) or func2(0) or funcTrue() or func3(25) or func4(25)
txt.print("ok!")
txt.print("\nif xor with false: [nothing]: ")
if func1(25) xor func2(25) xor funcFalse() xor func3(25) xor func4(25)
txt.print("failure!")
txt.print("\nif xor with true: [ok]: ")
if func1(25) xor func2(25) xor funcTrue() xor func3(25) xor func4(25)
txt.print("ok!")
txt.nl()
; a "pixelshader": ; a "pixelshader":
; sys.gfx_enable(0) ; enable lo res screen sys.gfx_enable(0) ; enable lo res screen
; ubyte shifter ubyte shifter
;
; repeat { repeat {
; uword xx uword xx
; uword yy = 0 uword yy = 0
; repeat 240 { repeat 240 {
; xx = 0 xx = 0
; repeat 320 { repeat 320 {
; sys.gfx_plot(xx, yy, xx*yy + shifter as ubyte) sys.gfx_plot(xx, yy, xx*yy + shifter as ubyte)
; xx++ xx++
; } }
; yy++ yy++
; } }
; shifter+=4 shifter+=4
; } }
} }
} }

View File

@ -8,9 +8,9 @@ Virtual machine:
65536 virtual floating point registers (32 bits single precision floats) fr0-fr65535 65536 virtual floating point registers (32 bits single precision floats) fr0-fr65535
65536 bytes of memory. Thus memory pointers (addresses) are limited to 16 bits. 65536 bytes of memory. Thus memory pointers (addresses) are limited to 16 bits.
Value stack, max 128 entries of 1 byte each. Value stack, max 128 entries of 1 byte each.
Status registers: Carry, Zero, Negative. Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC!!!
logical or arithmetic operations DO NOT AFFECT THE STATUS FLAGS UNLESS EXPLICITLY NOTED!
Program to execute is not stored in this memory, it's just a separate list of instructions. Program to execute is not stored in this memory, it's just a separate list of instructions.
Most instructions have an associated data type 'b','w','f'. (omitting it means 'b'/byte). Most instructions have an associated data type 'b','w','f'. (omitting it means 'b'/byte).
Currently NO support for 24 or 32 bits integers. Currently NO support for 24 or 32 bits integers.
Floating point operations are just 'f' typed regular instructions, and additionally there are Floating point operations are just 'f' typed regular instructions, and additionally there are

View File

@ -39,7 +39,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
numIns++ numIns++
if(throttle && stepCount and 32767 == 0) { if(throttle && stepCount and 32767 == 0) {
Thread.sleep(0, 10) // avoid 100% cpu core usage Thread.sleep(1) // avoid 100% cpu core usage
} }
if(stepCount and 0xffffff == 0) { if(stepCount and 0xffffff == 0) {
@ -1132,8 +1132,14 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++ pc++
} }
private fun booleanValue(number: UInt): UInt = if(number == 0u) 0u else 1u
private fun booleanValue(number: UByte): UByte = if(number == 0u.toUByte()) 0u else 1u
private fun booleanValue(number: UShort): UShort = if(number == 0u.toUShort()) 0u else 1u
private fun InsAND(i: Instruction) { private fun InsAND(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i) val (leftV: UInt, rightV: UInt) = getLogicalOperandsU(i)
val left = booleanValue(leftV)
val right = booleanValue(rightV)
when(i.type!!) { when(i.type!!) {
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left and right).toUByte()) VmDataType.BYTE -> registers.setUB(i.reg1!!, (left and right).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (left and right).toUShort()) VmDataType.WORD -> registers.setUW(i.reg1!!, (left and right).toUShort())
@ -1145,15 +1151,25 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsANDM(i: Instruction) { private fun InsANDM(i: Instruction) {
val address = i.value!! val address = i.value!!
when(i.type!!) { when(i.type!!) {
VmDataType.BYTE -> memory.setUB(address, (memory.getUB(address) and registers.getUB(i.reg1!!))) VmDataType.BYTE -> {
VmDataType.WORD -> memory.setUW(address, (memory.getUW(address) and registers.getUW(i.reg1!!))) val left = booleanValue(memory.getUB(address))
val right = booleanValue(registers.getUB(i.reg1!!))
memory.setUB(address, left and right)
}
VmDataType.WORD -> {
val left = booleanValue(memory.getUW(address))
val right = booleanValue(registers.getUW(i.reg1!!))
memory.setUW(address, left and right)
}
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
} }
pc++ pc++
} }
private fun InsOR(i: Instruction) { private fun InsOR(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i) val (leftV: UInt, rightV: UInt) = getLogicalOperandsU(i)
val left = booleanValue(leftV)
val right = booleanValue(rightV)
when(i.type!!) { when(i.type!!) {
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left or right).toUByte()) VmDataType.BYTE -> registers.setUB(i.reg1!!, (left or right).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (left or right).toUShort()) VmDataType.WORD -> registers.setUW(i.reg1!!, (left or right).toUShort())
@ -1165,15 +1181,25 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsORM(i: Instruction) { private fun InsORM(i: Instruction) {
val address = i.value!! val address = i.value!!
when(i.type!!) { when(i.type!!) {
VmDataType.BYTE -> memory.setUB(address, (memory.getUB(address) or registers.getUB(i.reg1!!))) VmDataType.BYTE -> {
VmDataType.WORD -> memory.setUW(address, (memory.getUW(address) or registers.getUW(i.reg1!!))) val left = booleanValue(memory.getUB(address))
val right = booleanValue(registers.getUB(i.reg1!!))
memory.setUB(address, left or right)
}
VmDataType.WORD -> {
val left = booleanValue(memory.getUW(address))
val right = booleanValue(registers.getUW(i.reg1!!))
memory.setUW(address, left or right)
}
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
} }
pc++ pc++
} }
private fun InsXOR(i: Instruction) { private fun InsXOR(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i) val (leftV: UInt, rightV: UInt) = getLogicalOperandsU(i)
val left = booleanValue(leftV)
val right = booleanValue(rightV)
when(i.type!!) { when(i.type!!) {
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left xor right).toUByte()) VmDataType.BYTE -> registers.setUB(i.reg1!!, (left xor right).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (left xor right).toUShort()) VmDataType.WORD -> registers.setUW(i.reg1!!, (left xor right).toUShort())
@ -1185,8 +1211,16 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsXORM(i: Instruction) { private fun InsXORM(i: Instruction) {
val address = i.value!! val address = i.value!!
when(i.type!!) { when(i.type!!) {
VmDataType.BYTE -> memory.setUB(address, (memory.getUB(address) xor registers.getUB(i.reg1!!))) VmDataType.BYTE -> {
VmDataType.WORD -> memory.setUW(address, (memory.getUW(address) xor registers.getUW(i.reg1!!))) val left = booleanValue(memory.getUB(address))
val right = booleanValue(registers.getUB(i.reg1!!))
memory.setUB(address, left xor right)
}
VmDataType.WORD -> {
val left = booleanValue(memory.getUW(address))
val right = booleanValue(registers.getUW(i.reg1!!))
memory.setUW(address, left xor right)
}
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
} }
pc++ pc++