diff --git a/codeCore/src/prog8/code/ast/AstExpressions.kt b/codeCore/src/prog8/code/ast/AstExpressions.kt index c7c30932e..fcba13cd7 100644 --- a/codeCore/src/prog8/code/ast/AstExpressions.kt +++ b/codeCore/src/prog8/code/ast/AstExpressions.kt @@ -92,7 +92,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit is PtBinaryExpression -> false is PtBuiltinFunctionCall -> { when (name) { - in arrayOf("msb", "lsb", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() } + in arrayOf("msb", "lsb", "bnk", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() } else -> false } } diff --git a/codeCore/src/prog8/code/core/BuiltinFunctions.kt b/codeCore/src/prog8/code/core/BuiltinFunctions.kt index 11f8999e7..a6498f61f 100644 --- a/codeCore/src/prog8/code/core/BuiltinFunctions.kt +++ b/codeCore/src/prog8/code/core/BuiltinFunctions.kt @@ -109,8 +109,9 @@ val BuiltinFunctions: Map = mapOf( "divmod" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divisor", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("quotient", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null), "divmod__ubyte" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE)), FParam("divisor", arrayOf(DataType.UBYTE)), FParam("quotient", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null), "divmod__uword" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UWORD)), FParam("divisor", arrayOf(DataType.UWORD)), FParam("quotient", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null), - "lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), - "msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), + "lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD, DataType.LONG))), DataType.UBYTE), + "msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD, DataType.LONG))), DataType.UBYTE), + "bnk" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD, DataType.LONG))), DataType.UBYTE), "mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD), "clamp" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), null), "clamp__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), DataType.BYTE), diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 9fcc8b9e2..d8ded20d2 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -24,6 +24,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, val sscope = fcall.definingISub() when (fcall.name) { + "bnk" -> throw AssemblyError("bnk() should have been replaced by a const value at all times (either the bank number of a long const, or zero for any other smaller value)") "msb" -> funcMsb(fcall, resultRegister) "lsb" -> funcLsb(fcall, resultRegister) "mkword" -> funcMkword(fcall, resultRegister) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt index 4f9d20192..b0c59ed52 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt @@ -161,7 +161,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as private fun usesOtherRegistersWhileEvaluating(arg: PtExpression): Boolean { return when(arg) { is PtBuiltinFunctionCall -> { - if (arg.name == "lsb" || arg.name == "msb") + if (arg.name in arrayOf("lsb", "msb", "bnk")) return usesOtherRegistersWhileEvaluating(arg.args[0]) if (arg.name == "mkword") return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1]) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 00d801991..f6a8f65f0 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -924,7 +924,7 @@ internal class AssignmentAsmGen( is PtIdentifier -> true is PtIrRegister -> true is PtNumber -> true - is PtBuiltinFunctionCall -> expr.name in arrayOf("lsb", "msb") + is PtBuiltinFunctionCall -> expr.name in arrayOf("lsb", "msb", "bnk") else -> false } } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index e2b01fbab..d766728da 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -19,6 +19,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe "callfar" -> funcCallfar(call) "callfar2" -> funcCallfar2(call) "call" -> funcCall(call) + "bnk" -> throw AssemblyError("bnk() should have been replaced by a const value at all times (either the bank number of a long const, or zero for any other smaller value)") "msb" -> funcMsb(call) "lsb" -> funcLsb(call) "memory" -> funcMemory(call) diff --git a/compiler/src/prog8/compiler/BuiltinFunctions.kt b/compiler/src/prog8/compiler/BuiltinFunctions.kt index 7be27983e..132668795 100644 --- a/compiler/src/prog8/compiler/BuiltinFunctions.kt +++ b/compiler/src/prog8/compiler/BuiltinFunctions.kt @@ -21,6 +21,7 @@ internal val constEvaluatorsForBuiltinFuncs: Map "sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) { sqrt(it) } }, "lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 255).toDouble() } }, "msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x ushr 8 and 255).toDouble()} }, + "bnk" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x ushr 16 and 255).toDouble()} }, "mkword" to ::builtinMkword, "clamp__ubyte" to ::builtinClampUByte, "clamp__byte" to ::builtinClampByte, diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index c6fe32000..3c18813ed 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1510,7 +1510,7 @@ internal class AstChecker(private val program: Program, ident = arg.value as IdentifierReference else if(arg.value is FunctionCallExpression) { val fcall = arg.value as FunctionCallExpression - if(fcall.target.nameInSource == listOf("lsb") || fcall.target.nameInSource == listOf("msb")) + if(fcall.target.nameInSource == listOf("lsb") || fcall.target.nameInSource == listOf("msb") || fcall.target.nameInSource == listOf("bnk")) ident = fcall.args[0] as? IdentifierReference } if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) { diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstPostprocess.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstPostprocess.kt index 21eeda051..85dcfa5a8 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstPostprocess.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstPostprocess.kt @@ -118,7 +118,7 @@ private fun integrateDefers(subdefers: Map>, program: PtPro is PtAddressOf -> value.arrayIndexExpr == null || notComplex(value.arrayIndexExpr!!) is PtBuiltinFunctionCall -> { when (value.name) { - in arrayOf("msb", "lsb", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> value.args.all { notComplex(it) } + in arrayOf("msb", "lsb", "bnk", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> value.args.all { notComplex(it) } else -> false } } diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index 3b2e6fd78..66c1083af 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -341,5 +341,16 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, } return noModifications } + + override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable { + if(functionCallExpr.target.nameInSource==listOf("bnk")) { + val valueDt = functionCallExpr.args[0].inferType(program) + if(valueDt.isWords || valueDt.isBytes) { + val zero = NumericLiteral.optimalInteger(0, functionCallExpr.position) + return listOf(IAstModification.ReplaceNode(functionCallExpr, zero, parent)) + } + } + return noModifications + } } diff --git a/compiler/test/TestTypecasts.kt b/compiler/test/TestTypecasts.kt index d92435709..329c84eb2 100644 --- a/compiler/test/TestTypecasts.kt +++ b/compiler/test/TestTypecasts.kt @@ -36,7 +36,7 @@ class TestTypecasts: FunSpec({ val result = compileText(C64Target(), false, text, writeAssembly = false, errors=errors) result shouldBe null errors.errors.size shouldBe 1 - errors.errors[0] shouldContain "type mismatch, was: FLOAT expected one of: [UWORD, WORD]" + errors.errors[0] shouldContain "type mismatch" } test("not casting bool operands to logical operators") { diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index b99ab680a..8b50dab79 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -91,10 +91,20 @@ cmp (x,y) Normally you should just use a comparison expression (``x < y``) lsb (x) - Get the least significant byte of the word x. Equivalent to the cast "x as ubyte". + Get the least significant (or 'lower') byte of the value x. Equivalent to the cast "x as ubyte". msb (x) - Get the most significant byte of the word x. + Get the most significant (or 'higher') byte of the word value x. + If x is a value greater than a word, it will not actually return the *highest* byte of this value, + but it will only look a the lower word part of this value and return the higher byte from that. + More accurately, you'll get bits 8-16 of the value x. So msb($1234) is $12, whereas msb($123456) is $34. + If you want to extract the actual highest byte from a long value, we call that the 'bank' byte and you + can do that using ``bnk(x)``. + +bnk (x) + Get the 'bank' byte from the value x. This means bits 16-24 of that value: bnk($1234567) = $12. + If x is a word or smaller, bnk(x) will always be zero. + You can consider this equivalent to the expression ``lsb(x >> 16)``. mkword (msb, lsb) Efficiently create a word value from two bytes (the msb and the lsb). Avoids multiplication and shifting. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 50b408034..a0e60d77e 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,6 @@ TODO ... - Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/examples/cx16/balloonflight.p8 b/examples/cx16/balloonflight.p8 index 43d45e341..46b01f547 100644 --- a/examples/cx16/balloonflight.p8 +++ b/examples/cx16/balloonflight.p8 @@ -18,7 +18,7 @@ main { word balloon_y = 120 ; clear the screen (including all the tiles outside of the visible area) - cx16.vaddr(txt.VERA_TEXTMATRIX>>16, txt.VERA_TEXTMATRIX & $ffff, 0, 1) + cx16.vaddr(bnk(txt.VERA_TEXTMATRIX), txt.VERA_TEXTMATRIX & $ffff, 0, 1) repeat 128 * txt.DEFAULT_HEIGHT { cx16.VERA_DATA0 = sc:' ' cx16.VERA_DATA0 = $00 diff --git a/examples/cx16/sprites/dragon.p8 b/examples/cx16/sprites/dragon.p8 index 9edf89a6e..549e7eac8 100644 --- a/examples/cx16/sprites/dragon.p8 +++ b/examples/cx16/sprites/dragon.p8 @@ -16,12 +16,12 @@ main { txt.print("there be dragons!") ; load the sprite data and color palette directly into Vera ram - void diskio.vload_raw("dragonsprite.bin", SPRITE_DATA>>16, SPRITE_DATA & $ffff) + void diskio.vload_raw("dragonsprite.bin", bnk(SPRITE_DATA), SPRITE_DATA & $ffff) void diskio.vload_raw("dragonsprite.pal", 1, $fa00 + SPRITE_PALETTE_OFFSET*16*2) ; initialize the dragon sprites - sprites.init(1, SPRITE_DATA>>16, SPRITE_DATA & $ffff, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16, SPRITE_PALETTE_OFFSET) - sprites.init(2, SPRITE_DATA>>16, (SPRITE_DATA & $ffff) + 64*64/2, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16,SPRITE_PALETTE_OFFSET) + sprites.init(1, bnk(SPRITE_DATA), SPRITE_DATA & $ffff, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16, SPRITE_PALETTE_OFFSET) + sprites.init(2, bnk(SPRITE_DATA), (SPRITE_DATA & $ffff) + 64*64/2, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16,SPRITE_PALETTE_OFFSET) ubyte tt = 0 word xpos = -64 diff --git a/examples/cx16/sprites/dragons.p8 b/examples/cx16/sprites/dragons.p8 index 79f0044c6..690de6732 100644 --- a/examples/cx16/sprites/dragons.p8 +++ b/examples/cx16/sprites/dragons.p8 @@ -20,14 +20,14 @@ main { txt.print("there be many dragons!") ; load the sprite data and color palette directly into Vera ram - void diskio.vload_raw("dragonsprite.bin", SPRITE_DATA>>16, SPRITE_DATA & $ffff) + void diskio.vload_raw("dragonsprite.bin", bnk(SPRITE_DATA), SPRITE_DATA & $ffff) void diskio.vload_raw("dragonsprite.pal", 1, $fa00 + SPRITE_PALETTE_OFFSET*16*2) ; initialize the dragon sprites (every dragon needs 2 sprites, top and bottom half) ubyte sprite_num for sprite_num in 0 to NUM_DRAGONS*2-2 step 2 { - sprites.init(sprite_num+1, SPRITE_DATA>>16, SPRITE_DATA & $ffff, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16, SPRITE_PALETTE_OFFSET) - sprites.init(sprite_num+2, SPRITE_DATA>>16, (SPRITE_DATA & $ffff) + 64*64/2, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16, SPRITE_PALETTE_OFFSET) + sprites.init(sprite_num+1, bnk(SPRITE_DATA), SPRITE_DATA & $ffff, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16, SPRITE_PALETTE_OFFSET) + sprites.init(sprite_num+2, bnk(SPRITE_DATA), (SPRITE_DATA & $ffff) + 64*64/2, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16, SPRITE_PALETTE_OFFSET) xpositions[sprite_num] = math.rndw() % (640-64) as word xpositions[sprite_num+1] = xpositions[sprite_num] diff --git a/gradle.properties b/gradle.properties index 4d286c4ff..c24dff178 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ org.gradle.daemon=true kotlin.code.style=official javaVersion=11 kotlinVersion=2.0.21 -version=10.5.1 +version=10.6-SNAPSHOT diff --git a/syntax-files/IDEA/Prog8.xml b/syntax-files/IDEA/Prog8.xml index 3b19d468d..73af9898c 100644 --- a/syntax-files/IDEA/Prog8.xml +++ b/syntax-files/IDEA/Prog8.xml @@ -14,7 +14,7 @@ - + diff --git a/syntax-files/NotepadPlusPlus/Prog8.xml b/syntax-files/NotepadPlusPlus/Prog8.xml index 40d4d1b76..12c04204d 100644 --- a/syntax-files/NotepadPlusPlus/Prog8.xml +++ b/syntax-files/NotepadPlusPlus/Prog8.xml @@ -27,7 +27,7 @@ void const str byte ubyte bool long word uword float zp shared split requirezp nozp %address %asm %ir %asmbinary %asminclude %align %breakpoint %encoding %import %memtop %launcher %option %output %zeropage %zpreserved %zpallowed inline sub asmsub extsub clobbers asm if when else if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z for in step do while repeat unroll break continue return goto - abs call callfar callfar2 clamp cmp defer divmod len lsb lsl lsr memory mkword min max msb peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw + abs call callfar callfar2 clamp cmp defer divmod len lsb lsl lsr memory mkword min max msb bnk peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw true false not and or xor as to downto |> diff --git a/syntax-files/SublimeText/Prog8.sublime-syntax b/syntax-files/SublimeText/Prog8.sublime-syntax index 34d18244c..e04e48612 100644 --- a/syntax-files/SublimeText/Prog8.sublime-syntax +++ b/syntax-files/SublimeText/Prog8.sublime-syntax @@ -154,7 +154,7 @@ contexts: - match: (\b(const)\b) scope: storage.modifier.prog8 support: - - match: (\b(abs|atan|ceil|cos|cos8u|cos8|cos16u|cos16|deg|floor|ln|log2|rad|round|sin|sgn|sin8u|sin8|sin16u|sin16|sqrt16|sqrt|tan|any|all|len|max|min|reverse|sum|sort|memcopy|memset|memsetw|leftstr|rightstr|strlen|strcmp|substr|exit|lsb|msb|mkword|rnd|rndw|rndf|rol|rol2|ror|ror2|rsave|rrestore|read_flags|sizeof|set_carry|clear_carry|set_irqd|clear_irqd|swap)\b) + - match: (\b(abs|atan|ceil|cos|cos8u|cos8|cos16u|cos16|deg|floor|ln|log2|rad|round|sin|sgn|sin8u|sin8|sin16u|sin16|sqrt16|sqrt|tan|any|all|len|max|min|reverse|sum|sort|memcopy|memset|memsetw|leftstr|rightstr|strlen|strcmp|substr|exit|lsb|msb|bnk|mkword|rnd|rndw|rndf|rol|rol2|ror|ror2|rsave|rrestore|read_flags|sizeof|set_carry|clear_carry|set_irqd|clear_irqd|swap)\b) scope: support.function.prog8 variable: - match: (\b\w+\b) diff --git a/syntax-files/Vim/prog8_builtins.vim b/syntax-files/Vim/prog8_builtins.vim index c7d8f8908..6e5d8a5ff 100644 --- a/syntax-files/Vim/prog8_builtins.vim +++ b/syntax-files/Vim/prog8_builtins.vim @@ -13,7 +13,7 @@ syn keyword prog8BuiltInFunc sgn sqrtw syn keyword prog8BuiltInFunc len " Miscellaneous functions -syn keyword prog8BuiltInFunc cmp divmod lsb msb mkword min max peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex +syn keyword prog8BuiltInFunc cmp divmod lsb msb bnk mkword min max peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex syn keyword prog8BuiltInFunc rol rol2 ror ror2 sizeof setlsb setmsb syn keyword prog8BuiltInFunc memory call callfar callfar2 clamp defer