fix symbol prefixing on goto with expression

added coroutines example
This commit is contained in:
Irmen de Jong 2024-12-17 12:52:01 +01:00
parent 8ea032ed66
commit 7c79cdbd2f
11 changed files with 149 additions and 43 deletions

View File

@ -116,10 +116,9 @@ class PtIfElse(position: Position) : PtNode(position) {
}
class PtJump(val target: PtExpression, position: Position) : PtNode(position) {
init {
target.parent = this
}
class PtJump(position: Position) : PtNode(position) {
val target: PtExpression
get() = children.single() as PtExpression
}

View File

@ -73,16 +73,6 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
nodesToPrefix += node.parent to index
}
}
is PtJump -> {
val identifier = node.target as? PtIdentifier
if(identifier!=null) {
val stNode = st.lookup(identifier.name) ?: throw AssemblyError("name not found $identifier")
if (stNode.astNode.definingBlock()?.options?.noSymbolPrefixing != true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
}
}
is PtBlock -> prefixNamedNode(node)
is PtConstant -> prefixNamedNode(node)
is PtLabel -> prefixNamedNode(node)
@ -107,7 +97,6 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
when(val node = parent.children[index]) {
is PtIdentifier -> parent.children[index] = node.prefix(parent, st)
is PtFunctionCall -> throw AssemblyError("PtFunctionCall should be processed in their own list, last")
is PtJump -> parent.children[index] = node.prefix(parent, st)
is PtVariable -> parent.children[index] = node.prefix(parent, st)
else -> throw AssemblyError("weird node to prefix $node")
}
@ -177,17 +166,6 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
else this
}
private fun PtJump.prefix(parent: PtNode, st: SymbolTable): PtJump {
val identifier = target as? PtIdentifier
if(identifier!=null) {
val prefixedIdent = identifier.prefix(this, st)
val jump = PtJump(prefixedIdent, position)
jump.parent = parent
return jump
}
return this
}
private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall {
val newName = prefixScopedName(name, 's')
val call = PtFunctionCall(newName, void, type, position)

View File

@ -1660,6 +1660,7 @@ class IRCodeGen(
IRInstruction(Opcode.JUMP, labelSymbol = identifier.name)
}
} else {
// val tr = expressionEval.translateExpression(jump.target)
TODO("JUMP to expression address ${jump.target}")
}
}

View File

