mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 16:29:21 +00:00
fix logical expressions on arbitrary values, for now with boolean() around the operands
This commit is contained in:
parent
06184bdcb1
commit
ef92451d1a
@ -323,6 +323,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
return false
|
||||
|
||||
// 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...
|
||||
if(expr.operator=="and") {
|
||||
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.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out(
|
||||
" stz cx16.${
|
||||
target.register.toString().lowercase()
|
||||
} | stz cx16.${target.register.toString().lowercase()}+1")
|
||||
asmgen.out(" stz cx16.${target.register.toString().lowercase()} | stz cx16.${target.register.toString().lowercase()}+1")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.SyntaxError
|
||||
@ -7,10 +8,7 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.NumericDatatypes
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,6 @@ internal class StatementReorderer(val program: Program,
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||
// (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.)
|
||||
@ -247,38 +246,6 @@ internal class StatementReorderer(val program: Program,
|
||||
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
|
||||
}
|
||||
|
@ -102,6 +102,24 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
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> {
|
||||
// see if a typecast is needed to convert the value's type into the proper target type
|
||||
val valueItype = assignment.value.inferType(program)
|
||||
|
@ -12,6 +12,7 @@ import prog8.ast.statements.FunctionCallStatement
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
|
||||
|
||||
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> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,14 +285,14 @@ class TestOptimization: FunSpec({
|
||||
"""
|
||||
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 expr = bbAssign.value as BinaryExpression
|
||||
expr.operator shouldBe "or"
|
||||
expr.left shouldBe instanceOf<TypecastExpression>() // casted to word
|
||||
expr.right shouldBe instanceOf<PrefixExpression>()
|
||||
expr.left.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UWORD
|
||||
expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UWORD
|
||||
expr.left shouldBe instanceOf<PrefixExpression>()
|
||||
expr.right shouldBe instanceOf<TypecastExpression>() // not of word typecast into ubyte
|
||||
expr.left.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||
expr.right.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(),
|
||||
@ -301,10 +301,11 @@ class TestOptimization: FunSpec({
|
||||
compTarget = C64Target(),
|
||||
loadAddress = 0u, outputDir= outputDir)
|
||||
result.program.processAstBeforeAsmGeneration(options, ErrorReporterForTests())
|
||||
printProgram(result.program)
|
||||
|
||||
// assignment is now split into:
|
||||
// bb = not bb
|
||||
// bb = (bb or not ww)
|
||||
// bb = (bb or (not ww as ubyte)
|
||||
|
||||
val assigns = result.program.entrypoint.statements.filterIsInstance<Assignment>()
|
||||
val bbAssigns = assigns.filter { it.value !is NumericLiteral }
|
||||
@ -320,9 +321,10 @@ class TestOptimization: FunSpec({
|
||||
val bbAssigns1expr = bbAssigns[1].value as BinaryExpression
|
||||
bbAssigns1expr.operator shouldBe "or"
|
||||
(bbAssigns1expr.left as? IdentifierReference)?.nameInSource shouldBe listOf("bb")
|
||||
bbAssigns1expr.right shouldBe instanceOf<PrefixExpression>()
|
||||
(bbAssigns1expr.right as PrefixExpression).operator shouldBe "not"
|
||||
((bbAssigns1expr.right as PrefixExpression).expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww")
|
||||
bbAssigns1expr.right shouldBe instanceOf<TypecastExpression>()
|
||||
val castedValue = (bbAssigns1expr.right as TypecastExpression).expression as PrefixExpression
|
||||
castedValue.operator shouldBe "not"
|
||||
(castedValue.expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww")
|
||||
bbAssigns1expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
|
||||
|
||||
val asm = generateAssembly(result.program, options)
|
||||
|
174
compiler/test/arithmetic/logical.p8
Normal file
174
compiler/test/arithmetic/logical.p8
Normal 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()
|
||||
}
|
||||
}
|
@ -124,6 +124,10 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
|
||||
return when(operator) {
|
||||
"+" -> inferred
|
||||
"~", "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)) {
|
||||
in ByteDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
|
||||
in WordDatatypes -> InferredTypes.knownFor(DataType.UWORD)
|
||||
|
@ -3,7 +3,11 @@ TODO
|
||||
|
||||
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
|
||||
- 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.
|
||||
|
156
examples/test.p8
156
examples/test.p8
@ -5,147 +5,23 @@
|
||||
|
||||
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() {
|
||||
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":
|
||||
; sys.gfx_enable(0) ; enable lo res screen
|
||||
; ubyte shifter
|
||||
;
|
||||
; repeat {
|
||||
; uword xx
|
||||
; uword yy = 0
|
||||
; repeat 240 {
|
||||
; xx = 0
|
||||
; repeat 320 {
|
||||
; sys.gfx_plot(xx, yy, xx*yy + shifter as ubyte)
|
||||
; xx++
|
||||
; }
|
||||
; yy++
|
||||
; }
|
||||
; shifter+=4
|
||||
; }
|
||||
sys.gfx_enable(0) ; enable lo res screen
|
||||
ubyte shifter
|
||||
|
||||
repeat {
|
||||
uword xx
|
||||
uword yy = 0
|
||||
repeat 240 {
|
||||
xx = 0
|
||||
repeat 320 {
|
||||
sys.gfx_plot(xx, yy, xx*yy + shifter as ubyte)
|
||||
xx++
|
||||
}
|
||||
yy++
|
||||
}
|
||||
shifter+=4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ Virtual machine:
|
||||
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.
|
||||
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.
|
||||
|
||||
Most instructions have an associated data type 'b','w','f'. (omitting it means 'b'/byte).
|
||||
Currently NO support for 24 or 32 bits integers.
|
||||
Floating point operations are just 'f' typed regular instructions, and additionally there are
|
||||
|
@ -39,7 +39,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
||||
numIns++
|
||||
|
||||
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) {
|
||||
@ -1132,8 +1132,14 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
||||
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) {
|
||||
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!!) {
|
||||
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left and right).toUByte())
|
||||
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) {
|
||||
val address = i.value!!
|
||||
when(i.type!!) {
|
||||
VmDataType.BYTE -> memory.setUB(address, (memory.getUB(address) and registers.getUB(i.reg1!!)))
|
||||
VmDataType.WORD -> memory.setUW(address, (memory.getUW(address) and registers.getUW(i.reg1!!)))
|
||||
VmDataType.BYTE -> {
|
||||
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")
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
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!!) {
|
||||
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left or right).toUByte())
|
||||
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) {
|
||||
val address = i.value!!
|
||||
when(i.type!!) {
|
||||
VmDataType.BYTE -> memory.setUB(address, (memory.getUB(address) or registers.getUB(i.reg1!!)))
|
||||
VmDataType.WORD -> memory.setUW(address, (memory.getUW(address) or registers.getUW(i.reg1!!)))
|
||||
VmDataType.BYTE -> {
|
||||
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")
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
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!!) {
|
||||
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left xor right).toUByte())
|
||||
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) {
|
||||
val address = i.value!!
|
||||
when(i.type!!) {
|
||||
VmDataType.BYTE -> memory.setUB(address, (memory.getUB(address) xor registers.getUB(i.reg1!!)))
|
||||
VmDataType.WORD -> memory.setUW(address, (memory.getUW(address) xor registers.getUW(i.reg1!!)))
|
||||
VmDataType.BYTE -> {
|
||||
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")
|
||||
}
|
||||
pc++
|
||||
|
Loading…
Reference in New Issue
Block a user