Compare commits

...

13 Commits

Author SHA1 Message Date
b5d1575823 added boolean typed versions of the cx16.r0-r15 virtual registers 2025-06-18 00:05:10 +02:00
e3e395836d fix splitting of array decl and initializer for non numeric types 2025-06-13 23:31:56 +02:00
8dc2e47507 fix partial unused code removal in vm target 2025-06-11 23:31:29 +02:00
daf7c3357c better detection of missing return statement
preparing 11.4
2025-06-09 16:01:56 +02:00
e8795859c5 added sorting library for target virtual
added sorting routines that sort a values array together with the keys array
optimized gnomesort a little
2025-06-07 19:42:40 +02:00
bebe60b687 fix compiler crash on for x in wordvar, add sys.get_as_returnaddress() 2025-06-05 16:10:40 +02:00
ddceec364e optimized coroutines library 2025-06-04 21:34:32 +02:00
d067fa4b73 added strings.find_eol() 2025-06-03 21:09:44 +02:00
b5e51ab937 cleaner timings output 2025-06-02 19:30:25 +02:00
552e55c29f fix missing cmp #0 when asmsub call is part of a boolean expression 2025-06-02 19:22:00 +02:00
a228908c1a fix wrong address calculation for &wordarray[i] where i is a variable 2025-06-02 03:13:23 +02:00
15fc3b6c04 replace old antlr2kotlin code with the new visitor-based translator 2025-06-02 01:56:07 +02:00
0456badd02 creating on a new visitor-based antlr to kotlin translator 2025-06-02 01:18:07 +02:00
50 changed files with 2024 additions and 1857 deletions

1
.idea/misc.xml generated
View File

@ -12,6 +12,7 @@
<option name="pkg" value="" /> <option name="pkg" value="" />
<option name="language" value="Java" /> <option name="language" value="Java" />
<option name="generateListener" value="false" /> <option name="generateListener" value="false" />
<option name="generateVisitor" value="true" />
</PerGrammarGenerationSettings> </PerGrammarGenerationSettings>
</list> </list>
</option> </option>

View File