@ -312,7 +312,7 @@ class TestVmCodeGen: FunSpec({
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY), Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
if1.add(PtNodeGroup())
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
@ -320,7 +320,7 @@ class TestVmCodeGen: FunSpec({
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY), Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
if2.add(PtNodeGroup())
sub.add(if2)
block.add(sub)
@ -505,7 +505,7 @@ class TestVmCodeGen: FunSpec({
cmp1.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY), Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
if1.add(PtNodeGroup())
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
@ -513,7 +513,7 @@ class TestVmCodeGen: FunSpec({
cmp2.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY), Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also {it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
if2.add(PtNodeGroup())
sub.add(if2)
block.add(sub)

View File

@ -102,7 +102,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
inline
}
is Jump -> true
is Jump -> stmt.target is NumericLiteral || stmt.target is IdentifierReference
else -> false
}
}

View File

@ -378,7 +378,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
val scopedEndLabel = (srcIf.definingScope.scopedName + endLabel).joinToString(".")
val elseLbl = PtIdentifier(scopedElseLabel, DataType.forDt(BaseDataType.UNDEFINED), srcIf.position)
val endLbl = PtIdentifier(scopedEndLabel, DataType.forDt(BaseDataType.UNDEFINED), srcIf.position)
ifScope.add(PtJump(elseLbl, srcIf.position))
ifScope.add(PtJump(srcIf.position).also { it.add(elseLbl) })
val elseScope = PtNodeGroup()
branch.add(ifScope)
branch.add(elseScope)
@ -386,7 +386,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
for (stmt in srcIf.truepart.statements)
nodes.add(transformStatement(stmt))
if(srcIf.elsepart.isNotEmpty())
nodes.add(PtJump(endLbl, srcIf.position))
nodes.add(PtJump(srcIf.position).also { it.add(endLbl) })
nodes.add(PtLabel(elseLabel, srcIf.position))
if(srcIf.elsepart.isNotEmpty()) {
for (stmt in srcIf.elsepart.statements)
@ -451,7 +451,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
private fun transform(srcJump: Jump): PtJump {
val target = transformExpression(srcJump.target)
return PtJump(target, srcJump.position)
return PtJump(srcJump.position).also { it.add(target) }
}
private fun transform(label: Label): PtLabel =

View File

@ -189,7 +189,9 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
val skiplabel = "prog8_defer_skip_${idx+1}"
val branchcc = PtConditionalBranch(BranchCondition.CC, Position.DUMMY)
branchcc.add(PtNodeGroup().also {
it.add(PtJump(PtIdentifier(defersRoutine.scopedName+"."+skiplabel, DataType.forDt(BaseDataType.UBYTE), Position.DUMMY), Position.DUMMY))
val jump = PtJump(Position.DUMMY)
jump.add(PtIdentifier(defersRoutine.scopedName+"."+skiplabel, DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
it.add(jump)
})
branchcc.add(PtNodeGroup())
defersRoutine.add(branchcc)

View File

@ -171,6 +171,7 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
listOf(
"animals",
"balls",
"coroutines",
"cube3d",
"cube3d-float",
"cube3d-gfx",

View File

@ -1,10 +1,6 @@
TODO
====
- FIX: uword[] @nosplit tasklist = [&start-1] "initialisation value has incompatible type"
- FIX: goto pointers[4]-1
- DONE: make word arrays split by default and add new @nosplit tag to make an array use the old linear storage format
- DONE: &splitarray will give you the start address of the lsb-array (which is immediately followed by the msb-array)
- DONE: add &< and &> operators to get the address of the lsb-array and msb-array, respectively. (&< is just syntactic sugar for &)
@ -58,7 +54,7 @@ IR/VM
- fix call() return value handling
- fix float register parameters (FAC1,FAC2) for extsubs, search for TODO("floating point register parameters not supported")
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
- make it possible to jump and branch to a computed address (expression), see TODO("JUMP to expression address
- make it possible to jump and branch to a computed address (expression), see TODO("JUMP to expression address" . This needs a change in the JUMPI instruction: it has to take a register instead (like CALLI)
- idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
global initialization values are simply a list of LOAD instructions.
Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings.

128
examples/coroutines.p8 Normal file
View File

@ -0,0 +1,128 @@
; Cooperative multitasking / Coroutines
; Uses stack return address juggling to cycle between the different tasks when they call yield().
%import textio
main {
sub start() {
txt.print("cooperative multitasking / coroutines")
coroutines.start()
}
}
coroutines {
uword[] tasklist = [&task1, &task2, &task3, &task4, &vsynctask]
sub start() {
goto tasklist[0]
}
uword[len(tasklist)] returnaddresses
ubyte active_task
sub yield() {
; store the return address of the yielding task,
; and continue with the next one instead (round-robin)
returnaddresses[active_task] = sys.popw()
active_task++
if active_task==len(returnaddresses)
active_task=0
cx16.r0 = returnaddresses[active_task]
if cx16.r0==0 {
; fetch start address of next task.
; address on the stack must be pushed in reverse byte order
; also, subtract 1 from the start address because JSR pushes returnaddress minus 1
cx16.r0 = tasklist[active_task]-1
sys.push(cx16.r0H)
sys.push(cx16.r0L)
} else
sys.pushw(cx16.r0)
; returning from yield then continues with the next coroutine
}
sub task1() {
const ubyte x = 5
ubyte y
repeat {
for y in 10 to 24 {
txt.setchr(x, y-1, sc:' ')
txt.setchr(x, y, sc:'1')
yield()
}
for y in 24 downto 10 {
txt.setchr(x, y+1, sc:' ')
txt.setchr(x, y, sc:'1')
yield()
}
}
; need infinite loop
}
sub task2() {
const ubyte x = 10
ubyte y
repeat {
for y in 5 to 18 {
txt.setchr(x, y-1, sc:' ')
txt.setchr(x, y, sc:'2')
yield()
}
for y in 18 downto 5 {
txt.setchr(x, y+1, sc:' ')
txt.setchr(x, y, sc:'2')
yield()
}
}
; need infinite loop
}
sub task3() {
ubyte x
const ubyte y = 10
repeat {
for x in 14 to 38 {
txt.setchr(x-1, y, sc:' ')
txt.setchr(x, y, sc:'3')
yield()
}
for x in 38 downto 14 {
txt.setchr(x+1, y, sc:' ')
txt.setchr(x, y, sc:'3')
yield()
}
}
; need infinite loop
}
sub task4() {
ubyte x
const ubyte y = 14
repeat {
for x in 15 to 30 {
txt.setchr(x-1, y, sc:' ')
txt.setchr(x, y, sc:'4')
yield()
}
for x in 30 downto 15 {
txt.setchr(x+1, y, sc:' ')
txt.setchr(x, y, sc:'4')
yield()
}
}
; need infinite loop
}
sub vsynctask() {
repeat {
sys.waitvsync()
sys.waitvsync()
yield()
}
; need infinite loop
}
}

View File

@ -1,7 +1,8 @@
main {
sub start() {
uword[] @nosplit tasklist = [&start-1]
; uword task_address = tasklist[0]
; goto task_address
uword[] tasklist = [1111,2222,3333]
uword task_address = tasklist[0]
goto task_address
goto task_address+1
}
}