added '%ir' to write inline IR code, '%asm' is now only for real 6502 assembly.

(%ir is probably only used in the library modules for the virtual machine target)
This commit is contained in:
Irmen de Jong 2022-09-30 14:05:11 +02:00
parent 94f0f3e966
commit 0d4dd385b8
23 changed files with 137 additions and 80 deletions

View File

@ -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() {} override fun printProperties() {}
} }

View File

@ -237,7 +237,7 @@ class IRCodeGen(
return chunk return chunk
} }
is PtConditionalBranch -> translate(node) is PtConditionalBranch -> translate(node)
is PtInlineAssembly -> IRInlineAsmChunk(node.assembly, node.position) is PtInlineAssembly -> IRInlineAsmChunk(node.assembly, node.isIR, node.position)
is PtIncludeBinary -> { is PtIncludeBinary -> {
val chunk = IRCodeChunk(node.position) val chunk = IRCodeChunk(node.position)
val data = node.file.readBytes() val data = node.file.readBytes()
@ -979,15 +979,18 @@ class IRCodeGen(
vmblock += vmsub vmblock += vmsub
} }
is PtAsmSub -> { is PtAsmSub -> {
val assembly = if(child.children.isEmpty()) "" else (child.children.single() as PtInlineAssembly).assembly val assemblyChild = if(child.children.isEmpty()) null else (child.children.single() as PtInlineAssembly)
vmblock += IRAsmSubroutine(child.name, child.position, child.address, vmblock += IRAsmSubroutine(
child.name, child.position, child.address,
child.clobbers, child.clobbers,
child.parameters.map { Pair(it.first.type, it.second) }, // note: the name of the asmsub param is not used anymore. 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), child.returnTypes.zip(child.retvalRegisters),
assembly) assemblyChild?.isIR==true,
assemblyChild?.assembly ?: ""
)
} }
is PtInlineAssembly -> { is PtInlineAssembly -> {
vmblock += IRInlineAsmChunk(child.assembly, child.position) vmblock += IRInlineAsmChunk(child.assembly, child.isIR, child.position)
} }
else -> TODO("BLOCK HAS WEIRD CHILD NODE $child") else -> TODO("BLOCK HAS WEIRD CHILD NODE $child")
} }

View File