@ -657,7 +657,8 @@ class AsmGen6502Internal (
} }
} }
expr.type.isFloat -> { expr.type.isFloat -> {
require(options.compTarget.FLOAT_MEM_SIZE == 5) {"invalid float size ${expr.position}"} if(options.compTarget.FLOAT_MEM_SIZE != 5)
TODO("support float size other than 5 ${expr.position}")
assignExpressionToRegister(expr.index, RegisterOrPair.A) assignExpressionToRegister(expr.index, RegisterOrPair.A)
out(""" out("""
sta P8ZP_SCRATCH_REG sta P8ZP_SCRATCH_REG

View File

@ -420,6 +420,7 @@ $endLabel""")
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb") val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel) precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.romableError("self-modifying code (forloop over words range)", stmt.position) // TODO fix romable; there is self-modifying code below
asmgen.out(""" asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
@ -442,7 +443,6 @@ $modifiedLabel sbc #0 ; modified
eor #$80 eor #$80
+ bpl $loopLabel + bpl $loopLabel
$endLabel""") $endLabel""")
asmgen.romableError("self-modifying code (forloop over words range)", stmt.position) // TODO fix romable
} }
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) { private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {

View File

@ -1740,6 +1740,17 @@ internal class AssignmentAsmGen(
} }
} }
fun requiresCmp(expr: PtExpression) =
when (expr) {
is PtFunctionCall -> {
val function = asmgen.symbolTable.lookup(expr.name)
function is StExtSub // don't assume the extsub/asmsub has set the cpu flags correctly on exit, add an explicit cmp
}
is PtBuiltinFunctionCall -> true
is PtIfExpression -> true
else -> false
}
if(!expr.right.isSimple() && expr.operator!="xor") { if(!expr.right.isSimple() && expr.operator!="xor") {
// shortcircuit evaluation into A // shortcircuit evaluation into A
val shortcutLabel = asmgen.makeLabel("shortcut") val shortcutLabel = asmgen.makeLabel("shortcut")
@ -1747,15 +1758,23 @@ internal class AssignmentAsmGen(
"and" -> { "and" -> {
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT) // short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
assignExpressionToRegister(expr.left, RegisterOrPair.A, false) assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
if(requiresCmp(expr.left))
asmgen.out(" cmp #0")
asmgen.out(" beq $shortcutLabel") asmgen.out(" beq $shortcutLabel")
assignExpressionToRegister(expr.right, RegisterOrPair.A, false) assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
if(requiresCmp(expr.right))
asmgen.out(" cmp #0")
asmgen.out(shortcutLabel) asmgen.out(shortcutLabel)
} }
"or" -> { "or" -> {
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT // short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
assignExpressionToRegister(expr.left, RegisterOrPair.A, false) assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
if(requiresCmp(expr.left))
asmgen.out(" cmp #0")
asmgen.out(" bne $shortcutLabel") asmgen.out(" bne $shortcutLabel")
assignExpressionToRegister(expr.right, RegisterOrPair.A, false) assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
if(requiresCmp(expr.right))
asmgen.out(" cmp #0")
asmgen.out(shortcutLabel) asmgen.out(shortcutLabel)
} }
else -> throw AssemblyError("invalid logical operator") else -> throw AssemblyError("invalid logical operator")
@ -2608,10 +2627,10 @@ $endLabel""")
private fun assignAddressOf(target: AsmAssignTarget, sourceName: String, msb: Boolean, arrayDt: DataType?, arrayIndexExpr: PtExpression?) { private fun assignAddressOf(target: AsmAssignTarget, sourceName: String, msb: Boolean, arrayDt: DataType?, arrayIndexExpr: PtExpression?) {
if(arrayIndexExpr!=null) { if(arrayIndexExpr!=null) {
val arrayName = if(arrayDt!!.isSplitWordArray) sourceName+"_lsb" else sourceName // the _lsb split array comes first in memory
val constIndex = arrayIndexExpr.asConstInteger() val constIndex = arrayIndexExpr.asConstInteger()
if(constIndex!=null) { if(constIndex!=null) {
if (arrayDt.isUnsignedWord) { if (arrayDt!!.isUnsignedWord) {
// using a UWORD pointer with array indexing, always bytes
require(!msb) require(!msb)
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position) assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
if(constIndex in 1..255) if(constIndex in 1..255)
@ -2635,15 +2654,16 @@ $endLabel""")
else { else {
if(constIndex>0) { if(constIndex>0) {
val offset = if(arrayDt.isSplitWordArray) constIndex else program.memsizer.memorySize(arrayDt, constIndex) // add arrayIndexExpr * elementsize to the address of the array variable. val offset = if(arrayDt.isSplitWordArray) constIndex else program.memsizer.memorySize(arrayDt, constIndex) // add arrayIndexExpr * elementsize to the address of the array variable.
asmgen.out(" lda #<($arrayName + $offset) | ldy #>($arrayName + $offset)") asmgen.out(" lda #<($sourceName + $offset) | ldy #>($sourceName + $offset)")
} else { } else {
asmgen.out(" lda #<$arrayName | ldy #>$arrayName") asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
} }
} }
assignRegisterpairWord(target, RegisterOrPair.AY) assignRegisterpairWord(target, RegisterOrPair.AY)
return return
} else { } else {
if (arrayDt.isUnsignedWord) { if (arrayDt!!.isUnsignedWord) {
// using a UWORD pointer with array indexing, always bytes
require(!msb) require(!msb)
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position) assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
asmgen.saveRegisterStack(CpuRegister.A, false) asmgen.saveRegisterStack(CpuRegister.A, false)
@ -2678,10 +2698,29 @@ $endLabel""")
} }
else { else {
assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false) assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false)
val subtype = arrayDt.sub!!
if(subtype.isByteOrBool) {
// elt size 1, we're good
} else if(subtype.isWord) {
if(!arrayDt.isSplitWordArray) {
// elt size 2
asmgen.out(" asl a")
}
} else if(subtype==BaseDataType.FLOAT) {
if(asmgen.options.compTarget.FLOAT_MEM_SIZE != 5)
TODO("support float size other than 5 ${arrayIndexExpr.position}")
asmgen.out(""" asmgen.out("""
ldy #>$arrayName sta P8ZP_SCRATCH_REG
asl a
asl a
clc clc
adc #<$arrayName adc P8ZP_SCRATCH_REG"""
)
} else throw AssemblyError("weird type $subtype")
asmgen.out("""
ldy #>$sourceName
clc
adc #<$sourceName
bcc + bcc +
iny iny
+""") +""")

View File

@ -48,6 +48,36 @@ class IRUnusedCodeRemover(
} }
irprog.st.removeTree(blockLabel) irprog.st.removeTree(blockLabel)
removeBlockInits(irprog, blockLabel)
}
private fun removeBlockInits(code: IRProgram, blockLabel: String) {
val instructions = code.globalInits.instructions
instructions.toTypedArray().forEach {ins ->
if(ins.labelSymbol?.startsWith(blockLabel)==true) {
instructions.remove(ins)
}
}
// remove stray loads
instructions.toTypedArray().forEach { ins ->
if(ins.opcode in arrayOf(Opcode.LOAD, Opcode.LOADR, Opcode.LOADM)) {
if(ins.reg1!=0) {
if(instructions.count { it.reg1==ins.reg1 || it.reg2==ins.reg1 } <2) {
if(ins.labelSymbol!=null)
code.st.removeIfExists(ins.labelSymbol!!)
instructions.remove(ins)
}
}
else if(ins.fpReg1!=0) {
if (instructions.count { it.fpReg1 == ins.fpReg1 || it.fpReg2 == ins.fpReg1 } < 2) {
if(ins.labelSymbol!=null)
code.st.removeIfExists(ins.labelSymbol!!)
instructions.remove(ins)
}
}
}
}
} }
private fun removeUnusedSubroutines(): Int { private fun removeUnusedSubroutines(): Int {

View File

@ -49,7 +49,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if(truepart.statements.singleOrNull() is Jump) { if(truepart.statements.singleOrNull() is Jump) {
return listOf( return listOf(
IAstModification.InsertAfter(ifElse, elsepart, parent as IStatementContainer), IAstModification.InsertAfter(ifElse, elsepart, parent as IStatementContainer),
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse) IAstModification.ReplaceNode(elsepart, AnonymousScope.empty(), ifElse)
) )
} }
if(elsepart.statements.singleOrNull() is Jump) { if(elsepart.statements.singleOrNull() is Jump) {
@ -57,7 +57,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return listOf( return listOf(
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse), IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer), IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer),
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse), IAstModification.ReplaceNode(elsepart, AnonymousScope.empty(), ifElse),
IAstModification.ReplaceNode(truepart, elsepart, ifElse) IAstModification.ReplaceNode(truepart, elsepart, ifElse)
) )
} }

View File

@ -82,12 +82,11 @@ class StatementOptimizer(private val program: Program,
// empty true part? switch with the else part // empty true part? switch with the else part
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) { if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
val invertedCondition = invertCondition(ifElse.condition, program) val invertedCondition = invertCondition(ifElse.condition, program)
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position) val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
return listOf( return listOf(
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse), IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
IAstModification.ReplaceNode(ifElse.truepart, truepart, ifElse), IAstModification.ReplaceNode(ifElse.truepart, truepart, ifElse),
IAstModification.ReplaceNode(ifElse.elsepart, emptyscope, ifElse) IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope.empty(), ifElse)
) )
} }
@ -106,7 +105,7 @@ class StatementOptimizer(private val program: Program,
if(ifElse.truepart.statements.singleOrNull() is Return) { if(ifElse.truepart.statements.singleOrNull() is Return) {
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position) val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
return listOf( return listOf(
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse), IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope.empty(), ifElse),
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer) IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
) )
} }
@ -146,7 +145,7 @@ class StatementOptimizer(private val program: Program,
if (range.size() == 1) { if (range.size() == 1) {
// for loop over a (constant) range of just a single value-- optimize the loop away // for loop over a (constant) range of just a single value-- optimize the loop away
// loopvar/reg = range value , follow by block // loopvar/reg = range value , follow by block
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope.empty(forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position)) scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
@ -161,7 +160,7 @@ class StatementOptimizer(private val program: Program,
// loop over string of length 1 -> just assign the single character // loop over string of length 1 -> just assign the single character
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0] val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
val byte = NumericLiteral(BaseDataType.UBYTE, character.toDouble(), iterable.position) val byte = NumericLiteral(BaseDataType.UBYTE, character.toDouble(), iterable.position)
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope.empty(forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position)) scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
@ -173,7 +172,7 @@ class StatementOptimizer(private val program: Program,
// loop over array of length 1 -> just assign the single value // loop over array of length 1 -> just assign the single value
val av = (iterable.value as ArrayLiteral).value[0].constValue(program)?.number val av = (iterable.value as ArrayLiteral).value[0].constValue(program)?.number
if(av!=null) { if(av!=null) {
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope.empty(forLoop.position)
scope.statements.add(Assignment( scope.statements.add(Assignment(
AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position), AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
AssignmentOrigin.OPTIMIZER, forLoop.position)) AssignmentOrigin.OPTIMIZER, forLoop.position))
@ -466,7 +465,7 @@ class StatementOptimizer(private val program: Program,
return IfElse( return IfElse(
compare, compare,
AnonymousScope(mutableListOf(assign), position), AnonymousScope(mutableListOf(assign), position),
AnonymousScope(mutableListOf(), position), AnonymousScope.empty(),
position position
) )
} }

View File

@ -947,6 +947,18 @@ _no_msb_size
}} }}
} }
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
%asm {{
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
cpx #0
bne +
dey
+ dex
tya
rts
}}
}
inline asmsub pop() -> ubyte @A { inline asmsub pop() -> ubyte @A {
%asm {{ %asm {{
pla pla
@ -985,6 +997,7 @@ cx16 {
&uword r14 = $1bfc &uword r14 = $1bfc
&uword r15 = $1bfe &uword r15 = $1bfe
; signed word versions
&word r0s = $1be0 &word r0s = $1be0
&word r1s = $1be2 &word r1s = $1be2
&word r2s = $1be4 &word r2s = $1be4
@ -1002,6 +1015,7 @@ cx16 {
&word r14s = $1bfc &word r14s = $1bfc
&word r15s = $1bfe &word r15s = $1bfe
; ubyte versions (low and high bytes)
&ubyte r0L = $1be0 &ubyte r0L = $1be0
&ubyte r1L = $1be2 &ubyte r1L = $1be2
&ubyte r2L = $1be4 &ubyte r2L = $1be4
@ -1036,6 +1050,7 @@ cx16 {
&ubyte r14H = $1bfd &ubyte r14H = $1bfd
&ubyte r15H = $1bff &ubyte r15H = $1bff
; signed byte versions (low and high bytes)
&byte r0sL = $1be0 &byte r0sL = $1be0
&byte r1sL = $1be2 &byte r1sL = $1be2
&byte r2sL = $1be4 &byte r2sL = $1be4
@ -1070,6 +1085,42 @@ cx16 {
&byte r14sH = $1bfd &byte r14sH = $1bfd
&byte r15sH = $1bff &byte r15sH = $1bff
; boolean versions (low and high bytes)
&bool r0bL = $1be0
&bool r1bL = $1be2
&bool r2bL = $1be4
&bool r3bL = $1be6
&bool r4bL = $1be8
&bool r5bL = $1bea
&bool r6bL = $1bec
&bool r7bL = $1bee
&bool r8bL = $1bf0
&bool r9bL = $1bf2
&bool r10bL = $1bf4
&bool r11bL = $1bf6
&bool r12bL = $1bf8
&bool r13bL = $1bfa
&bool r14bL = $1bfc
&bool r15bL = $1bfe
&bool r0bH = $1be1
&bool r1bH = $1be3
&bool r2bH = $1be5
&bool r3bH = $1be7
&bool r4bH = $1be9
&bool r5bH = $1beb
&bool r6bH = $1bed
&bool r7bH = $1bef
&bool r8bH = $1bf1
&bool r9bH = $1bf3
&bool r10bH = $1bf5
&bool r11bH = $1bf7
&bool r12bH = $1bf9
&bool r13bH = $1bfb
&bool r14bH = $1bfd
&bool r15bH = $1bff
asmsub save_virtual_registers() clobbers(A,Y) { asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{ %asm {{
ldy #31 ldy #31

View File

@ -969,6 +969,18 @@ _no_msb_size
}} }}
} }
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
%asm {{
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
cpx #0
bne +
dey
+ dex
tya
rts
}}
}
inline asmsub pop() -> ubyte @A { inline asmsub pop() -> ubyte @A {
%asm {{ %asm {{
pla pla
@ -1007,6 +1019,7 @@ cx16 {
&uword r14 = $cffc &uword r14 = $cffc
&uword r15 = $cffe &uword r15 = $cffe
; signed word versions
&word r0s = $cfe0 &word r0s = $cfe0
&word r1s = $cfe2 &word r1s = $cfe2
&word r2s = $cfe4 &word r2s = $cfe4
@ -1024,6 +1037,7 @@ cx16 {
&word r14s = $cffc &word r14s = $cffc
&word r15s = $cffe &word r15s = $cffe
; ubyte versions (low and high bytes)
&ubyte r0L = $cfe0 &ubyte r0L = $cfe0
&ubyte r1L = $cfe2 &ubyte r1L = $cfe2
&ubyte r2L = $cfe4 &ubyte r2L = $cfe4
@ -1058,6 +1072,7 @@ cx16 {
&ubyte r14H = $cffd &ubyte r14H = $cffd
&ubyte r15H = $cfff &ubyte r15H = $cfff
; signed byte versions (low and high bytes)
&byte r0sL = $cfe0 &byte r0sL = $cfe0
&byte r1sL = $cfe2 &byte r1sL = $cfe2
&byte r2sL = $cfe4 &byte r2sL = $cfe4
@ -1092,6 +1107,42 @@ cx16 {
&byte r14sH = $cffd &byte r14sH = $cffd
&byte r15sH = $cfff &byte r15sH = $cfff
; boolean versions
&bool r0bL = $cfe0
&bool r1bL = $cfe2
&bool r2bL = $cfe4
&bool r3bL = $cfe6
&bool r4bL = $cfe8
&bool r5bL = $cfea
&bool r6bL = $cfec
&bool r7bL = $cfee
&bool r8bL = $cff0
&bool r9bL = $cff2
&bool r10bL = $cff4
&bool r11bL = $cff6
&bool r12bL = $cff8
&bool r13bL = $cffa
&bool r14bL = $cffc
&bool r15bL = $cffe
&bool r0bH = $cfe1
&bool r1bH = $cfe3
&bool r2bH = $cfe5
&bool r3bH = $cfe7
&bool r4bH = $cfe9
&bool r5bH = $cfeb
&bool r6bH = $cfed
&bool r7bH = $cfef
&bool r8bH = $cff1
&bool r9bH = $cff3
&bool r10bH = $cff5
&bool r11bH = $cff7
&bool r12bH = $cff9
&bool r13bH = $cffb
&bool r14bH = $cffd
&bool r15bH = $cfff
asmsub save_virtual_registers() clobbers(A,Y) { asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{ %asm {{
ldy #31 ldy #31

View File

@ -28,12 +28,20 @@
; that routine can for instance call current() (or just look at the active_task variable) to get the id of the next task to execute. ; that routine can for instance call current() (or just look at the active_task variable) to get the id of the next task to execute.
; It has then to return a boolean: true=next task is to be executed, false=skip the task this time. ; It has then to return a boolean: true=next task is to be executed, false=skip the task this time.
; - in tasks: call yield() to pass control to the next task. Use the returned userdata value to do different things. ; - in tasks: call yield() to pass control to the next task. Use the returned userdata value to do different things.
; For now, you MUST call yield() only from the actual subroutine that has been registered as a task!
; (this is because otherwise the cpu call stack gets messed up and an RTS in task1 could suddenly pop a return address belonging to another tasks' call frame)
; - call current() to get the current task id. ; - call current() to get the current task id.
; - call kill(taskid) to kill a task by id. ; - call kill(taskid) to kill a task by id.
; - call killall() to kill all tasks. ; - call killall() to kill all tasks.
; - IMPORTANT: if you add the same subroutine multiple times, IT CANNOT DEPEND ON ANY LOCAL VARIABLES OR R0-R15 TO KEEP STATE. NOT EVEN REPEAT LOOP COUNTERS. ; - IMPORTANT: if you add the same subroutine multiple times, IT CANNOT DEPEND ON ANY LOCAL VARIABLES OR R0-R15 TO KEEP STATE. NOT EVEN REPEAT LOOP COUNTERS.
; Those are all shared in the different tasks! You HAVE to use a mechanism around the userdata value (pointer?) to keep separate state elsewhere! ; Those are all shared in the different tasks! You HAVE to use a mechanism around the userdata value (pointer?) to keep separate state elsewhere!
; - IMPORTANT: ``defer`` cannot be used inside a coroutine that is reused for multiple tasks!!! ; - IMPORTANT: ``defer`` cannot be used inside a coroutine that is reused for multiple tasks!!!
;
; TIP: HOW TO WAIT without BLOCKING other coroutines?
; Make sure you call yield() in the waiting loop, for example:
; uword timer = cbm.RDTIM16() + 60
; while cbm.RDTIM16() != timer
; void coroutines.yield()
coroutines { coroutines {
%option ignore_unused %option ignore_unused
@ -41,19 +49,17 @@ coroutines {
const ubyte MAX_TASKS = 64 const ubyte MAX_TASKS = 64
uword[MAX_TASKS] tasklist uword[MAX_TASKS] tasklist
uword[MAX_TASKS] userdatas uword[MAX_TASKS] userdatas
uword[MAX_TASKS] returnaddresses
ubyte active_task ubyte active_task
uword supervisor uword supervisor
sub add(uword taskaddress, uword userdata) -> ubyte { sub add(uword @nozp taskaddress, uword @nozp userdata) -> ubyte {
; find the next empty slot in the tasklist and stick it there ; find the next empty slot in the tasklist and stick it there
; returns the task id of the new task, or 255 if there was no space for more tasks. 0 is a valid task id! ; returns the task id of the new task, or 255 if there was no space for more tasks. 0 is a valid task id!
; also returns the success in the Carry flag (carry set=success, carry clear = task was not added) ; also returns the success in the Carry flag (carry set=success, carry clear = task was not added)
for cx16.r0L in 0 to len(tasklist)-1 { for cx16.r0L in 0 to len(tasklist)-1 {
if tasklist[cx16.r0L] == 0 { if tasklist[cx16.r0L] == 0 {
tasklist[cx16.r0L] = taskaddress tasklist[cx16.r0L] = sys.get_as_returnaddress(taskaddress)
userdatas[cx16.r0L] = userdata userdatas[cx16.r0L] = userdata
returnaddresses[cx16.r0L] = 0
sys.set_carry() sys.set_carry()
return cx16.r0L return cx16.r0L
} }
@ -70,57 +76,48 @@ coroutines {
} }
} }
sub run(uword supervisor_routine) { sub run(uword @nozp supervisor_routine) {
supervisor = supervisor_routine supervisor = supervisor_routine
for active_task in 0 to len(tasklist)-1 { for active_task in 0 to len(tasklist)-1 {
if tasklist[active_task]!=0 { if tasklist[active_task]!=0 {
; activate the termination handler and start the first task ; activate the termination handler and start the first task
; note: cannot use pushw() because JSR doesn't push the return address in the same way ; note: cannot use pushw() because JSR doesn't push the return address in the same way
sys.push_returnaddress(&termination) sys.push_returnaddress(&termination)
goto tasklist[active_task] sys.pushw(tasklist[active_task])
return
} }
} }
} }
sub yield() -> uword { sub yield() -> uword {
; Store the return address of the yielding task, ; Store the return address of the yielding task, and continue with the next one instead (round-robin)
; and continue with the next one instead (round-robin) ; Returns the associated userdata value.
; Returns the associated userdata value ; NOTE: CAN ONLY BE CALLED FROM THE SCOPE OF THE SUBROUTINE THAT HAS BEEN REGISTERED AS THE TASK!
uword task_start, task_continue uword task_return_address
returnaddresses[active_task] = sys.popw() tasklist[active_task] = sys.popw()
resume_with_next_task: skip_task:
if not next_task() { if not next_task() {
void sys.popw() ; remove return to the termination handler void sys.popw() ; remove return to the termination handler
return 0 ; exiting here will now actually return from the start() call back to the calling program :) return 0 ; exiting here will now actually return back to the calling program that called run()
} }
if supervisor!=0 { if supervisor!=0
if lsb(call(supervisor))==0 if lsb(call(supervisor))==0
goto resume_with_next_task goto skip_task
}
if task_continue==0 { ; returning from yield then continues with the next coroutine:
; fetch start address of next task. sys.pushw(task_return_address)
; address on the stack must be pushed in reverse byte order
; also, subtract 1 from the start address because JSR pushes returnaddress minus 1
; note: cannot use pushw() because JSR doesn't push the return address in the same way
sys.push_returnaddress(task_start)
} else
sys.pushw(task_continue)
; returning from yield then continues with the next coroutine
return userdatas[active_task] return userdatas[active_task]
sub next_task() -> bool { sub next_task() -> bool {
; search through the task list for the next active task ; search through the task list for the next active task
repeat len(tasklist) { repeat len(tasklist) {
active_task++ active_task++
if active_task==len(returnaddresses) if active_task==len(tasklist)
active_task=0 active_task=0
task_start = tasklist[active_task] task_return_address = tasklist[active_task]
if task_start!=0 { if task_return_address!=0 {
task_continue = returnaddresses[active_task]
return true return true
} }
} }
@ -128,9 +125,8 @@ resume_with_next_task:
} }
} }
sub kill(ubyte taskid) { sub kill(ubyte @nozp taskid) {
tasklist[taskid] = 0 tasklist[taskid] = 0
returnaddresses[taskid] = 0
} }
sub current() -> ubyte { sub current() -> ubyte {
@ -138,12 +134,10 @@ resume_with_next_task:
} }
sub termination() { sub termination() {
; a task has terminated. wipe it from the list. ; internal routine: a task has terminated. wipe it from the list.
; this is an internal routine
kill(active_task) kill(active_task)
; reactivate this termination handler ; reactivate this termination handler and go to the next task
; note: cannot use pushw() because JSR doesn't push the return address in the same way
sys.push_returnaddress(&termination) sys.push_returnaddress(&termination)
goto coroutines.yield.resume_with_next_task goto coroutines.yield.skip_task
} }
} }

View File

@ -177,6 +177,7 @@ cx16 {
&uword r14 = $001e &uword r14 = $001e
&uword r15 = $0020 &uword r15 = $0020
; signed word versions
&word r0s = $0002 &word r0s = $0002
&word r1s = $0004 &word r1s = $0004
&word r2s = $0006 &word r2s = $0006
@ -194,6 +195,7 @@ cx16 {
&word r14s = $001e &word r14s = $001e
&word r15s = $0020 &word r15s = $0020
; ubyte versions (low and high bytes)
&ubyte r0L = $0002 &ubyte r0L = $0002
&ubyte r1L = $0004 &ubyte r1L = $0004
&ubyte r2L = $0006 &ubyte r2L = $0006
@ -228,6 +230,7 @@ cx16 {
&ubyte r14H = $001f &ubyte r14H = $001f
&ubyte r15H = $0021 &ubyte r15H = $0021
; signed byte versions (low and high bytes)
&byte r0sL = $0002 &byte r0sL = $0002
&byte r1sL = $0004 &byte r1sL = $0004
&byte r2sL = $0006 &byte r2sL = $0006
@ -262,6 +265,42 @@ cx16 {
&byte r14sH = $001f &byte r14sH = $001f
&byte r15sH = $0021 &byte r15sH = $0021
; boolean versions
&bool r0bL = $0002
&bool r1bL = $0004
&bool r2bL = $0006
&bool r3bL = $0008
&bool r4bL = $000a
&bool r5bL = $000c
&bool r6bL = $000e
&bool r7bL = $0010
&bool r8bL = $0012
&bool r9bL = $0014
&bool r10bL = $0016
&bool r11bL = $0018
&bool r12bL = $001a
&bool r13bL = $001c
&bool r14bL = $001e
&bool r15bL = $0020
&bool r0bH = $0003
&bool r1bH = $0005
&bool r2bH = $0007
&bool r3bH = $0009
&bool r4bH = $000b
&bool r5bH = $000d
&bool r6bH = $000f
&bool r7bH = $0011
&bool r8bH = $0013
&bool r9bH = $0015
&bool r10bH = $0017
&bool r11bH = $0019
&bool r12bH = $001b
&bool r13bH = $001d
&bool r14bH = $001f
&bool r15bH = $0021
; VERA registers ; VERA registers
const uword VERA_BASE = $9F20 const uword VERA_BASE = $9F20
@ -2028,6 +2067,18 @@ save_SCRATCH_ZPWORD2 .word ?
}} }}
} }
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
%asm {{
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
cpx #0
bne +
dey
+ dex
tya
rts
}}
}
inline asmsub pop() -> ubyte @A { inline asmsub pop() -> ubyte @A {
%asm {{ %asm {{
pla pla

View File

@ -482,6 +482,18 @@ save_SCRATCH_ZPWORD2 .word ?
}} }}
} }
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
%asm {{
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
cpx #0
bne +
dey
+ dex
tya
rts
}}
}
inline asmsub pop() -> ubyte @A { inline asmsub pop() -> ubyte @A {
%asm {{ %asm {{
pla pla
@ -520,6 +532,7 @@ cx16 {
&uword r14 = $7ffc &uword r14 = $7ffc
&uword r15 = $7ffe &uword r15 = $7ffe
; signed word versions
&word r0s = $7fe0 &word r0s = $7fe0
&word r1s = $7fe2 &word r1s = $7fe2
&word r2s = $7fe4 &word r2s = $7fe4
@ -537,6 +550,7 @@ cx16 {
&word r14s = $7ffc &word r14s = $7ffc
&word r15s = $7ffe &word r15s = $7ffe
; ubyte versions (low and high bytes)
&ubyte r0L = $7fe0 &ubyte r0L = $7fe0
&ubyte r1L = $7fe2 &ubyte r1L = $7fe2
&ubyte r2L = $7fe4 &ubyte r2L = $7fe4
@ -571,6 +585,7 @@ cx16 {
&ubyte r14H = $7ffd &ubyte r14H = $7ffd
&ubyte r15H = $7fff &ubyte r15H = $7fff
; signed byte versions (low and high bytes)
&byte r0sL = $7fe0 &byte r0sL = $7fe0
&byte r1sL = $7fe2 &byte r1sL = $7fe2
&byte r2sL = $7fe4 &byte r2sL = $7fe4
@ -605,6 +620,42 @@ cx16 {
&byte r14sH = $7ffd &byte r14sH = $7ffd
&byte r15sH = $7fff &byte r15sH = $7fff
; boolean versions
&bool r0bL = $7fe0
&bool r1bL = $7fe2
&bool r2bL = $7fe4
&bool r3bL = $7fe6
&bool r4bL = $7fe8
&bool r5bL = $7fea
&bool r6bL = $7fec
&bool r7bL = $7fee
&bool r8bL = $7ff0
&bool r9bL = $7ff2
&bool r10bL = $7ff4
&bool r11bL = $7ff6
&bool r12bL = $7ff8
&bool r13bL = $7ffa
&bool r14bL = $7ffc
&bool r15bL = $7ffe
&bool r0bH = $7fe1
&bool r1bH = $7fe3
&bool r2bH = $7fe5
&bool r3bH = $7fe7
&bool r4bH = $7fe9
&bool r5bH = $7feb
&bool r6bH = $7fed
&bool r7bH = $7fef
&bool r8bH = $7ff1
&bool r9bH = $7ff3
&bool r10bH = $7ff5
&bool r11bH = $7ff7
&bool r12bH = $7ff9
&bool r13bH = $7ffb
&bool r14bH = $7ffd
&bool r15bH = $7fff
asmsub save_virtual_registers() clobbers(A,Y) { asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{ %asm {{
ldy #31 ldy #31

View File

@ -43,56 +43,80 @@ _done
}} }}
} }
/* sub gnomesort_by_ub(uword @requirezp uw_keys, uword values, ubyte num_elements) {
prog8 source code for the above routine: ; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
sub gnomesort_ub(uword @requirezp values, ubyte num_elements) {
ubyte @zp pos=1 ubyte @zp pos=1
while pos != num_elements { while pos != num_elements {
if values[pos]>=values[pos-1] if uw_keys[pos]>=uw_keys[pos-1]
pos++ pos++
else { else {
; swap elements ; swap elements
cx16.r0L = values[pos-1] cx16.r0L = uw_keys[pos-1]
values[pos-1] = values[pos] uw_keys[pos-1] = uw_keys[pos]
values[pos] = cx16.r0L uw_keys[pos] = cx16.r0L
uword @requirezp vptr = values + pos*$0002 -2
cx16.r0 = peekw(vptr)
pokew(vptr, peekw(vptr+2))
pokew(vptr+2, cx16.r0)
pos-- pos--
if_z if_z
pos++ pos++
} }
} }
} }
*/
sub gnomesort_uw(uword values, ubyte num_elements) { sub gnomesort_uw(uword @requirezp values, ubyte num_elements) {
; When written in asm this is 10-20% faster, but unreadable. Not worth it. ; Sorts the values array (no-split unsigned words).
; Also, sorting just an array of word numbers is very seldomly used, most often you ; Max number of elements is 128. Clobbers R0 and R1.
; need to sort other things associated with it as well and that is not done here anyway, ubyte @zp pos=2
; so requires a custom user coded sorting routine anyway. num_elements *= 2
ubyte @zp pos = 1
uword @requirezp ptr = values+2
while pos != num_elements { while pos != num_elements {
cx16.r0 = peekw(ptr-2) cx16.r1L = pos-2
cx16.r1 = peekw(ptr) if peekw(values+pos) >= peekw(values + cx16.r1L)
if cx16.r0<=cx16.r1 { pos += 2
pos++
ptr+=2
}
else { else {
; swap elements ; swap elements
pokew(ptr-2, cx16.r1) cx16.r0 = peekw(values + cx16.r1L)
pokew(ptr, cx16.r0) pokew(values + cx16.r1L, peekw(values + pos))
if pos>1 { pokew(values + pos, cx16.r0)
pos-- pos-=2
ptr-=2 if_z
pos+=2
} }
} }
} }
sub gnomesort_by_uw(uword @requirezp uw_keys, uword wordvalues, ubyte num_elements) {
; Sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
; both arrays should be no-split array of words. uw_keys are unsigned.
; Max number of elements is 128. Clobbers R0 and R1.
ubyte @zp pos=2
num_elements *= 2
while pos != num_elements {
cx16.r1L = pos-2
if peekw(uw_keys+pos) >= peekw(uw_keys + cx16.r1L)
pos += 2
else {
; swap elements
cx16.r0 = peekw(uw_keys + cx16.r1L)
pokew(uw_keys + cx16.r1L, peekw(uw_keys+ pos))
pokew(uw_keys + pos, cx16.r0)
cx16.r0 = peekw(wordvalues + cx16.r1L)
pokew(wordvalues + cx16.r1L, peekw(wordvalues + pos))
pokew(wordvalues + pos, cx16.r0)
pos-=2
if_z
pos+=2
}
}
} }
; gnomesort_pointers is not worth it over shellshort_pointers. ; gnomesort_pointers is not worth it over shellshort_pointers.
sub shellsort_ub(uword @requirezp values, ubyte num_elements) { sub shellsort_ub(uword @requirezp values, ubyte num_elements) {
; sorts the values array (unsigned bytes).
num_elements-- num_elements--
ubyte @zp gap ubyte @zp gap
for gap in [132, 57, 23, 10, 4, 1] { for gap in [132, 57, 23, 10, 4, 1] {
@ -115,6 +139,7 @@ _done
} }
sub shellsort_uw(uword @requirezp values, ubyte num_elements) { sub shellsort_uw(uword @requirezp values, ubyte num_elements) {
; sorts the values array (no-split unsigned words).
num_elements-- num_elements--
ubyte gap ubyte gap
for gap in [132, 57, 23, 10, 4, 1] { for gap in [132, 57, 23, 10, 4, 1] {
@ -124,13 +149,65 @@ _done
ubyte @zp j = i ubyte @zp j = i
ubyte @zp k = j-gap ubyte @zp k = j-gap
while j>=gap { while j>=gap {
uword @zp v = peekw(values+k*2) uword @zp v = peekw(values+k*$0002)
if v <= temp break if v <= temp break
pokew(values+j*2, v) pokew(values+j*$0002, v)
j = k j = k
k -= gap k -= gap
} }
pokew(values+j*2, temp) pokew(values+j*$0002, temp)
}
}
}
sub shellsort_by_ub(uword @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
num_elements--
ubyte @zp gap
for gap in [132, 57, 23, 10, 4, 1] {
ubyte i
for i in gap to num_elements {
ubyte @zp temp = ub_keys[i]
uword temp_wv = peekw(wordvalues + i*$0002)
ubyte @zp j = i
ubyte @zp k = j-gap
repeat {
ubyte @zp v = ub_keys[k]
if v <= temp break
if j < gap break
ub_keys[j] = v
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
j = k
k -= gap
}
ub_keys[j] = temp
pokew(wordvalues + j*$0002, temp_wv)
}
}
}
sub shellsort_by_uw(uword @requirezp uw_keys, uword @requirezp wordvalues, ubyte num_elements) {
; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
; both arrays should be no-split array of words. uw_keys are unsigned.
num_elements--
ubyte gap
for gap in [132, 57, 23, 10, 4, 1] {
ubyte i
for i in gap to num_elements {
uword @zp temp = peekw(uw_keys+i*$0002)
uword temp_wv = peekw(wordvalues + i*$0002)
ubyte @zp j = i
ubyte @zp k = j-gap
while j>=gap {
uword @zp v = peekw(uw_keys+k*2)
if v <= temp break
pokew(uw_keys+j*2, v)
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
j = k
k -= gap
}
pokew(uw_keys+j*2, temp)
pokew(wordvalues + j*$0002, temp_wv)
} }
} }
} }
@ -147,14 +224,14 @@ _done
ubyte @zp j = i ubyte @zp j = i
ubyte @zp k = j-gap ubyte @zp k = j-gap
while j>=gap { while j>=gap {
cx16.r0 = peekw(pointers+k*2) cx16.r0 = peekw(pointers+k*$0002)
void call(comparefunc) void call(comparefunc)
if_cs break if_cs break
pokew(pointers+j*2, cx16.r0) pokew(pointers+j*$0002, cx16.r0)
j = k j = k
k -= gap k -= gap
} }
pokew(pointers+j*2, cx16.r1) pokew(pointers+j*$0002, cx16.r1)
} }
} }
} }

View File

@ -151,6 +151,33 @@ _found tya
}} }}
} }
asmsub find_eol(uword string @AY) -> ubyte @A, bool @Pc {
; Locates the position of the first End Of Line character in the string.
; This is a convenience function that looks for both a CR or LF (byte 13 or byte 10) as being a possible Line Ending.
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
%asm {{
; need to copy the the cx16 virtual registers to zeropage to make this run on C64...
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
- lda (P8ZP_SCRATCH_W1),y
beq _notfound
cmp #13
beq _found
cmp #10
beq _found
iny
bne -
_notfound lda #255
clc
rts
_found tya
sec
rts
}}
}
asmsub rfind(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc { asmsub rfind(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
; Locates the first position of the given character in the string, starting from the right. ; Locates the first position of the given character in the string, starting from the right.
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index). ; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).

View File

@ -0,0 +1,103 @@
; **experimental** data sorting routines, API subject to change!!
; NOTE: gnomesort is not implemented here, just use shellshort.
sorting {
%option ignore_unused
sub shellsort_ub(uword @requirezp values, ubyte num_elements) {
num_elements--
ubyte @zp gap
for gap in [132, 57, 23, 10, 4, 1] {
ubyte i
for i in gap to num_elements {
ubyte @zp temp = values[i]
ubyte @zp j = i
ubyte @zp k = j-gap
repeat {
ubyte @zp v = values[k]
if v <= temp break
if j < gap break
values[j] = v
j = k
k -= gap
}
values[j] = temp
}
}
}
sub shellsort_uw(uword @requirezp values, ubyte num_elements) {
num_elements--
ubyte gap
for gap in [132, 57, 23, 10, 4, 1] {
ubyte i
for i in gap to num_elements {
uword @zp temp = peekw(values+i*$0002)
ubyte @zp j = i
ubyte @zp k = j-gap
while j>=gap {
uword @zp v = peekw(values+k*2)
if v <= temp break
pokew(values+j*2, v)
j = k
k -= gap
}
pokew(values+j*2, temp)
}
}
}
sub shellsort_by_ub(uword @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
num_elements--
ubyte @zp gap
for gap in [132, 57, 23, 10, 4, 1] {
ubyte i
for i in gap to num_elements {
ubyte @zp temp = ub_keys[i]
uword temp_wv = peekw(wordvalues + i*$0002)
ubyte @zp j = i
ubyte @zp k = j-gap
repeat {
ubyte @zp v = ub_keys[k]
if v <= temp break
if j < gap break
ub_keys[j] = v
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
j = k
k -= gap
}
ub_keys[j] = temp
pokew(wordvalues + j*$0002, temp_wv)
}
}
}
sub shellsort_by_uw(uword @requirezp uw_keys, uword @requirezp wordvalues, ubyte num_elements) {
; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
; both arrays should be no-split array of words. uw_keys are unsigned.
num_elements--
ubyte gap
for gap in [132, 57, 23, 10, 4, 1] {
ubyte i
for i in gap to num_elements {
uword @zp temp = peekw(uw_keys+i*$0002)
uword temp_wv = peekw(wordvalues + i*$0002)
ubyte @zp j = i
ubyte @zp k = j-gap
while j>=gap {
uword @zp v = peekw(uw_keys+k*2)
if v <= temp break
pokew(uw_keys+j*2, v)
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
j = k
k -= gap
}
pokew(uw_keys+j*2, temp)
pokew(wordvalues + j*$0002, temp_wv)
}
}
}
}

View File

@ -53,34 +53,47 @@ strings {
target[ix]=0 target[ix]=0
} }
sub find(str st, ubyte character) -> ubyte { sub find(str st, ubyte character) -> ubyte, bool {
; Locates the first position of the given character in the string, ; Locates the first position of the given character in the string,
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index). ; returns index in A, and boolean if found or not. (when false A will also be 255, an invalid index).
; NOTE: because this isn't an asmsub, there's only a SINGLE return value here. On the c64/cx16 targets etc there are 2 return values.
ubyte ix ubyte ix
for ix in 0 to length(st)-1 { for ix in 0 to length(st)-1 {
if st[ix]==character { if st[ix]==character {
sys.set_carry() sys.set_carry()
return ix return ix, true
} }
} }
sys.clear_carry() sys.clear_carry()
return 255 return 255, false
} }
sub rfind(uword stringptr, ubyte character) -> ubyte { sub find_eol(str st) -> ubyte, bool {
; Locates the position of the first End Of Line character in the string.
; This is a convenience function that looks for both a CR or LF (byte 13 or byte 10) as being a possible Line Ending.
; returns index in A, and boolean if found or not. (when false A will also be 255, an invalid index).
ubyte ix
for ix in 0 to length(st)-1 {
if st[ix] in "\x0a\x0d" {
sys.set_carry()
return ix, true
}
}
sys.clear_carry()
return 255, false
}
sub rfind(uword stringptr, ubyte character) -> ubyte, bool {
; Locates the first position of the given character in the string, starting from the right. ; Locates the first position of the given character in the string, starting from the right.
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index). ; returns index in A, and boolean if found or not. (when false A will also be 255, an invalid index).
; NOTE: because this isn't an asmsub, there's only a SINGLE return value here. On the c64/cx16 targets etc there are 2 return values.
ubyte ix ubyte ix
for ix in length(stringptr)-1 downto 0 { for ix in length(stringptr)-1 downto 0 {
if stringptr[ix]==character { if stringptr[ix]==character {
sys.set_carry() sys.set_carry()
return ix return ix, true
} }
} }
sys.clear_carry() sys.clear_carry()
return 255 return 255, false
} }
sub contains(str st, ubyte character) -> bool { sub contains(str st, ubyte character) -> bool {

View File

@ -199,6 +199,12 @@ sys {
}} }}
} }
sub get_as_returnaddress(uword address) -> uword {
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
address--
return mkword(lsb(address), msb(address))
}
sub pop() -> ubyte { sub pop() -> ubyte {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works ; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{ %ir {{
@ -260,6 +266,7 @@ cx16 {
&uword r14 = $ff1e &uword r14 = $ff1e
&uword r15 = $ff20 &uword r15 = $ff20
; signed word versions
&word r0s = $ff02 &word r0s = $ff02
&word r1s = $ff04 &word r1s = $ff04
&word r2s = $ff06 &word r2s = $ff06
@ -277,6 +284,7 @@ cx16 {
&word r14s = $ff1e &word r14s = $ff1e
&word r15s = $ff20 &word r15s = $ff20
; ubyte versions (low and high bytes)
&ubyte r0L = $ff02 &ubyte r0L = $ff02
&ubyte r1L = $ff04 &ubyte r1L = $ff04
&ubyte r2L = $ff06 &ubyte r2L = $ff06
@ -311,6 +319,7 @@ cx16 {
&ubyte r14H = $ff1f &ubyte r14H = $ff1f
&ubyte r15H = $ff21 &ubyte r15H = $ff21
; signed byte versions (low and high bytes)
&byte r0sL = $ff02 &byte r0sL = $ff02
&byte r1sL = $ff04 &byte r1sL = $ff04
&byte r2sL = $ff06 &byte r2sL = $ff06
@ -345,6 +354,42 @@ cx16 {
&byte r14sH = $ff1f &byte r14sH = $ff1f
&byte r15sH = $ff21 &byte r15sH = $ff21
; boolean versions
&bool r0bL = $ff02
&bool r1bL = $ff04
&bool r2bL = $ff06
&bool r3bL = $ff08
&bool r4bL = $ff0a
&bool r5bL = $ff0c
&bool r6bL = $ff0e
&bool r7bL = $ff10
&bool r8bL = $ff12
&bool r9bL = $ff14
&bool r10bL = $ff16
&bool r11bL = $ff18
&bool r12bL = $ff1a
&bool r13bL = $ff1c
&bool r14bL = $ff1e
&bool r15bL = $ff20
&bool r0bH = $ff03
&bool r1bH = $ff05
&bool r2bH = $ff07
&bool r3bH = $ff09
&bool r4bH = $ff0b
&bool r5bH = $ff0d
&bool r6bH = $ff0f
&bool r7bH = $ff11
&bool r8bH = $ff13
&bool r9bH = $ff15
&bool r10bH = $ff17
&bool r11bH = $ff19
&bool r12bH = $ff1b
&bool r13bH = $ff1d
&bool r14bH = $ff1f
&bool r15bH = $ff21
sub save_virtual_registers() { sub save_virtual_registers() {
uword[32] storage uword[32] storage
storage[0] = r0 storage[0] = r0

View File

@ -26,6 +26,7 @@ import kotlin.io.path.isRegularFile
import kotlin.io.path.nameWithoutExtension import kotlin.io.path.nameWithoutExtension
import kotlin.system.exitProcess import kotlin.system.exitProcess
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.DurationUnit
import kotlin.time.measureTime import kotlin.time.measureTime
import kotlin.time.measureTimedValue import kotlin.time.measureTimedValue
@ -228,19 +229,20 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
System.err.flush() System.err.flush()
if(!args.quietAll && args.showTimings) { if(!args.quietAll && args.showTimings) {
println("\n**** TIMING ****") println("\n**** TIMINGS ****")
println("source parsing : ${parseDuration}") println("source parsing : ${parseDuration.toString(DurationUnit.SECONDS, 3)}")
println("ast processing : ${processDuration}") println("ast processing : ${processDuration.toString(DurationUnit.SECONDS, 3)}")
println("ast optimizing : ${optimizeDuration}") println("ast optimizing : ${optimizeDuration.toString(DurationUnit.SECONDS, 3)}")
println("ast postprocess : ${postprocessDuration}") println("ast postprocess : ${postprocessDuration.toString(DurationUnit.SECONDS, 3)}")
println("code prepare : ${simplifiedAstDuration}") println("code prepare : ${simplifiedAstDuration.toString(DurationUnit.SECONDS, 3)}")
println("code generation : ${createAssemblyDuration}") println("code generation : ${createAssemblyDuration.toString(DurationUnit.SECONDS, 3)}")
println(" total : ${parseDuration + processDuration + optimizeDuration + postprocessDuration + simplifiedAstDuration + createAssemblyDuration}") val totalDuration = parseDuration + processDuration + optimizeDuration + postprocessDuration + simplifiedAstDuration + createAssemblyDuration
println(" total : ${totalDuration.toString(DurationUnit.SECONDS, 3)}")
} }
} }
if(!args.quietAll) { if(!args.quietAll) {
println("\nTotal compilation+assemble time: ${totalTime}.") println("\nTotal compilation+assemble time: ${totalTime.toString(DurationUnit.SECONDS, 3)}.")
} }
return CompilationResult(resultingProgram!!, ast, compilationOptions, importedFiles) return CompilationResult(resultingProgram!!, ast, compilationOptions, importedFiles)
} catch (px: ParseError) { } catch (px: ParseError) {

View File

@ -184,8 +184,6 @@ internal class AstChecker(private val program: Program,
val iterableDt = forLoop.iterable.inferType(program).getOrUndef() val iterableDt = forLoop.iterable.inferType(program).getOrUndef()
if(iterableDt.isNumeric) TODO("iterable type should not be simple numeric "+forLoop.position)
if(forLoop.iterable is IFunctionCall) { if(forLoop.iterable is IFunctionCall) {
errors.err("can not loop over function call return value", forLoop.position) errors.err("can not loop over function call return value", forLoop.position)
} else if(!(iterableDt.isIterable) && forLoop.iterable !is RangeExpression) { } else if(!(iterableDt.isIterable) && forLoop.iterable !is RangeExpression) {
@ -408,11 +406,42 @@ internal class AstChecker(private val program: Program,
// subroutine must contain at least one 'return' or 'goto' // subroutine must contain at least one 'return' or 'goto'
// (or if it has an asm block, that must contain a 'rts' or 'jmp' or 'bra') // (or if it has an asm block, that must contain a 'rts' or 'jmp' or 'bra')
var haveReturnError = false
if(!hasReturnOrExternalJumpOrRts(subroutine)) { if(!hasReturnOrExternalJumpOrRts(subroutine)) {
if (subroutine.returntypes.isNotEmpty()) { if (subroutine.returntypes.isNotEmpty()) {
// for asm subroutines with an address, no statement check is possible. // for asm subroutines with an address, no statement check is possible.
if (subroutine.asmAddress == null && !subroutine.inline) if (subroutine.asmAddress == null && !subroutine.inline) {
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or external 'goto' in it (or the assembler equivalent in case of %asm)") err("non-inline subroutine has result value(s) and thus must have at least one 'return' or external 'goto' in it (or the assembler equivalent in case of %asm)")
haveReturnError = true
}
}
}
val lastStatement = subroutine.statements.reversed().dropWhile { it is Subroutine || it is VarDecl || it is Directive || (it is Assignment && it.origin== AssignmentOrigin.VARINIT) }.firstOrNull()
if(!haveReturnError && !subroutine.isAsmSubroutine && !subroutine.inline && subroutine.returntypes.isNotEmpty() && lastStatement !is Return) {
if(lastStatement==null)
err("subroutine '${subroutine.name}' has result value(s) but doesn't end with a return statement")
else {
val returnError = when (lastStatement) {
is Jump, is OnGoto -> false
is IStatementContainer -> !hasReturnOrExternalJumpOrRts(lastStatement as IStatementContainer)
is InlineAssembly -> !lastStatement.hasReturnOrRts()
is ForLoop -> hasReturnOrExternalJumpOrRts(lastStatement.body)
is IfElse -> !hasReturnOrExternalJumpOrRts(lastStatement.truepart) && !hasReturnOrExternalJumpOrRts(lastStatement.elsepart)
is ConditionalBranch -> !hasReturnOrExternalJumpOrRts(lastStatement.truepart) && !hasReturnOrExternalJumpOrRts(lastStatement.elsepart)
is RepeatLoop -> {
lastStatement.iterations!=null || !hasReturnOrExternalJumpOrRts(lastStatement.body)
}
is UntilLoop -> !hasReturnOrExternalJumpOrRts(lastStatement.body)
is WhileLoop -> !hasReturnOrExternalJumpOrRts(lastStatement.body)
is When -> lastStatement.choices.all { !hasReturnOrExternalJumpOrRts(it.statements) }
else -> true
}
if(returnError) {
val pos = if(lastStatement is Subroutine) subroutine.position else lastStatement.position
errors.err("subroutine '${subroutine.name}' has result value(s) but doesn't end with a return statement", pos)
}
} }
} }

View File

@ -139,7 +139,7 @@ class AstPreprocessor(val program: Program,
} }
} else { } else {
// handle declaration of a single variable // handle declaration of a single variable
if(decl.value!=null && decl.datatype.isNumericOrBool) { if(decl.value!=null && !decl.datatype.isIterable) {
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, null, false, decl.position) val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, null, false, decl.position)
val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position) val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position)
replacements.add(IAstModification.ReplaceNode(decl, assign, scope)) replacements.add(IAstModification.ReplaceNode(decl, assign, scope))

View File

@ -49,7 +49,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
val mods = mutableListOf<IAstModification>() val mods = mutableListOf<IAstModification>()
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine. // add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
// and if an assembly block doesn't contain a rts/rti. // and if an assembly block doesn't contain a rts/rti. AND if there's no return value(s) because we can't make one up!
if (!subroutine.isAsmSubroutine) { if (!subroutine.isAsmSubroutine) {
if(subroutine.isEmpty()) { if(subroutine.isEmpty()) {
if(subroutine.returntypes.isNotEmpty()) if(subroutine.returntypes.isNotEmpty())

View File

@ -104,7 +104,7 @@ if not CONDITION
untilLoop.body, untilLoop.body,
IfElse(invertCondition(untilLoop.condition, program), IfElse(invertCondition(untilLoop.condition, program),
AnonymousScope(mutableListOf(program.jumpLabel(loopLabel)), pos), AnonymousScope(mutableListOf(program.jumpLabel(loopLabel)), pos),
AnonymousScope(mutableListOf(), pos), AnonymousScope.empty(),
pos) pos)
), pos) ), pos)
return listOf(IAstModification.ReplaceNode(untilLoop, replacement, parent)) return listOf(IAstModification.ReplaceNode(untilLoop, replacement, parent))
@ -148,7 +148,7 @@ _after:
loopLabel, loopLabel,
IfElse(invertCondition(whileLoop.condition, program), IfElse(invertCondition(whileLoop.condition, program),
AnonymousScope(mutableListOf(program.jumpLabel(afterLabel)), pos), AnonymousScope(mutableListOf(program.jumpLabel(afterLabel)), pos),
AnonymousScope(mutableListOf(), pos), AnonymousScope.empty(),
pos), pos),
whileLoop.body, whileLoop.body,
program.jumpLabel(loopLabel), program.jumpLabel(loopLabel),
@ -380,7 +380,7 @@ _after:
} }
val callTarget = ArrayIndexedExpression(IdentifierReference(listOf(jumplistArray.name), jumplistArray.position), ArrayIndex(indexValue.copy(), indexValue.position), ongoto.position) val callTarget = ArrayIndexedExpression(IdentifierReference(listOf(jumplistArray.name), jumplistArray.position), ArrayIndex(indexValue.copy(), indexValue.position), ongoto.position)
val callIndexed = AnonymousScope(mutableListOf(), ongoto.position) val callIndexed = AnonymousScope.empty(ongoto.position)
if(ongoto.isCall) { if(ongoto.isCall) {
callIndexed.statements.add(FunctionCallStatement(IdentifierReference(listOf("call"), ongoto.position), mutableListOf(callTarget), true, ongoto.position)) callIndexed.statements.add(FunctionCallStatement(IdentifierReference(listOf("call"), ongoto.position), mutableListOf(callTarget), true, ongoto.position))
} else { } else {
@ -390,7 +390,7 @@ _after:
val ifSt = if(ongoto.elsepart==null || ongoto.elsepart!!.isEmpty()) { val ifSt = if(ongoto.elsepart==null || ongoto.elsepart!!.isEmpty()) {
// if index<numlabels call(labels[index]) // if index<numlabels call(labels[index])
val compare = BinaryExpression(indexValue.copy(), "<", NumericLiteral.optimalInteger(numlabels, ongoto.position), ongoto.position) val compare = BinaryExpression(indexValue.copy(), "<", NumericLiteral.optimalInteger(numlabels, ongoto.position), ongoto.position)
IfElse(compare, callIndexed, AnonymousScope(mutableListOf(), ongoto.position), ongoto.position) IfElse(compare, callIndexed, AnonymousScope.empty(), ongoto.position)
} else { } else {
// if index>=numlabels elselabel() else call(labels[index]) // if index>=numlabels elselabel() else call(labels[index])
val compare = BinaryExpression(indexValue.copy(), ">=", NumericLiteral.optimalInteger(numlabels, ongoto.position), ongoto.position) val compare = BinaryExpression(indexValue.copy(), ">=", NumericLiteral.optimalInteger(numlabels, ongoto.position), ongoto.position)

View File

@ -213,7 +213,7 @@ class TestNumericLiteral: FunSpec({
test("cast can change value") { test("cast can change value") {
fun num(dt: BaseDataType, num: Double): NumericLiteral { fun num(dt: BaseDataType, num: Double): NumericLiteral {
val n = NumericLiteral(dt, num, Position.DUMMY) val n = NumericLiteral(dt, num, Position.DUMMY)
n.linkParents(AnonymousScope(mutableListOf(), Position.DUMMY)) n.linkParents(AnonymousScope.empty())
return n return n
} }
val cast1 = num(BaseDataType.UBYTE, 200.0).cast(BaseDataType.BYTE, false) val cast1 = num(BaseDataType.UBYTE, 200.0).cast(BaseDataType.BYTE, false)
@ -233,7 +233,7 @@ class TestNumericLiteral: FunSpec({
test("convert cannot change value") { test("convert cannot change value") {
fun num(dt: BaseDataType, num: Double): NumericLiteral { fun num(dt: BaseDataType, num: Double): NumericLiteral {
val n = NumericLiteral(dt, num, Position.DUMMY) val n = NumericLiteral(dt, num, Position.DUMMY)
n.linkParents(AnonymousScope(mutableListOf(), Position.DUMMY)) n.linkParents(AnonymousScope.empty())
return n return n
} }
num(BaseDataType.UBYTE, 200.0).convertTypeKeepValue(BaseDataType.BYTE).isValid shouldBe false num(BaseDataType.UBYTE, 200.0).convertTypeKeepValue(BaseDataType.BYTE).isValid shouldBe false

View File

@ -22,7 +22,9 @@ import prog8.code.core.Position
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.code.target.Cx16Target import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget import prog8.code.target.VMTarget
import prog8.vm.VmRunner
import prog8tests.helpers.* import prog8tests.helpers.*
import kotlin.io.path.readText
class TestOptimization: FunSpec({ class TestOptimization: FunSpec({
@ -1178,4 +1180,54 @@ main {
}""" }"""
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
} }
test("correct unused block removal for virtual target") {
val src="""
main {
sub start() {
cx16.r0++
}
}
some_block {
uword buffer = memory("arena", 2000, 0)
}
other_block {
sub redherring (uword buffer) {
%ir {{
loadm.w r99000,other_block.redherring.buffer
}}
}
}
"""
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
VmRunner().runProgram(virtfile.readText(), false)
}
test("correct unused block removal for c64 target") {
val src="""
main {
sub start() {
cx16.r0++
}
}
some_block {
uword buffer = memory("arena", 2000, 0)
}
other_block {
sub redherring (uword buffer) {
%asm {{
lda #<p8b_other_block.p8s_redherring.p8v_buffer
ldy #>p8b_other_block.p8s_redherring.p8v_buffer
}}
}
}"""
compileText(C64Target(), true, src, outputDir) shouldNotBe null
}
}) })

View File

@ -288,13 +288,13 @@ class TestProg8Parser: FunSpec( {
} }
""" """
val module = parseModule(SourceCode.Text(srcText)) val module = parseModule(SourceCode.Text(srcText))
assertPositionOf(module, Regex("^string:[0-9a-f\\-]+$"), 1, 0) assertPositionOf(module, Regex("^string:[0-9a-f\\-]+$"), 1, 1)
} }
test("of Module parsed from a file") { test("of Module parsed from a file") {
val path = assumeReadableFile(fixturesDir, "ast_simple_main.p8") val path = assumeReadableFile(fixturesDir, "ast_simple_main.p8")
val module = parseModule(ImportFileSystem.getFile(path)) val module = parseModule(ImportFileSystem.getFile(path))
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0) assertPositionOf(module, SourceCode.relative(path).toString(), 1, 1)
} }
test("of non-root Nodes parsed from file") { test("of non-root Nodes parsed from file") {
@ -302,7 +302,7 @@ class TestProg8Parser: FunSpec( {
val module = parseModule(ImportFileSystem.getFile(path)) val module = parseModule(ImportFileSystem.getFile(path))
val mpf = module.position.file val mpf = module.position.file
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0) assertPositionOf(module, SourceCode.relative(path).toString(), 1, 1)
val mainBlock = module.statements.filterIsInstance<Block>()[0] val mainBlock = module.statements.filterIsInstance<Block>()[0]
assertPositionOf(mainBlock, mpf, 2, 1, 4) assertPositionOf(mainBlock, mpf, 2, 1, 4)
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0] val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]

View File

@ -243,4 +243,40 @@ main {
errors.errors.size shouldBe 1 errors.errors.size shouldBe 1
errors.errors[0] shouldContain ":8:5: address must be a constant" errors.errors[0] shouldContain ":8:5: address must be a constant"
} }
test("detect missing return") {
val src="""
main {
sub start() {
void read_loadlist()
void test2()
}
sub read_loadlist() -> bool {
cx16.r0++
if cx16.r0==0
return false
cx16.r1++
; TODO missing return! ERROR!
}
sub test2() -> bool {
cx16.r0++
return false
sub sub1() {
cx16.r0++
}
sub sub2() {
cx16.r0++
}
}
}"""
val errors = ErrorReporterForTests()
compileText(Cx16Target(), false, src, outputDir, errors, false) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "doesn't end with a return"
}
}) })

View File

@ -17,9 +17,7 @@ import prog8.code.ast.*
import prog8.code.core.BaseDataType import prog8.code.core.BaseDataType
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.Position import prog8.code.core.Position
import prog8.code.target.C64Target import prog8.code.target.*
import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
@ -1057,6 +1055,42 @@ main {
compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
} }
test("using the cx16 virtual registers as various datatypes") {
val src="""
main {
sub start() {
uword uw = 9999
word sw = -2222
ubyte ub = 42
byte sb = -99
bool bb = true
cx16.r0 = uw
cx16.r0s = sw
cx16.r0L = ub
cx16.r0H = ub
cx16.r0sL = sb
cx16.r0sH = sb
cx16.r0bL = bb
cx16.r0bH = bb
uw = cx16.r0
sw = cx16.r0s
ub = cx16.r0L
ub = cx16.r0H
sb = cx16.r0sL
sb = cx16.r0sH
bb = cx16.r0bL
bb = cx16.r0bH
}
}"""
compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
compileText(PETTarget(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
compileText(C128Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
}
test("on..goto") { test("on..goto") {
val src=""" val src="""

View File

@ -38,8 +38,7 @@ main {
zz = words[3] zz = words[3]
} }
}""" }"""
val target = VMTarget() val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
val result = compileText(target, false, src, outputDir, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir") val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
VmRunner().runProgram(virtfile.readText(), false) VmRunner().runProgram(virtfile.readText(), false)
} }
@ -58,8 +57,7 @@ main {
zz = words[3] zz = words[3]
} }
}""" }"""
val target = VMTarget() val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
val result = compileText(target, false, src, outputDir, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir") val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
VmRunner().runProgram(virtfile.readText(), false) VmRunner().runProgram(virtfile.readText(), false)
} }
@ -107,7 +105,7 @@ test {
} }
}""" }"""
val target = VMTarget() val target = VMTarget()
var result = compileText(target, false, src, outputDir, writeAssembly = true)!! var result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
var virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir") var virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
VmRunner().runProgram(virtfile.readText(), false) VmRunner().runProgram(virtfile.readText(), false)

View File

@ -1,842 +0,0 @@
package prog8.ast.antlr
import org.antlr.v4.runtime.ParserRuleContext
import org.antlr.v4.runtime.Token
import org.antlr.v4.runtime.tree.TerminalNode
import prog8.ast.FatalAstException
import prog8.ast.SyntaxError
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.core.*
import prog8.code.source.SourceCode
import prog8.parser.Prog8ANTLRParser.*
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
/***************** Antlr Extension methods to create AST ****************/
private data class NumericLiteralNode(val number: Double, val datatype: BaseDataType)
private fun ParserRuleContext.toPosition() : Position {
val pathString = start.inputStream.sourceName
val filename = if(SourceCode.isRegularFilesystemPath(pathString)) {
val path = Path(pathString)
if(path.isRegularFile()) {
SourceCode.relative(path).toString()
} else {
path.toString()
}
} else {
pathString
}
// note: beware of TAB characters in the source text, they count as 1 column...
return Position(filename, start.line, start.charPositionInLine+1, start.charPositionInLine + 1 + start.stopIndex - start.startIndex)
}
internal fun BlockContext.toAst(isInLibrary: Boolean) : Block {
val blockstatements = block_statement().map {
when {
it.variabledeclaration()!=null -> it.variabledeclaration().toAst()
it.subroutinedeclaration()!=null -> it.subroutinedeclaration().toAst()
it.directive()!=null -> it.directive().toAst()
it.inlineasm()!=null -> it.inlineasm().toAst()
it.inlineir()!=null -> it.inlineir().toAst()
it.labeldef()!=null -> it.labeldef().toAst()
it.alias()!=null -> it.alias().toAst()
else -> throw FatalAstException("weird block node $it")
}
}
return Block(identifier().text, integerliteral()?.toAst()?.number?.toUInt(), blockstatements.toMutableList(), isInLibrary, toPosition())
}
private fun Statement_blockContext.toAst(): MutableList<Statement> =
statement().asSequence().map { it.toAst() }.toMutableList()
private fun VariabledeclarationContext.toAst() : Statement {
vardecl()?.let {
return it.toAst(VarDeclType.VAR, null)
}
varinitializer()?.let {
return it.vardecl().toAst(VarDeclType.VAR, it.expression().toAst())
}
constdecl()?.let {
val cvarinit = it.varinitializer()
return cvarinit.vardecl().toAst(VarDeclType.CONST, cvarinit.expression().toAst())
}
memoryvardecl()?.let {
val mvarinit = it.varinitializer()
return mvarinit.vardecl().toAst(VarDeclType.MEMORY, mvarinit.expression().toAst())
}
throw FatalAstException("weird variable decl $this")
}
private fun SubroutinedeclarationContext.toAst() : Subroutine {
return when {
subroutine()!=null -> subroutine().toAst()
asmsubroutine()!=null -> asmsubroutine().toAst()
extsubroutine()!=null -> extsubroutine().toAst()
else -> throw FatalAstException("weird subroutine decl $this")
}
}
private fun StatementContext.toAst() : Statement {
val vardecl = variabledeclaration()?.toAst()
if(vardecl!=null) return vardecl
val assignment = assignment()?.toAst()
if(assignment!=null) return assignment
val augassign = augassignment()?.toAst()
if(augassign!=null) return augassign
postincrdecr()?.let {
val tgt = it.assign_target().toAst()
val operator = it.operator.text
val pos = it.toPosition()
// print("\u001b[92mINFO\u001B[0m ") // bright green
// println("${pos}: ++ and -- will be removed in a future version, please use +=1 or -=1 instead.") // .... if we decode to remove them one day
val addSubOne = BinaryExpression(tgt.toExpression(), if(operator=="++") "+" else "-", NumericLiteral.optimalInteger(1, pos), pos)
return Assignment(tgt, addSubOne, AssignmentOrigin.USERCODE, pos)
}
val directive = directive()?.toAst()
if(directive!=null) return directive
val label = labeldef()?.toAst()
if(label!=null) return label
val jump = unconditionaljump()?.toAst()
if(jump!=null) return jump
val fcall = functioncall_stmt()?.toAst()
if(fcall!=null) return fcall
val ifstmt = if_stmt()?.toAst()
if(ifstmt!=null) return ifstmt
val returnstmt = returnstmt()?.toAst()
if(returnstmt!=null) return returnstmt
val subroutine = subroutinedeclaration()?.toAst()
if(subroutine!=null) return subroutine
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
val forloop = forloop()?.toAst()
if(forloop!=null) return forloop
val untilloop = untilloop()?.toAst()
if(untilloop!=null) return untilloop
val whileloop = whileloop()?.toAst()
if(whileloop!=null) return whileloop
val repeatloop = repeatloop()?.toAst()
if(repeatloop!=null) return repeatloop
val whenstmt = whenstmt()?.toAst()
if(whenstmt!=null) return whenstmt
val breakstmt = breakstmt()?.toAst()
if(breakstmt!=null) return breakstmt
val continuestmt = continuestmt()?.toAst()
if(continuestmt!=null) return continuestmt
val unrollstmt = unrollloop()?.toAst()
if(unrollstmt!=null) return unrollstmt
val deferstmt = defer()?.toAst()
if(deferstmt!=null) return deferstmt
val aliasstmt = alias()?.toAst()
if(aliasstmt!=null) return aliasstmt
val ongotostmt = ongoto()?.toAst()
if(ongotostmt!=null) return ongotostmt
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
}
private fun AsmsubroutineContext.toAst(): Subroutine {
val inline = this.INLINE()!=null
val subdecl = asmsub_decl().toAst()
val statements = statement_block()?.toAst() ?: mutableListOf()
return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
subdecl.asmClobbers, null, true, inline, statements = statements, position = toPosition()
)
}
private fun ExtsubroutineContext.toAst(): Subroutine {
val subdecl = asmsub_decl().toAst()
val constbank = constbank?.toAst()?.number?.toUInt()?.toUByte()
val varbank = varbank?.toAst()
val addr = address.toAst()
val address = Subroutine.Address(constbank, varbank, addr)
return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
subdecl.asmClobbers, address, true, inline = false, statements = mutableListOf(), position = toPosition()
)
}
private class AsmsubDecl(val name: String,
val parameters: List<SubroutineParameter>,
val returntypes: List<DataType>,
val asmParameterRegisters: List<RegisterOrStatusflag>,
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
val asmClobbers: Set<CpuRegister>)
private fun Asmsub_declContext.toAst(): AsmsubDecl {
val name = identifier().text
val params = asmsub_params()?.toAst() ?: emptyList()
val returns = asmsub_returns()?.toAst() ?: emptyList()
val clobbers = asmsub_clobbers()?.clobber()?.toAst() ?: emptySet()
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.zp, it.registerOrPair, it.position) }
val normalReturntypes = returns.map { it.type }
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }
return AsmsubDecl(name, normalParameters, normalReturntypes, paramRegisters, returnRegisters, clobbers)
}
private class AsmSubroutineParameter(name: String,
type: DataType,
registerOrPair: RegisterOrPair?,
val statusflag: Statusflag?,
position: Position) : SubroutineParameter(name, type, ZeropageWish.DONTCARE, registerOrPair, position)
private class AsmSubroutineReturn(val type: DataType,
val registerOrPair: RegisterOrPair?,
val statusflag: Statusflag?)
private fun Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
= asmsub_return().map {
val register = it.register.text
var registerorpair: RegisterOrPair? = null
var statusregister: Statusflag? = null
if(register!=null) {
when (register) {
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(register)
in Statusflag.names -> statusregister = Statusflag.valueOf(register)
else -> throw SyntaxError("invalid register or status flag", toPosition())
}
}
// asmsubs currently only return a base datatype
val returnBaseDt = it.datatype().toAst()
AsmSubroutineReturn(
DataType.forDt(returnBaseDt),
registerorpair,
statusregister)
}
private fun Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter> = asmsub_param().map {
val vardecl = it.vardecl()
val baseDt = vardecl.datatype()?.toAst() ?: BaseDataType.UNDEFINED
var datatype = DataType.forDt(baseDt)
if(vardecl.ARRAYSIG()!=null || vardecl.arrayindex()!=null)
datatype = datatype.elementToArray()
val (registerorpair, statusregister) = parseParamRegister(it.register, it.toPosition())
val identifiers = vardecl.identifier()
if(identifiers.size>1)
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
val identifiername = getname(identifiers)
AsmSubroutineParameter(identifiername, datatype, registerorpair, statusregister, toPosition())
}
private fun getname(identifiers: MutableList<IdentifierContext>): String = identifiers[0].children[0].text
private fun getname(identifier: IdentifierContext): String = identifier.children[0].text
// return if (identifier == null) null
// else identifier.children[0].text
// //else (identifier.NAME() ?: identifier.UNDERSCORENAME() ?: identifier.ON() ?: identifier.CALL()).text
//}
private fun parseParamRegister(registerTok: Token?, pos: Position): Pair<RegisterOrPair?, Statusflag?> {
if(registerTok==null)
return Pair(null, null)
val register = registerTok.text
var registerorpair: RegisterOrPair? = null
var statusregister: Statusflag? = null
if(register!=null) {
when (register) {
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(register)
in Statusflag.names -> statusregister = Statusflag.valueOf(register)
else -> {
throw SyntaxError("invalid register or status flag", Position(pos.file, registerTok.line, registerTok.charPositionInLine, registerTok.charPositionInLine+1))
}
}
}
return Pair(registerorpair, statusregister)
}
private fun Functioncall_stmtContext.toAst(): Statement {
val void = this.VOID() != null
val location = scoped_identifier().toAst()
return if(expression_list() == null)
FunctionCallStatement(location, mutableListOf(), void, toPosition())
else
FunctionCallStatement(location, expression_list().toAst().toMutableList(), void, toPosition())
}
private fun FunctioncallContext.toAst(): FunctionCallExpression {
val location = scoped_identifier().toAst()
return if(expression_list() == null)
FunctionCallExpression(location, mutableListOf(), toPosition())
else
FunctionCallExpression(location, expression_list().toAst().toMutableList(), toPosition())
}
private fun InlineasmContext.toAst(): InlineAssembly {
val text = INLINEASMBLOCK().text
return InlineAssembly(text.substring(2, text.length-2), false, toPosition())
}
private fun InlineirContext.toAst(): InlineAssembly {
val text = INLINEASMBLOCK().text
return InlineAssembly(text.substring(2, text.length-2), true, toPosition())
}
private fun ReturnstmtContext.toAst() : Return {
val values = if(returnvalues()==null || returnvalues().expression().isEmpty()) arrayOf() else returnvalues().expression().map { it.toAst() }.toTypedArray()
return Return(values, toPosition())
}
private fun UnconditionaljumpContext.toAst(): Jump {
return Jump(expression().toAst(), toPosition())
}
private fun LabeldefContext.toAst(): Statement =
Label(children[0].text, toPosition())
private fun AliasContext.toAst(): Statement =
Alias(identifier().text, scoped_identifier().toAst(), toPosition())
private fun SubroutineContext.toAst() : Subroutine {
// non-asm subroutine
val returntypes = sub_return_part()?.datatype()?.map { it.toAst() } ?: emptyList()
return Subroutine(
identifier().text,
sub_params()?.toAst()?.toMutableList() ?: mutableListOf(),
returntypes.map { DataType.forDt(it) }.toMutableList(),
emptyList(),
emptyList(),
emptySet(),
asmAddress = null,
isAsmSubroutine = false,
inline = false,
statements = statement_block()?.toAst() ?: mutableListOf(),
position = toPosition()
)
}
private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
sub_param().map {
val decl = it.vardecl()
val tags = decl.TAG().map { t -> t.text }
val validTags = arrayOf("@zp", "@requirezp", "@nozp", "@split", "@nosplit", "@shared")
for(tag in tags) {
if(tag !in validTags)
throw SyntaxError("invalid parameter tag '$tag'", toPosition())
}
val zp = getZpOption(tags)
val baseDt = decl.datatype()?.toAst() ?: BaseDataType.UNDEFINED
var datatype = DataType.forDt(baseDt)
if(decl.ARRAYSIG()!=null || decl.arrayindex()!=null)
datatype = datatype.elementToArray()
val identifiers = decl.identifier()
if(identifiers.size>1)
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
val identifiername = getname(identifiers)
val (registerorpair, statusregister) = parseParamRegister(it.register, it.toPosition())
if(statusregister!=null) {
throw SyntaxError("can't use status register as param for normal subroutines", Position(toPosition().file, it.register.line, it.register.charPositionInLine, it.register.charPositionInLine+1))
}
SubroutineParameter(identifiername, datatype, zp, registerorpair, it.toPosition())
}
private fun getZpOption(tags: List<String>): ZeropageWish = when {
"@requirezp" in tags -> ZeropageWish.REQUIRE_ZEROPAGE
"@zp" in tags -> ZeropageWish.PREFER_ZEROPAGE
"@nozp" in tags -> ZeropageWish.NOT_IN_ZEROPAGE
else -> ZeropageWish.DONTCARE
}
private fun getSplitOption(tags: List<String>): SplitWish {
return when {
"@nosplit" in tags -> SplitWish.NOSPLIT
"@split" in tags -> SplitWish.SPLIT
else -> SplitWish.DONTCARE
}
}
private fun Assign_targetContext.toAst() : AssignTarget {
return when(this) {
is IdentifierTargetContext -> {
val identifier = scoped_identifier().toAst()
AssignTarget(identifier, null, null, null, false, scoped_identifier().toPosition())
}
is MemoryTargetContext ->
AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), directmemory().toPosition()), null, false, toPosition())
is ArrayindexedTargetContext -> {
val ax = arrayindexed()
val arrayvar = ax.scoped_identifier().toAst()
val index = ax.arrayindex().toAst()
val arrayindexed = ArrayIndexedExpression(arrayvar, index, ax.toPosition())
AssignTarget(null, arrayindexed, null, null, false, toPosition())
}
is VoidTargetContext -> {
AssignTarget(null, null, null, null, true, this.toPosition())
}
else -> throw FatalAstException("weird assign target node $this")
}
}
private fun Multi_assign_targetContext.toAst() : AssignTarget {
val targets = this.assign_target().map { it.toAst() }
return AssignTarget(null, null, null, targets, false, toPosition())
}
private fun ClobberContext.toAst() : Set<CpuRegister> {
val names = this.NAME().map { it.text }
try {
return names.map { CpuRegister.valueOf(it) }.toSet()
} catch(_: IllegalArgumentException) {
throw SyntaxError("invalid cpu register", toPosition())
}
}
private fun AssignmentContext.toAst(): Statement {
val multiAssign = multi_assign_target()
if(multiAssign!=null) {
return Assignment(multiAssign.toAst(), expression().toAst(), AssignmentOrigin.USERCODE, toPosition())
}
val nestedAssign = assignment()
return if(nestedAssign==null)
Assignment(assign_target().toAst(), expression().toAst(), AssignmentOrigin.USERCODE, toPosition())
else
ChainedAssignment(assign_target().toAst(), nestedAssign.toAst(), toPosition())
}
private fun AugassignmentContext.toAst(): Assignment {
// replace A += X with A = A + X
val target = assign_target().toAst()
val oper = operator.text.substringBefore('=')
val expression = BinaryExpression(target.toExpression(), oper, expression().toAst(), expression().toPosition())
return Assignment(assign_target().toAst(), expression, AssignmentOrigin.USERCODE, toPosition())
}
private fun DatatypeContext.toAst(): BaseDataType {
return try {
BaseDataType.valueOf(text.uppercase())
} catch (_: IllegalArgumentException) {
BaseDataType.UNDEFINED
}
}
private fun ArrayindexContext.toAst() : ArrayIndex =
ArrayIndex(expression().toAst(), toPosition())
internal fun DirectiveContext.toAst() : Directive {
if(directivenamelist() != null) {
val identifiers = directivenamelist().scoped_identifier().map { DirectiveArg(it.text, null, it.toPosition()) }
return Directive(directivename.text, identifiers, toPosition())
}
else
return Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
}
private fun DirectiveargContext.toAst() : DirectiveArg {
val str = stringliteral()
if(str!=null) {
if (str.encoding?.text != null)
throw SyntaxError("don't use a string encoding for directive arguments", toPosition())
return DirectiveArg(str.text.substring(1, text.length-1), integerliteral()?.toAst()?.number?.toUInt(), toPosition())
}
return DirectiveArg(identifier()?.text, integerliteral()?.toAst()?.number?.toUInt(), toPosition())
}
private fun IntegerliteralContext.toAst(): NumericLiteralNode {
fun makeLiteral(literalTextWithGrouping: String, radix: Int): NumericLiteralNode {
val literalText = literalTextWithGrouping.replace("_", "")
val integer: Int
var datatype = BaseDataType.UBYTE
when (radix) {
10 -> {
integer = try {
literalText.toInt()
} catch(x: NumberFormatException) {
throw SyntaxError("invalid decimal literal ${x.message}", toPosition())
}
datatype = when(integer) {
in 0..255 -> BaseDataType.UBYTE
in -128..127 -> BaseDataType.BYTE
in 0..65535 -> BaseDataType.UWORD
in -32768..32767 -> BaseDataType.WORD
in -2147483647..2147483647 -> BaseDataType.LONG
else -> BaseDataType.FLOAT
}
}
2 -> {
if(literalText.length>16)
datatype = BaseDataType.LONG
else if(literalText.length>8)
datatype = BaseDataType.UWORD
try {
integer = literalText.toInt(2)
} catch(x: NumberFormatException) {
throw SyntaxError("invalid binary literal ${x.message}", toPosition())
}
}
16 -> {
if(literalText.length>4)
datatype = BaseDataType.LONG
else if(literalText.length>2)
datatype = BaseDataType.UWORD
try {
integer = literalText.toInt(16)
} catch(x: NumberFormatException) {
throw SyntaxError("invalid hexadecimal literal ${x.message}", toPosition())
}
}
else -> throw FatalAstException("invalid radix")
}
return NumericLiteralNode(integer.toDouble(), datatype)
}
val terminal: TerminalNode = children[0] as TerminalNode
val integerPart = this.intpart.text
return when (terminal.symbol.type) {
DEC_INTEGER -> makeLiteral(integerPart, 10)
HEX_INTEGER -> makeLiteral(integerPart.substring(1), 16)
BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2)
else -> throw FatalAstException(terminal.text)
}
}
private fun ExpressionContext.toAst(insideParentheses: Boolean=false) : Expression {
val litval = literalvalue()
if(litval!=null) {
val booleanlit = litval.booleanliteral()?.toAst()
return if(booleanlit!=null) {
NumericLiteral.fromBoolean(booleanlit, litval.toPosition())
}
else {
val intLit = litval.integerliteral()?.toAst()
when {
intLit!=null -> when(intLit.datatype) {
BaseDataType.UBYTE -> NumericLiteral(BaseDataType.UBYTE, intLit.number, litval.toPosition())
BaseDataType.BYTE -> NumericLiteral(BaseDataType.BYTE, intLit.number, litval.toPosition())
BaseDataType.UWORD -> NumericLiteral(BaseDataType.UWORD, intLit.number, litval.toPosition())
BaseDataType.WORD -> NumericLiteral(BaseDataType.WORD, intLit.number, litval.toPosition())
BaseDataType.LONG -> NumericLiteral(BaseDataType.LONG, intLit.number, litval.toPosition())
BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, intLit.number, litval.toPosition())
else -> throw FatalAstException("invalid datatype for numeric literal")
}
litval.floatliteral()!=null -> NumericLiteral(BaseDataType.FLOAT, litval.floatliteral().toAst(), litval.toPosition())
litval.stringliteral()!=null -> litval.stringliteral().toAst()
litval.charliteral()!=null -> litval.charliteral().toAst()
litval.arrayliteral()!=null -> {
val array = litval.arrayliteral().toAst()
// the actual type of the arraysize can not yet be determined here
// the ConstantFold takes care of that and converts the type if needed.
ArrayLiteral(InferredTypes.InferredType.unknown(), array, position = litval.toPosition())
}
else -> throw FatalAstException("invalid parsed literal")
}
}
}
if(arrayindexed()!=null) {
val ax = arrayindexed()
val identifier = ax.scoped_identifier().toAst()
val index = ax.arrayindex().toAst()
return ArrayIndexedExpression(identifier, index, ax.toPosition())
}
if(scoped_identifier()!=null)
return scoped_identifier().toAst()
if(bop!=null) {
val operator = bop.text.trim().replace("\\s+".toRegex(), " ")
return BinaryExpression(
left.toAst(),
operator,
right.toAst(),
toPosition(),
insideParentheses = insideParentheses
)
}
if(prefix!=null)
return PrefixExpression(prefix.text, expression(0).toAst(), toPosition())
val funcall = functioncall()?.toAst()
if(funcall!=null) return funcall
if (rangefrom!=null && rangeto!=null) {
val defaultstep = if(rto.text == "to") 1 else -1
val step = rangestep?.toAst() ?: NumericLiteral.optimalInteger(defaultstep, toPosition())
return RangeExpression(rangefrom.toAst(), rangeto.toAst(), step, toPosition())
}
if(childCount==3 && children[0].text=="(" && children[2].text==")")
return expression(0).toAst(insideParentheses=true) // expression within ( )
if(typecast()!=null) {
// typecast is always to a base datatype
val baseDt = typecast().datatype().toAst()
return TypecastExpression(expression(0).toAst(), baseDt, false, toPosition())
}
if(directmemory()!=null)
return DirectMemoryRead(directmemory().expression().toAst(), toPosition())
if(addressof()!=null) {
val addressOf = addressof()
val identifier = addressOf.scoped_identifier()
val msb = addressOf.ADDRESS_OF_MSB()!=null
// note: &< (ADDRESS_OF_LSB) is equivalent to a regular &.
return if (identifier != null)
AddressOf(addressof().scoped_identifier().toAst(), null, msb, toPosition())
else {
val array = addressOf.arrayindexed()
AddressOf(array.scoped_identifier().toAst(), array.arrayindex().toAst(), msb, toPosition())
}
}
if(if_expression()!=null) {
val ifex = if_expression()
val (condition, truevalue, falsevalue) = ifex.expression()
return IfExpression(condition.toAst(), truevalue.toAst(), falsevalue.toAst(), toPosition())
}
if(sizeof_expression!=null) {
val datatype = sizeof_argument().datatype()?.toAst()
val expression = sizeof_argument().expression()?.toAst()
val sizeof = IdentifierReference(listOf("sizeof"), toPosition())
val arg = if(expression!=null) expression else {
require(datatype!=null)
IdentifierReference(listOf(datatype.name.lowercase()), toPosition())
}
return FunctionCallExpression(sizeof, mutableListOf(arg), toPosition())
}
throw FatalAstException(text)
}
private fun CharliteralContext.toAst(): CharLiteral {
val text = this.SINGLECHAR().text
val enc = this.encoding?.text
val encoding =
if(enc!=null)
Encoding.entries.singleOrNull { it.prefix == enc }
?: throw SyntaxError("invalid encoding", toPosition())
else
Encoding.DEFAULT
val raw = text.substring(1, text.length - 1)
try {
return CharLiteral.fromEscaped(raw, encoding, toPosition())
} catch(ex: IllegalArgumentException) {
throw SyntaxError(ex.message!!, toPosition())
}
}
private fun StringliteralContext.toAst(): StringLiteral {
val text=this.STRING().text
val enc = encoding?.text
val encoding =
if(enc!=null)
Encoding.entries.singleOrNull { it.prefix == enc }
?: throw SyntaxError("invalid encoding", toPosition())
else
Encoding.DEFAULT
val raw = text.substring(1, text.length-1)
try {
return StringLiteral.fromEscaped(raw, encoding, toPosition())
} catch(ex: IllegalArgumentException) {
throw SyntaxError(ex.message!!, toPosition())
}
}
private fun Expression_listContext.toAst() = expression().map{ it.toAst() }
private fun Scoped_identifierContext.toAst() : IdentifierReference {
return IdentifierReference(identifier().map { it.text }, toPosition())
}
private fun FloatliteralContext.toAst() = text.replace("_","").toDouble()
private fun BooleanliteralContext.toAst() = when(text) {
"true" -> true
"false" -> false
else -> throw FatalAstException(text)
}
private fun ArrayliteralContext.toAst() : Array<Expression> =
expression().map { it.toAst() }.toTypedArray()
private fun If_stmtContext.toAst(): IfElse {
val condition = expression().toAst()
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val elseStatements = else_part()?.toAst() ?: mutableListOf()
val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition()
?: statement().toPosition())
val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
return IfElse(condition, trueScope, elseScope, toPosition())
}
private fun Else_partContext.toAst(): MutableList<Statement> {
return statement_block()?.toAst() ?: mutableListOf(statement().toAst())
}
private fun Branch_stmtContext.toAst(): ConditionalBranch {
val branchcondition = branchcondition().toAst()
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val elseStatements = else_part()?.toAst() ?: mutableListOf()
val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition()
?: statement().toPosition())
val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
return ConditionalBranch(branchcondition, trueScope, elseScope, toPosition())
}
private fun BranchconditionContext.toAst() = BranchCondition.valueOf(
text.substringAfter('_').uppercase()
)
private fun ForloopContext.toAst(): ForLoop {
val loopvar = scoped_identifier().toAst()
val iterable = expression()!!.toAst()
val scope =
if(statement()!=null)
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
else
AnonymousScope(statement_block().toAst(), statement_block().toPosition())
return ForLoop(loopvar, iterable, scope, toPosition())
}
private fun BreakstmtContext.toAst() = Break(toPosition())
private fun ContinuestmtContext.toAst() = Continue(toPosition())
private fun DeferContext.toAst(): Defer {
val block = statement_block()?.toAst()
if(block!=null) {
val scope = AnonymousScope(block, statement_block()?.toPosition() ?: toPosition())
return Defer(scope, toPosition())
}
val singleStmt = statement()!!.toAst()
val scope = AnonymousScope(mutableListOf(singleStmt), statement().toPosition())
return Defer(scope, toPosition())
}
private fun WhileloopContext.toAst(): WhileLoop {
val condition = expression().toAst()
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition())
return WhileLoop(condition, scope, toPosition())
}
private fun RepeatloopContext.toAst(): RepeatLoop {
val iterations = expression()?.toAst()
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition())
return RepeatLoop(iterations, scope, toPosition())
}
private fun UnrollloopContext.toAst(): UnrollLoop {
val iterations = expression().toAst()
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition())
return UnrollLoop(iterations, scope, toPosition())
}
private fun UntilloopContext.toAst(): UntilLoop {
val untilCondition = expression().toAst()
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition())
return UntilLoop(scope, untilCondition, toPosition())
}
private fun WhenstmtContext.toAst(): When {
val condition = expression().toAst()
val choices = this.when_choice()?.map { it.toAst() }?.toMutableList() ?: mutableListOf()
return When(condition, choices, toPosition())
}
private fun When_choiceContext.toAst(): WhenChoice {
val values = expression_list()?.toAst()
val stmt = statement()?.toAst()
val stmtBlock = statement_block()?.toAst()?.toMutableList() ?: mutableListOf()
if(stmt!=null)
stmtBlock.add(stmt)
val scope = AnonymousScope(stmtBlock, toPosition())
return WhenChoice(values?.toMutableList(), scope, toPosition())
}
private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl {
val tags = TAG().map { it.text }
val validTags = arrayOf("@zp", "@requirezp", "@nozp", "@split", "@nosplit", "@shared", "@alignword", "@alignpage", "@align64", "@dirty")
for(tag in tags) {
if(tag !in validTags)
throw SyntaxError("invalid variable tag '$tag'", toPosition())
}
val zp = getZpOption(tags)
val split = getSplitOption(tags)
val identifiers = identifier()
val identifiername = getname(identifiers)
val name = if(identifiers.size==1) identifiername else "<multiple>"
val isArray = ARRAYSIG() != null || arrayindex() != null
val alignword = "@alignword" in tags
val align64 = "@align64" in tags
val alignpage = "@alignpage" in tags
if(alignpage && alignword)
throw SyntaxError("choose a single alignment option", toPosition())
val baseDt = datatype()?.toAst() ?: BaseDataType.UNDEFINED
val dt = if(isArray) DataType.arrayFor(baseDt, split!=SplitWish.NOSPLIT) else DataType.forDt(baseDt)
return VarDecl(
type, VarDeclOrigin.USERCODE,
dt,
zp,
split,
arrayindex()?.toAst(),
name,
if(identifiers.size==1) emptyList() else identifiers.map { getname(it) },
value,
"@shared" in tags,
if(alignword) 2u else if(align64) 64u else if(alignpage) 256u else 0u,
"@dirty" in tags,
toPosition()
)
}
private fun OngotoContext.toAst(): Statement {
val elseStatements = this.else_part()?.toAst()
val elseScope = if(elseStatements==null) null else AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
val isCall = this.kind.text == "call"
val index = this.expression().toAst()
val labels = directivenamelist().scoped_identifier().map { it.toAst() }
return OnGoto(isCall, index, labels, elseScope, toPosition())
}

View File

@ -0,0 +1,750 @@
package prog8.ast.antlr
import org.antlr.v4.runtime.ParserRuleContext
import org.antlr.v4.runtime.Token
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor
import org.antlr.v4.runtime.tree.TerminalNode
import prog8.ast.FatalAstException
import prog8.ast.Module
import prog8.ast.Node
import prog8.ast.SyntaxError
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.core.*
import prog8.code.source.SourceCode
import prog8.parser.Prog8ANTLRParser.*
import prog8.parser.Prog8ANTLRVisitor
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node>(), Prog8ANTLRVisitor<Node> {
override fun visitModule(ctx: ModuleContext): Module {
val statements = ctx.module_element().map { it.accept(this) as Statement }
return Module(statements.toMutableList(), ctx.toPosition(), source)
}
override fun visitBlock(ctx: BlockContext): Block {
val name = getname(ctx.identifier())
val address = (ctx.integerliteral()?.accept(this) as NumericLiteral?)?.number?.toUInt()
val statements = ctx.block_statement().map { it.accept(this) as Statement }
return Block(name, address, statements.toMutableList(), source.isFromLibrary, ctx.toPosition())
}
override fun visitExpression(ctx: ExpressionContext): Expression {
if(ctx.sizeof_expression!=null) {
val sdt = ctx.sizeof_argument().datatype()
val datatype = if(sdt!=null) baseDatatypeFor(sdt) else null
val expression = ctx.sizeof_argument().expression()?.accept(this) as Expression?
val sizeof = IdentifierReference(listOf("sizeof"), ctx.toPosition())
val arg = if (expression != null) expression else {
require(datatype != null)
IdentifierReference(listOf(datatype.name.lowercase()), ctx.toPosition())
}
return FunctionCallExpression(sizeof, mutableListOf(arg), ctx.toPosition())
}
if(ctx.bop!=null) {
val operator = ctx.bop.text.trim().replace("\\s+".toRegex(), " ")
return BinaryExpression(
ctx.left.accept(this) as Expression,
operator,
ctx.right.accept(this) as Expression,
ctx.toPosition()
)
}
if(ctx.prefix!=null) {
return PrefixExpression(ctx.prefix.text, ctx.expression(0).accept(this) as Expression, ctx.toPosition())
}
if(ctx.rangefrom!=null && ctx.rangeto!=null) {
val defaultstep = if(ctx.rto.text == "to") 1 else -1
return RangeExpression(
ctx.rangefrom.accept(this) as Expression,
ctx.rangeto.accept(this) as Expression,
ctx.rangestep?.accept(this) as Expression? ?: NumericLiteral.optimalInteger(defaultstep, ctx.toPosition()),
ctx.toPosition())
}
if(ctx.typecast()!=null) {
// typecast is always to a base datatype
val baseDt = baseDatatypeFor(ctx.typecast().datatype())
return TypecastExpression(ctx.expression(0).accept(this) as Expression, baseDt, false, ctx.toPosition())
}
if(ctx.childCount==3 && ctx.children[0].text=="(" && ctx.children[2].text==")")
return ctx.expression(0).accept(this) as Expression // expression within ( )
return visitChildren(ctx) as Expression
}
override fun visitSubroutinedeclaration(ctx: SubroutinedeclarationContext): Subroutine {
if(ctx.subroutine()!=null)
return ctx.subroutine().accept(this) as Subroutine
if(ctx.asmsubroutine()!=null)
return ctx.asmsubroutine().accept(this) as Subroutine
if(ctx.extsubroutine()!=null)
return ctx.extsubroutine().accept(this) as Subroutine
throw FatalAstException("weird subroutine")
}
override fun visitAlias(ctx: AliasContext): Alias {
val identifier = getname(ctx.identifier())
val target = ctx.scoped_identifier().accept(this) as IdentifierReference
return Alias(identifier, target, ctx.toPosition())
}
override fun visitDefer(ctx: DeferContext): Defer {
val statements = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
return Defer(statements, ctx.toPosition())
}
override fun visitLabeldef(ctx: LabeldefContext): Label {
return Label(getname(ctx.identifier()), ctx.toPosition())
}
override fun visitUnconditionaljump(ctx: UnconditionaljumpContext): Jump {
return Jump(ctx.expression().accept(this) as Expression, ctx.toPosition())
}
override fun visitDirective(ctx: DirectiveContext): Directive {
if(ctx.directivenamelist() != null) {
val namelist = ctx.directivenamelist().scoped_identifier().map { it.accept(this) as IdentifierReference }
val identifiers = namelist.map { DirectiveArg(it.nameInSource.joinToString("."), null, ctx.toPosition()) }
return Directive(ctx.directivename.text, identifiers, ctx.toPosition())
}
else
return Directive(ctx.directivename.text, ctx.directivearg().map { it.accept(this) as DirectiveArg }, ctx.toPosition())
}
override fun visitDirectivearg(ctx: DirectiveargContext): DirectiveArg {
val integer = (ctx.integerliteral()?.accept(this) as NumericLiteral?)?.number?.toUInt()
val str = ctx.stringliteral()
if(str!=null) {
if (str.encoding?.text != null)
throw SyntaxError("don't use a string encoding for directive arguments", ctx.toPosition())
return DirectiveArg(str.text.substring(1, str.text.length-1), integer, ctx.toPosition())
}
val identifier = ctx.identifier()?.accept(this) as IdentifierReference?
return DirectiveArg(identifier?.nameInSource?.single(), integer, ctx.toPosition())
}
override fun visitVardecl(ctx: VardeclContext): VarDecl {
val tags = ctx.TAG().map { it.text }
val validTags = arrayOf("@zp", "@requirezp", "@nozp", "@split", "@nosplit", "@shared", "@alignword", "@alignpage", "@align64", "@dirty")
for(tag in tags) {
if(tag !in validTags)
throw SyntaxError("invalid variable tag '$tag'", ctx.toPosition())
}
val zp = getZpOption(tags)
val split = getSplitOption(tags)
val alignword = "@alignword" in tags
val align64 = "@align64" in tags
val alignpage = "@alignpage" in tags
if(alignpage && alignword)
throw SyntaxError("choose a single alignment option", ctx.toPosition())
val identifiers = ctx.identifier().map { getname(it) }
val identifiername = identifiers[0]
val name = if(identifiers.size==1) identifiername else "<multiple>"
val arrayIndex = ctx.arrayindex()?.accept(this) as ArrayIndex?
val isArray = ctx.ARRAYSIG() != null || arrayIndex != null
val baseDt = baseDatatypeFor(ctx.datatype())
val dt = if(isArray) DataType.arrayFor(baseDt, split!=SplitWish.NOSPLIT) else DataType.forDt(baseDt)
return VarDecl(
VarDeclType.VAR, // can be changed to MEMORY or CONST as required
VarDeclOrigin.USERCODE,
dt,
zp,
split,
arrayIndex,
name,
if(identifiers.size==1) emptyList() else identifiers,
null,
"@shared" in tags,
if(alignword) 2u else if(align64) 64u else if(alignpage) 256u else 0u,
"@dirty" in tags,
ctx.toPosition()
)
}
override fun visitVarinitializer(ctx: VarinitializerContext): VarDecl {
val vardecl = ctx.vardecl().accept(this) as VarDecl
vardecl.value = ctx.expression().accept(this) as Expression
return vardecl
}
override fun visitConstdecl(ctx: ConstdeclContext): VarDecl {
val vardecl = ctx.varinitializer().accept(this) as VarDecl
vardecl.type = VarDeclType.CONST
return vardecl
}
override fun visitMemoryvardecl(ctx: MemoryvardeclContext): VarDecl {
val vardecl = ctx.varinitializer().accept(this) as VarDecl
vardecl.type = VarDeclType.MEMORY
return vardecl
}
override fun visitArrayindex(ctx: ArrayindexContext): ArrayIndex {
return ArrayIndex(ctx.expression().accept(this) as Expression, ctx.toPosition())
}
override fun visitAssignment(ctx: AssignmentContext): Statement {
val multiAssign = ctx.multi_assign_target()
if(multiAssign!=null) {
return Assignment(multiAssign.accept(this) as AssignTarget, ctx.expression().accept(this) as Expression, AssignmentOrigin.USERCODE, ctx.toPosition())
}
val nestedAssign = ctx.assignment()
return if(nestedAssign==null)
Assignment(ctx.assign_target().accept(this) as AssignTarget, ctx.expression().accept(this) as Expression, AssignmentOrigin.USERCODE, ctx.toPosition())
else
ChainedAssignment(ctx.assign_target().accept(this) as AssignTarget, nestedAssign.accept(this) as Statement, ctx.toPosition())
}
override fun visitAugassignment(ctx: AugassignmentContext): Assignment {
// replace A += X with A = A + X
val target = ctx.assign_target().accept(this) as AssignTarget
val oper = ctx.operator.text.substringBefore('=')
val expression = BinaryExpression(target.toExpression(), oper, ctx.expression().accept(this) as Expression, ctx.toPosition())
return Assignment(target, expression, AssignmentOrigin.USERCODE, ctx.toPosition())
}
override fun visitIdentifierTarget(ctx: IdentifierTargetContext): AssignTarget {
val identifier = ctx.scoped_identifier().accept(this) as IdentifierReference
return AssignTarget(identifier, null, null, null, false, ctx.toPosition())
}
override fun visitArrayindexedTarget(ctx: ArrayindexedTargetContext): AssignTarget {
val ax = ctx.arrayindexed()
val arrayvar = ax.scoped_identifier().accept(this) as IdentifierReference
val index = ax.arrayindex().accept(this) as ArrayIndex
val arrayindexed = ArrayIndexedExpression(arrayvar, index, ax.toPosition())
return AssignTarget(null, arrayindexed, null, null, false, ctx.toPosition())
}
override fun visitMemoryTarget(ctx: MemoryTargetContext): AssignTarget {
return AssignTarget(null, null,
DirectMemoryWrite(ctx.directmemory().expression().accept(this) as Expression, ctx.toPosition()),
null, false, ctx.toPosition())
}
override fun visitVoidTarget(ctx: VoidTargetContext): AssignTarget {
return AssignTarget(null, null, null, null, true, ctx.toPosition())
}
override fun visitMulti_assign_target(ctx: Multi_assign_targetContext): AssignTarget {
val targets = ctx.assign_target().map { it.accept(this) as AssignTarget }
return AssignTarget(null, null, null, targets, false, ctx.toPosition())
}
override fun visitPostincrdecr(ctx: PostincrdecrContext): Assignment {
val tgt = ctx.assign_target().accept(this) as AssignTarget
val operator = ctx.operator.text
val pos = ctx.toPosition()
val addSubOne = BinaryExpression(tgt.toExpression(), if(operator=="++") "+" else "-", NumericLiteral.optimalInteger(1, pos), pos)
return Assignment(tgt, addSubOne, AssignmentOrigin.USERCODE, pos)
}
override fun visitArrayindexed(ctx: ArrayindexedContext): ArrayIndexedExpression {
val identifier = ctx.scoped_identifier().accept(this) as IdentifierReference
val index = ctx.arrayindex().accept(this) as ArrayIndex
return ArrayIndexedExpression(identifier, index, ctx.toPosition())
}
override fun visitDirectmemory(ctx: DirectmemoryContext): DirectMemoryRead {
return DirectMemoryRead(ctx.expression().accept(this) as Expression, ctx.toPosition())
}
override fun visitAddressof(ctx: AddressofContext): AddressOf {
val identifier = ctx.scoped_identifier()?.accept(this) as IdentifierReference?
val msb = ctx.ADDRESS_OF_MSB()!=null
// note: &< (ADDRESS_OF_LSB) is equivalent to a regular &.
return if (identifier != null)
AddressOf(identifier, null, msb, ctx.toPosition())
else {
val array = ctx.arrayindexed()
AddressOf(array.scoped_identifier().accept(this) as IdentifierReference,
array.arrayindex().accept(this) as ArrayIndex,
msb, ctx.toPosition())
}
}
override fun visitFunctioncall(ctx: FunctioncallContext): FunctionCallExpression {
val name = ctx.scoped_identifier().accept(this) as IdentifierReference
val args = ctx.expression_list()?.expression()?.map { it.accept(this) as Expression } ?: emptyList()
return FunctionCallExpression(name, args.toMutableList(), ctx.toPosition())
}
override fun visitFunctioncall_stmt(ctx: Functioncall_stmtContext): FunctionCallStatement {
val void = ctx.VOID() != null
val name = ctx.scoped_identifier().accept(this) as IdentifierReference
val args = ctx.expression_list()?.expression()?.map { it.accept(this) as Expression } ?: emptyList()
return FunctionCallStatement(name, args.toMutableList(), void, ctx.toPosition())
}
override fun visitReturnstmt(ctx: ReturnstmtContext): Return {
val cvalues = ctx.returnvalues()
val values = if(cvalues==null || cvalues.expression().isEmpty()) arrayOf() else cvalues.expression().map { it.accept(this) as Expression }.toTypedArray()
return Return(values, ctx.toPosition())
}
override fun visitBreakstmt(ctx: BreakstmtContext): Break {
return Break(ctx.toPosition())
}
override fun visitContinuestmt(ctx: ContinuestmtContext): Continue {
return Continue(ctx.toPosition())
}
override fun visitIdentifier(ctx: IdentifierContext): IdentifierReference {
return IdentifierReference(listOf(getname(ctx)), ctx.toPosition())
}
override fun visitScoped_identifier(ctx: Scoped_identifierContext): IdentifierReference {
val children = ctx.identifier().map { it.text }
return IdentifierReference(children, ctx.toPosition())
}
override fun visitIntegerliteral(ctx: IntegerliteralContext): NumericLiteral {
fun makeLiteral(literalTextWithGrouping: String, radix: Int): Pair<Double, BaseDataType> {
val literalText = literalTextWithGrouping.replace("_", "")
val integer: Int
var datatype = BaseDataType.UBYTE
when (radix) {
10 -> {
integer = try {
literalText.toInt()
} catch(x: NumberFormatException) {
throw SyntaxError("invalid decimal literal ${x.message}", ctx.toPosition())
}
datatype = when(integer) {
in 0..255 -> BaseDataType.UBYTE
in -128..127 -> BaseDataType.BYTE
in 0..65535 -> BaseDataType.UWORD
in -32768..32767 -> BaseDataType.WORD
in -2147483647..2147483647 -> BaseDataType.LONG
else -> BaseDataType.FLOAT
}
}
2 -> {
if(literalText.length>16)
datatype = BaseDataType.LONG
else if(literalText.length>8)
datatype = BaseDataType.UWORD
try {
integer = literalText.toInt(2)
} catch(x: NumberFormatException) {
throw SyntaxError("invalid binary literal ${x.message}", ctx.toPosition())
}
}
16 -> {
if(literalText.length>4)
datatype = BaseDataType.LONG
else if(literalText.length>2)
datatype = BaseDataType.UWORD
try {
integer = literalText.toInt(16)
} catch(x: NumberFormatException) {
throw SyntaxError("invalid hexadecimal literal ${x.message}", ctx.toPosition())
}
}
else -> throw FatalAstException("invalid radix")
}
return integer.toDouble() to datatype
}
val terminal: TerminalNode = ctx.children[0] as TerminalNode
val integerPart = ctx.intpart.text
val integer = when (terminal.symbol.type) {
DEC_INTEGER -> makeLiteral(integerPart, 10)
HEX_INTEGER -> makeLiteral(integerPart.substring(1), 16)
BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2)
else -> throw FatalAstException(terminal.text)
}
return NumericLiteral(integer.second, integer.first, ctx.toPosition())
}
override fun visitBooleanliteral(ctx: BooleanliteralContext): NumericLiteral {
val boolean = when(ctx.text) {
"true" -> true
"false" -> false
else -> throw FatalAstException(ctx.text)
}
return NumericLiteral.fromBoolean(boolean, ctx.toPosition())
}
override fun visitArrayliteral(ctx: ArrayliteralContext): ArrayLiteral {
val array = ctx.expression().map { it.accept(this) as Expression }.toTypedArray()
// the actual type of the arraysize can not yet be determined here
// the ConstantFold takes care of that and converts the type if needed.
return ArrayLiteral(InferredTypes.InferredType.unknown(), array, position = ctx.toPosition())
}
override fun visitStringliteral(ctx: StringliteralContext): StringLiteral {
val text = ctx.STRING().text
val enc = ctx.encoding?.text
val encoding =
if(enc!=null)
Encoding.entries.singleOrNull { it.prefix == enc }
?: throw SyntaxError("invalid encoding", ctx.toPosition())
else
Encoding.DEFAULT
val raw = text.substring(1, text.length-1)
try {
return StringLiteral.fromEscaped(raw, encoding, ctx.toPosition())
} catch(ex: IllegalArgumentException) {
throw SyntaxError(ex.message!!, ctx.toPosition())
}
}
override fun visitCharliteral(ctx: CharliteralContext): CharLiteral {
val text = ctx.SINGLECHAR().text
val enc = ctx.encoding?.text
val encoding =
if(enc!=null)
Encoding.entries.singleOrNull { it.prefix == enc }
?: throw SyntaxError("invalid encoding", ctx.toPosition())
else
Encoding.DEFAULT
val raw = text.substring(1, text.length - 1)
try {
return CharLiteral.fromEscaped(raw, encoding, ctx.toPosition())
} catch(ex: IllegalArgumentException) {
throw SyntaxError(ex.message!!, ctx.toPosition())
}
}
override fun visitFloatliteral(ctx: FloatliteralContext): NumericLiteral {
val floatvalue = ctx.text.replace("_","").toDouble()
return NumericLiteral(BaseDataType.FLOAT, floatvalue, ctx.toPosition())
}
override fun visitInlineasm(ctx: InlineasmContext): InlineAssembly {
val text = ctx.INLINEASMBLOCK().text
return InlineAssembly(text.substring(2, text.length-2), false, ctx.toPosition())
}
override fun visitInlineir(ctx: InlineirContext): InlineAssembly {
val text = ctx.INLINEASMBLOCK().text
return InlineAssembly(text.substring(2, text.length-2), true, ctx.toPosition())
}
override fun visitSubroutine(ctx: SubroutineContext): Subroutine {
val name = getname(ctx.identifier())
val parameters = ctx.sub_params()?.sub_param()?.map { it.accept(this) as SubroutineParameter } ?: emptyList()
val returntypes = ctx.sub_return_part()?.datatype()?. map { dataTypeFor(it) } ?: emptyList()
val statements = ctx.statement_block().accept(this) as AnonymousScope
return Subroutine(
name,
parameters.toMutableList(),
returntypes.toMutableList(),
emptyList(),
emptyList(),
emptySet(),
asmAddress = null,
isAsmSubroutine = false,
inline = false,
statements = statements.statements,
position = ctx.toPosition()
)
}
override fun visitStatement_block(ctx: Statement_blockContext): AnonymousScope {
val statements = ctx.statement().map { it.accept(this) as Statement }
return AnonymousScope(statements.toMutableList(), ctx.toPosition())
}
override fun visitSub_param(pctx: Sub_paramContext): SubroutineParameter {
val decl = pctx.vardecl()
val tags = decl.TAG().map { t -> t.text }
val validTags = arrayOf("@zp", "@requirezp", "@nozp", "@split", "@nosplit", "@shared")
for(tag in tags) {
if(tag !in validTags)
throw SyntaxError("invalid parameter tag '$tag'", pctx.toPosition())
}
val zp = getZpOption(tags)
val decldt = decl.datatype()
var datatype = if(decldt!=null) dataTypeFor(decldt) else DataType.UNDEFINED
if(decl.ARRAYSIG()!=null || decl.arrayindex()!=null)
datatype = datatype.elementToArray()
val identifiers = decl.identifier()
if(identifiers.size>1)
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
val identifiername = getname(identifiers[0])
val (registerorpair, statusregister) = parseParamRegister(pctx.register, pctx.toPosition())
if(statusregister!=null) {
throw SyntaxError("can't use status register as param for normal subroutines", Position(pctx.toPosition().file, pctx.register.line, pctx.register.charPositionInLine, pctx.register.charPositionInLine+1))
}
return SubroutineParameter(identifiername, datatype, zp, registerorpair, pctx.toPosition())
}
override fun visitAsmsubroutine(ctx: AsmsubroutineContext): Subroutine {
val inline = ctx.INLINE()!=null
val ad = asmSubDecl(ctx.asmsub_decl())
val statements = ctx.statement_block().accept(this) as AnonymousScope
return Subroutine(ad.name,
ad.parameters.toMutableList(),
ad.returntypes.toMutableList(),
ad.asmParameterRegisters,
ad.asmReturnvaluesRegisters,
ad.asmClobbers, null, true, inline,
statements = statements.statements, position = ctx.toPosition()
)
}
override fun visitExtsubroutine(ctx: ExtsubroutineContext): Subroutine {
val subdecl = asmSubDecl(ctx.asmsub_decl())
val constbank = (ctx.constbank?.accept(this) as NumericLiteral?)?.number?.toUInt()?.toUByte()
val varbank = ctx.varbank?.accept(this) as IdentifierReference?
val addr = ctx.address.accept(this) as Expression
val address = Subroutine.Address(constbank, varbank, addr)
return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
subdecl.asmClobbers, address, true, inline = false, statements = mutableListOf(), position = ctx.toPosition()
)
}
override fun visitIf_stmt(ctx: If_stmtContext): IfElse {
val condition = ctx.expression().accept(this) as Expression
val truepart = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
val elsepart = ctx.else_part()?.accept(this) as AnonymousScope? ?: AnonymousScope.empty()
return IfElse(condition, truepart, elsepart, ctx.toPosition())
}
override fun visitElse_part(ctx: Else_partContext): AnonymousScope {
return stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
}
override fun visitIf_expression(ctx: If_expressionContext): IfExpression {
val (condition, truevalue, falsevalue) = ctx.expression().map { it.accept(this) as Expression }
return IfExpression(condition, truevalue, falsevalue, ctx.toPosition())
}
override fun visitBranch_stmt(ctx: Branch_stmtContext): ConditionalBranch {
val branchcondition = branchCondition(ctx.branchcondition())
val truepart = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
val elsepart = ctx.else_part()?.accept(this) as AnonymousScope? ?: AnonymousScope.empty()
return ConditionalBranch(branchcondition, truepart, elsepart, ctx.toPosition())
}
override fun visitForloop(ctx: ForloopContext): ForLoop {
val loopvar = ctx.scoped_identifier().accept(this) as IdentifierReference
val iterable = ctx.expression().accept(this) as Expression
val scope = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
return ForLoop(loopvar, iterable, scope, ctx.toPosition())
}
override fun visitWhileloop(ctx: WhileloopContext): WhileLoop {
val condition = ctx.expression().accept(this) as Expression
val statements = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
return WhileLoop(condition, statements, ctx.toPosition())
}
override fun visitUntilloop(ctx: UntilloopContext): UntilLoop {
val condition = ctx.expression().accept(this) as Expression
val statements = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
return UntilLoop(statements, condition, ctx.toPosition())
}
override fun visitRepeatloop(ctx: RepeatloopContext): RepeatLoop {
val iterations = ctx.expression()?.accept(this) as Expression?
val statements = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
return RepeatLoop(iterations, statements, ctx.toPosition())
}
override fun visitUnrollloop(ctx: UnrollloopContext): UnrollLoop {
val iterations = ctx.expression().accept(this) as Expression
val statements = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
return UnrollLoop(iterations, statements, ctx.toPosition())
}
override fun visitWhenstmt(ctx: WhenstmtContext): When {
val condition = ctx.expression().accept(this) as Expression
val choices = ctx.when_choice()?.map { it.accept(this) as WhenChoice }?.toMutableList() ?: mutableListOf()
return When(condition, choices, ctx.toPosition())
}
override fun visitWhen_choice(ctx: When_choiceContext): WhenChoice {
val values = ctx.expression_list()?.expression()?.map { it.accept(this) as Expression }
val statements = stmtBlockOrSingle(ctx.statement_block(), ctx.statement())
return WhenChoice(values?.toMutableList(), statements, ctx.toPosition())
}
override fun visitOngoto(ctx: OngotoContext): OnGoto {
val elsepart = ctx.else_part()?.accept(this) as AnonymousScope? ?: AnonymousScope.empty()
val isCall = ctx.kind.text == "call"
val index = ctx.expression().accept(this) as Expression
val labels = ctx.directivenamelist().scoped_identifier().map { it.accept(this) as IdentifierReference }
return OnGoto(isCall, index, labels, elsepart, ctx.toPosition())
}
override fun visitModule_element(ctx: Module_elementContext): Node = visitChildren(ctx)
override fun visitBlock_statement(ctx: Block_statementContext): Statement = visitChildren(ctx) as Statement
override fun visitStatement(ctx: StatementContext): Statement = visitChildren(ctx) as Statement
override fun visitVariabledeclaration(ctx: VariabledeclarationContext): VarDecl = visitChildren(ctx) as VarDecl
override fun visitLiteralvalue(ctx: LiteralvalueContext): Expression = visitChildren(ctx) as Expression
override fun visitDirectivenamelist(ctx: DirectivenamelistContext) = throw FatalAstException("should not be called")
override fun visitAsmsub_decl(ctx: Asmsub_declContext?) = throw FatalAstException("should not be called")
override fun visitAsmsub_params(ctx: Asmsub_paramsContext) = throw FatalAstException("should not be called")
override fun visitExpression_list(ctx: Expression_listContext) = throw FatalAstException("should not be called")
override fun visitBranchcondition(ctx: BranchconditionContext) = throw FatalAstException("should not be called")
override fun visitDatatype(ctx: DatatypeContext) = throw FatalAstException("should not be called")
override fun visitSizeof_argument(ctx: Sizeof_argumentContext) = throw FatalAstException("should not be called")
override fun visitReturnvalues(ctx: ReturnvaluesContext) = throw FatalAstException("should not be called")
override fun visitTypecast(ctx: TypecastContext) = throw FatalAstException("should not be called")
override fun visitSub_params(ctx: Sub_paramsContext) = throw FatalAstException("should not be called")
override fun visitAsmsub_param(ctx: Asmsub_paramContext) = throw FatalAstException("should not be called")
override fun visitAsmsub_clobbers(ctx: Asmsub_clobbersContext) = throw FatalAstException("should not be called")
override fun visitClobber(ctx: ClobberContext) = throw FatalAstException("should not be called")
override fun visitAsmsub_returns(ctx: Asmsub_returnsContext) = throw FatalAstException("should not be called")
override fun visitAsmsub_return(ctx: Asmsub_returnContext) = throw FatalAstException("should not be called")
override fun visitSub_return_part(ctx: Sub_return_partContext) = throw FatalAstException("should not be called")
private fun getname(identifier: IdentifierContext): String = identifier.children[0].text
private fun ParserRuleContext.toPosition() : Position {
val pathString = start.inputStream.sourceName
val filename = if(SourceCode.isRegularFilesystemPath(pathString)) {
val path = Path(pathString)
if(path.isRegularFile()) {
SourceCode.relative(path).toString()
} else {
path.toString()
}
} else {
pathString
}
// note: beware of TAB characters in the source text, they count as 1 column...
return Position(filename, start.line, start.charPositionInLine+1, start.charPositionInLine + 1 + start.stopIndex - start.startIndex)
}
private fun getZpOption(tags: List<String>): ZeropageWish = when {
"@requirezp" in tags -> ZeropageWish.REQUIRE_ZEROPAGE
"@zp" in tags -> ZeropageWish.PREFER_ZEROPAGE
"@nozp" in tags -> ZeropageWish.NOT_IN_ZEROPAGE
else -> ZeropageWish.DONTCARE
}
private fun getSplitOption(tags: List<String>): SplitWish {
return when {
"@nosplit" in tags -> SplitWish.NOSPLIT
"@split" in tags -> SplitWish.SPLIT
else -> SplitWish.DONTCARE
}
}
private fun asmSubroutineParam(pctx: Asmsub_paramContext): AsmSubroutineParameter {
val vardecl = pctx.vardecl()
val decldt = vardecl.datatype()
var datatype = if(decldt!=null) dataTypeFor(decldt) else DataType.UNDEFINED
if(vardecl.ARRAYSIG()!=null || vardecl.arrayindex()!=null)
datatype = datatype.elementToArray()
val (registerorpair, statusregister) = parseParamRegister(pctx.register, pctx.toPosition())
val identifiers = vardecl.identifier()
if(identifiers.size>1)
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
val identifiername = getname(identifiers[0])
return AsmSubroutineParameter(identifiername, datatype, registerorpair, statusregister, pctx.toPosition())
}
private fun parseParamRegister(registerTok: Token?, pos: Position): Pair<RegisterOrPair?, Statusflag?> {
if(registerTok==null)
return Pair(null, null)
val register = registerTok.text
var registerorpair: RegisterOrPair? = null
var statusregister: Statusflag? = null
if(register!=null) {
when (register) {
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(register)
in Statusflag.names -> statusregister = Statusflag.valueOf(register)
else -> {
throw SyntaxError("invalid register or status flag", Position(pos.file, registerTok.line, registerTok.charPositionInLine, registerTok.charPositionInLine+1))
}
}
}
return Pair(registerorpair, statusregister)
}
private fun asmReturn(rctx: Asmsub_returnContext): AsmSubroutineReturn {
val register = rctx.register.text
var registerorpair: RegisterOrPair? = null
var statusregister: Statusflag? = null
if(register!=null) {
when (register) {
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(register)
in Statusflag.names -> statusregister = Statusflag.valueOf(register)
else -> throw SyntaxError("invalid register or status flag", rctx.toPosition())
}
}
return AsmSubroutineReturn(
dataTypeFor(rctx.datatype()),
registerorpair,
statusregister)
}
private fun cpuRegister(text: String, pos: Position): CpuRegister {
try {
return CpuRegister.valueOf(text)
} catch(_: IllegalArgumentException) {
throw SyntaxError("invalid cpu register", pos)
}
}
private fun dataTypeFor(it: DatatypeContext) = DataType.forDt(baseDatatypeFor(it))
private fun baseDatatypeFor(it: DatatypeContext) = BaseDataType.valueOf(it.text.uppercase())
private fun stmtBlockOrSingle(statementBlock: Statement_blockContext?, statement: StatementContext?): AnonymousScope {
return if(statementBlock!=null)
statementBlock.accept(this) as AnonymousScope
else if(statement!=null)
AnonymousScope(mutableListOf(statement.accept(this) as Statement), statement.toPosition())
else
AnonymousScope.empty()
}
private fun branchCondition(ctx: BranchconditionContext) = BranchCondition.valueOf(ctx.text.substringAfter('_').uppercase())
private fun asmSubDecl(ad: Asmsub_declContext): AsmsubDecl {
val name = getname(ad.identifier())
val params = ad.asmsub_params()?.asmsub_param()?.map { asmSubroutineParam(it) } ?: emptyList()
val returns = ad.asmsub_returns()?.asmsub_return()?.map { asmReturn(it) } ?: emptyList()
val clobbers = ad.asmsub_clobbers()?.clobber()?.NAME()?.map { cpuRegister(it.text, ad.toPosition()) } ?: emptyList()
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.zp, it.registerOrPair, it.position) }
val normalReturntypes = returns.map { it.type }
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }
return AsmsubDecl(name, normalParameters, normalReturntypes, paramRegisters, returnRegisters, clobbers.toSet())
}
private class AsmsubDecl(val name: String,
val parameters: List<SubroutineParameter>,
val returntypes: List<DataType>,
val asmParameterRegisters: List<RegisterOrStatusflag>,
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
val asmClobbers: Set<CpuRegister>)
private class AsmSubroutineParameter(name: String,
type: DataType,
registerOrPair: RegisterOrPair?,
val statusflag: Statusflag?,
position: Position) : SubroutineParameter(name, type, ZeropageWish.DONTCARE, registerOrPair, position)
private class AsmSubroutineReturn(val type: DataType,
val registerOrPair: RegisterOrPair?,
val statusflag: Statusflag?)
}

View File

@ -151,8 +151,7 @@ class BinaryExpression(
var left: Expression, var left: Expression,
var operator: String, var operator: String,
var right: Expression, var right: Expression,
override val position: Position, override val position: Position
private val insideParentheses: Boolean = false
) : Expression() { ) : Expression() {
override lateinit var parent: Node override lateinit var parent: Node
@ -172,7 +171,7 @@ class BinaryExpression(
replacement.parent = this replacement.parent = this
} }
override fun copy() = BinaryExpression(left.copy(), operator, right.copy(), position, insideParentheses) override fun copy() = BinaryExpression(left.copy(), operator, right.copy(), position)
override fun toString() = "[$left $operator $right]" override fun toString() = "[$left $operator $right]"
override val isSimple = false override val isSimple = false

View File

@ -239,7 +239,8 @@ enum class VarDeclType {
MEMORY MEMORY
} }
class VarDecl(val type: VarDeclType, class VarDecl(
var type: VarDeclType,
val origin: VarDeclOrigin, val origin: VarDeclOrigin,
val datatype: DataType, val datatype: DataType,
val zeropage: ZeropageWish, val zeropage: ZeropageWish,
@ -830,6 +831,10 @@ class AnonymousScope(override val statements: MutableList<Statement>,
statements.forEach { it.linkParents(this) } statements.forEach { it.linkParents(this) }
} }
companion object {
fun empty(pos: Position?=null): AnonymousScope = AnonymousScope(mutableListOf(), pos ?: Position.DUMMY)
}
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Statement) require(replacement is Statement)
val idx = statements.indexOfFirst { it===node } val idx = statements.indexOfFirst { it===node }

View File

@ -2,9 +2,7 @@ package prog8.parser
import org.antlr.v4.runtime.* import org.antlr.v4.runtime.*
import prog8.ast.Module import prog8.ast.Module
import prog8.ast.antlr.toAst import prog8.ast.antlr.Antlr2KotlinVisitor
import prog8.ast.statements.Block
import prog8.ast.statements.Directive
import prog8.code.core.Position import prog8.code.core.Position
import prog8.code.source.SourceCode import prog8.code.source.SourceCode
@ -25,41 +23,12 @@ object Prog8Parser {
parser.addErrorListener(antlrErrorListener) parser.addErrorListener(antlrErrorListener)
val parseTree = parser.module() val parseTree = parser.module()
val module = ParsedModule(src)
parseTree.module_element().forEach { val visitor = Antlr2KotlinVisitor(src)
val block = it.block()?.toAst(module.isLibrary) val visitorResult = visitor.visit(parseTree)
val directive = it.directive()?.toAst() return visitorResult as Module
if(directive != null) module.add(directive)
if(block != null) module.add(block)
} }
return module
}
private class ParsedModule(source: SourceCode) :
Module(mutableListOf(), Position(source.origin, 1, 0, 0), source)
{
/**
* Adds a [Directive] to [statements] and
* sets this Module as its [parent].
* Note: you can only add [Directive]s or [Block]s to a Module.
*/
fun add(child: Directive) {
child.linkParents(this)
statements.add(child)
}
/**
* Adds a [Block] to [statements] and
* sets this Module as its [parent].
* Note: you can only add [Directive]s or [Block]s to a Module.
*/
fun add(child: Block) {
child.linkParents(this)
statements.add(child)
}
}
private object Prog8ErrorStrategy: BailErrorStrategy() { private object Prog8ErrorStrategy: BailErrorStrategy() {
private fun fillIn(e: RecognitionException?, ctx: ParserRuleContext?) { private fun fillIn(e: RecognitionException?, ctx: ParserRuleContext?) {

View File

@ -1,6 +1,5 @@
Prog8 compiler v11.4-SNAPSHOT by Irmen de Jong (irmen@razorvine.net) Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net)
Prerelease version from git commit a87f2640 in branch master
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-c128.p8 Compiling program import-all-c128.p8
@ -109,7 +108,6 @@ LIBRARY MODULE NAME: coroutines
coroutines { coroutines {
const ubyte MAX_TASKS const ubyte MAX_TASKS
ubyte active_task ubyte active_task
uword[] returnaddresses
uword supervisor uword supervisor
uword[] tasklist uword[] tasklist
uword[] userdatas uword[] userdatas
@ -238,6 +236,7 @@ strings {
copy (uword source @R0, uword target @AY) -> clobbers (A) -> ubyte @Y copy (uword source @R0, uword target @AY) -> clobbers (A) -> ubyte @Y
endswith (str st, str suffix) -> bool endswith (str st, str suffix) -> bool
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
find_eol (uword string @AY) -> ubyte @A, bool @Pc
findstr (str haystack, str needle) -> ubyte findstr (str haystack, str needle) -> ubyte
hash (str string @AY) -> ubyte @A hash (str string @AY) -> ubyte @A
isdigit (ubyte petsciichar @A) -> bool @Pc isdigit (ubyte petsciichar @A) -> bool @Pc
@ -529,6 +528,7 @@ sys {
exit (ubyte returnvalue @A) exit (ubyte returnvalue @A)
exit2 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y) exit2 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y)
exit3 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc) exit3 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc)
get_as_returnaddress (uword address @XY) -> uword @AX
internal_stringcopy (uword source @R0, uword target @AY) -> clobbers (A,Y) internal_stringcopy (uword source @R0, uword target @AY) -> clobbers (A,Y)
irqsafe_clear_irqd () irqsafe_clear_irqd ()
irqsafe_set_irqd () irqsafe_set_irqd ()
@ -561,6 +561,8 @@ cx16 {
&uword r0 &uword r0
&ubyte r0H &ubyte r0H
&ubyte r0L &ubyte r0L
&bool r0bH
&bool r0bL
&word r0s &word r0s
&byte r0sH &byte r0sH
&byte r0sL &byte r0sL
@ -568,89 +570,119 @@ cx16 {
&uword r10 &uword r10
&ubyte r10H &ubyte r10H
&ubyte r10L &ubyte r10L
&bool r10bH
&bool r10bL
&word r10s &word r10s
&byte r10sH &byte r10sH
&byte r10sL &byte r10sL
&uword r11 &uword r11
&ubyte r11H &ubyte r11H
&ubyte r11L &ubyte r11L
&bool r11bH
&bool r11bL
&word r11s &word r11s
&byte r11sH &byte r11sH
&byte r11sL &byte r11sL
&uword r12 &uword r12
&ubyte r12H &ubyte r12H
&ubyte r12L &ubyte r12L
&bool r12bH
&bool r12bL
&word r12s &word r12s
&byte r12sH &byte r12sH
&byte r12sL &byte r12sL
&uword r13 &uword r13
&ubyte r13H &ubyte r13H
&ubyte r13L &ubyte r13L
&bool r13bH
&bool r13bL
&word r13s &word r13s
&byte r13sH &byte r13sH
&byte r13sL &byte r13sL
&uword r14 &uword r14
&ubyte r14H &ubyte r14H
&ubyte r14L &ubyte r14L
&bool r14bH
&bool r14bL
&word r14s &word r14s
&byte r14sH &byte r14sH
&byte r14sL &byte r14sL
&uword r15 &uword r15
&ubyte r15H &ubyte r15H
&ubyte r15L &ubyte r15L
&bool r15bH
&bool r15bL
&word r15s &word r15s
&byte r15sH &byte r15sH
&byte r15sL &byte r15sL
&ubyte r1H &ubyte r1H
&ubyte r1L &ubyte r1L
&bool r1bH
&bool r1bL
&word r1s &word r1s
&byte r1sH &byte r1sH
&byte r1sL &byte r1sL
&uword r2 &uword r2
&ubyte r2H &ubyte r2H
&ubyte r2L &ubyte r2L
&bool r2bH
&bool r2bL
&word r2s &word r2s
&byte r2sH &byte r2sH
&byte r2sL &byte r2sL
&uword r3 &uword r3
&ubyte r3H &ubyte r3H
&ubyte r3L &ubyte r3L
&bool r3bH
&bool r3bL
&word r3s &word r3s
&byte r3sH &byte r3sH
&byte r3sL &byte r3sL
&uword r4 &uword r4
&ubyte r4H &ubyte r4H
&ubyte r4L &ubyte r4L
&bool r4bH
&bool r4bL
&word r4s &word r4s
&byte r4sH &byte r4sH
&byte r4sL &byte r4sL
&uword r5 &uword r5
&ubyte r5H &ubyte r5H
&ubyte r5L &ubyte r5L
&bool r5bH
&bool r5bL
&word r5s &word r5s
&byte r5sH &byte r5sH
&byte r5sL &byte r5sL
&uword r6 &uword r6
&ubyte r6H &ubyte r6H
&ubyte r6L &ubyte r6L
&bool r6bH
&bool r6bL
&word r6s &word r6s
&byte r6sH &byte r6sH
&byte r6sL &byte r6sL
&uword r7 &uword r7
&ubyte r7H &ubyte r7H
&ubyte r7L &ubyte r7L
&bool r7bH
&bool r7bL
&word r7s &word r7s
&byte r7sH &byte r7sH
&byte r7sL &byte r7sL
&uword r8 &uword r8
&ubyte r8H &ubyte r8H
&ubyte r8L &ubyte r8L
&bool r8bH
&bool r8bL
&word r8s &word r8s
&byte r8sH &byte r8sH
&byte r8sL &byte r8sL
&uword r9 &uword r9
&ubyte r9H &ubyte r9H
&ubyte r9L &ubyte r9L
&bool r9bH
&bool r9bL
&word r9s &word r9s
&byte r9sH &byte r9sH
&byte r9sL &byte r9sL

View File

@ -1,6 +1,5 @@
Prog8 compiler v11.4-SNAPSHOT by Irmen de Jong (irmen@razorvine.net) Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net)
Prerelease version from git commit a87f2640 in branch master
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-c64.p8 Compiling program import-all-c64.p8
@ -109,7 +108,6 @@ LIBRARY MODULE NAME: coroutines
coroutines { coroutines {
const ubyte MAX_TASKS const ubyte MAX_TASKS
ubyte active_task ubyte active_task
uword[] returnaddresses
uword supervisor uword supervisor
uword[] tasklist uword[] tasklist
uword[] userdatas uword[] userdatas
@ -365,6 +363,7 @@ strings {
copy (uword source @R0, uword target @AY) -> clobbers (A) -> ubyte @Y copy (uword source @R0, uword target @AY) -> clobbers (A) -> ubyte @Y
endswith (str st, str suffix) -> bool endswith (str st, str suffix) -> bool
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
find_eol (uword string @AY) -> ubyte @A, bool @Pc
findstr (str haystack, str needle) -> ubyte findstr (str haystack, str needle) -> ubyte
hash (str string @AY) -> ubyte @A hash (str string @AY) -> ubyte @A
isdigit (ubyte petsciichar @A) -> bool @Pc isdigit (ubyte petsciichar @A) -> bool @Pc
@ -658,6 +657,7 @@ sys {
exit (ubyte returnvalue @A) exit (ubyte returnvalue @A)
exit2 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y) exit2 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y)
exit3 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc) exit3 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc)
get_as_returnaddress (uword address @XY) -> uword @AX
internal_stringcopy (uword source @R0, uword target @AY) -> clobbers (A,Y) internal_stringcopy (uword source @R0, uword target @AY) -> clobbers (A,Y)
irqsafe_clear_irqd () irqsafe_clear_irqd ()
irqsafe_set_irqd () irqsafe_set_irqd ()
@ -690,6 +690,8 @@ cx16 {
&uword r0 &uword r0
&ubyte r0H &ubyte r0H
&ubyte r0L &ubyte r0L
&bool r0bH
&bool r0bL
&word r0s &word r0s
&byte r0sH &byte r0sH
&byte r0sL &byte r0sL
@ -697,89 +699,119 @@ cx16 {
&uword r10 &uword r10
&ubyte r10H &ubyte r10H
&ubyte r10L &ubyte r10L
&bool r10bH
&bool r10bL
&word r10s &word r10s
&byte r10sH &byte r10sH
&byte r10sL &byte r10sL
&uword r11 &uword r11
&ubyte r11H &ubyte r11H
&ubyte r11L &ubyte r11L
&bool r11bH
&bool r11bL
&word r11s &word r11s
&byte r11sH &byte r11sH
&byte r11sL &byte r11sL
&uword r12 &uword r12
&ubyte r12H &ubyte r12H
&ubyte r12L &ubyte r12L
&bool r12bH
&bool r12bL
&word r12s &word r12s
&byte r12sH &byte r12sH
&byte r12sL &byte r12sL
&uword r13 &uword r13
&ubyte r13H &ubyte r13H
&ubyte r13L &ubyte r13L
&bool r13bH
&bool r13bL
&word r13s &word r13s
&byte r13sH &byte r13sH
&byte r13sL &byte r13sL
&uword r14 &uword r14
&ubyte r14H &ubyte r14H
&ubyte r14L &ubyte r14L
&bool r14bH
&bool r14bL
&word r14s &word r14s
&byte r14sH &byte r14sH
&byte r14sL &byte r14sL
&uword r15 &uword r15
&ubyte r15H &ubyte r15H
&ubyte r15L &ubyte r15L
&bool r15bH
&bool r15bL
&word r15s &word r15s
&byte r15sH &byte r15sH
&byte r15sL &byte r15sL
&ubyte r1H &ubyte r1H
&ubyte r1L &ubyte r1L
&bool r1bH
&bool r1bL
&word r1s &word r1s
&byte r1sH &byte r1sH
&byte r1sL &byte r1sL
&uword r2 &uword r2
&ubyte r2H &ubyte r2H
&ubyte r2L &ubyte r2L
&bool r2bH
&bool r2bL
&word r2s &word r2s
&byte r2sH &byte r2sH
&byte r2sL &byte r2sL
&uword r3 &uword r3
&ubyte r3H &ubyte r3H
&ubyte r3L &ubyte r3L
&bool r3bH
&bool r3bL
&word r3s &word r3s
&byte r3sH &byte r3sH
&byte r3sL &byte r3sL
&uword r4 &uword r4
&ubyte r4H &ubyte r4H
&ubyte r4L &ubyte r4L
&bool r4bH
&bool r4bL
&word r4s &word r4s
&byte r4sH &byte r4sH
&byte r4sL &byte r4sL
&uword r5 &uword r5
&ubyte r5H &ubyte r5H
&ubyte r5L &ubyte r5L
&bool r5bH
&bool r5bL
&word r5s &word r5s
&byte r5sH &byte r5sH
&byte r5sL &byte r5sL
&uword r6 &uword r6
&ubyte r6H &ubyte r6H
&ubyte r6L &ubyte r6L
&bool r6bH
&bool r6bL
&word r6s &word r6s
&byte r6sH &byte r6sH
&byte r6sL &byte r6sL
&uword r7 &uword r7
&ubyte r7H &ubyte r7H
&ubyte r7L &ubyte r7L
&bool r7bH
&bool r7bL
&word r7s &word r7s
&byte r7sH &byte r7sH
&byte r7sL &byte r7sL
&uword r8 &uword r8
&ubyte r8H &ubyte r8H
&ubyte r8L &ubyte r8L
&bool r8bH
&bool r8bL
&word r8s &word r8s
&byte r8sH &byte r8sH
&byte r8sL &byte r8sL
&uword r9 &uword r9
&ubyte r9H &ubyte r9H
&ubyte r9L &ubyte r9L
&bool r9bH
&bool r9bL
&word r9s &word r9s
&byte r9sH &byte r9sH
&byte r9sL &byte r9sL

View File

@ -1,6 +1,5 @@
Prog8 compiler v11.4-SNAPSHOT by Irmen de Jong (irmen@razorvine.net) Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net)
Prerelease version from git commit a87f2640 in branch master
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-cx16.p8 Compiling program import-all-cx16.p8
@ -147,7 +146,6 @@ LIBRARY MODULE NAME: coroutines
coroutines { coroutines {
const ubyte MAX_TASKS const ubyte MAX_TASKS
ubyte active_task ubyte active_task
uword[] returnaddresses
uword supervisor uword supervisor
uword[] tasklist uword[] tasklist
uword[] userdatas uword[] userdatas
@ -676,6 +674,7 @@ strings {
copy (uword source @R0, uword target @AY) -> clobbers (A) -> ubyte @Y copy (uword source @R0, uword target @AY) -> clobbers (A) -> ubyte @Y
endswith (str st, str suffix) -> bool endswith (str st, str suffix) -> bool
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
find_eol (uword string @AY) -> ubyte @A, bool @Pc
findstr (str haystack, str needle) -> ubyte findstr (str haystack, str needle) -> ubyte
hash (str string @AY) -> ubyte @A hash (str string @AY) -> ubyte @A
isdigit (ubyte petsciichar @A) -> bool @Pc isdigit (ubyte petsciichar @A) -> bool @Pc
@ -894,6 +893,8 @@ cx16 {
&uword r0 &uword r0
&ubyte r0H &ubyte r0H
&ubyte r0L &ubyte r0L
&bool r0bH
&bool r0bL
&word r0s &word r0s
&byte r0sH &byte r0sH
&byte r0sL &byte r0sL
@ -901,89 +902,119 @@ cx16 {
&uword r10 &uword r10
&ubyte r10H &ubyte r10H
&ubyte r10L &ubyte r10L
&bool r10bH
&bool r10bL
&word r10s &word r10s
&byte r10sH &byte r10sH
&byte r10sL &byte r10sL
&uword r11 &uword r11
&ubyte r11H &ubyte r11H
&ubyte r11L &ubyte r11L
&bool r11bH
&bool r11bL
&word r11s &word r11s
&byte r11sH &byte r11sH
&byte r11sL &byte r11sL
&uword r12 &uword r12
&ubyte r12H &ubyte r12H
&ubyte r12L &ubyte r12L
&bool r12bH
&bool r12bL
&word r12s &word r12s
&byte r12sH &byte r12sH
&byte r12sL &byte r12sL
&uword r13 &uword r13
&ubyte r13H &ubyte r13H
&ubyte r13L &ubyte r13L
&bool r13bH
&bool r13bL
&word r13s &word r13s
&byte r13sH &byte r13sH
&byte r13sL &byte r13sL
&uword r14 &uword r14
&ubyte r14H &ubyte r14H
&ubyte r14L &ubyte r14L
&bool r14bH
&bool r14bL
&word r14s &word r14s
&byte r14sH &byte r14sH
&byte r14sL &byte r14sL
&uword r15 &uword r15
&ubyte r15H &ubyte r15H
&ubyte r15L &ubyte r15L
&bool r15bH
&bool r15bL
&word r15s &word r15s
&byte r15sH &byte r15sH
&byte r15sL &byte r15sL
&ubyte r1H &ubyte r1H
&ubyte r1L &ubyte r1L
&bool r1bH
&bool r1bL
&word r1s &word r1s
&byte r1sH &byte r1sH
&byte r1sL &byte r1sL
&uword r2 &uword r2
&ubyte r2H &ubyte r2H
&ubyte r2L &ubyte r2L
&bool r2bH
&bool r2bL
&word r2s &word r2s
&byte r2sH &byte r2sH
&byte r2sL &byte r2sL
&uword r3 &uword r3
&ubyte r3H &ubyte r3H
&ubyte r3L &ubyte r3L
&bool r3bH
&bool r3bL
&word r3s &word r3s
&byte r3sH &byte r3sH
&byte r3sL &byte r3sL
&uword r4 &uword r4
&ubyte r4H &ubyte r4H
&ubyte r4L &ubyte r4L
&bool r4bH
&bool r4bL
&word r4s &word r4s
&byte r4sH &byte r4sH
&byte r4sL &byte r4sL
&uword r5 &uword r5
&ubyte r5H &ubyte r5H
&ubyte r5L &ubyte r5L
&bool r5bH
&bool r5bL
&word r5s &word r5s
&byte r5sH &byte r5sH
&byte r5sL &byte r5sL
&uword r6 &uword r6
&ubyte r6H &ubyte r6H
&ubyte r6L &ubyte r6L
&bool r6bH
&bool r6bL
&word r6s &word r6s
&byte r6sH &byte r6sH
&byte r6sL &byte r6sL
&uword r7 &uword r7
&ubyte r7H &ubyte r7H
&ubyte r7L &ubyte r7L
&bool r7bH
&bool r7bL
&word r7s &word r7s
&byte r7sH &byte r7sH
&byte r7sL &byte r7sL
&uword r8 &uword r8
&ubyte r8H &ubyte r8H
&ubyte r8L &ubyte r8L
&bool r8bH
&bool r8bL
&word r8s &word r8s
&byte r8sH &byte r8sH
&byte r8sL &byte r8sL
&uword r9 &uword r9
&ubyte r9H &ubyte r9H
&ubyte r9L &ubyte r9L
&bool r9bH
&bool r9bL
&word r9s &word r9s
&byte r9sH &byte r9sH
&byte r9sL &byte r9sL
@ -1233,6 +1264,7 @@ sys {
exit (ubyte returnvalue @A) exit (ubyte returnvalue @A)
exit2 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y) exit2 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y)
exit3 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc) exit3 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc)
get_as_returnaddress (uword address @XY) -> uword @AX
internal_stringcopy (uword source @R0, uword target @AY) -> clobbers (A,Y) internal_stringcopy (uword source @R0, uword target @AY) -> clobbers (A,Y)
irqsafe_clear_irqd () irqsafe_clear_irqd ()
irqsafe_set_irqd () irqsafe_set_irqd ()

View File

@ -1,6 +1,5 @@
Prog8 compiler v11.4-SNAPSHOT by Irmen de Jong (irmen@razorvine.net) Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net)
Prerelease version from git commit a87f2640 in branch master
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-pet32.p8 Compiling program import-all-pet32.p8
@ -109,7 +108,6 @@ LIBRARY MODULE NAME: coroutines
coroutines { coroutines {
const ubyte MAX_TASKS const ubyte MAX_TASKS
ubyte active_task ubyte active_task
uword[] returnaddresses
uword supervisor uword supervisor
uword[] tasklist uword[] tasklist
uword[] userdatas uword[] userdatas
@ -189,6 +187,7 @@ strings {
copy (uword source @R0, uword target @AY) -> clobbers (A) -> ubyte @Y copy (uword source @R0, uword target @AY) -> clobbers (A) -> ubyte @Y
endswith (str st, str suffix) -> bool endswith (str st, str suffix) -> bool
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
find_eol (uword string @AY) -> ubyte @A, bool @Pc
findstr (str haystack, str needle) -> ubyte findstr (str haystack, str needle) -> ubyte
hash (str string @AY) -> ubyte @A hash (str string @AY) -> ubyte @A
isdigit (ubyte petsciichar @A) -> bool @Pc isdigit (ubyte petsciichar @A) -> bool @Pc
@ -276,6 +275,7 @@ sys {
exit (ubyte returnvalue @A) exit (ubyte returnvalue @A)
exit2 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y) exit2 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y)
exit3 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc) exit3 (ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc)
get_as_returnaddress (uword address @XY) -> uword @AX
internal_stringcopy (uword source @R0, uword target @AY) -> clobbers (A,Y) internal_stringcopy (uword source @R0, uword target @AY) -> clobbers (A,Y)
irqsafe_clear_irqd () irqsafe_clear_irqd ()
irqsafe_set_irqd () irqsafe_set_irqd ()
@ -304,6 +304,8 @@ cx16 {
&uword r0 &uword r0
&ubyte r0H &ubyte r0H
&ubyte r0L &ubyte r0L
&bool r0bH
&bool r0bL
&word r0s &word r0s
&byte r0sH &byte r0sH
&byte r0sL &byte r0sL
@ -311,89 +313,119 @@ cx16 {
&uword r10 &uword r10
&ubyte r10H &ubyte r10H
&ubyte r10L &ubyte r10L
&bool r10bH
&bool r10bL
&word r10s &word r10s
&byte r10sH &byte r10sH
&byte r10sL &byte r10sL
&uword r11 &uword r11
&ubyte r11H &ubyte r11H
&ubyte r11L &ubyte r11L
&bool r11bH
&bool r11bL
&word r11s &word r11s
&byte r11sH &byte r11sH
&byte r11sL &byte r11sL
&uword r12 &uword r12
&ubyte r12H &ubyte r12H
&ubyte r12L &ubyte r12L
&bool r12bH
&bool r12bL
&word r12s &word r12s
&byte r12sH &byte r12sH
&byte r12sL &byte r12sL
&uword r13 &uword r13
&ubyte r13H &ubyte r13H
&ubyte r13L &ubyte r13L
&bool r13bH
&bool r13bL
&word r13s &word r13s
&byte r13sH &byte r13sH
&byte r13sL &byte r13sL
&uword r14 &uword r14
&ubyte r14H &ubyte r14H
&ubyte r14L &ubyte r14L
&bool r14bH
&bool r14bL
&word r14s &word r14s
&byte r14sH &byte r14sH
&byte r14sL &byte r14sL
&uword r15 &uword r15
&ubyte r15H &ubyte r15H
&ubyte r15L &ubyte r15L
&bool r15bH
&bool r15bL
&word r15s &word r15s
&byte r15sH &byte r15sH
&byte r15sL &byte r15sL
&ubyte r1H &ubyte r1H
&ubyte r1L &ubyte r1L
&bool r1bH
&bool r1bL
&word r1s &word r1s
&byte r1sH &byte r1sH
&byte r1sL &byte r1sL
&uword r2 &uword r2
&ubyte r2H &ubyte r2H
&ubyte r2L &ubyte r2L
&bool r2bH
&bool r2bL
&word r2s &word r2s
&byte r2sH &byte r2sH
&byte r2sL &byte r2sL
&uword r3 &uword r3
&ubyte r3H &ubyte r3H
&ubyte r3L &ubyte r3L
&bool r3bH
&bool r3bL
&word r3s &word r3s
&byte r3sH &byte r3sH
&byte r3sL &byte r3sL
&uword r4 &uword r4
&ubyte r4H &ubyte r4H
&ubyte r4L &ubyte r4L
&bool r4bH
&bool r4bL
&word r4s &word r4s
&byte r4sH &byte r4sH
&byte r4sL &byte r4sL
&uword r5 &uword r5
&ubyte r5H &ubyte r5H
&ubyte r5L &ubyte r5L
&bool r5bH
&bool r5bL
&word r5s &word r5s
&byte r5sH &byte r5sH
&byte r5sL &byte r5sL
&uword r6 &uword r6
&ubyte r6H &ubyte r6H
&ubyte r6L &ubyte r6L
&bool r6bH
&bool r6bL
&word r6s &word r6s
&byte r6sH &byte r6sH
&byte r6sL &byte r6sL
&uword r7 &uword r7
&ubyte r7H &ubyte r7H
&ubyte r7L &ubyte r7L
&bool r7bH
&bool r7bL
&word r7s &word r7s
&byte r7sH &byte r7sH
&byte r7sL &byte r7sL
&uword r8 &uword r8
&ubyte r8H &ubyte r8H
&ubyte r8L &ubyte r8L
&bool r8bH
&bool r8bL
&word r8s &word r8s
&byte r8sH &byte r8sH
&byte r8sL &byte r8sL
&uword r9 &uword r9
&ubyte r9H &ubyte r9H
&ubyte r9L &ubyte r9L
&bool r9bH
&bool r9bL
&word r9s &word r9s
&byte r9sH &byte r9sH
&byte r9sL &byte r9sL

View File

@ -1,6 +1,5 @@
Prog8 compiler v11.4-SNAPSHOT by Irmen de Jong (irmen@razorvine.net) Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net)
Prerelease version from git commit a87f2640 in branch master
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-virtual.p8 Compiling program import-all-virtual.p8
@ -280,7 +279,8 @@ strings {
contains (str st, ubyte character) -> bool contains (str st, ubyte character) -> bool
copy (str source, str target) -> ubyte copy (str source, str target) -> ubyte
endswith (str st, str suffix) -> bool endswith (str st, str suffix) -> bool
find (str st, ubyte character) -> ubyte find (str st, ubyte character) -> ubyte, bool
find_eol (str st) -> ubyte, bool
findstr (str haystack, str needle) -> ubyte findstr (str haystack, str needle) -> ubyte
hash (str st) -> ubyte hash (str st) -> ubyte
isdigit (ubyte character) -> bool isdigit (ubyte character) -> bool
@ -298,7 +298,7 @@ strings {
ltrim (str s) ltrim (str s)
ltrimmed (str s) -> str ltrimmed (str s) -> str
ncompare (str st1, str st2, ubyte length) -> byte ncompare (str st1, str st2, ubyte length) -> byte
rfind (uword stringptr, ubyte character) -> ubyte rfind (uword stringptr, ubyte character) -> ubyte, bool
right (str source, ubyte slen, str target) right (str source, ubyte slen, str target)
rstrip (str s) rstrip (str s)
rtrim (str s) rtrim (str s)
@ -338,6 +338,7 @@ sys {
disable_caseswitch () disable_caseswitch ()
enable_caseswitch () enable_caseswitch ()
exit (ubyte returnvalue) exit (ubyte returnvalue)
get_as_returnaddress (uword address) -> uword
gfx_clear (ubyte color) gfx_clear (ubyte color)
gfx_enable (ubyte mode) gfx_enable (ubyte mode)
gfx_getpixel (uword xx, uword yy) -> ubyte gfx_getpixel (uword xx, uword yy) -> ubyte
@ -366,6 +367,8 @@ cx16 {
&uword r0 &uword r0
&ubyte r0H &ubyte r0H
&ubyte r0L &ubyte r0L
&bool r0bH
&bool r0bL
&word r0s &word r0s
&byte r0sH &byte r0sH
&byte r0sL &byte r0sL
@ -373,89 +376,119 @@ cx16 {
&uword r10 &uword r10
&ubyte r10H &ubyte r10H
&ubyte r10L &ubyte r10L
&bool r10bH
&bool r10bL
&word r10s &word r10s
&byte r10sH &byte r10sH
&byte r10sL &byte r10sL
&uword r11 &uword r11
&ubyte r11H &ubyte r11H
&ubyte r11L &ubyte r11L
&bool r11bH
&bool r11bL
&word r11s &word r11s
&byte r11sH &byte r11sH
&byte r11sL &byte r11sL
&uword r12 &uword r12
&ubyte r12H &ubyte r12H
&ubyte r12L &ubyte r12L
&bool r12bH
&bool r12bL
&word r12s &word r12s
&byte r12sH &byte r12sH
&byte r12sL &byte r12sL
&uword r13 &uword r13
&ubyte r13H &ubyte r13H
&ubyte r13L &ubyte r13L
&bool r13bH
&bool r13bL
&word r13s &word r13s
&byte r13sH &byte r13sH
&byte r13sL &byte r13sL
&uword r14 &uword r14
&ubyte r14H &ubyte r14H
&ubyte r14L &ubyte r14L
&bool r14bH
&bool r14bL
&word r14s &word r14s
&byte r14sH &byte r14sH
&byte r14sL &byte r14sL
&uword r15 &uword r15
&ubyte r15H &ubyte r15H
&ubyte r15L &ubyte r15L
&bool r15bH
&bool r15bL
&word r15s &word r15s
&byte r15sH &byte r15sH
&byte r15sL &byte r15sL
&ubyte r1H &ubyte r1H
&ubyte r1L &ubyte r1L
&bool r1bH
&bool r1bL
&word r1s &word r1s
&byte r1sH &byte r1sH
&byte r1sL &byte r1sL
&uword r2 &uword r2
&ubyte r2H &ubyte r2H
&ubyte r2L &ubyte r2L
&bool r2bH
&bool r2bL
&word r2s &word r2s
&byte r2sH &byte r2sH
&byte r2sL &byte r2sL
&uword r3 &uword r3
&ubyte r3H &ubyte r3H
&ubyte r3L &ubyte r3L
&bool r3bH
&bool r3bL
&word r3s &word r3s
&byte r3sH &byte r3sH
&byte r3sL &byte r3sL
&uword r4 &uword r4
&ubyte r4H &ubyte r4H
&ubyte r4L &ubyte r4L
&bool r4bH
&bool r4bL
&word r4s &word r4s
&byte r4sH &byte r4sH
&byte r4sL &byte r4sL
&uword r5 &uword r5
&ubyte r5H &ubyte r5H
&ubyte r5L &ubyte r5L
&bool r5bH
&bool r5bL
&word r5s &word r5s
&byte r5sH &byte r5sH
&byte r5sL &byte r5sL
&uword r6 &uword r6
&ubyte r6H &ubyte r6H
&ubyte r6L &ubyte r6L
&bool r6bH
&bool r6bL
&word r6s &word r6s
&byte r6sH &byte r6sH
&byte r6sL &byte r6sL
&uword r7 &uword r7
&ubyte r7H &ubyte r7H
&ubyte r7L &ubyte r7L
&bool r7bH
&bool r7bL
&word r7s &word r7s
&byte r7sH &byte r7sH
&byte r7sL &byte r7sL
&uword r8 &uword r8
&ubyte r8H &ubyte r8H
&ubyte r8L &ubyte r8L
&bool r8bH
&bool r8bL
&word r8s &word r8s
&byte r8sH &byte r8sH
&byte r8sL &byte r8sL
&uword r9 &uword r9
&ubyte r9H &ubyte r9H
&ubyte r9L &ubyte r9L
&bool r9bH
&bool r9bL
&word r9s &word r9s
&byte r9sH &byte r9sH
&byte r9sL &byte r9sL

View File

@ -362,15 +362,13 @@ Read the `conv source code <https://github.com/irmen/prog8/tree/master/compiler/
to see what's in there. to see what's in there.
coroutines (experimental) coroutines
------------------------- ----------
Provides a system to make cooperative multitasking programs via coroutines. Provides a system to make cooperative multitasking programs via coroutines.
A 'coroutine' is a subroutine whose execution you can pause and resume. A 'coroutine' is a subroutine whose execution you can pause and resume.
This library handles the voodoo for you to switch between such coroutines transparently, This library handles the voodoo for you to switch between such coroutines transparently,
so it can seem that your program is executing many subroutines at the same time. so it can seem that your program is executing many subroutines at the same time.
API is experimental and may change or disappear in a future version.
Read the `coroutines source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/coroutines.p8>`_ Read the `coroutines source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/coroutines.p8>`_
to see what's in there. And look at the ``multitasking`` example to see how it can be used. to see what's in there. And look at the ``multitasking`` example to see how it can be used.
Here is a minimal example (if the library gets more stable, better docs will be written here):: Here is a minimal example (if the library gets more stable, better docs will be written here)::
@ -1178,9 +1176,7 @@ to see what's in there. (Note: slight variations for different compiler targets)
verafx (cx16 only) verafx (cx16 only)
------------------- -------------------
Available for the Cx16 target. Available for the Cx16 target. Routines that use the Vera FX logic to accelerate certain operations.
Experimental routines that use the new Vera FX logic (hopefully coming in the Vera in new X16 boards,
the emulators already support it).
``available`` ``available``
Returns true if Vera FX is available, false if not (that would be an older Vera chip) Returns true if Vera FX is available, false if not (that would be an older Vera chip)

View File

@ -24,7 +24,6 @@ Comments
Everything on the line after a semicolon ``;`` is a comment and is ignored by the compiler. Everything on the line after a semicolon ``;`` is a comment and is ignored by the compiler.
If the whole line is just a comment, this line will be copied into the resulting assembly source code for reference. If the whole line is just a comment, this line will be copied into the resulting assembly source code for reference.
There's also a block-comment: everything surrounded with ``/*`` and ``*/`` is ignored and this can span multiple lines. There's also a block-comment: everything surrounded with ``/*`` and ``*/`` is ignored and this can span multiple lines.
This block comment is experimental for now: it may change or even be removed again in a future compiler version.
The recommended way to comment out a bunch of lines remains to just bulk comment them individually with ``;``. The recommended way to comment out a bunch of lines remains to just bulk comment them individually with ``;``.
Directive Directive
@ -1114,14 +1113,19 @@ so pay attention to any jumps and rts instructions in the inlined code!
don't want a ``rts`` or ``jmp`` or ``bra`` in it! don't want a ``rts`` or ``jmp`` or ``bra`` in it!
.. note:: .. note::
The 'virtual' 16-bit registers from the Commander X16 can also be specified as ``R0`` .. ``R15`` . The **sixteen 'virtual' 16-bit registers** from the Commander X16 can also be specified as ``R0`` .. ``R15`` .
This means you don't have to set them up manually before calling a subroutine that takes This means you don't have to set them up manually before calling a subroutine that takes
one or more parameters in those 'registers'. You can just list the arguments directly. one or more parameters in those 'registers'. You can just list the arguments directly.
*This also works on the Commodore 64!* (however they are not as efficient there because they're not in zeropage) *This also works on the other compilation targets!* (however they might not be as efficient there as on the X16,
In prog8 and assembly code these 'registers' are directly accessible too via because on most other targets such as the C64, these registers are not placed in zeropage due to lack of space)
``cx16.r0`` .. ``cx16.r15`` (these are memory-mapped uword values), In both regular **prog8** *and* **assembly** code these 'registers' are directly accessible too via:
``cx16.r0s`` .. ``cx16.r15s`` (these are memory-mapped word values),
and ``L`` / ``H`` variants are also available to directly access the low and high bytes of these. - ``cx16.r0`` - ``cx16.r15`` (memory-mapped **uword** values, most often these are used)
- ``cx16.r0s`` - ``cx16.r15s`` (memory-mapped **word** values, used when you need a signed word)
- ``cx16.r0H``, ``cx16.r0L`` (for each r0..r15; memory-mapped **ubyte** values, both bytes of the register)
- ``cx16.r0sH``, ``cx16.r0sL`` (for each r0..r15; memory-mapped **byte** values, both bytes of the register)
- ``cx16.r0bH``, ``cx16.r0bL`` (for each r0..r15; memory-mapped **bool** values, both bytes of the register)
You can use them directly but their name isn't very descriptive, so it may be useful to define You can use them directly but their name isn't very descriptive, so it may be useful to define
an alias for them when using them regularly. an alias for them when using them regularly.

View File

@ -10,9 +10,7 @@ Idea is to make it feature complete in the IR/Virtual target, then merge it to m
Future Things and Ideas Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
- is "checkAssignmentCompatible" redundant (gets called just 1 time!) when we also have "checkValueTypeAndRange" ? - romable: should we have a way to explicitly set the memory address for the BSS area (add a -varsaddress and -slabsaddress options?)
- enums?
- romable: should we have a way to explicitly set the memory address for the BSS area (instead of only the highram bank number on X16, allow a memory address too for the -varshigh option?)
- romable: fix remaining codegens (some for loops, see ForLoopsAsmGen) - romable: fix remaining codegens (some for loops, see ForLoopsAsmGen)
- Kotlin: can we use inline value classes in certain spots? - Kotlin: can we use inline value classes in certain spots?
- add float support to the configurable compiler targets - add float support to the configurable compiler targets
@ -25,7 +23,7 @@ Future Things and Ideas
Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEvaluating() and argumentsViaRegisters(). Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEvaluating() and argumentsViaRegisters().
- Does it make codegen easier if everything is an expression? Start with the PtProgram ast classes, change statements to expressions that have (new) VOID data type - Does it make codegen easier if everything is an expression? Start with the PtProgram ast classes, change statements to expressions that have (new) VOID data type
- Can we support signed % (remainder) somehow? - Can we support signed % (remainder) somehow?
- Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays. Probaby only useful if we have typed pointers. (addressed in 'struct' branch) - Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays. Probaby only useful once we have typed pointers. (addressed in 'struct' branch)
- make a form of "manual generics" possible like: varsub routine(T arg)->T where T is expanded to a specific type - make a form of "manual generics" possible like: varsub routine(T arg)->T where T is expanded to a specific type
(this is already done hardcoded for several of the builtin functions) (this is already done hardcoded for several of the builtin functions)
- [much work:] more support for (64tass) SEGMENTS in the prog8 syntax itself? - [much work:] more support for (64tass) SEGMENTS in the prog8 syntax itself?
@ -61,7 +59,6 @@ IR/VM
Libraries Libraries
--------- ---------
- Add split-word array sorting routines to sorting module? - Add split-word array sorting routines to sorting module?
- Add double-array sorting routines to sorting module? (that allows you to sort a second array in sync with the array of numbers)
- See if the raster interrupt handler on the C64 can be tweaked to be a more stable raster irq - See if the raster interrupt handler on the C64 can be tweaked to be a more stable raster irq
- pet32 target: make syslib more complete (missing kernal routines)? - pet32 target: make syslib more complete (missing kernal routines)?
- need help with: PET disk routines (OPEN, SETLFS etc are not exposed as kernal calls) - need help with: PET disk routines (OPEN, SETLFS etc are not exposed as kernal calls)
@ -71,6 +68,7 @@ Libraries
Optimizations Optimizations
------------- -------------
- in Identifier: use typedarray of strings instead of listOf? Other places?
- Compilation speed: try to join multiple modifications in 1 result in the AST processors instead of returning it straight away every time - Compilation speed: try to join multiple modifications in 1 result in the AST processors instead of returning it straight away every time
- Compare output of some Oscar64 samples to what prog8 does for the equivalent code (see https://github.com/drmortalwombat/OscarTutorials/tree/main and https://github.com/drmortalwombat/oscar64/tree/main/samples) - Compare output of some Oscar64 samples to what prog8 does for the equivalent code (see https://github.com/drmortalwombat/OscarTutorials/tree/main and https://github.com/drmortalwombat/oscar64/tree/main/samples)
- Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression" - Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression"

View File

@ -384,6 +384,18 @@ save_SCRATCH_ZPWORD2 .word ?
}} }}
} }
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
%asm {{
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
cpx #0
bne +
dey
+ dex
tya
rts
}}
}
inline asmsub pop() -> ubyte @A { inline asmsub pop() -> ubyte @A {
%asm {{ %asm {{
pla pla
@ -403,7 +415,6 @@ save_SCRATCH_ZPWORD2 .word ?
cx16 { cx16 {
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage ; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the Atari as well but their location in memory is different ; they are simulated on the Atari as well but their location in memory is different
; TODO
&uword r0 = $1b00 &uword r0 = $1b00
&uword r1 = $1b02 &uword r1 = $1b02
&uword r2 = $1b04 &uword r2 = $1b04
@ -421,6 +432,7 @@ cx16 {
&uword r14 = $1b1c &uword r14 = $1b1c
&uword r15 = $1b1e &uword r15 = $1b1e
; signed word versions
&word r0s = $1b00 &word r0s = $1b00
&word r1s = $1b02 &word r1s = $1b02
&word r2s = $1b04 &word r2s = $1b04
@ -438,6 +450,7 @@ cx16 {
&word r14s = $1b1c &word r14s = $1b1c
&word r15s = $1b1e &word r15s = $1b1e
; ubyte versions (low and high bytes)
&ubyte r0L = $1b00 &ubyte r0L = $1b00
&ubyte r1L = $1b02 &ubyte r1L = $1b02
&ubyte r2L = $1b04 &ubyte r2L = $1b04
@ -472,6 +485,7 @@ cx16 {
&ubyte r14H = $1b1d &ubyte r14H = $1b1d
&ubyte r15H = $1b1f &ubyte r15H = $1b1f
; signed byte versions (low and high bytes)
&byte r0sL = $1b00 &byte r0sL = $1b00
&byte r1sL = $1b02 &byte r1sL = $1b02
&byte r2sL = $1b04 &byte r2sL = $1b04
@ -506,6 +520,41 @@ cx16 {
&byte r14sH = $1b1d &byte r14sH = $1b1d
&byte r15sH = $1b1f &byte r15sH = $1b1f
; boolean versions
&bool r0bL = $1b00
&bool r1bL = $1b02
&bool r2bL = $1b04
&bool r3bL = $1b06
&bool r4bL = $1b08
&bool r5bL = $1b0a
&bool r6bL = $1b0c
&bool r7bL = $1b0e
&bool r8bL = $1b10
&bool r9bL = $1b12
&bool r10bL = $1b14
&bool r11bL = $1b16
&bool r12bL = $1b18
&bool r13bL = $1b1a
&bool r14bL = $1b1c
&bool r15bL = $1b1e
&bool r0bH = $1b01
&bool r1bH = $1b03
&bool r2bH = $1b05
&bool r3bH = $1b07
&bool r4bH = $1b09
&bool r5bH = $1b0b
&bool r6bH = $1b0d
&bool r7bH = $1b0f
&bool r8bH = $1b11
&bool r9bH = $1b13
&bool r10bH = $1b15
&bool r11bH = $1b17
&bool r12bH = $1b19
&bool r13bH = $1b1b
&bool r14bH = $1b1d
&bool r15bH = $1b1f
asmsub save_virtual_registers() clobbers(A,Y) { asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{ %asm {{
ldy #31 ldy #31

View File

@ -317,6 +317,18 @@ save_SCRATCH_ZPWORD2 .word ?
}} }}
} }
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
%asm {{
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
cpx #0
bne +
dey
+ dex
tya
rts
}}
}
inline asmsub pop() -> ubyte @A { inline asmsub pop() -> ubyte @A {
%asm {{ %asm {{
pla pla
@ -352,6 +364,7 @@ cx16 {
&uword r14 = $001e &uword r14 = $001e
&uword r15 = $0020 &uword r15 = $0020
; signed word versions
&word r0s = $0002 &word r0s = $0002
&word r1s = $0004 &word r1s = $0004
&word r2s = $0006 &word r2s = $0006
@ -369,6 +382,7 @@ cx16 {
&word r14s = $001e &word r14s = $001e
&word r15s = $0020 &word r15s = $0020
; ubyte versions (low and high bytes)
&ubyte r0L = $0002 &ubyte r0L = $0002
&ubyte r1L = $0004 &ubyte r1L = $0004
&ubyte r2L = $0006 &ubyte r2L = $0006
@ -403,6 +417,7 @@ cx16 {
&ubyte r14H = $001f &ubyte r14H = $001f
&ubyte r15H = $0021 &ubyte r15H = $0021
; signed byte versions (low and high bytes)
&byte r0sL = $0002 &byte r0sL = $0002
&byte r1sL = $0004 &byte r1sL = $0004
&byte r2sL = $0006 &byte r2sL = $0006
@ -437,6 +452,42 @@ cx16 {
&byte r14sH = $001f &byte r14sH = $001f
&byte r15sH = $0021 &byte r15sH = $0021
; boolean versions
&bool r0bL = $0002
&bool r1bL = $0004
&bool r2bL = $0006
&bool r3bL = $0008
&bool r4bL = $000a
&bool r5bL = $000c
&bool r6bL = $000e
&bool r7bL = $0010
&bool r8bL = $0012
&bool r9bL = $0014
&bool r10bL = $0016
&bool r11bL = $0018
&bool r12bL = $001a
&bool r13bL = $001c
&bool r14bL = $001e
&bool r15bL = $0020
&bool r0bH = $0003
&bool r1bH = $0005
&bool r2bH = $0007
&bool r3bH = $0009
&bool r4bH = $000b
&bool r5bH = $000d
&bool r6bH = $000f
&bool r7bH = $0011
&bool r8bH = $0013
&bool r9bH = $0015
&bool r10bH = $0017
&bool r11bH = $0019
&bool r12bH = $001b
&bool r13bH = $001d
&bool r14bH = $001f
&bool r15bH = $0021
asmsub save_virtual_registers() clobbers(A,Y) { asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{ %asm {{
ldy #31 ldy #31

View File

@ -159,6 +159,7 @@ cx16 {
&uword r14 = $cffc &uword r14 = $cffc
&uword r15 = $cffe &uword r15 = $cffe
; signed word versions
&word r0s = $cfe0 &word r0s = $cfe0
&word r1s = $cfe2 &word r1s = $cfe2
&word r2s = $cfe4 &word r2s = $cfe4
@ -176,6 +177,7 @@ cx16 {
&word r14s = $cffc &word r14s = $cffc
&word r15s = $cffe &word r15s = $cffe
; ubyte versions (low and high bytes)
&ubyte r0L = $cfe0 &ubyte r0L = $cfe0
&ubyte r1L = $cfe2 &ubyte r1L = $cfe2
&ubyte r2L = $cfe4 &ubyte r2L = $cfe4
@ -210,6 +212,7 @@ cx16 {
&ubyte r14H = $cffd &ubyte r14H = $cffd
&ubyte r15H = $cfff &ubyte r15H = $cfff
; signed byte versions (low and high bytes)
&byte r0sL = $cfe0 &byte r0sL = $cfe0
&byte r1sL = $cfe2 &byte r1sL = $cfe2
&byte r2sL = $cfe4 &byte r2sL = $cfe4
@ -243,4 +246,39 @@ cx16 {
&byte r13sH = $cffb &byte r13sH = $cffb
&byte r14sH = $cffd &byte r14sH = $cffd
&byte r15sH = $cfff &byte r15sH = $cfff
; boolean versions
&bool r0bL = $cfe0
&bool r1bL = $cfe2
&bool r2bL = $cfe4
&bool r3bL = $cfe6
&bool r4bL = $cfe8
&bool r5bL = $cfea
&bool r6bL = $cfec
&bool r7bL = $cfee
&bool r8bL = $cff0
&bool r9bL = $cff2
&bool r10bL = $cff4
&bool r11bL = $cff6
&bool r12bL = $cff8
&bool r13bL = $cffa
&bool r14bL = $cffc
&bool r15bL = $cffe
&bool r0bH = $cfe1
&bool r1bH = $cfe3
&bool r2bH = $cfe5
&bool r3bH = $cfe7
&bool r4bH = $cfe9
&bool r5bH = $cfeb
&bool r6bH = $cfed
&bool r7bH = $cfef
&bool r8bH = $cff1
&bool r9bH = $cff3
&bool r10bH = $cff5
&bool r11bH = $cff7
&bool r12bH = $cff9
&bool r13bH = $cffb
&bool r14bH = $cffd
&bool r15bH = $cfff
} }

View File

@ -159,6 +159,7 @@ cx16 {
&uword r14 = $001e &uword r14 = $001e
&uword r15 = $0020 &uword r15 = $0020
; signed word versions
&word r0s = $0002 &word r0s = $0002
&word r1s = $0004 &word r1s = $0004
&word r2s = $0006 &word r2s = $0006
@ -176,6 +177,7 @@ cx16 {
&word r14s = $001e &word r14s = $001e
&word r15s = $0020 &word r15s = $0020
; ubyte versions (low and high bytes)
&ubyte r0L = $0002 &ubyte r0L = $0002
&ubyte r1L = $0004 &ubyte r1L = $0004
&ubyte r2L = $0006 &ubyte r2L = $0006
@ -210,6 +212,7 @@ cx16 {
&ubyte r14H = $001f &ubyte r14H = $001f
&ubyte r15H = $0021 &ubyte r15H = $0021
; signed byte versions (low and high bytes)
&byte r0sL = $0002 &byte r0sL = $0002
&byte r1sL = $0004 &byte r1sL = $0004
&byte r2sL = $0006 &byte r2sL = $0006
@ -243,4 +246,39 @@ cx16 {
&byte r13sH = $001d &byte r13sH = $001d
&byte r14sH = $001f &byte r14sH = $001f
&byte r15sH = $0021 &byte r15sH = $0021
; boolean versions
&bool r0bL = $0002
&bool r1bL = $0004
&bool r2bL = $0006
&bool r3bL = $0008
&bool r4bL = $000a
&bool r5bL = $000c
&bool r6bL = $000e
&bool r7bL = $0010
&bool r8bL = $0012
&bool r9bL = $0014
&bool r10bL = $0016
&bool r11bL = $0018
&bool r12bL = $001a
&bool r13bL = $001c
&bool r14bL = $001e
&bool r15bL = $0020
&bool r0bH = $0003
&bool r1bH = $0005
&bool r2bH = $0007
&bool r3bH = $0009
&bool r4bH = $000b
&bool r5bH = $000d
&bool r6bH = $000f
&bool r7bH = $0011
&bool r8bH = $0013
&bool r9bH = $0015
&bool r10bH = $0017
&bool r11bH = $0019
&bool r12bH = $001b
&bool r13bH = $001d
&bool r14bH = $001f
&bool r15bH = $0021
} }

View File

@ -155,6 +155,7 @@ cx16 {
&uword r14 = $7ffc &uword r14 = $7ffc
&uword r15 = $7ffe &uword r15 = $7ffe
; signed word versions
&word r0s = $7fe0 &word r0s = $7fe0
&word r1s = $7fe2 &word r1s = $7fe2
&word r2s = $7fe4 &word r2s = $7fe4
@ -172,6 +173,7 @@ cx16 {
&word r14s = $7ffc &word r14s = $7ffc
&word r15s = $7ffe &word r15s = $7ffe
; ubyte versions (low and high bytes)
&ubyte r0L = $7fe0 &ubyte r0L = $7fe0
&ubyte r1L = $7fe2 &ubyte r1L = $7fe2
&ubyte r2L = $7fe4 &ubyte r2L = $7fe4
@ -206,6 +208,7 @@ cx16 {
&ubyte r14H = $7ffd &ubyte r14H = $7ffd
&ubyte r15H = $7fff &ubyte r15H = $7fff
; signed byte versions (low and high bytes)
&byte r0sL = $7fe0 &byte r0sL = $7fe0
&byte r1sL = $7fe2 &byte r1sL = $7fe2
&byte r2sL = $7fe4 &byte r2sL = $7fe4
@ -239,4 +242,39 @@ cx16 {
&byte r13sH = $7ffb &byte r13sH = $7ffb
&byte r14sH = $7ffd &byte r14sH = $7ffd
&byte r15sH = $7fff &byte r15sH = $7fff
; boolean versions
&bool r0bL = $7fe0
&bool r1bL = $7fe2
&bool r2bL = $7fe4
&bool r3bL = $7fe6
&bool r4bL = $7fe8
&bool r5bL = $7fea
&bool r6bL = $7fec
&bool r7bL = $7fee
&bool r8bL = $7ff0
&bool r9bL = $7ff2
&bool r10bL = $7ff4
&bool r11bL = $7ff6
&bool r12bL = $7ff8
&bool r13bL = $7ffa
&bool r14bL = $7ffc
&bool r15bL = $7ffe
&bool r0bH = $7fe1
&bool r1bH = $7fe3
&bool r2bH = $7fe5
&bool r3bH = $7fe7
&bool r4bH = $7fe9
&bool r5bH = $7feb
&bool r6bH = $7fed
&bool r7bH = $7fef
&bool r8bH = $7ff1
&bool r9bH = $7ff3
&bool r10bH = $7ff5
&bool r11bH = $7ff7
&bool r12bH = $7ff9
&bool r13bH = $7ffb
&bool r14bH = $7ffd
&bool r15bH = $7fff
} }

View File

@ -1,800 +1,27 @@
%import textio
%zeropage basicsafe
main { main {
sub start() { sub start() {
txt.plot(0, 49) uword uw = 9999
bytesoverflow() word sw = -2222
bytesoverflow_jump() ubyte ub = 42
bytesoverflow_jump_indirect() byte sb = -99
bytessmall() bool bb = true
bytessmall_jump()
bytessmall_jump_indirect()
bytes99()
bytes100()
bytes101()
words()
zerobytes()
zerowords()
}
sub zerobytes() { cx16.r0 = uw
byte @shared sb = -100 cx16.r0s = sw
byte @shared p = 0 cx16.r0L = ub
const byte cb = -100 cx16.r0H = ub
cx16.r0sL = sb
cx16.r0sH = sb
cx16.r0bL = bb
cx16.r0bH = bb
txt.print("\nsigned bytes with 0\n") uw = cx16.r0
txt.print("expected: ") sw = cx16.r0s
txt.print_b(cb) ub = cx16.r0L
txt.spc() ub = cx16.r0H
txt.print_bool(cb>0) sb = cx16.r0sL
txt.spc() sb = cx16.r0sH
txt.print_bool(cb>=0) bb = cx16.r0bL
txt.spc() bb = cx16.r0bH
txt.print_bool(cb<0)
txt.spc()
txt.print_bool(cb<=0)
txt.nl()
txt.print(" calc'd: ")
txt.print_b(sb)
txt.spc()
txt.print_bool(sb>0)
txt.spc()
txt.print_bool(sb>=0)
txt.spc()
txt.print_bool(sb<0)
txt.spc()
txt.print_bool(sb<=0)
txt.nl()
txt.print("calc'd 2: ")
txt.print_b(sb)
txt.spc()
txt.print_bool(sb>p)
txt.spc()
txt.print_bool(sb>=p)
txt.spc()
txt.print_bool(sb<p)
txt.spc()
txt.print_bool(sb<=p)
txt.nl()
txt.print(" if stmt: ")
txt.print_b(sb)
txt.spc()
if sb>0 txt.print("true ") else txt.print("false ")
if sb>=0 txt.print("true ") else txt.print("false ")
if sb<0 txt.print("true ") else txt.print("false ")
if sb<=0 txt.print("true ") else txt.print("false ")
txt.nl()
txt.print(" ifstmt2: ")
txt.print_b(sb)
txt.spc()
if sb>p txt.print("true ") else txt.print("false ")
if sb>=p txt.print("true ") else txt.print("false ")
if sb<p txt.print("true ") else txt.print("false ")
if sb<=p txt.print("true ") else txt.print("false ")
txt.nl()
}
sub zerowords() {
word @shared sbw = -30000
word @shared pw = 0
const word cbw = -30000
txt.print("\nsigned words\n")
txt.print("expected: ")
txt.print_w(cbw)
txt.spc()
txt.print_bool(cbw>0)
txt.spc()
txt.print_bool(cbw>=0)
txt.spc()
txt.print_bool(cbw<0)
txt.spc()
txt.print_bool(cbw<=0)
txt.nl()
txt.print(" calc'd: ")
txt.print_w(sbw)
txt.spc()
txt.print_bool(sbw>0)
txt.spc()
txt.print_bool(sbw>=0)
txt.spc()
txt.print_bool(sbw<0)
txt.spc()
txt.print_bool(sbw<=0)
txt.nl()
txt.print("calc'd 2: ")
txt.print_w(sbw)
txt.spc()
txt.print_bool(sbw>pw)
txt.spc()
txt.print_bool(sbw>=pw)
txt.spc()
txt.print_bool(sbw<pw)
txt.spc()
txt.print_bool(sbw<=pw)
txt.nl()
txt.print(" if stmt: ")
txt.print_w(sbw)
txt.spc()
if sbw>0 txt.print("true ") else txt.print("false ")
if sbw>=0 txt.print("true ") else txt.print("false ")
if sbw<0 txt.print("true ") else txt.print("false ")
if sbw<=0 txt.print("true ") else txt.print("false ")
txt.nl()
txt.print(" ifstmt2: ")
txt.print_w(sbw)
txt.spc()
if sbw>pw txt.print("true ") else txt.print("false ")
if sbw>=pw txt.print("true ") else txt.print("false ")
if sbw<pw txt.print("true ") else txt.print("false ")
if sbw<=pw txt.print("true ") else txt.print("false ")
txt.nl()
}
sub bytes99() {
const byte cb = 99
byte @shared sb = 99
byte @shared p = 100
txt.print("\nsigned bytes, 99\n")
txt.print("expected: ")
txt.print_b(100)
txt.spc()
txt.print_bool(cb>100)
txt.spc()
txt.print_bool(cb>=100)
txt.spc()
txt.print_bool(cb<100)
txt.spc()
txt.print_bool(cb<=100)
txt.nl()
txt.print(" calc'd: ")
txt.print_b(100)
txt.spc()
txt.print_bool(sb>100)
txt.spc()
txt.print_bool(sb>=100)
txt.spc()
txt.print_bool(sb<100)
txt.spc()
txt.print_bool(sb<=100)
txt.nl()
txt.print("calc'd 2: ")
txt.print_b(p)
txt.spc()
txt.print_bool(sb>p)
txt.spc()
txt.print_bool(sb>=p)
txt.spc()
txt.print_bool(sb<p)
txt.spc()
txt.print_bool(sb<=p)
txt.nl()
txt.print(" if stmt: ")
txt.print_b(100)
txt.spc()
if sb>100 txt.print("true ") else txt.print("false ")
if sb>=100 txt.print("true ") else txt.print("false ")
if sb<100 txt.print("true ") else txt.print("false ")
if sb<=100 txt.print("true ") else txt.print("false ")
txt.nl()
txt.print(" ifstmt2: ")
txt.print_b(p)
txt.spc()
if sb>p txt.print("true ") else txt.print("false ")
if sb>=p txt.print("true ") else txt.print("false ")
if sb<p txt.print("true ") else txt.print("false ")
if sb<=p txt.print("true ") else txt.print("false ")
txt.nl()
}
sub bytes100() {
const byte cb = 100
byte @shared sb = 100
byte @shared p = 100
txt.print("\nsigned bytes, 100\n")
txt.print("expected: ")
txt.print_b(100)
txt.spc()
txt.print_bool(cb>100)
txt.spc()
txt.print_bool(cb>=100)
txt.spc()
txt.print_bool(cb<100)
txt.spc()
txt.print_bool(cb<=100)
txt.nl()
txt.print(" calc'd: ")
txt.print_b(100)
txt.spc()
txt.print_bool(sb>100)
txt.spc()
txt.print_bool(sb>=100)
txt.spc()
txt.print_bool(sb<100)
txt.spc()
txt.print_bool(sb<=100)
txt.nl()
txt.print("calc'd 2: ")
txt.print_b(p)
txt.spc()
txt.print_bool(sb>p)
txt.spc()
txt.print_bool(sb>=p)
txt.spc()
txt.print_bool(sb<p)
txt.spc()
txt.print_bool(sb<=p)
txt.nl()
txt.print(" if stmt: ")
txt.print_b(100)
txt.spc()
if sb>100 txt.print("true ") else txt.print("false ")
if sb>=100 txt.print("true ") else txt.print("false ")
if sb<100 txt.print("true ") else txt.print("false ")
if sb<=100 txt.print("true ") else txt.print("false ")
txt.nl()
txt.print(" ifstmt2: ")
txt.print_b(p)
txt.spc()
if sb>p txt.print("true ") else txt.print("false ")
if sb>=p txt.print("true ") else txt.print("false ")
if sb<p txt.print("true ") else txt.print("false ")
if sb<=p txt.print("true ") else txt.print("false ")
txt.nl()
}
sub bytes101() {
const byte cb = 101
byte @shared sb = 101
byte @shared p = 100
txt.print("\nsigned bytes, 101\n")
txt.print("expected: ")
txt.print_b(100)
txt.spc()
txt.print_bool(cb>100)
txt.spc()
txt.print_bool(cb>=100)
txt.spc()
txt.print_bool(cb<100)
txt.spc()
txt.print_bool(cb<=100)
txt.nl()
txt.print(" calc'd: ")
txt.print_b(100)
txt.spc()
txt.print_bool(sb>100)
txt.spc()
txt.print_bool(sb>=100)
txt.spc()
txt.print_bool(sb<100)
txt.spc()
txt.print_bool(sb<=100)
txt.nl()
txt.print("calc'd 2: ")
txt.print_b(p)
txt.spc()
txt.print_bool(sb>p)
txt.spc()
txt.print_bool(sb>=p)
txt.spc()
txt.print_bool(sb<p)
txt.spc()
txt.print_bool(sb<=p)
txt.nl()
txt.print(" if stmt: ")
txt.print_b(100)
txt.spc()
if sb>100 txt.print("true ") else txt.print("false ")
if sb>=100 txt.print("true ") else txt.print("false ")
if sb<100 txt.print("true ") else txt.print("false ")
if sb<=100 txt.print("true ") else txt.print("false ")
txt.nl()
txt.print(" ifstmt2: ")
txt.print_b(p)
txt.spc()
if sb>p txt.print("true ") else txt.print("false ")
if sb>=p txt.print("true ") else txt.print("false ")
if sb<p txt.print("true ") else txt.print("false ")
if sb<=p txt.print("true ") else txt.print("false ")
txt.nl()
}
sub bytesoverflow() {
byte @shared sb = -100
byte @shared p = 100
const byte cb = -100
txt.print("\nsigned bytes, overflow\n")
txt.print("expected: ")
txt.print_b(100)
txt.spc()
txt.print_bool(cb>100)
txt.spc()
txt.print_bool(cb>=100)
txt.spc()
txt.print_bool(cb<100)
txt.spc()
txt.print_bool(cb<=100)
txt.nl()
txt.print(" calc'd: ")
txt.print_b(100)
txt.spc()
txt.print_bool(sb>100)
txt.spc()
txt.print_bool(sb>=100)
txt.spc()
txt.print_bool(sb<100)
txt.spc()
txt.print_bool(sb<=100)
txt.nl()
txt.print("calc'd 2: ")
txt.print_b(p)
txt.spc()
txt.print_bool(sb>p)
txt.spc()
txt.print_bool(sb>=p)
txt.spc()
txt.print_bool(sb<p)
txt.spc()
txt.print_bool(sb<=p)
txt.nl()
txt.print(" if stmt: ")
txt.print_b(100)
txt.spc()
if sb>100 txt.print("true ") else txt.print("false ")
if sb>=100 txt.print("true ") else txt.print("false ")
if sb<100 txt.print("true ") else txt.print("false ")
if sb<=100 txt.print("true ") else txt.print("false ")
txt.nl()
txt.print(" ifstmt2: ")
txt.print_b(p)
txt.spc()
if sb>p txt.print("true ") else txt.print("false ")
if sb>=p txt.print("true ") else txt.print("false ")
if sb<p txt.print("true ") else txt.print("false ")
if sb<=p txt.print("true ") else txt.print("false ")
txt.nl()
}
sub bytessmall() {
byte @shared sb = -10
byte @shared p = 10
const byte cb = -10
txt.print("\nsigned bytes, small value\n")
txt.print("expected: ")
txt.print_b(10)
txt.spc()
txt.print_bool(cb>10)
txt.spc()
txt.print_bool(cb>=10)
txt.spc()
txt.print_bool(cb<10)
txt.spc()
txt.print_bool(cb<=10)
txt.nl()
txt.print(" calc'd: ")
txt.print_b(10)
txt.spc()
txt.print_bool(sb>10)
txt.spc()
txt.print_bool(sb>=10)
txt.spc()
txt.print_bool(sb<10)
txt.spc()
txt.print_bool(sb<=10)
txt.nl()
txt.print("calc'd 2: ")
txt.print_b(p)
txt.spc()
txt.print_bool(sb>p)
txt.spc()
txt.print_bool(sb>=p)
txt.spc()
txt.print_bool(sb<p)
txt.spc()
txt.print_bool(sb<=p)
txt.nl()
txt.print(" if stmt: ")
txt.print_b(10)
txt.spc()
if sb>10 txt.print("true ") else txt.print("false ")
if sb>=10 txt.print("true ") else txt.print("false ")
if sb<10 txt.print("true ") else txt.print("false ")
if sb<=10 txt.print("true ") else txt.print("false ")
txt.nl()
txt.print(" ifstmt2: ")
txt.print_b(p)
txt.spc()
if sb>p txt.print("true ") else txt.print("false ")
if sb>=p txt.print("true ") else txt.print("false ")
if sb<p txt.print("true ") else txt.print("false ")
if sb<=p txt.print("true ") else txt.print("false ")
txt.nl()
}
sub bytesoverflow_jump() {
byte @shared sb = -100
byte @shared p = 100
const byte cb = -100
txt.print("\nsigned bytes, overflow, jmp after if\n")
txt.print("expected: ")
txt.print_b(100)
txt.spc()
txt.print_bool(cb>100)
txt.spc()
txt.print_bool(cb>=100)
txt.spc()
txt.print_bool(cb<100)
txt.spc()
txt.print_bool(cb<=100)
txt.nl()
txt.print(" calc'd: ")
txt.print_b(100)
txt.spc()
if sb>100 goto jump1
else { txt.print("false ") goto next1 }
jump1:
txt.print("true ")
next1:
if sb>=100 goto jump2
else { txt.print("false ") goto next2 }
jump2:
txt.print("true ")
next2:
if sb<100 goto jump3
else { txt.print("false ") goto next3 }
jump3:
txt.print("true ")
next3:
if sb<=100 goto jump4
else { txt.print("false ") goto next4 }
jump4:
txt.print("true ")
next4:
txt.nl()
txt.print("calc'd 2: ")
txt.print_b(p)
txt.spc()
if sb>p goto jump1b
else { txt.print("false ") goto next1b }
jump1b:
txt.print("true ")
next1b:
if sb>=p goto jump2b
else { txt.print("false ") goto next2b }
jump2b:
txt.print("true ")
next2b:
if sb<p goto jump3b
else { txt.print("false ") goto next3b }
jump3b:
txt.print("true ")
next3b:
if sb<=p goto jump4b
else { txt.print("false ") goto next4b }
jump4b:
txt.print("true ")
next4b:
txt.nl()
}
sub bytesoverflow_jump_indirect() {
byte @shared sb = -100
byte @shared p = 100
const byte cb = -100
txt.print("\nsigned bytes, overflow, jmp indirect after if\n")
txt.print("expected: ")
txt.print_b(100)
txt.spc()
txt.print_bool(cb>100)
txt.spc()
txt.print_bool(cb>=100)
txt.spc()
txt.print_bool(cb<100)
txt.spc()
txt.print_bool(cb<=100)
txt.nl()
txt.print(" calc'd: ")
txt.print_b(100)
txt.spc()
uword tgt = &jump1
if sb>100 goto tgt
else { txt.print("false ") goto next1 }
jump1:
txt.print("true ")
next1:
tgt = &jump2
if sb>=100 goto tgt
else { txt.print("false ") goto next2 }
jump2:
txt.print("true ")
next2:
tgt = &jump3
if sb<100 goto tgt
else { txt.print("false ") goto next3 }
jump3:
txt.print("true ")
next3:
tgt = &jump4
if sb<=100 goto tgt
else { txt.print("false ") goto next4 }
jump4:
txt.print("true ")
next4:
txt.nl()
txt.print("calc'd 2: ")
txt.print_b(p)
txt.spc()
tgt = &jump1b
if sb>p goto tgt
else { txt.print("false ") goto next1b }
jump1b:
txt.print("true ")
next1b:
tgt = &jump2b
if sb>=p goto tgt
else { txt.print("false ") goto next2b }
jump2b:
txt.print("true ")
next2b:
tgt = &jump3b
if sb<p goto tgt
else { txt.print("false ") goto next3b }
jump3b:
txt.print("true ")
next3b:
tgt = &jump4b
if sb<=p goto tgt
else { txt.print("false ") goto next4b }
jump4b:
txt.print("true ")
next4b:
txt.nl()
}
sub bytessmall_jump() {
byte @shared sb = -10
byte @shared p = 10
const byte cb = -10
txt.print("\nsigned bytes, small value, jmp after if\n")
txt.print("expected: ")
txt.print_b(10)
txt.spc()
txt.print_bool(cb>10)
txt.spc()
txt.print_bool(cb>=10)
txt.spc()
txt.print_bool(cb<10)
txt.spc()
txt.print_bool(cb<=10)
txt.nl()
txt.print(" calc'd: ")
txt.print_b(10)
txt.spc()
if sb>10 goto jump1
else { txt.print("false ") goto next1 }
jump1:
txt.print("true ")
next1:
if sb>=10 goto jump2
else { txt.print("false ") goto next2 }
jump2:
txt.print("true ")
next2:
if sb<10 goto jump3
else { txt.print("false ") goto next3 }
jump3:
txt.print("true ")
next3:
if sb<=10 goto jump4
else { txt.print("false ") goto next4 }
jump4:
txt.print("true ")
next4:
txt.nl()
txt.print("calc'd 2: ")
txt.print_b(p)
txt.spc()
if sb>p goto jump1b
else { txt.print("false ") goto next1b }
jump1b:
txt.print("true ")
next1b:
if sb>=p goto jump2b
else { txt.print("false ") goto next2b }
jump2b:
txt.print("true ")
next2b:
if sb<p goto jump3b
else { txt.print("false ") goto next3b }
jump3b:
txt.print("true ")
next3b:
if sb<=p goto jump4b
else { txt.print("false ") goto next4b }
jump4b:
txt.print("true ")
next4b:
txt.nl()
}
sub bytessmall_jump_indirect() {
byte @shared sb = -10
byte @shared p = 10
const byte cb = -10
txt.print("\nsigned bytes, small value, jmp indirect after if\n")
txt.print("expected: ")
txt.print_b(10)
txt.spc()
txt.print_bool(cb>10)
txt.spc()
txt.print_bool(cb>=10)
txt.spc()
txt.print_bool(cb<10)
txt.spc()
txt.print_bool(cb<=10)
txt.nl()
txt.print(" calc'd: ")
txt.print_b(10)
txt.spc()
uword tgt = &jump1
if sb>10 goto tgt
else { txt.print("false ") goto next1 }
jump1:
txt.print("true ")
next1:
tgt = &jump2
if sb>=10 goto tgt
else { txt.print("false ") goto next2 }
jump2:
txt.print("true ")
next2:
tgt = &jump3
if sb<10 goto tgt
else { txt.print("false ") goto next3 }
jump3:
txt.print("true ")
next3:
tgt = &jump4
if sb<=10 goto tgt
else { txt.print("false ") goto next4 }
jump4:
txt.print("true ")
next4:
txt.nl()
txt.print("calc'd 2: ")
txt.print_b(p)
txt.spc()
tgt = &jump1b
if sb>p goto tgt
else { txt.print("false ") goto next1b }
jump1b:
txt.print("true ")
next1b:
tgt = &jump2b
if sb>=p goto tgt
else { txt.print("false ") goto next2b }
jump2b:
txt.print("true ")
next2b:
tgt = &jump3b
if sb<p goto tgt
else { txt.print("false ") goto next3b }
jump3b:
txt.print("true ")
next3b:
tgt = &jump4b
if sb<=p goto tgt
else { txt.print("false ") goto next4b }
jump4b:
txt.print("true ")
next4b:
txt.nl()
}
sub words() {
word @shared sbw = -30000
word @shared pw = 30000
const word cbw = -30000
txt.print("\nsigned words\n")
txt.print("expected: ")
txt.print_w(cbw)
txt.spc()
txt.print_bool(cbw>30000)
txt.spc()
txt.print_bool(cbw>=30000)
txt.spc()
txt.print_bool(cbw<30000)
txt.spc()
txt.print_bool(cbw<=30000)
txt.nl()
txt.print(" calc'd: ")
txt.print_w(sbw)
txt.spc()
txt.print_bool(sbw>30000)
txt.spc()
txt.print_bool(sbw>=30000)
txt.spc()
txt.print_bool(sbw<30000)
txt.spc()
txt.print_bool(sbw<=30000)
txt.nl()
txt.print("calc'd 2: ")
txt.print_w(sbw)
txt.spc()
txt.print_bool(sbw>pw)
txt.spc()
txt.print_bool(sbw>=pw)
txt.spc()
txt.print_bool(sbw<pw)
txt.spc()
txt.print_bool(sbw<=pw)
txt.nl()
txt.print(" if stmt: ")
txt.print_w(sbw)
txt.spc()
if sbw>30000 txt.print("true ") else txt.print("false ")
if sbw>=30000 txt.print("true ") else txt.print("false ")
if sbw<30000 txt.print("true ") else txt.print("false ")
if sbw<=30000 txt.print("true ") else txt.print("false ")
txt.nl()
txt.print(" ifstmt2: ")
txt.print_w(sbw)
txt.spc()
if sbw>pw txt.print("true ") else txt.print("false ")
if sbw>=pw txt.print("true ") else txt.print("false ")
if sbw<pw txt.print("true ") else txt.print("false ")
if sbw<=pw txt.print("true ") else txt.print("false ")
txt.nl()
} }
} }

View File

@ -3,4 +3,4 @@ org.gradle.console=rich
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.daemon=true org.gradle.daemon=true
kotlin.code.style=official kotlin.code.style=official
version=11.4-SNAPSHOT version=11.4

View File

@ -52,6 +52,10 @@ class IRSymbolTable {
fun validate() { fun validate() {
require(table.all { it.key == it.value.name }) require(table.all { it.key == it.value.name })
} }
fun removeIfExists(labelSymbol: String) {
table.remove(labelSymbol)
}
} }

View File

@ -1,5 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
id("antlr") id("antlr")
id("java") id("java")
@ -16,7 +14,7 @@ configurations.all {
tasks.generateGrammarSource { tasks.generateGrammarSource {
outputDirectory = file("src/prog8/parser") outputDirectory = file("src/prog8/parser")
arguments.addAll(listOf("-no-listener", "-no-visitor")) arguments.addAll(listOf("-no-listener", "-visitor"))
} }
tasks.compileJava { tasks.compileJava {