diff --git a/codeCore/src/prog8/code/ast/AstBase.kt b/codeCore/src/prog8/code/ast/AstBase.kt index 128eb80d6..ea32f1180 100644 --- a/codeCore/src/prog8/code/ast/AstBase.kt +++ b/codeCore/src/prog8/code/ast/AstBase.kt @@ -96,7 +96,7 @@ class PtBlock(name: String, } -class PtInlineAssembly(val assembly: String, position: Position) : PtNode(position) { +class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Position) : PtNode(position) { override fun printProperties() {} } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 68f44beca..5503c2e18 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -237,7 +237,7 @@ class IRCodeGen( return chunk } is PtConditionalBranch -> translate(node) - is PtInlineAssembly -> IRInlineAsmChunk(node.assembly, node.position) + is PtInlineAssembly -> IRInlineAsmChunk(node.assembly, node.isIR, node.position) is PtIncludeBinary -> { val chunk = IRCodeChunk(node.position) val data = node.file.readBytes() @@ -979,15 +979,18 @@ class IRCodeGen( vmblock += vmsub } is PtAsmSub -> { - val assembly = if(child.children.isEmpty()) "" else (child.children.single() as PtInlineAssembly).assembly - vmblock += IRAsmSubroutine(child.name, child.position, child.address, + val assemblyChild = if(child.children.isEmpty()) null else (child.children.single() as PtInlineAssembly) + vmblock += IRAsmSubroutine( + child.name, child.position, child.address, child.clobbers, child.parameters.map { Pair(it.first.type, it.second) }, // note: the name of the asmsub param is not used anymore. child.returnTypes.zip(child.retvalRegisters), - assembly) + assemblyChild?.isIR==true, + assemblyChild?.assembly ?: "" + ) } is PtInlineAssembly -> { - vmblock += IRInlineAsmChunk(child.assembly, child.position) + vmblock += IRInlineAsmChunk(child.assembly, child.isIR, child.position) } else -> TODO("BLOCK HAS WEIRD CHILD NODE $child") } diff --git a/compiler/res/prog8lib/virtual/conv.p8 b/compiler/res/prog8lib/virtual/conv.p8 index 5f35a3b4e..35775bed8 100644 --- a/compiler/res/prog8lib/virtual/conv.p8 +++ b/compiler/res/prog8lib/virtual/conv.p8 @@ -195,7 +195,7 @@ sub str2uword(str string) -> uword { ; -- returns the unsigned word value of the string number argument in AY ; the number may NOT be preceded by a + sign and may NOT contain spaces ; (any non-digit character will terminate the number string that is parsed) - %asm {{ + %ir {{ loadm.w r0,conv.str2uword.string syscall 11 return @@ -206,7 +206,7 @@ sub str2word(str string) -> word { ; -- returns the signed word value of the string number argument in AY ; the number may be preceded by a + or - sign but may NOT contain spaces ; (any non-digit character will terminate the number string that is parsed) - %asm {{ + %ir {{ loadm.w r0,conv.str2word.string syscall 12 return diff --git a/compiler/res/prog8lib/virtual/floats.p8 b/compiler/res/prog8lib/virtual/floats.p8 index 9521bd3bb..1dcd61d63 100644 --- a/compiler/res/prog8lib/virtual/floats.p8 +++ b/compiler/res/prog8lib/virtual/floats.p8 @@ -9,7 +9,7 @@ floats { sub print_f(float value) { ; ---- prints the floating point value (without a newline). - %asm {{ + %ir {{ loadm.f fr0,floats.print_f.value syscall 25 return @@ -17,7 +17,7 @@ sub print_f(float value) { } sub pow(float value, float power) -> float { - %asm {{ + %ir {{ loadm.f fr0,floats.pow.value loadm.f fr1,floats.pow.power fpow.f fr0,fr1 @@ -26,7 +26,7 @@ sub pow(float value, float power) -> float { } sub fabs(float value) -> float { - %asm {{ + %ir {{ loadm.f fr0,floats.fabs.value fabs.f fr0,fr0 return @@ -34,7 +34,7 @@ sub fabs(float value) -> float { } sub sin(float angle) -> float { - %asm {{ + %ir {{ loadm.f fr0,floats.sin.angle fsin.f fr0,fr0 return @@ -42,7 +42,7 @@ sub sin(float angle) -> float { } sub cos(float angle) -> float { - %asm {{ + %ir {{ loadm.f fr0,floats.cos.angle fcos.f fr0,fr0 return @@ -50,7 +50,7 @@ sub cos(float angle) -> float { } sub tan(float value) -> float { - %asm {{ + %ir {{ loadm.f fr0,floats.tan.value ftan.f fr0,fr0 return @@ -58,7 +58,7 @@ sub tan(float value) -> float { } sub atan(float value) -> float { - %asm {{ + %ir {{ loadm.f fr0,floats.atan.value fatan.f fr0,fr0 return @@ -66,7 +66,7 @@ sub atan(float value) -> float { } sub ln(float value) -> float { - %asm {{ + %ir {{ loadm.f fr0,floats.ln.value fln.f fr0,fr0 return @@ -74,7 +74,7 @@ sub ln(float value) -> float { } sub log2(float value) -> float { - %asm {{ + %ir {{ loadm.f fr0,floats.log2.value flog.f fr0,fr0 return @@ -82,7 +82,7 @@ sub log2(float value) -> float { } sub sqrt(float value) -> float { - %asm {{ + %ir {{ loadm.f fr0,floats.sqrt.value sqrt.f fr0,fr0 return @@ -100,7 +100,7 @@ sub deg(float angle) -> float { } sub round(float value) -> float { - %asm {{ + %ir {{ loadm.f fr0,floats.round.value fround.f fr0,fr0 return @@ -108,7 +108,7 @@ sub round(float value) -> float { } sub floor(float value) -> float { - %asm {{ + %ir {{ loadm.f fr0,floats.floor.value ffloor.f fr0,fr0 return @@ -117,7 +117,7 @@ sub floor(float value) -> float { sub ceil(float value) -> float { ; -- ceil: tr = int(f); if tr==f -> return else return tr+1 - %asm {{ + %ir {{ loadm.f fr0,floats.ceil.value fceil.f fr0,fr0 return @@ -125,7 +125,7 @@ sub ceil(float value) -> float { } sub rndf() -> float { - %asm {{ + %ir {{ rnd.f fr0 return }} diff --git a/compiler/res/prog8lib/virtual/prog8_lib.p8 b/compiler/res/prog8lib/virtual/prog8_lib.p8 index da2f687ec..91a6e7f90 100644 --- a/compiler/res/prog8lib/virtual/prog8_lib.p8 +++ b/compiler/res/prog8lib/virtual/prog8_lib.p8 @@ -41,7 +41,7 @@ prog8_lib { ; Returns -1 (255), 0 or 1 depending on wether string1 sorts before, equal or after string2. ; Note that you can also directly compare strings and string values with eachother using ; comparison operators ==, < etcetera (it will use strcmp for you under water automatically). - %asm {{ + %ir {{ loadm.w r0,prog8_lib.string_compare.st1 loadm.w r1,prog8_lib.string_compare.st2 syscall 29 diff --git a/compiler/res/prog8lib/virtual/syslib.p8 b/compiler/res/prog8lib/virtual/syslib.p8 index 2734a6b5b..8a20c3411 100644 --- a/compiler/res/prog8lib/virtual/syslib.p8 +++ b/compiler/res/prog8lib/virtual/syslib.p8 @@ -7,14 +7,14 @@ sys { sub reset_system() { ; Soft-reset the system back to initial power-on Basic prompt. - %asm {{ + %ir {{ syscall 0 }} } sub wait(uword jiffies) { ; --- wait approximately the given number of jiffies (1/60th seconds) - %asm {{ + %ir {{ loadm.w r0,sys.wait.jiffies syscall 13 }} @@ -22,7 +22,7 @@ sys { sub waitvsync() { ; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling. - %asm {{ + %ir {{ syscall 14 }} } @@ -61,41 +61,41 @@ sys { sub exit(ubyte returnvalue) { ; -- immediately exit the program with a return code in the A register - %asm {{ + %ir {{ loadm.b r0,sys.exit.returnvalue syscall 1 }} } sub set_carry() { - %asm {{ + %ir {{ sec }} } sub clear_carry() { - %asm {{ + %ir {{ clc }} } sub gfx_enable(ubyte mode) { - %asm {{ + %ir {{ loadm.b r0,sys.gfx_enable.mode syscall 8 }} } sub gfx_clear(ubyte color) { - %asm {{ + %ir {{ loadm.b r0,sys.gfx_clear.color syscall 9 }} } sub gfx_plot(uword xx, uword yy, ubyte color) { - %asm {{ + %ir {{ loadm.w r0,sys.gfx_plot.xx loadm.w r1,sys.gfx_plot.yy loadm.b r2,sys.gfx_plot.color @@ -104,7 +104,7 @@ sys { } sub gfx_getpixel(uword xx, uword yy) -> ubyte { - %asm {{ + %ir {{ loadm.w r0,sys.gfx_getpixel.xx loadm.w r1,sys.gfx_getpixel.yy syscall 30 diff --git a/compiler/res/prog8lib/virtual/textio.p8 b/compiler/res/prog8lib/virtual/textio.p8 index 21ccc76c3..166199c05 100644 --- a/compiler/res/prog8lib/virtual/textio.p8 +++ b/compiler/res/prog8lib/virtual/textio.p8 @@ -6,7 +6,7 @@ txt { sub clear_screen() { str @shared sequence = "\x1b[2J\x1B[H" - %asm {{ + %ir {{ load.w r0,txt.clear_screen.sequence syscall 3 }} @@ -29,14 +29,14 @@ sub uppercase() { } sub chrout(ubyte char) { - %asm {{ + %ir {{ loadm.b r0,txt.chrout.char syscall 2 }} } sub print (str text) { - %asm {{ + %ir {{ loadm.w r0,txt.print.text syscall 3 }} @@ -113,7 +113,7 @@ sub print_w (word value) { sub input_chars (uword buffer) -> ubyte { ; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well) ; It assumes the keyboard is selected as I/O channel! - %asm {{ + %ir {{ loadm.w r0,txt.input_chars.buffer syscall 6 return diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt index 2d9d319e9..84d3eb3a1 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt @@ -167,8 +167,10 @@ internal class BeforeAsmAstChanger(val program: Program, if (subroutine.isAsmSubroutine && subroutine.asmAddress==null && !subroutine.hasRtsInAsm(options.compTarget)) { // make sure the NOT INLINED asm subroutine actually has a rts at the end // (non-asm routines get a Return statement as needed, above) - val instruction = if(options.compTarget.name==VMTarget.NAME) " return\n" else " rts\n" - mods += IAstModification.InsertLast(InlineAssembly(instruction, Position.DUMMY), subroutine) + mods += if(options.compTarget.name==VMTarget.NAME) + IAstModification.InsertLast(InlineAssembly(" return\n", true, Position.DUMMY), subroutine) + else + IAstModification.InsertLast(InlineAssembly(" rts\n", false, Position.DUMMY), subroutine) } } diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index cc17511f8..12da2fdc8 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -197,7 +197,7 @@ class IntermediateAstMaker(val program: Program) { "%asminclude" -> { val result = loadAsmIncludeFile(directive.args[0].str!!, directive.definingModule.source) val assembly = result.getOrElse { throw it } - PtInlineAssembly(assembly, directive.position) + PtInlineAssembly(assembly, false, directive.position) } else -> { // other directives don't output any code (but could end up in option flags somewhere else) @@ -252,7 +252,7 @@ class IntermediateAstMaker(val program: Program) { } private fun transform(srcNode: InlineAssembly): PtInlineAssembly = - PtInlineAssembly(srcNode.assembly, srcNode.position) + PtInlineAssembly(srcNode.assembly, srcNode.isIR, srcNode.position) private fun transform(srcJump: Jump): PtJump { val identifier = if(srcJump.identifier!=null) transform(srcJump.identifier!!) else null @@ -306,13 +306,22 @@ class IntermediateAstMaker(val program: Program) { sub.parameters.forEach { it.first.parent=sub } if(srcSub.asmAddress==null) { - var combinedAsm = "" - for (asm in srcSub.statements) - combinedAsm += (asm as InlineAssembly).assembly + "\n" - if(combinedAsm.isNotEmpty()) - sub.add(PtInlineAssembly(combinedAsm, srcSub.statements[0].position)) - else - sub.add(PtInlineAssembly("", srcSub.position)) + var combinedTrueAsm = "" + var combinedIrAsm = "" + for (asm in srcSub.statements) { + asm as InlineAssembly + if(asm.isIR) + combinedIrAsm += asm.assembly + "\n" + else + combinedTrueAsm += asm.assembly + "\n" + } + + if(combinedTrueAsm.isNotEmpty()) + sub.add(PtInlineAssembly(combinedTrueAsm, false, srcSub.statements[0].position)) + if(combinedIrAsm.isNotEmpty()) + sub.add(PtInlineAssembly(combinedIrAsm, true, srcSub.statements[0].position)) + if(combinedIrAsm.isEmpty() && combinedTrueAsm.isEmpty()) + sub.add(PtInlineAssembly("", true, srcSub.position)) } return sub diff --git a/compiler/test/ast/TestVarious.kt b/compiler/test/ast/TestVarious.kt index e602bfd32..9c07e71a5 100644 --- a/compiler/test/ast/TestVarious.kt +++ b/compiler/test/ast/TestVarious.kt @@ -17,7 +17,7 @@ class TestVarious: FunSpec({ test("symbol names in inline assembly blocks") { val names1 = InlineAssembly(""" - """, Position.DUMMY).names + """, false, Position.DUMMY).names names1 shouldBe emptySet() val names2 = InlineAssembly(""" @@ -29,7 +29,7 @@ label2: ; also not these ;; ...or these // valid words 123456 - """, Position.DUMMY).names + """, false, Position.DUMMY).names names2 shouldBe setOf("label", "lda", "sta", "ea", "value", "label2", "othervalue", "valid", "words") } diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt index 2ec20b69a..6cc0df373 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt @@ -125,6 +125,9 @@ private fun Prog8ANTLRParser.StatementContext.toAst() : Statement { val asm = inlineasm()?.toAst() if(asm!=null) return asm + val ir = inlineir()?.toAst() + if(ir!=null) return ir + val branchstmt = branch_stmt()?.toAst() if(branchstmt!=null) return branchstmt @@ -252,7 +255,12 @@ private fun Prog8ANTLRParser.FunctioncallContext.toAst(): FunctionCallExpression private fun Prog8ANTLRParser.InlineasmContext.toAst(): InlineAssembly { val text = INLINEASMBLOCK().text - return InlineAssembly(text.substring(2, text.length-2), toPosition()) + return InlineAssembly(text.substring(2, text.length-2), false, toPosition()) +} + +private fun Prog8ANTLRParser.InlineirContext.toAst(): InlineAssembly { + val text = INLINEASMBLOCK().text + return InlineAssembly(text.substring(2, text.length-2), true, toPosition()) } private fun Prog8ANTLRParser.ReturnstmtContext.toAst() : Return { diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index 0797a2748..a59d26941 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -616,14 +616,14 @@ class FunctionCallStatement(override var target: IdentifierReference, override fun toString() = "FunctionCallStatement(target=$target, pos=$position)" } -class InlineAssembly(val assembly: String, override val position: Position) : Statement() { +class InlineAssembly(val assembly: String, val isIR: Boolean, override val position: Position) : Statement() { override lateinit var parent: Node override fun linkParents(parent: Node) { this.parent = parent } - override fun copy() = InlineAssembly(assembly, position) + override fun copy() = InlineAssembly(assembly, isIR, position) override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here") override fun accept(visitor: IAstVisitor) = visitor.visit(this) diff --git a/examples/test.p8 b/examples/test.p8 index d10698756..7506bbfcc 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -2,6 +2,17 @@ main { sub start() { + %asm {{ + lda #99 + rts + }} + + %ir {{ + nop + loadr r1,r2 + return + }} + ubyte @shared @zp var1 = 42 uword @shared @zp var2 = 4242 str @shared name = "irmen" diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index db17ceaf4..d00758a18 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -265,7 +265,7 @@ class IRFileReader { } private val blockPattern = Regex("") - private val inlineAsmPattern = Regex("") + private val inlineAsmPattern = Regex("") private val asmsubPattern = Regex("") private val subPattern = Regex("") private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]") @@ -299,16 +299,17 @@ class IRFileReader { } private fun parseInlineAssembly(startline: String, lines: Iterator): IRInlineAsmChunk { - // + // val match = inlineAsmPattern.matchEntire(startline) ?: throw IRParseException("invalid INLINEASM") - val pos = parsePosition(match.groupValues[1]) + val isIr = match.groupValues[1].toBoolean() + val pos = parsePosition(match.groupValues[2]) val asmlines = mutableListOf() var line = lines.next() while(line!="") { asmlines.add(line) line = lines.next() } - return IRInlineAsmChunk(asmlines.joinToString("\n"), pos) + return IRInlineAsmChunk(asmlines.joinToString("\n"), isIr, pos) } private fun parseAsmSubroutine(startline: String, lines: Iterator): IRAsmSubroutine { @@ -341,12 +342,15 @@ class IRFileReader { val regsf = parseRegisterOrStatusflag(regstr) returns.add(Pair(dt, regsf)) } - return IRAsmSubroutine(scopedname, + return IRAsmSubroutine( + scopedname, parsePosition(pos), if(address=="null") null else address.toUInt(), clobberRegs.toSet(), params, returns, - asm.assembly) + asm.isIR, + asm.assembly + ) } private fun parseSubroutine(startline: String, lines: Iterator, variables: List): IRSubroutine { diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index c63456da6..0e0f993c1 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -78,7 +78,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { out.write("${dt.toString().lowercase()} $reg\n") } out.write("\n") - out.write("\n") + out.write("\n") out.write(it.assembly.trimStart('\r', '\n').trimEnd(' ', '\r', '\n')) out.write("\n\n\n") } @@ -87,7 +87,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { } private fun writeInlineAsm(chunk: IRInlineAsmChunk) { - out.write("\n") + out.write("\n") out.write(chunk.assembly.trimStart('\r', '\n').trimEnd(' ', '\r', '\n')) out.write("\n\n") } diff --git a/intermediate/src/prog8/intermediate/IRProgram.kt b/intermediate/src/prog8/intermediate/IRProgram.kt index 5023c372a..554afbc4d 100644 --- a/intermediate/src/prog8/intermediate/IRProgram.kt +++ b/intermediate/src/prog8/intermediate/IRProgram.kt @@ -104,13 +104,16 @@ class IRSubroutine(val name: String, operator fun plusAssign(chunk: IRCodeChunkBase) { chunks+= chunk } } -class IRAsmSubroutine(val name: String, - val position: Position, - val address: UInt?, - val clobbers: Set, - val parameters: List>, - val returns: List>, - val assembly: String) { +class IRAsmSubroutine( + val name: String, + val position: Position, + val address: UInt?, + val clobbers: Set, + val parameters: List>, + val returns: List>, + val isIR: Boolean, + val assembly: String +) { init { require('.' in name) { "subroutine name is not scoped: $name" } require(!name.startsWith("main.main.")) { "subroutine name invalid main prefix: $name" } @@ -146,7 +149,7 @@ class IRCodeChunk(position: Position): IRCodeChunkBase(position) { } } -class IRInlineAsmChunk(val assembly: String, position: Position): IRCodeChunkBase(position) { +class IRInlineAsmChunk(val assembly: String, val isIR: Boolean, position: Position): IRCodeChunkBase(position) { // note: no lines, asm is in the property override fun isEmpty() = assembly.isBlank() override fun isNotEmpty() = assembly.isNotBlank() diff --git a/intermediate/test/TestIRFileInOut.kt b/intermediate/test/TestIRFileInOut.kt index b498a9550..64654d56f 100644 --- a/intermediate/test/TestIRFileInOut.kt +++ b/intermediate/test/TestIRFileInOut.kt @@ -83,7 +83,7 @@ return uword sys.wait.jiffies - + loadm.w r0,sys.wait.jiffies syscall 13 diff --git a/parser/antlr/Prog8ANTLR.g4 b/parser/antlr/Prog8ANTLR.g4 index 8a858f599..846487bd8 100644 --- a/parser/antlr/Prog8ANTLR.g4 +++ b/parser/antlr/Prog8ANTLR.g4 @@ -72,6 +72,7 @@ block_statement: | variabledeclaration | subroutinedeclaration | inlineasm + | inlineir | labeldef ; @@ -88,6 +89,7 @@ statement : | branch_stmt | subroutinedeclaration | inlineasm + | inlineir | returnstmt | forloop | whileloop @@ -229,6 +231,8 @@ literalvalue : inlineasm : '%asm' INLINEASMBLOCK; +inlineir: '%ir' INLINEASMBLOCK; + inline: 'inline'; subroutine : diff --git a/syntax-files/IDEA/Prog8.xml b/syntax-files/IDEA/Prog8.xml index f10780578..ecb77cdc9 100644 --- a/syntax-files/IDEA/Prog8.xml +++ b/syntax-files/IDEA/Prog8.xml @@ -12,7 +12,7 @@