diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index 9c2f0f2ae..eb5159ea8 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -220,7 +220,7 @@ private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: programAst.processAstBeforeAsmGeneration(errors) errors.handle() - // printAst(programAst) + printAst(programAst) // TODO CompilationTarget.instance.machine.initializeZeropage(compilerOptions) val assembly = CompilationTarget.instance.asmGenerator( diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index 947c757e4..93d4db5f0 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -194,11 +194,10 @@ internal class AsmGen(private val program: Program, } private fun assignInitialValueToVar(decl: VarDecl, variableName: List) { - val variable = IdentifierReference(variableName, decl.position) - variable.linkParents(decl.parent) + val asmName = asmVariableName(variableName) val asgn = AsmAssignment( - AsmAssignSource.fromAstSource(decl.value!!, program), - AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, decl.datatype, decl.definingSubroutine(), variable = variable), + AsmAssignSource.fromAstSource(decl.value!!, program, this), + AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, decl.datatype, decl.definingSubroutine(), variableAsmName = asmName), false, decl.position) assignmentAsmGen.translateNormalAssignment(asgn) } @@ -496,6 +495,12 @@ internal class AsmGen(private val program: Program, } } + internal fun asmSymbolName(name: String) = fixNameSymbols(name) + internal fun asmVariableName(name: String) = fixNameSymbols(name) + internal fun asmSymbolName(name: Iterable) = fixNameSymbols(name.joinToString(".")) + internal fun asmVariableName(name: Iterable) = fixNameSymbols(name.joinToString(".")) + + internal fun loadByteFromPointerIntoA(pointervar: IdentifierReference): Pair { // returns if the pointer is already on the ZP itself or not (in the latter case SCRATCH_W1 is used as intermediary) val sourceName = asmVariableName(pointervar) @@ -538,8 +543,7 @@ internal class AsmGen(private val program: Program, } } - internal fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names - + private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names private val saveRegisterLabels = Stack() diff --git a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt index 69bb694a8..534f0f0cc 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt @@ -498,7 +498,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val // all other types of swap() calls are done via the evaluation stack fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget { return when (expr) { - is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, expr.definingSubroutine(), variable=expr) + is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, expr.definingSubroutine(), variableAsmName = asmgen.asmVariableName(expr)) is ArrayIndexedExpression -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, datatype, expr.definingSubroutine(), array = expr) is DirectMemoryRead -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, datatype, expr.definingSubroutine(), memory = DirectMemoryWrite(expr.addressExpression, expr.position)) else -> throw AssemblyError("invalid expression object $expr") @@ -509,12 +509,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen.translateExpression(second) val datatype = first.inferType(program).typeOrElse(DataType.STRUCT) val assignFirst = AsmAssignment( - AsmAssignSource(SourceStorageKind.STACK, program, datatype), + AsmAssignSource(SourceStorageKind.STACK, program, asmgen, datatype), targetFromExpr(first, datatype), false, first.position ) val assignSecond = AsmAssignment( - AsmAssignSource(SourceStorageKind.STACK, program, datatype), + AsmAssignSource(SourceStorageKind.STACK, program, asmgen, datatype), targetFromExpr(second, datatype), false, second.position ) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt index 09e06af2f..7156cce00 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt @@ -610,8 +610,8 @@ $endLabel""") } private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) { - val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine(), variable=stmt.loopVar) - val src = AsmAssignSource.fromAstSource(range.from, program).adjustSignedUnsigned(target) + val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine(), variableAsmName=asmgen.asmVariableName(stmt.loopVar)) + val src = AsmAssignSource.fromAstSource(range.from, program, asmgen).adjustSignedUnsigned(target) val assign = AsmAssignment(src, target, false, range.position) asmgen.translateNormalAssignment(assign) } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt index cc98b9e91..5603ef778 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt @@ -155,11 +155,9 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg if(!isArgumentTypeCompatible(valueDt, parameter.value.type)) throw AssemblyError("argument type incompatible") - val scopedParamVar = (sub.scopedname+"."+parameter.value.name).split(".") - val identifier = IdentifierReference(scopedParamVar, sub.position) - identifier.linkParents(value.parent) - val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, sub, variable = identifier) - val source = AsmAssignSource.fromAstSource(value, program).adjustSignedUnsigned(tgt) + val varName = asmgen.asmVariableName(sub.scopedname+"."+parameter.value.name) + val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, sub, variableAsmName = varName) + val source = AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(tgt) val asgn = AsmAssignment(source, tgt, false, Position.DUMMY) asmgen.translateNormalAssignment(asgn) } @@ -234,23 +232,25 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg // via register or register pair val target = AsmAssignTarget.fromRegisters(register!!, sub, program, asmgen) if(requiredDt largerThan valueDt) { - // TODO we need to sign extend the source, do this via stack (slow) - println("warning: slow stack evaluation used for sign-extend: into $requiredDt at ${value.position}") - asmgen.translateExpression(value) - asmgen.signExtendStackLsb(valueDt) - val src = AsmAssignSource(SourceStorageKind.STACK, program, valueDt) + // we need to sign extend the source, do this via temporary word variable + val scratchVar = asmgen.asmVariableName("P8ZP_SCRATCH_W1") + val scratchTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UBYTE, sub, scratchVar) + val source = AsmAssignSource.fromAstSource(value, program, asmgen) + asmgen.translateNormalAssignment(AsmAssignment(source, scratchTarget, false, value.position)) + asmgen.signExtendVariableLsb(scratchVar, valueDt) + val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, DataType.UWORD, scratchVar) asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY)) } else { val src = if(valueDt in PassByReferenceDatatypes) { if(value is IdentifierReference) { val addr = AddressOf(value, Position.DUMMY) - AsmAssignSource.fromAstSource(addr, program).adjustSignedUnsigned(target) + AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target) } else { - AsmAssignSource.fromAstSource(value, program).adjustSignedUnsigned(target) + AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target) } } else { - AsmAssignSource.fromAstSource(value, program).adjustSignedUnsigned(target) + AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target) } asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY)) } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt index a4ba9663a..dcdd5c7a0 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt @@ -31,11 +31,11 @@ internal enum class SourceStorageKind { } internal class AsmAssignTarget(val kind: TargetStorageKind, - program: Program, - asmgen: AsmGen, + private val program: Program, + private val asmgen: AsmGen, val datatype: DataType, val scope: Subroutine?, - val variable: IdentifierReference? = null, + private val variableAsmName: String? = null, val array: ArrayIndexedExpression? = null, val memory: DirectMemoryWrite? = null, val register: RegisterOrPair? = null, @@ -44,19 +44,15 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, { val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0} val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() } - val vardecl by lazy { variable!!.targetVarDecl(program.namespace)!! } - val asmVarname by lazy { - if(variable!=null) - asmgen.asmVariableName(variable) + val asmVarname: String + get() = if(array==null) + variableAsmName!! else - asmgen.asmVariableName(array!!.identifier) - } + asmgen.asmVariableName(array.identifier) lateinit var origAssign: AsmAssignment init { - if(variable!=null && vardecl.type == VarDeclType.CONST) - throw AssemblyError("can't assign to a constant") if(register!=null && datatype !in IntegerDatatypes) throw AssemblyError("register must be integer type") } @@ -65,7 +61,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget = with(assign.target) { val dt = inferType(program, assign).typeOrElse(DataType.STRUCT) when { - identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variable=identifier, origAstTarget = this) + identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this) arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine(), array = arrayindexed, origAstTarget = this) memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, assign.definingSubroutine(), memory = memoryAddress, origAstTarget = this) else -> throw AssemblyError("weird target") @@ -86,8 +82,9 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, internal class AsmAssignSource(val kind: SourceStorageKind, private val program: Program, + private val asmgen: AsmGen, val datatype: DataType, - val variable: IdentifierReference? = null, + private val variableAsmName: String? = null, val array: ArrayIndexedExpression? = null, val memory: DirectMemoryRead? = null, val register: CpuRegister? = null, @@ -97,28 +94,33 @@ internal class AsmAssignSource(val kind: SourceStorageKind, { val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0} val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() } - val vardecl by lazy { variable?.targetVarDecl(program.namespace)!! } + + val asmVarname: String + get() = if(array==null) + variableAsmName!! + else + asmgen.asmVariableName(array.identifier) companion object { - fun fromAstSource(value: Expression, program: Program): AsmAssignSource { + fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource { val cv = value.constValue(program) if(cv!=null) - return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, cv.type, number = cv) + return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv) return when(value) { - is NumericLiteralValue -> AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, value.type, number = cv) + is NumericLiteralValue -> AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, value.type, number = cv) is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation") is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation") is IdentifierReference -> { val dt = value.inferType(program).typeOrElse(DataType.STRUCT) - AsmAssignSource(SourceStorageKind.VARIABLE, program, dt, variable = value) + AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = asmgen.asmVariableName(value)) } is DirectMemoryRead -> { - AsmAssignSource(SourceStorageKind.MEMORY, program, DataType.UBYTE, memory = value) + AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value) } is ArrayIndexedExpression -> { val dt = value.inferType(program).typeOrElse(DataType.STRUCT) - AsmAssignSource(SourceStorageKind.ARRAY, program, dt, array = value) + AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value) } else -> { if(value is FunctionCall) { @@ -138,7 +140,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind, RegisterOrPair.AY, RegisterOrPair.XY -> DataType.UWORD } - return AsmAssignSource(SourceStorageKind.EXPRESSION, program, dt, expression = value) + return AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt, expression = value) } else -> throw AssemblyError("can't translate multiple return values in assignment") } @@ -146,24 +148,14 @@ internal class AsmAssignSource(val kind: SourceStorageKind, } val dt = value.inferType(program).typeOrElse(DataType.STRUCT) - return AsmAssignSource(SourceStorageKind.EXPRESSION, program, dt, expression = value) + return AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt, expression = value) } } } } - fun getAstValue(): Expression = when(kind) { - SourceStorageKind.LITERALNUMBER -> number!! - SourceStorageKind.VARIABLE -> variable!! - SourceStorageKind.ARRAY -> array!! - SourceStorageKind.MEMORY -> memory!! - SourceStorageKind.EXPRESSION -> expression!! - SourceStorageKind.REGISTER -> throw AssemblyError("cannot get a register source as Ast node") - SourceStorageKind.STACK -> throw AssemblyError("cannot get a stack source as Ast node") - } - fun withAdjustedDt(newType: DataType) = - AsmAssignSource(kind, program, newType, variable, array, memory, register, number, expression) + AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, expression) fun adjustSignedUnsigned(target: AsmAssignTarget): AsmAssignSource { // allow some signed/unsigned relaxations diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt index 26dc5422e..fcab2f0f9 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt @@ -18,7 +18,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen fun translate(assignment: Assignment) { val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen) - val source = AsmAssignSource.fromAstSource(assignment.value, program).adjustSignedUnsigned(target) + val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target) val assign = AsmAssignment(source, target, assignment.isAugmentable, assignment.position) target.origAssign = assign @@ -43,13 +43,16 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } SourceStorageKind.VARIABLE -> { // simple case: assign from another variable - val variable = assign.source.variable!! + val variable = assign.source.asmVarname!! when (assign.target.datatype) { DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable) DataType.UWORD, DataType.WORD -> assignVariableWord(assign.target, variable) DataType.FLOAT -> assignVariableFloat(assign.target, variable) DataType.STR -> assignVariableString(assign.target, variable) - in PassByReferenceDatatypes -> assignAddressOf(assign.target, variable) + in PassByReferenceDatatypes -> { + // TODO what about when the name is a struct? name.firstStructVarName(program.namespace) + assignAddressOf(assign.target, variable) + } else -> throw AssemblyError("unsupported assignment target type ${assign.target.datatype}") } } @@ -117,7 +120,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen SourceStorageKind.EXPRESSION -> { val value = assign.source.expression!! when(value) { - is AddressOf -> assignAddressOf(assign.target, value.identifier) + is AddressOf -> { + val sourceName = value.identifier.firstStructVarName(program.namespace) ?: asmgen.asmVariableName(value.identifier) + assignAddressOf(assign.target, sourceName) + } is NumericLiteralValue -> throw AssemblyError("source kind should have been literalnumber") is IdentifierReference -> throw AssemblyError("source kind should have been variable") is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array") @@ -349,9 +355,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } } - private fun assignAddressOf(target: AsmAssignTarget, name: IdentifierReference) { - val sourceName = name.firstStructVarName(program.namespace) ?: asmgen.fixNameSymbols(name.nameInSource.joinToString(".")) - + private fun assignAddressOf(target: AsmAssignTarget, sourceName: String) { when(target.kind) { TargetStorageKind.VARIABLE -> { asmgen.out(""" @@ -376,19 +380,17 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } } TargetStorageKind.STACK -> { - val srcname = asmgen.asmVariableName(name) asmgen.out(""" - lda #<$srcname + lda #<$sourceName sta P8ESTACK_LO,x - lda #>$srcname + lda #>$sourceName sta P8ESTACK_HI,x dex""") } } } - private fun assignVariableString(target: AsmAssignTarget, variable: IdentifierReference) { - val sourceName = asmgen.asmVariableName(variable) + private fun assignVariableString(target: AsmAssignTarget, sourceName: String) { when(target.kind) { TargetStorageKind.VARIABLE -> { when(target.datatype) { @@ -425,8 +427,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } } - private fun assignVariableWord(target: AsmAssignTarget, variable: IdentifierReference) { - val sourceName = asmgen.asmVariableName(variable) + private fun assignVariableWord(target: AsmAssignTarget, sourceName: String) { when(target.kind) { TargetStorageKind.VARIABLE -> { asmgen.out(""" @@ -529,8 +530,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } } - private fun assignVariableFloat(target: AsmAssignTarget, variable: IdentifierReference) { - val sourceName = asmgen.asmVariableName(variable) + private fun assignVariableFloat(target: AsmAssignTarget, sourceName: String) { when(target.kind) { TargetStorageKind.VARIABLE -> { asmgen.out(""" @@ -564,8 +564,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } } - private fun assignVariableByte(target: AsmAssignTarget, variable: IdentifierReference) { - val sourceName = asmgen.asmVariableName(variable) + private fun assignVariableByte(target: AsmAssignTarget, sourceName: String) { when(target.kind) { TargetStorageKind.VARIABLE -> { asmgen.out(""" diff --git a/examples/mandelbrot-gfx.p8 b/examples/mandelbrot-gfx.p8 index b83f07f7b..0063bb4ce 100644 --- a/examples/mandelbrot-gfx.p8 +++ b/examples/mandelbrot-gfx.p8 @@ -7,16 +7,17 @@ ; even in Vice in warp mode (700% speed on my machine) it's slow, but you can see progress ; Note: this program is compatible with C64 and CX16. +; TODO why is there a weird black column on the right part on CX16? main { - const ubyte width = 255 + const uword width = 320 const ubyte height = 200 const ubyte max_iter = 16 sub start() { graphics.enable_bitmap_mode() - ubyte pixelx + uword pixelx ubyte pixely for pixely in 0 to height-1 { diff --git a/examples/test.p8 b/examples/test.p8 index 2a5033247..d166df350 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,29 +7,9 @@ main { sub start() { - print_10s(1) - txt.chrout('\n') - print_10s(123) - txt.chrout('\n') - print_10s(54321) - txt.chrout('\n') - txt.chrout('\n') - - ubyte ww - ww=1 - print_10s(ww) - txt.chrout('\n') - ww=123 - print_10s(ww) - txt.chrout('\n') - ww=255 - print_10s(ww) - txt.chrout('\n') - - txt.print("\nCommands are:\n"+ - "buy jump inf cash\n" + - "sell teleport market hold\n" + - "fuel galhyp local quit\n") + ubyte xx + ubyte yy + regx(xx) ; str name = "irmen de jong" ; uword strptr = &name @@ -49,6 +29,14 @@ main { } + asmsub regx(uword value @AX) { + %asm {{ + + nop + rts + }} + } + asmsub print_10s(uword value @AY) clobbers(A, X, Y) { %asm {{ jsr conv.uword2decimal diff --git a/examples/textelite.p8 b/examples/textelite.p8 index c76be15a2..91c960b60 100644 --- a/examples/textelite.p8 +++ b/examples/textelite.p8 @@ -25,6 +25,8 @@ main { repeat { str input = "????????" + txt.print("\nCash: ") + util.print_10s(ship.cash) txt.print("\nCommand (?=help): ") ubyte num_chars = txt.input_chars(input) txt.chrout('\n') @@ -59,19 +61,19 @@ trader { ubyte num_chars sub do_jump() { - txt.print("\nTODO JUMP\n") + txt.print("\nTODO JUMP\n") ; TODO } sub do_teleport() { - txt.print("\nTODO TELEPORT\n") + txt.print("\nTODO TELEPORT\n") ; TODO } sub do_buy() { - txt.print("\nTODO BUY\n") + txt.print("\nTODO BUY\n") ; TODO } sub do_sell() { - txt.print("\nTODO SELL\n") + txt.print("\nTODO SELL\n") ; TODO } sub do_fuel() { @@ -88,7 +90,7 @@ trader { } sub do_hold() { - txt.print("\nCheat! TODO adjust cargo hold size\n") + txt.print("\nCheat! TODO adjust cargo hold size\n") ; TODO } sub do_next_galaxy() { @@ -101,7 +103,7 @@ trader { txt.print("\nSystem name (empty=current): ") num_chars = txt.input_chars(input) if num_chars { - txt.print("\nTODO INFO\n") + txt.print("\nTODO INFO\n") ; TODO } else { planet.display(false) }