mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Merge branch 'master' into new-codegens
# Conflicts: # codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/PointerAssignmentsGen.kt # compiler/test/TestPointers.kt # docs/source/todo.rst # examples/test.p8
This commit is contained in:
commit
4b320013cf
11 files changed
+323
-118
No files matched your search
@@ -16,7 +16,7 @@ which aims to provide many conveniences over raw assembly code (even when using
|
||||
|
||||
**Want to buy me a coffee or a pizza perhaps?**
|
||||
|
||||
This project was created over the last couple of years by dedicating a huge amoung of my free time to it, to make it the best I possibly can.
|
||||
This project was created over the last couple of years by dedicating a huge amount of my free time to it, to make it the best I possibly can.
|
||||
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
|
||||
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen) or [PayPal](https://paypal.me/irmendejong)
|
||||
|
||||
@@ -95,7 +95,7 @@ What does Prog8 provide?
|
||||
*Rapid edit-compile-run-debug cycle:*
|
||||
|
||||
- use a modern PC to do the work on, use nice editors and enjoy quick compilation times
|
||||
- can automatically run the program in the Vice emulator after succesful compilation
|
||||
- can automatically run the program in the Vice emulator after successful compilation
|
||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||
|
||||
|
||||
@@ -5945,6 +5945,7 @@ $endLabel""")
|
||||
eor #255
|
||||
sta ${target.asmVarname}+3""")
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceLongInvert(PtrTarget(target))
|
||||
else -> TODO("LONG INVERT ${target.kind} ${target.position}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,79 +114,117 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
|
||||
internal fun inplaceByteInvert(target: PtrTarget) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
eor #255
|
||||
sta ($zpPtrVar),y""")
|
||||
val eorVal = if(target.dt.isBool) 1 else 255
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
asmgen.out(" lda ($zpPtrVar) | eor #$eorVal | sta ($zpPtrVar)")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
eor #$eorVal
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun inplaceWordInvert(target: PtrTarget) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
eor #255
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
eor #255
|
||||
sta ($zpPtrVar),y""")
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
eor #255
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
eor #255
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
internal fun inplaceLongInvert(target: PtrTarget) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
eor #255
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
eor #255
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
eor #255
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
eor #255
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
internal fun inplaceByteNegate(target: PtrTarget, scope: IPtSubroutine?) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda #0
|
||||
sec
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
eor #255
|
||||
ina
|
||||
sta ($zpPtrVar),y""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
eor #255
|
||||
clc
|
||||
adc #1
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun inplaceWordNegate(target: PtrTarget, scope: IPtSubroutine?) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda #0
|
||||
sec
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda #0
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
ldy #$offset
|
||||
sec
|
||||
lda #0
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda #0
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
internal fun inplaceLongNegate(target: PtrTarget, scope: IPtSubroutine?) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda #0
|
||||
sec
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda #0
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda #0
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda #0
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
ldy #$offset
|
||||
sec
|
||||
lda #0
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda #0
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda #0
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda #0
|
||||
sbc ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
internal fun inplaceFloatNegate(target: PtrTarget, scope: IPtSubroutine?) {
|
||||
val (zpPtrVar, _) = deref(target.pointer, addOffsetToPointer=true)
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
// flip sign bit in second byte of 5-byte float
|
||||
asmgen.out("""
|
||||
ldy #1
|
||||
lda ($zpPtrVar),y
|
||||
eor #$80
|
||||
sta ($zpPtrVar),y""")
|
||||
ldy #$offset
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
eor #${'$'}80
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class VarConstantValueTypeAdjuster(
|
||||
if (decl.value != null && !isConstantInitializer(decl.value!!))
|
||||
return noModifications
|
||||
if (decl.value != null && isMemorySlabInit(decl.value!!))
|
||||
return noModifications // a pointer to a memory slab is usually accessed often and/or in ways that benefit more from the pointer being an actual variable (preferrably in zeropage)
|
||||
return noModifications // a pointer to a memory slab is usually accessed often and/or in ways that benefit more from the pointer being an actual variable (preferably in zeropage)
|
||||
val usages = callGraph.usages(decl)
|
||||
val (writes, reads) = usages
|
||||
.partition {
|
||||
|
||||
@@ -2212,7 +2212,7 @@ internal class AstChecker(private val program: Program,
|
||||
private fun checkPointer(pointervar: IdentifierReference) {
|
||||
val vardecl = pointervar.targetVarDecl()
|
||||
if(vardecl?.zeropage == ZeropageWish.NOT_IN_ZEROPAGE)
|
||||
errors.info("pointer variable should preferrably be in zeropage but is marked nozp", vardecl.position)
|
||||
errors.info("pointer variable should preferably be in zeropage but is marked nozp", vardecl.position)
|
||||
}
|
||||
|
||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||
|
||||
@@ -628,7 +628,13 @@ _after:
|
||||
|
||||
if(expr.operator==".") {
|
||||
if(expr.left is IdentifierReference) {
|
||||
require(expr.right !is IdentifierReference)
|
||||
if(expr.right is IdentifierReference) {
|
||||
// (a.b).c -> a.b.c - merge dotted names (handles parenthesized pointer chains like (t.next).flag)
|
||||
val leftIdent = expr.left as IdentifierReference
|
||||
val rightIdent = expr.right as IdentifierReference
|
||||
val combined = IdentifierReference(leftIdent.nameInSource + rightIdent.nameInSource, expr.position)
|
||||
return listOf(AstReplaceNode(expr, combined, parent))
|
||||
}
|
||||
val ri = expr.right as? ArrayIndexedExpression
|
||||
if(ri!=null && ri.plainarrayvar!=null) {
|
||||
val leftIdent = expr.left as IdentifierReference
|
||||
@@ -687,6 +693,52 @@ _after:
|
||||
val ident = IdentifierReference(right.chain, right.position)
|
||||
return listOf(AstReplaceNode(expr.right, ident, expr))
|
||||
}
|
||||
|
||||
// Generic pointer field access on complex expressions, e.g. (expr as ^^Struct).field
|
||||
// Simple identifier/array left-hand-sides (a.b.c, ptr[i].field) are handled elsewhere.
|
||||
// Writes through such an expression can't occur: the grammar only allows them as read expressions.
|
||||
if(expr.left !is IdentifierReference && expr.left !is ArrayIndexedExpression) {
|
||||
val fieldIdent = expr.right as? IdentifierReference
|
||||
if(fieldIdent!=null) {
|
||||
val leftDt = expr.left.inferType(program).getOrUndef()
|
||||
val struct = if(leftDt.isPointer) leftDt.subType as? StructDecl else null
|
||||
val fieldName = fieldIdent.nameInSource.first()
|
||||
val fieldDt = struct?.getFieldType(fieldName)
|
||||
if(struct!=null && fieldDt!=null) {
|
||||
// (ptr).field --> peekXXX(ptr as uword + offsetof(Struct.field))
|
||||
val offset = struct.offsetof(fieldName, program.target)!!.toInt()
|
||||
val ptrAsUword = TypecastExpression(expr.left.copy(), DataType.UWORD, true, expr.left.position)
|
||||
val address: Expression = if(offset==0) ptrAsUword
|
||||
else BinaryExpression(ptrAsUword, "+", NumericLiteral.optimalInteger(offset, expr.position), expr.position)
|
||||
fun peekCall(func: String) =
|
||||
FunctionCallExpression(IdentifierReference(listOf(func), expr.position), mutableListOf(address), expr.position)
|
||||
val readExpr: Expression = when {
|
||||
fieldDt.isBool -> peekCall("peekbool")
|
||||
fieldDt.isUnsignedByte -> DirectMemoryRead(address, expr.position)
|
||||
fieldDt.isSignedByte -> TypecastExpression(DirectMemoryRead(address, expr.position), DataType.BYTE, true, expr.position)
|
||||
fieldDt.isUnsignedWord -> peekCall("peekw")
|
||||
fieldDt.isSignedWord -> TypecastExpression(peekCall("peekw"), DataType.WORD, true, expr.position)
|
||||
fieldDt.isLong -> peekCall("peekl")
|
||||
fieldDt.isFloat -> peekCall("peekf")
|
||||
fieldDt.isPointer -> TypecastExpression(peekCall("peekw"), fieldDt, true, expr.position)
|
||||
else -> {
|
||||
errors.err("unsupported field type for pointer dereference", expr.position)
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
if(fieldIdent.nameInSource.size==1)
|
||||
return listOf(AstReplaceNode(expr, readExpr, parent))
|
||||
// (ptr).field.rest... --> (read of first field) . rest... (will be desugared further in the next pass)
|
||||
if(!fieldDt.isPointer) {
|
||||
errors.err("cannot dereference non-pointer field '$fieldName'", fieldIdent.position)
|
||||
return noModifications
|
||||
}
|
||||
val rest = IdentifierReference(fieldIdent.nameInSource.drop(1), fieldIdent.position)
|
||||
val chained = BinaryExpression(readExpr, ".", rest, expr.position)
|
||||
return listOf(AstReplaceNode(expr, chained, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
|
||||
@@ -3198,4 +3198,158 @@ main {
|
||||
compileText(Amiga500Target(), true, srcReturn, outputDir, writeAssembly = false) shouldNotBe null
|
||||
}
|
||||
|
||||
|
||||
test("parenthesized pointer field access desugars same as unparenthesized") {
|
||||
val src="""
|
||||
main {
|
||||
struct Thing {
|
||||
uword port
|
||||
^^Thing next
|
||||
bool flag
|
||||
}
|
||||
|
||||
sub start() {
|
||||
^^Thing @shared t
|
||||
bool @shared d1 = t.next.flag
|
||||
bool @shared d2 = (t.next).flag
|
||||
ubyte @shared d3 = t.next.flag as ubyte
|
||||
ubyte @shared d4 = (t.next).flag as ubyte
|
||||
bool @shared d5 = t.next.next.flag
|
||||
bool @shared d6 = (t.next).next.flag
|
||||
}
|
||||
}"""
|
||||
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
|
||||
val assigns = result.compilerAst.entrypoint.statements.filterIsInstance<Assignment>()
|
||||
.associateBy { it.target.identifier!!.nameInSource.last() }
|
||||
|
||||
val d1 = assigns.getValue("d1").value as PtrDereference
|
||||
d1.chain shouldBe listOf("t", "next", "flag")
|
||||
(assigns.getValue("d2").value as PtrDereference).chain shouldBe d1.chain
|
||||
((assigns.getValue("d3").value as TypecastExpression).expression as PtrDereference).chain shouldBe d1.chain
|
||||
((assigns.getValue("d4").value as TypecastExpression).expression as PtrDereference).chain shouldBe d1.chain
|
||||
val d5 = assigns.getValue("d5").value as PtrDereference
|
||||
d5.chain shouldBe listOf("t", "next", "next", "flag")
|
||||
(assigns.getValue("d6").value as PtrDereference).chain shouldBe d5.chain
|
||||
|
||||
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("field access on casted struct pointer expression") {
|
||||
val src="""
|
||||
main {
|
||||
struct Thing {
|
||||
uword port
|
||||
^^Thing next
|
||||
bool flag
|
||||
}
|
||||
|
||||
sub start() {
|
||||
^^Thing @shared t
|
||||
bool @shared d1 = (t.port as ^^Thing).flag
|
||||
ubyte @shared d2 = (t.port as ^^Thing).flag as ubyte
|
||||
^^Thing @shared d3 = (t.port as ^^Thing).next
|
||||
bool @shared d4 = (t.port as ^^Thing).next.flag
|
||||
bool @shared d5 = ((t.port as ^^Thing).next).flag
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = compileText(VMTarget(), false, src, outputDir, errors, writeAssembly = false)
|
||||
withClue(errors.errors.joinToString("\n")) {
|
||||
result shouldNotBe null
|
||||
}
|
||||
val assigns = result!!.compilerAst.entrypoint.statements.filterIsInstance<Assignment>()
|
||||
.associateBy { it.target.identifier!!.nameInSource.last() }
|
||||
|
||||
// (t.port as ^^Thing).field --> peekXXX((t.port as ^^Thing as uword) + offsetof(field))
|
||||
val d1 = assigns.getValue("d1").value as FunctionCallExpression
|
||||
d1.target.nameInSource shouldBe listOf("peekbool")
|
||||
val d1addr = d1.args.single() as BinaryExpression
|
||||
d1addr.operator shouldBe "+"
|
||||
(d1addr.right as NumericLiteral).number shouldBe 4.0 // offsetof(Thing.flag)
|
||||
|
||||
val d2 = assigns.getValue("d2").value as TypecastExpression
|
||||
d2.type shouldBe DataType.UBYTE
|
||||
(d2.expression as FunctionCallExpression).target.nameInSource shouldBe listOf("peekbool")
|
||||
|
||||
// pointer-typed field read: peekw + cast back to the pointer type
|
||||
val d3 = assigns.getValue("d3").value as TypecastExpression
|
||||
d3.type.isPointer shouldBe true
|
||||
val d3peek = d3.expression as FunctionCallExpression
|
||||
d3peek.target.nameInSource shouldBe listOf("peekw")
|
||||
((d3peek.args.single() as BinaryExpression).right as NumericLiteral).number shouldBe 2.0 // offsetof(Thing.next)
|
||||
|
||||
// chained: inner pointer-typed field is read via peekw first, then .flag via peekbool
|
||||
val d4 = assigns.getValue("d4").value as FunctionCallExpression
|
||||
d4.target.nameInSource shouldBe listOf("peekbool")
|
||||
val d4addr = d4.args.single() as BinaryExpression
|
||||
(d4addr.right as NumericLiteral).number shouldBe 4.0
|
||||
val d4inner = (d4addr.left as TypecastExpression).expression as TypecastExpression
|
||||
(d4inner.expression as FunctionCallExpression).target.nameInSource shouldBe listOf("peekw")
|
||||
|
||||
// explicit parenthesized chain desugars the same
|
||||
(assigns.getValue("d5").value as FunctionCallExpression).target.nameInSource shouldBe listOf("peekbool")
|
||||
|
||||
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
xtest("function call returning pointer - field access chaining as assignment target [nasty parser problem]") {
|
||||
val src="""
|
||||
main {
|
||||
struct Thing {
|
||||
uword port
|
||||
^^Thing next
|
||||
bool flag
|
||||
}
|
||||
|
||||
sub getThing() -> ^^Thing {
|
||||
return 0
|
||||
}
|
||||
|
||||
sub start() {
|
||||
getThing().flag = true
|
||||
getThing().next.flag = true
|
||||
getThing().next.next.flag = false
|
||||
getThing().port = 1234
|
||||
getThing().next.port = 5678
|
||||
}
|
||||
}"""
|
||||
// currently fails at parse stage, should eventually compile for both VM and 6502 without needing assembly generation
|
||||
compileText(VMTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("function call returning pointer - field access chaining as value (RHS) works") {
|
||||
val src="""
|
||||
main {
|
||||
struct Thing {
|
||||
uword port
|
||||
^^Thing next
|
||||
bool flag
|
||||
}
|
||||
|
||||
sub getThing() -> ^^Thing {
|
||||
return 0
|
||||
}
|
||||
|
||||
sub start() {
|
||||
bool a = getThing().flag
|
||||
bool b = getThing().next.flag
|
||||
bool c = getThing().next.next.flag
|
||||
uword d = getThing().port
|
||||
uword e = getThing().next.port
|
||||
bool f = getThing().flag and getThing().next.flag
|
||||
uword g = getThing().port + getThing().next.port
|
||||
if getThing().flag {
|
||||
a = true
|
||||
}
|
||||
}
|
||||
}"""
|
||||
// RHS value case already works for both VM and 6502 without needing assembly generation
|
||||
compileText(VMTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
}
|
||||
})
|
||||
@@ -276,7 +276,11 @@ class BinaryExpression(
|
||||
// SOMEEXPRESSION . NAME
|
||||
val leftDt = leftExpr.inferType(program)
|
||||
if (leftDt.isPointer) leftDt.getOrUndef().subType else null
|
||||
} else null
|
||||
} else {
|
||||
// generic pointer expression . field (e.g. (expr as ^^Struct).field, or (ptrNext).field)
|
||||
val leftDt = left.inferType(program)
|
||||
if (leftDt.isPointer) leftDt.getOrUndef().subType else null
|
||||
}
|
||||
if (struct == null)
|
||||
InferredTypes.unknown()
|
||||
else {
|
||||
|
||||
@@ -125,7 +125,7 @@ scoped names always need to be fully scoped (because they always start in the gl
|
||||
.. index:: pair: Identifiers; Aliases
|
||||
|
||||
The ``alias`` statement makes it easier to refer to symbols from other places, and they can save
|
||||
you from having to type the fully scoped name everytime you need to access that symbol.
|
||||
you from having to type the fully scoped name every time you need to access that symbol.
|
||||
Aliases can be created in any scope except at the module level.
|
||||
An alias is created with ``alias <name> = <target>`` and then you can use ``<name>`` as if it were ``<target>``.
|
||||
It is possible to alias variables, labels and subroutines, and even whole blocks and words
|
||||
@@ -196,7 +196,7 @@ to access a variable from a nested subroutine::
|
||||
}
|
||||
|
||||
**:index:`Aliases`** make it easier to refer to symbols from other places. They save
|
||||
you from having to type the fully scoped name everytime you need to access that symbol.
|
||||
you from having to type the fully scoped name every time you need to access that symbol.
|
||||
Aliases can be created in any scope except at the module level.
|
||||
You can create and use an alias with the ``alias`` statement like so::
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- "expression does not return a value" error: exec.FreeSignal((IOAudio3.ReplyPort as ^^exec.MsgPort).SigBit as byte)
|
||||
- finish the Amiga high level audio interface, see examples/test.p8 for now
|
||||
- finish the Amiga high level audio interface, see examples/amiga/bdmusic.p8
|
||||
- need a bunch of type casting/conversion checks that test the handling of the 4-byte/long pointer datatype on the qemu68k target.
|
||||
- amiga library structs: use more typed pointers if it knows the struct type from the same (or another amiga library module) , rather than using `pointer`. Consider both the extsubs but also the struct fields in the amigaDOS structs in the generated library modules.
|
||||
|
||||
|
||||
+15
-58
@@ -1,65 +1,22 @@
|
||||
%import lists
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
struct Thing {
|
||||
uword port
|
||||
^^Thing next
|
||||
bool flag
|
||||
}
|
||||
|
||||
sub start() {
|
||||
reverseiter()
|
||||
listiter()
|
||||
}
|
||||
^^Thing @shared t
|
||||
|
||||
sub reverseiter() {
|
||||
for x in "hello" {
|
||||
txt.chrout(x)
|
||||
}
|
||||
; this compiles fine:
|
||||
bool @shared derp1 = t.next.flag
|
||||
ubyte @shared derp2 = t.next.flag as ubyte
|
||||
|
||||
txt.nl()
|
||||
bool @shared derp3 = (t.next).flag
|
||||
ubyte @shared derp4 = (t.next).flag as ubyte
|
||||
|
||||
for x in "hello2" step -1 {
|
||||
txt.chrout(x)
|
||||
}
|
||||
txt.nl()
|
||||
|
||||
for y in [11,22,33,44] {
|
||||
txt.print_ub(y)
|
||||
txt.spc()
|
||||
}
|
||||
txt.nl()
|
||||
|
||||
for y in [11,22,33,44] step -1 {
|
||||
txt.print_ub(y)
|
||||
txt.spc()
|
||||
}
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
struct MyNode {
|
||||
^^MyNode Succ
|
||||
^^MyNode Pred
|
||||
ubyte value
|
||||
}
|
||||
|
||||
struct MyList {
|
||||
^^MyNode Head
|
||||
^^MyNode Tail
|
||||
^^MyNode TailPred
|
||||
}
|
||||
|
||||
sub listiter() {
|
||||
^^MyList mylist = []
|
||||
^^MyNode n1 = [0,0, 11]
|
||||
^^MyNode n2 = [0,0, 22]
|
||||
^^MyNode n3 = [0,0, 33]
|
||||
list.init(mylist)
|
||||
list.add_tail(mylist, n1)
|
||||
list.add_tail(mylist, n2)
|
||||
list.add_tail(mylist, n3)
|
||||
|
||||
for node in mylist {
|
||||
txt.print_ulhex(node as long, true)
|
||||
txt.spc()
|
||||
txt.print_ub(node.value)
|
||||
txt.nl()
|
||||
}
|
||||
bool @shared derp5 = (t.port as ^^Thing).flag
|
||||
ubyte @shared derp6 = (t.port as ^^Thing).flag as ubyte
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user