@ -195,7 +195,7 @@ sub str2uword(str string) -> uword {
; -- returns the unsigned word value of the string number argument in AY ; -- 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 ; 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) ; (any non-digit character will terminate the number string that is parsed)
%asm {{ %ir {{
loadm.w r0,conv.str2uword.string loadm.w r0,conv.str2uword.string
syscall 11 syscall 11
return return
@ -206,7 +206,7 @@ sub str2word(str string) -> word {
; -- returns the signed word value of the string number argument in AY ; -- 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 ; 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) ; (any non-digit character will terminate the number string that is parsed)
%asm {{ %ir {{
loadm.w r0,conv.str2word.string loadm.w r0,conv.str2word.string
syscall 12 syscall 12
return return

View File

@ -9,7 +9,7 @@ floats {
sub print_f(float value) { sub print_f(float value) {
; ---- prints the floating point value (without a newline). ; ---- prints the floating point value (without a newline).
%asm {{ %ir {{
loadm.f fr0,floats.print_f.value loadm.f fr0,floats.print_f.value
syscall 25 syscall 25
return return
@ -17,7 +17,7 @@ sub print_f(float value) {
} }
sub pow(float value, float power) -> float { sub pow(float value, float power) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.pow.value loadm.f fr0,floats.pow.value
loadm.f fr1,floats.pow.power loadm.f fr1,floats.pow.power
fpow.f fr0,fr1 fpow.f fr0,fr1
@ -26,7 +26,7 @@ sub pow(float value, float power) -> float {
} }
sub fabs(float value) -> float { sub fabs(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.fabs.value loadm.f fr0,floats.fabs.value
fabs.f fr0,fr0 fabs.f fr0,fr0
return return
@ -34,7 +34,7 @@ sub fabs(float value) -> float {
} }
sub sin(float angle) -> float { sub sin(float angle) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.sin.angle loadm.f fr0,floats.sin.angle
fsin.f fr0,fr0 fsin.f fr0,fr0
return return
@ -42,7 +42,7 @@ sub sin(float angle) -> float {
} }
sub cos(float angle) -> float { sub cos(float angle) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.cos.angle loadm.f fr0,floats.cos.angle
fcos.f fr0,fr0 fcos.f fr0,fr0
return return
@ -50,7 +50,7 @@ sub cos(float angle) -> float {
} }
sub tan(float value) -> float { sub tan(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.tan.value loadm.f fr0,floats.tan.value
ftan.f fr0,fr0 ftan.f fr0,fr0
return return
@ -58,7 +58,7 @@ sub tan(float value) -> float {
} }
sub atan(float value) -> float { sub atan(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.atan.value loadm.f fr0,floats.atan.value
fatan.f fr0,fr0 fatan.f fr0,fr0
return return
@ -66,7 +66,7 @@ sub atan(float value) -> float {
} }
sub ln(float value) -> float { sub ln(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.ln.value loadm.f fr0,floats.ln.value
fln.f fr0,fr0 fln.f fr0,fr0
return return
@ -74,7 +74,7 @@ sub ln(float value) -> float {
} }
sub log2(float value) -> float { sub log2(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.log2.value loadm.f fr0,floats.log2.value
flog.f fr0,fr0 flog.f fr0,fr0
return return
@ -82,7 +82,7 @@ sub log2(float value) -> float {
} }
sub sqrt(float value) -> float { sub sqrt(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.sqrt.value loadm.f fr0,floats.sqrt.value
sqrt.f fr0,fr0 sqrt.f fr0,fr0
return return
@ -100,7 +100,7 @@ sub deg(float angle) -> float {
} }
sub round(float value) -> float { sub round(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.round.value loadm.f fr0,floats.round.value
fround.f fr0,fr0 fround.f fr0,fr0
return return
@ -108,7 +108,7 @@ sub round(float value) -> float {
} }
sub floor(float value) -> float { sub floor(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.floor.value loadm.f fr0,floats.floor.value
ffloor.f fr0,fr0 ffloor.f fr0,fr0
return return
@ -117,7 +117,7 @@ sub floor(float value) -> float {
sub ceil(float value) -> float { sub ceil(float value) -> float {
; -- ceil: tr = int(f); if tr==f -> return else return tr+1 ; -- ceil: tr = int(f); if tr==f -> return else return tr+1
%asm {{ %ir {{
loadm.f fr0,floats.ceil.value loadm.f fr0,floats.ceil.value
fceil.f fr0,fr0 fceil.f fr0,fr0
return return
@ -125,7 +125,7 @@ sub ceil(float value) -> float {
} }
sub rndf() -> float { sub rndf() -> float {
%asm {{ %ir {{
rnd.f fr0 rnd.f fr0
return return
}} }}

View File

@ -41,7 +41,7 @@ prog8_lib {
; Returns -1 (255), 0 or 1 depending on wether string1 sorts before, equal or after string2. ; 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 ; 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). ; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
%asm {{ %ir {{
loadm.w r0,prog8_lib.string_compare.st1 loadm.w r0,prog8_lib.string_compare.st1
loadm.w r1,prog8_lib.string_compare.st2 loadm.w r1,prog8_lib.string_compare.st2
syscall 29 syscall 29

View File

@ -7,14 +7,14 @@ sys {
sub reset_system() { sub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt. ; Soft-reset the system back to initial power-on Basic prompt.
%asm {{ %ir {{
syscall 0 syscall 0
}} }}
} }
sub wait(uword jiffies) { sub wait(uword jiffies) {
; --- wait approximately the given number of jiffies (1/60th seconds) ; --- wait approximately the given number of jiffies (1/60th seconds)
%asm {{ %ir {{
loadm.w r0,sys.wait.jiffies loadm.w r0,sys.wait.jiffies
syscall 13 syscall 13
}} }}
@ -22,7 +22,7 @@ sys {
sub waitvsync() { sub waitvsync() {
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling. ; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
%asm {{ %ir {{
syscall 14 syscall 14
}} }}
} }
@ -61,41 +61,41 @@ sys {
sub exit(ubyte returnvalue) { sub exit(ubyte returnvalue) {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
%asm {{ %ir {{
loadm.b r0,sys.exit.returnvalue loadm.b r0,sys.exit.returnvalue
syscall 1 syscall 1
}} }}
} }
sub set_carry() { sub set_carry() {
%asm {{ %ir {{
sec sec
}} }}
} }
sub clear_carry() { sub clear_carry() {
%asm {{ %ir {{
clc clc
}} }}
} }
sub gfx_enable(ubyte mode) { sub gfx_enable(ubyte mode) {
%asm {{ %ir {{
loadm.b r0,sys.gfx_enable.mode loadm.b r0,sys.gfx_enable.mode
syscall 8 syscall 8
}} }}
} }
sub gfx_clear(ubyte color) { sub gfx_clear(ubyte color) {
%asm {{ %ir {{
loadm.b r0,sys.gfx_clear.color loadm.b r0,sys.gfx_clear.color
syscall 9 syscall 9
}} }}
} }
sub gfx_plot(uword xx, uword yy, ubyte color) { sub gfx_plot(uword xx, uword yy, ubyte color) {
%asm {{ %ir {{
loadm.w r0,sys.gfx_plot.xx loadm.w r0,sys.gfx_plot.xx
loadm.w r1,sys.gfx_plot.yy loadm.w r1,sys.gfx_plot.yy
loadm.b r2,sys.gfx_plot.color loadm.b r2,sys.gfx_plot.color
@ -104,7 +104,7 @@ sys {
} }
sub gfx_getpixel(uword xx, uword yy) -> ubyte { sub gfx_getpixel(uword xx, uword yy) -> ubyte {
%asm {{ %ir {{
loadm.w r0,sys.gfx_getpixel.xx loadm.w r0,sys.gfx_getpixel.xx
loadm.w r1,sys.gfx_getpixel.yy loadm.w r1,sys.gfx_getpixel.yy
syscall 30 syscall 30

View File

@ -6,7 +6,7 @@ txt {
sub clear_screen() { sub clear_screen() {
str @shared sequence = "\x1b[2J\x1B[H" str @shared sequence = "\x1b[2J\x1B[H"
%asm {{ %ir {{
load.w r0,txt.clear_screen.sequence load.w r0,txt.clear_screen.sequence
syscall 3 syscall 3
}} }}
@ -29,14 +29,14 @@ sub uppercase() {
} }
sub chrout(ubyte char) { sub chrout(ubyte char) {
%asm {{ %ir {{
loadm.b r0,txt.chrout.char loadm.b r0,txt.chrout.char
syscall 2 syscall 2
}} }}
} }
sub print (str text) { sub print (str text) {
%asm {{ %ir {{
loadm.w r0,txt.print.text loadm.w r0,txt.print.text
syscall 3 syscall 3
}} }}
@ -113,7 +113,7 @@ sub print_w (word value) {
sub input_chars (uword buffer) -> ubyte { 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) ; ---- 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! ; It assumes the keyboard is selected as I/O channel!
%asm {{ %ir {{
loadm.w r0,txt.input_chars.buffer loadm.w r0,txt.input_chars.buffer
syscall 6 syscall 6
return return

View File

@ -167,8 +167,10 @@ internal class BeforeAsmAstChanger(val program: Program,
if (subroutine.isAsmSubroutine && subroutine.asmAddress==null && !subroutine.hasRtsInAsm(options.compTarget)) { if (subroutine.isAsmSubroutine && subroutine.asmAddress==null && !subroutine.hasRtsInAsm(options.compTarget)) {
// make sure the NOT INLINED asm subroutine actually has a rts at the end // make sure the NOT INLINED asm subroutine actually has a rts at the end
// (non-asm routines get a Return statement as needed, above) // (non-asm routines get a Return statement as needed, above)
val instruction = if(options.compTarget.name==VMTarget.NAME) " return\n" else " rts\n" mods += if(options.compTarget.name==VMTarget.NAME)
mods += IAstModification.InsertLast(InlineAssembly(instruction, Position.DUMMY), subroutine) IAstModification.InsertLast(InlineAssembly(" return\n", true, Position.DUMMY), subroutine)
else
IAstModification.InsertLast(InlineAssembly(" rts\n", false, Position.DUMMY), subroutine)
} }
} }

View File

@ -197,7 +197,7 @@ class IntermediateAstMaker(val program: Program) {
"%asminclude" -> { "%asminclude" -> {
val result = loadAsmIncludeFile(directive.args[0].str!!, directive.definingModule.source) val result = loadAsmIncludeFile(directive.args[0].str!!, directive.definingModule.source)
val assembly = result.getOrElse { throw it } val assembly = result.getOrElse { throw it }
PtInlineAssembly(assembly, directive.position) PtInlineAssembly(assembly, false, directive.position)
} }
else -> { else -> {
// other directives don't output any code (but could end up in option flags somewhere 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 = private fun transform(srcNode: InlineAssembly): PtInlineAssembly =
PtInlineAssembly(srcNode.assembly, srcNode.position) PtInlineAssembly(srcNode.assembly, srcNode.isIR, srcNode.position)
private fun transform(srcJump: Jump): PtJump { private fun transform(srcJump: Jump): PtJump {
val identifier = if(srcJump.identifier!=null) transform(srcJump.identifier!!) else null 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 } sub.parameters.forEach { it.first.parent=sub }
if(srcSub.asmAddress==null) { if(srcSub.asmAddress==null) {
var combinedAsm = "" var combinedTrueAsm = ""
for (asm in srcSub.statements) var combinedIrAsm = ""
combinedAsm += (asm as InlineAssembly).assembly + "\n" for (asm in srcSub.statements) {
if(combinedAsm.isNotEmpty()) asm as InlineAssembly
sub.add(PtInlineAssembly(combinedAsm, srcSub.statements[0].position)) if(asm.isIR)
else combinedIrAsm += asm.assembly + "\n"
sub.add(PtInlineAssembly("", srcSub.position)) 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 return sub

View File

@ -17,7 +17,7 @@ class TestVarious: FunSpec({
test("symbol names in inline assembly blocks") { test("symbol names in inline assembly blocks") {
val names1 = InlineAssembly(""" val names1 = InlineAssembly("""
""", Position.DUMMY).names """, false, Position.DUMMY).names
names1 shouldBe emptySet() names1 shouldBe emptySet()
val names2 = InlineAssembly(""" val names2 = InlineAssembly("""
@ -29,7 +29,7 @@ label2:
; also not these ; also not these
;; ...or these ;; ...or these
// valid words 123456 // valid words 123456
""", Position.DUMMY).names """, false, Position.DUMMY).names
names2 shouldBe setOf("label", "lda", "sta", "ea", "value", "label2", "othervalue", "valid", "words") names2 shouldBe setOf("label", "lda", "sta", "ea", "value", "label2", "othervalue", "valid", "words")
} }

View File

@ -125,6 +125,9 @@ private fun Prog8ANTLRParser.StatementContext.toAst() : Statement {
val asm = inlineasm()?.toAst() val asm = inlineasm()?.toAst()
if(asm!=null) return asm if(asm!=null) return asm
val ir = inlineir()?.toAst()
if(ir!=null) return ir
val branchstmt = branch_stmt()?.toAst() val branchstmt = branch_stmt()?.toAst()
if(branchstmt!=null) return branchstmt if(branchstmt!=null) return branchstmt
@ -252,7 +255,12 @@ private fun Prog8ANTLRParser.FunctioncallContext.toAst(): FunctionCallExpression
private fun Prog8ANTLRParser.InlineasmContext.toAst(): InlineAssembly { private fun Prog8ANTLRParser.InlineasmContext.toAst(): InlineAssembly {
val text = INLINEASMBLOCK().text 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 { private fun Prog8ANTLRParser.ReturnstmtContext.toAst() : Return {

View File

@ -616,14 +616,14 @@ class FunctionCallStatement(override var target: IdentifierReference,
override fun toString() = "FunctionCallStatement(target=$target, pos=$position)" 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 lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent 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 replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)

View File

@ -2,6 +2,17 @@
main { main {
sub start() { sub start() {
%asm {{
lda #99
rts
}}
%ir {{
nop
loadr r1,r2
return
}}
ubyte @shared @zp var1 = 42 ubyte @shared @zp var1 = 42
uword @shared @zp var2 = 4242 uword @shared @zp var2 = 4242
str @shared name = "irmen" str @shared name = "irmen"

View File

@ -265,7 +265,7 @@ class IRFileReader {
} }
private val blockPattern = Regex("<BLOCK NAME=(.+) ADDRESS=(.+) ALIGN=(.+) POS=(.+)>") private val blockPattern = Regex("<BLOCK NAME=(.+) ADDRESS=(.+) ALIGN=(.+) POS=(.+)>")
private val inlineAsmPattern = Regex("<INLINEASM POS=(.+)>") private val inlineAsmPattern = Regex("<INLINEASM IR=(.+) POS=(.+)>")
private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) CLOBBERS=(.*) RETURNS=(.*) POS=(.+)>") private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) CLOBBERS=(.*) RETURNS=(.*) POS=(.+)>")
private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>") private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>")
private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]") private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]")
@ -299,16 +299,17 @@ class IRFileReader {
} }
private fun parseInlineAssembly(startline: String, lines: Iterator<String>): IRInlineAsmChunk { private fun parseInlineAssembly(startline: String, lines: Iterator<String>): IRInlineAsmChunk {
// <INLINEASM POS=[examples/test.p8: line 8 col 6-9]> // <INLINEASM IR=true POS=[examples/test.p8: line 8 col 6-9]>
val match = inlineAsmPattern.matchEntire(startline) ?: throw IRParseException("invalid INLINEASM") 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<String>() val asmlines = mutableListOf<String>()
var line = lines.next() var line = lines.next()
while(line!="</INLINEASM>") { while(line!="</INLINEASM>") {
asmlines.add(line) asmlines.add(line)
line = lines.next() line = lines.next()
} }
return IRInlineAsmChunk(asmlines.joinToString("\n"), pos) return IRInlineAsmChunk(asmlines.joinToString("\n"), isIr, pos)
} }
private fun parseAsmSubroutine(startline: String, lines: Iterator<String>): IRAsmSubroutine { private fun parseAsmSubroutine(startline: String, lines: Iterator<String>): IRAsmSubroutine {
@ -341,12 +342,15 @@ class IRFileReader {
val regsf = parseRegisterOrStatusflag(regstr) val regsf = parseRegisterOrStatusflag(regstr)
returns.add(Pair(dt, regsf)) returns.add(Pair(dt, regsf))
} }
return IRAsmSubroutine(scopedname, return IRAsmSubroutine(
scopedname,
parsePosition(pos), if(address=="null") null else address.toUInt(), parsePosition(pos), if(address=="null") null else address.toUInt(),
clobberRegs.toSet(), clobberRegs.toSet(),
params, params,
returns, returns,
asm.assembly) asm.isIR,
asm.assembly
)
} }
private fun parseSubroutine(startline: String, lines: Iterator<String>, variables: List<StStaticVariable>): IRSubroutine { private fun parseSubroutine(startline: String, lines: Iterator<String>, variables: List<StStaticVariable>): IRSubroutine {

View File

@ -78,7 +78,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
out.write("${dt.toString().lowercase()} $reg\n") out.write("${dt.toString().lowercase()} $reg\n")
} }
out.write("</PARAMS>\n") out.write("</PARAMS>\n")
out.write("<INLINEASM POS=${it.position}>\n") out.write("<INLINEASM IR=${it.isIR} POS=${it.position}>\n")
out.write(it.assembly.trimStart('\r', '\n').trimEnd(' ', '\r', '\n')) out.write(it.assembly.trimStart('\r', '\n').trimEnd(' ', '\r', '\n'))
out.write("\n</INLINEASM>\n</ASMSUB>\n") out.write("\n</INLINEASM>\n</ASMSUB>\n")
} }
@ -87,7 +87,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
} }
private fun writeInlineAsm(chunk: IRInlineAsmChunk) { private fun writeInlineAsm(chunk: IRInlineAsmChunk) {
out.write("<INLINEASM POS=${chunk.position}>\n") out.write("<INLINEASM IR=${chunk.isIR} POS=${chunk.position}>\n")
out.write(chunk.assembly.trimStart('\r', '\n').trimEnd(' ', '\r', '\n')) out.write(chunk.assembly.trimStart('\r', '\n').trimEnd(' ', '\r', '\n'))
out.write("\n</INLINEASM>\n") out.write("\n</INLINEASM>\n")
} }

View File

@ -104,13 +104,16 @@ class IRSubroutine(val name: String,
operator fun plusAssign(chunk: IRCodeChunkBase) { chunks+= chunk } operator fun plusAssign(chunk: IRCodeChunkBase) { chunks+= chunk }
} }
class IRAsmSubroutine(val name: String, class IRAsmSubroutine(
val position: Position, val name: String,
val address: UInt?, val position: Position,
val clobbers: Set<CpuRegister>, val address: UInt?,
val parameters: List<Pair<DataType, RegisterOrStatusflag>>, val clobbers: Set<CpuRegister>,
val returns: List<Pair<DataType, RegisterOrStatusflag>>, val parameters: List<Pair<DataType, RegisterOrStatusflag>>,
val assembly: String) { val returns: List<Pair<DataType, RegisterOrStatusflag>>,
val isIR: Boolean,
val assembly: String
) {
init { init {
require('.' in name) { "subroutine name is not scoped: $name" } require('.' in name) { "subroutine name is not scoped: $name" }
require(!name.startsWith("main.main.")) { "subroutine name invalid main prefix: $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 // note: no lines, asm is in the property
override fun isEmpty() = assembly.isBlank() override fun isEmpty() = assembly.isBlank()
override fun isNotEmpty() = assembly.isNotBlank() override fun isNotEmpty() = assembly.isNotBlank()

View File

@ -83,7 +83,7 @@ return
<PARAMS> <PARAMS>
uword sys.wait.jiffies uword sys.wait.jiffies
</PARAMS> </PARAMS>
<INLINEASM POS=[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]> <INLINEASM IR=true POS=[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]>
loadm.w r0,sys.wait.jiffies loadm.w r0,sys.wait.jiffies
syscall 13 syscall 13
</INLINEASM> </INLINEASM>

View File

@ -72,6 +72,7 @@ block_statement:
| variabledeclaration | variabledeclaration
| subroutinedeclaration | subroutinedeclaration
| inlineasm | inlineasm
| inlineir
| labeldef | labeldef
; ;
@ -88,6 +89,7 @@ statement :
| branch_stmt | branch_stmt
| subroutinedeclaration | subroutinedeclaration
| inlineasm | inlineasm
| inlineir
| returnstmt | returnstmt
| forloop | forloop
| whileloop | whileloop
@ -229,6 +231,8 @@ literalvalue :
inlineasm : '%asm' INLINEASMBLOCK; inlineasm : '%asm' INLINEASMBLOCK;
inlineir: '%ir' INLINEASMBLOCK;
inline: 'inline'; inline: 'inline';
subroutine : subroutine :

View File

@ -12,7 +12,7 @@
<option name="HAS_STRING_ESCAPES" value="true" /> <option name="HAS_STRING_ESCAPES" value="true" />
</options> </options>
<keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" /> <keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%zeropage;%zpreserved;iso:;petscii:;sc:" /> <keywords2 keywords="%address;%asm;%ir;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%zeropage;%zpreserved;iso:;petscii:;sc:" />
<keywords3 keywords="@requirezp;@shared;@zp;byte;const;float;str;ubyte;uword;bool;void;word" /> <keywords3 keywords="@requirezp;@shared;@zp;byte;const;float;str;ubyte;uword;bool;void;word" />
<keywords4 keywords="abs;all;any;avg;callfar;callrom;cmp;len;lsb;memory;mkword;msb;peek;peekw;poke;pokew;pop;popw;push;pushw;reverse;rnd;rndw;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;sgn;sizeof;sort;sqrt16;swap;|&gt;" /> <keywords4 keywords="abs;all;any;avg;callfar;callrom;cmp;len;lsb;memory;mkword;msb;peek;peekw;poke;pokew;pop;popw;push;pushw;reverse;rnd;rndw;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;sgn;sizeof;sort;sqrt16;swap;|&gt;" />
</highlighting> </highlighting>

View File

@ -25,7 +25,7 @@
<Keywords name="Folders in comment, middle"></Keywords> <Keywords name="Folders in comment, middle"></Keywords>
<Keywords name="Folders in comment, close"></Keywords> <Keywords name="Folders in comment, close"></Keywords>
<Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared requirezp</Keywords> <Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared requirezp</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%import&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved</Keywords> <Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%import&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved</Keywords>
<Keywords name="Keywords3">inline sub asmsub romsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat&#x000D;&#x000A;break return goto</Keywords> <Keywords name="Keywords3">inline sub asmsub romsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat&#x000D;&#x000A;break return goto</Keywords>
<Keywords name="Keywords4">abs all any avg callfar callrom cmp len lsb lsl lsr memory mkword msb peek peekw poke pokew push pushw pop popw rsave rsavex rrestore rrestorex reverse rnd rndw rol rol2 ror ror2 sgn sizeof sort sqrt16 swap</Keywords> <Keywords name="Keywords4">abs all any avg callfar callrom cmp len lsb lsl lsr memory mkword msb peek peekw poke pokew push pushw pop popw rsave rsavex rrestore rrestorex reverse rnd rndw rol rol2 ror ror2 sgn sizeof sort sqrt16 swap</Keywords>
<Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto |&gt;</Keywords> <Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto |&gt;</Keywords>

View File

@ -36,7 +36,7 @@ syn keyword prog8Operator and or to downto as void
syn match prog8Directive "\(^\|\s\)%\(output\|launcher\|zeropage\)\>" syn match prog8Directive "\(^\|\s\)%\(output\|launcher\|zeropage\)\>"
syn match prog8Directive "\(^\|\s\)%\(zpreserved\|address\|import\|option\)\>" syn match prog8Directive "\(^\|\s\)%\(zpreserved\|address\|import\|option\)\>"
syn match prog8Directive "\(^\|\s\)%\(asmbinary\|asminclude\|breakpoint\)\>" syn match prog8Directive "\(^\|\s\)%\(asmbinary\|asminclude\|breakpoint\)\>"
syn match prog8Directive "\(^\|\s\)%asm\>" syn match prog8Directive "\(^\|\s\)%\(asm\|ir\)\>"
syn match prog8Type "\<\%(u\?byte\|u\?word\|float\|str\|bool\)\>" syn match prog8Type "\<\%(u\?byte\|u\?word\|float\|str\|bool\)\>"
syn region prog8ArrayType matchgroup=prog8Type syn region prog8ArrayType matchgroup=prog8Type

View File

@ -229,12 +229,16 @@ class VmProgramLoader {
program: MutableList<IRInstruction>, program: MutableList<IRInstruction>,
symbolAddresses: MutableMap<String, Int>, symbolAddresses: MutableMap<String, Int>,
) { ) {
asmChunk.assembly.lineSequence().forEach { if(asmChunk.isIR) {
val parsed = parseIRCodeLine(it.trim(), program.size, placeholders) asmChunk.assembly.lineSequence().forEach {
if(parsed is IRInstruction) val parsed = parseIRCodeLine(it.trim(), program.size, placeholders)
program += parsed if (parsed is IRInstruction)
else if(parsed is IRCodeLabel) program += parsed
symbolAddresses[parsed.name] = program.size else if (parsed is IRCodeLabel)
symbolAddresses[parsed.name] = program.size
}
} else {
throw IRParseException("vm currently does not support real inlined assembly (only IR)': ${asmChunk.position}")
} }
} }
} }

View File

@ -84,7 +84,16 @@ class TestVm: FunSpec( {
test("vm asmbinary not supported") { test("vm asmbinary not supported") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRAsmSubroutine("main.asmstart", Position.DUMMY, 0x2000u, emptySet(), emptyList(), emptyList(), "inlined asm here") val startSub = IRAsmSubroutine(
"main.asmstart",
Position.DUMMY,
0x2000u,
emptySet(),
emptyList(),
emptyList(),
true,
"inlined asm here"
)
block += startSub block += startSub
program.addBlock(block) program.addBlock(block)
shouldThrowWithMessage<IRParseException>("vm currently does not support asmsubs: main.asmstart") { shouldThrowWithMessage<IRParseException>("vm currently does not support asmsubs: main.asmstart") {