mirror of
https://github.com/irmen/prog8.git
synced 2025-02-20 03:29:01 +00:00
reverted subroutine inlining, it was a mistake
This commit is contained in:
parent
4bfdbad2e4
commit
3050156325
@ -1 +1 @@
|
|||||||
2.3
|
2.4-SNAPSHOT
|
||||||
|
@ -25,12 +25,6 @@ internal fun Program.reorderStatements() {
|
|||||||
reorder.applyModifications()
|
reorder.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.inlineSubroutines(): Int {
|
|
||||||
val reorder = SubroutineInliner(this)
|
|
||||||
reorder.visit(this)
|
|
||||||
return reorder.applyModifications()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Program.addTypecasts(errors: ErrorReporter) {
|
internal fun Program.addTypecasts(errors: ErrorReporter) {
|
||||||
val caster = TypecastsAdder(this, errors)
|
val caster = TypecastsAdder(this, errors)
|
||||||
caster.visit(this)
|
caster.visit(this)
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
package prog8.ast.processing
|
|
||||||
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.statements.*
|
|
||||||
import prog8.optimizer.CallGraph
|
|
||||||
|
|
||||||
|
|
||||||
internal class SubroutineInliner(private val program: Program) : AstWalker() {
|
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
private val callgraph = CallGraph(program)
|
|
||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
|
||||||
|
|
||||||
if(!subroutine.isAsmSubroutine && callgraph.calledBy[subroutine]!=null && subroutine.containsCodeOrVars()) {
|
|
||||||
|
|
||||||
// TODO for now, inlined subroutines can't have parameters or local variables - improve this
|
|
||||||
if(subroutine.parameters.isEmpty() && subroutine.containsNoVars()) {
|
|
||||||
if (subroutine.countStatements() <= 5) {
|
|
||||||
if (callgraph.calledBy.getValue(subroutine).size == 1 || !subroutine.statements.any { it.expensiveToInline })
|
|
||||||
return inline(subroutine)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun inline(subroutine: Subroutine): Iterable<IAstModification> {
|
|
||||||
val calls = callgraph.calledBy.getValue(subroutine)
|
|
||||||
return calls.map {
|
|
||||||
call -> IAstModification.ReplaceNode(
|
|
||||||
call,
|
|
||||||
AnonymousScope(subroutine.statements, call.position),
|
|
||||||
call.parent
|
|
||||||
)
|
|
||||||
}.plus(IAstModification.Remove(subroutine, subroutine.parent))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -29,8 +29,6 @@ sealed class Statement : Node {
|
|||||||
return scope.joinToString(".")
|
return scope.joinToString(".")
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract val expensiveToInline: Boolean
|
|
||||||
|
|
||||||
fun definingBlock(): Block {
|
fun definingBlock(): Block {
|
||||||
if(this is Block)
|
if(this is Block)
|
||||||
return this
|
return this
|
||||||
@ -48,7 +46,6 @@ class BuiltinFunctionStatementPlaceholder(val name: String, override val positio
|
|||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
override val expensiveToInline = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean)
|
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean)
|
||||||
@ -59,8 +56,6 @@ class Block(override val name: String,
|
|||||||
val isInLibrary: Boolean,
|
val isInLibrary: Boolean,
|
||||||
override val position: Position) : Statement(), INameScope {
|
override val position: Position) : Statement(), INameScope {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = statements.any { it.expensiveToInline }
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -86,7 +81,6 @@ class Block(override val name: String,
|
|||||||
|
|
||||||
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : Statement() {
|
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -109,7 +103,6 @@ data class DirectiveArg(val str: String?, val name: String?, val int: Int?, over
|
|||||||
|
|
||||||
data class Label(val name: String, override val position: Position) : Statement() {
|
data class Label(val name: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -126,7 +119,6 @@ data class Label(val name: String, override val position: Position) : Statement(
|
|||||||
|
|
||||||
open class Return(var value: Expression?, override val position: Position) : Statement() {
|
open class Return(var value: Expression?, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = value!=null && value !is NumericLiteralValue
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -158,7 +150,6 @@ class ReturnFromIrq(override val position: Position) : Return(null, position) {
|
|||||||
|
|
||||||
class Continue(override val position: Position) : Statement() {
|
class Continue(override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent=parent
|
this.parent=parent
|
||||||
@ -171,7 +162,6 @@ class Continue(override val position: Position) : Statement() {
|
|||||||
|
|
||||||
class Break(override val position: Position) : Statement() {
|
class Break(override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent=parent
|
this.parent=parent
|
||||||
@ -207,9 +197,6 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
var structHasBeenFlattened = false // set later
|
var structHasBeenFlattened = false // set later
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override val expensiveToInline
|
|
||||||
get() = value!=null && value !is NumericLiteralValue
|
|
||||||
|
|
||||||
// prefix for literal values that are turned into a variable on the heap
|
// prefix for literal values that are turned into a variable on the heap
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -339,8 +326,6 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
|
|||||||
|
|
||||||
open class Assignment(var target: AssignTarget, var aug_op : String?, var value: Expression, override val position: Position) : Statement() {
|
open class Assignment(var target: AssignTarget, var aug_op : String?, var value: Expression, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = value is BinaryExpression
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -509,7 +494,6 @@ data class AssignTarget(val register: Register?,
|
|||||||
|
|
||||||
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() {
|
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -535,7 +519,6 @@ class Jump(val address: Int?,
|
|||||||
val generatedLabel: String?, // used in code generation scenarios
|
val generatedLabel: String?, // used in code generation scenarios
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -556,8 +539,6 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
val void: Boolean,
|
val void: Boolean,
|
||||||
override val position: Position) : Statement(), IFunctionCall {
|
override val position: Position) : Statement(), IFunctionCall {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = args.any { it !is NumericLiteralValue }
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -585,7 +566,6 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
|
|
||||||
class InlineAssembly(val assembly: String, override val position: Position) : Statement() {
|
class InlineAssembly(val assembly: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -600,8 +580,6 @@ class AnonymousScope(override var statements: MutableList<Statement>,
|
|||||||
override val position: Position) : INameScope, Statement() {
|
override val position: Position) : INameScope, Statement() {
|
||||||
override val name: String
|
override val name: String
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = statements.any { it.expensiveToInline }
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var sequenceNumber = 1
|
private var sequenceNumber = 1
|
||||||
@ -630,7 +608,6 @@ class AnonymousScope(override var statements: MutableList<Statement>,
|
|||||||
|
|
||||||
class NopStatement(override val position: Position): Statement() {
|
class NopStatement(override val position: Position): Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -664,11 +641,7 @@ class Subroutine(override val name: String,
|
|||||||
override val position: Position) : Statement(), INameScope {
|
override val position: Position) : Statement(), INameScope {
|
||||||
|
|
||||||
var keepAlways: Boolean = false
|
var keepAlways: Boolean = false
|
||||||
override val expensiveToInline
|
|
||||||
get() = statements.any { it.expensiveToInline }
|
|
||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
val scopedname: String by lazy { makeScopedName(name) }
|
val scopedname: String by lazy { makeScopedName(name) }
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -746,8 +719,6 @@ class IfStatement(var condition: Expression,
|
|||||||
var elsepart: AnonymousScope,
|
var elsepart: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean
|
|
||||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -776,8 +747,6 @@ class BranchStatement(var condition: BranchCondition,
|
|||||||
var elsepart: AnonymousScope,
|
var elsepart: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean
|
|
||||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -805,7 +774,6 @@ class ForLoop(val loopRegister: Register?,
|
|||||||
var body: AnonymousScope,
|
var body: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent=parent
|
this.parent=parent
|
||||||
@ -842,7 +810,6 @@ class WhileLoop(var condition: Expression,
|
|||||||
var body: AnonymousScope,
|
var body: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -865,7 +832,6 @@ class WhileLoop(var condition: Expression,
|
|||||||
|
|
||||||
class ForeverLoop(var body: AnonymousScope, override val position: Position) : Statement() {
|
class ForeverLoop(var body: AnonymousScope, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -886,7 +852,6 @@ class RepeatLoop(var body: AnonymousScope,
|
|||||||
var untilCondition: Expression,
|
var untilCondition: Expression,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -911,7 +876,6 @@ class WhenStatement(var condition: Expression,
|
|||||||
var choices: MutableList<WhenChoice>,
|
var choices: MutableList<WhenChoice>,
|
||||||
override val position: Position): Statement() {
|
override val position: Position): Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -981,7 +945,6 @@ class StructDecl(override val name: String,
|
|||||||
override val position: Position): Statement(), INameScope {
|
override val position: Position): Statement(), INameScope {
|
||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
|
@ -163,10 +163,9 @@ private fun optimizeAst(programAst: Program, errors: ErrorReporter) {
|
|||||||
// keep optimizing expressions and statements until no more steps remain
|
// keep optimizing expressions and statements until no more steps remain
|
||||||
val optsDone1 = programAst.simplifyExpressions()
|
val optsDone1 = programAst.simplifyExpressions()
|
||||||
val optsDone2 = programAst.optimizeStatements(errors)
|
val optsDone2 = programAst.optimizeStatements(errors)
|
||||||
val optsDone3 = programAst.inlineSubroutines()
|
|
||||||
programAst.constantFold(errors) // because simplified statements and expressions could give rise to more constants that can be folded away:
|
programAst.constantFold(errors) // because simplified statements and expressions could give rise to more constants that can be folded away:
|
||||||
errors.handle()
|
errors.handle()
|
||||||
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
if (optsDone1 + optsDone2 == 0)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,13 +20,13 @@ Add more compiler optimizations to the existing ones.
|
|||||||
- more targeted optimizations for assigment asm code, such as the following:
|
- more targeted optimizations for assigment asm code, such as the following:
|
||||||
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise.
|
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise.
|
||||||
- remove unreachable code after an exit(), return or goto
|
- remove unreachable code after an exit(), return or goto
|
||||||
- working subroutine inlining (start with trivial routines, grow to taking care of vars and identifier refs to them)
|
|
||||||
- add a compiler option to not include variable initialization code (useful if the program is expected to run only once, such as a game)
|
- add a compiler option to not include variable initialization code (useful if the program is expected to run only once, such as a game)
|
||||||
the program will then rely solely on the values as they are in memory at the time of program startup.
|
the program will then rely solely on the values as they are in memory at the time of program startup.
|
||||||
- Also some library routines and code patterns could perhaps be optimized further
|
- Also some library routines and code patterns could perhaps be optimized further
|
||||||
- can the parameter passing to subroutines be optimized to avoid copying?
|
- can the parameter passing to subroutines be optimized to avoid copying?
|
||||||
- more optimizations on the language AST level
|
- more optimizations on the language AST level
|
||||||
- more optimizations on the final assembly source level
|
- more optimizations on the final assembly source level
|
||||||
|
- note: abandoned subroutine inlining because of problems referencing non-local stuff. Can't move everything around.
|
||||||
|
|
||||||
|
|
||||||
Eval stack redesign? (lot of work)
|
Eval stack redesign? (lot of work)
|
||||||
|
@ -12,36 +12,25 @@
|
|||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
function(20, calculate())
|
turtle.pu()
|
||||||
asmfunction(20, calculate())
|
turtle.pu()
|
||||||
|
turtle.pu()
|
||||||
c64.CHROUT('\n')
|
turtle.pu()
|
||||||
|
turtle.pu()
|
||||||
if @($0400)==@($0402) and @($0401) == @($0403) {
|
turtle.pu()
|
||||||
c64scr.print("ok: results are same\n")
|
turtle.pu()
|
||||||
} else {
|
turtle.pu()
|
||||||
c64scr.print("error: result differ; arg got clobbered\n")
|
turtle.pu()
|
||||||
}
|
turtle.pu()
|
||||||
}
|
|
||||||
|
|
||||||
sub function(ubyte a1, ubyte a2) {
|
|
||||||
; non-asm function passes via stack, this is ok
|
|
||||||
@($0400) = a1
|
|
||||||
@($0401) = a2
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub asmfunction(ubyte a1 @ Y, ubyte a2 @ A) {
|
|
||||||
; asm-function passes via registers, risk of clobbering
|
|
||||||
%asm {{
|
|
||||||
sty $0402
|
|
||||||
sta $0403
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub calculate() -> ubyte {
|
|
||||||
Y = 99
|
|
||||||
return Y
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
turtle {
|
||||||
|
ubyte pendown
|
||||||
|
|
||||||
|
sub pu() {
|
||||||
|
pendown = false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -43,26 +43,32 @@ turtle {
|
|||||||
c64.SPENA = 1
|
c64.SPENA = 1
|
||||||
c64.SP0COL = 5
|
c64.SP0COL = 5
|
||||||
|
|
||||||
turtlepos()
|
update_turtle_sprite()
|
||||||
}
|
}
|
||||||
|
|
||||||
sub turtlepos() {
|
sub update_turtle_sprite() {
|
||||||
uword xx = xpos as uword
|
uword xx = xpos as uword
|
||||||
c64.SPXY[0] = lsb(xx) + 12
|
c64.SPXY[0] = lsb(xx) + 12
|
||||||
if msb(xx)
|
c64.MSIGX = msb(xx) > 0
|
||||||
c64.MSIGX = 1
|
|
||||||
else
|
|
||||||
c64.MSIGX = 0
|
|
||||||
c64.SPXY[1] = lsb(ypos) + 40
|
c64.SPXY[1] = lsb(ypos) + 40
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub pos(float x, float y) {
|
||||||
|
if pendown {
|
||||||
|
graphics.line(xpos as uword, ypos as ubyte, x as uword, y as ubyte)
|
||||||
|
}
|
||||||
|
xpos = x
|
||||||
|
ypos = y
|
||||||
|
update_turtle_sprite()
|
||||||
|
}
|
||||||
|
|
||||||
sub fd(uword length) {
|
sub fd(uword length) {
|
||||||
float flen = length as float
|
float flen = length as float
|
||||||
float sx = xpos
|
float sx = xpos
|
||||||
float sy = ypos
|
float sy = ypos
|
||||||
xpos += flen * sin(angle)
|
xpos += flen * sin(angle)
|
||||||
ypos -= flen * cos(angle)
|
ypos -= flen * cos(angle)
|
||||||
turtlepos()
|
update_turtle_sprite()
|
||||||
if pendown {
|
if pendown {
|
||||||
graphics.line(sx as uword, lsb(sy), xpos as uword, lsb(ypos))
|
graphics.line(sx as uword, lsb(sy), xpos as uword, lsb(ypos))
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user