6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Mon Apr 29, 2024 6:39 am

All times are UTC




Post new topic Reply to topic  [ 171 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7, 8 ... 12  Next
Author Message
PostPosted: Fri Jul 26, 2019 2:42 pm 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
And here's

Code:
var b  = 2
var c: uword
var d: uword

fun main() {
    b = when(d) {
        0 -> 6
        1 -> 7
        else -> 9
    }
}


compiled:

Code:
__wolin_pl_qus_wolin_main:

; allocSP<__wolin_reg2>,#1

    dex

; allocSP<__wolin_reg3>,#1

    dex

; allocSP<__wolin_reg4>,#2


    dex
    dex

; letSP(0)<__wolin_reg4>[uword]=__wolin_pl_qus_wolin_d<pl.qus.wolin.d>[uword]


    lda #<__wolin_pl_qus_wolin_d
    sta 0,x
    lda #>__wolin_pl_qus_wolin_d
    sta 0+1,x

; allocSP<__wolin_reg5>,#1

    dex

; allocSP<__wolin_reg6>,#1

    dex

; label__wolin_lab_whenLabel_0

__wolin_lab_whenLabel_0:

; letSP(0)<__wolin_reg6>[ubyte]=#0[ubyte]


    lda #0
    sta 0,x

; evaleqSP(1)<__wolin_reg5>[bool]=SP(2)<__wolin_reg4>[uword],SP(0)<__wolin_reg6>[ubyte]


    lda #0 // rozne
    sta 1,x
    lda 2+1,x
    bne +
    lda 2,x
    cmp 0,x
    bne +
    lda #1 // rowne
    sta 1,x
:

; bneSP(1)<__wolin_reg5>[bool]=#1[bool],__wolin_lab_whenLabel_1[adr]


    lda 1,x
    cmp #1
    bne __wolin_lab_whenLabel_1

; letSP(0)<__wolin_reg6>[ubyte]=#6[ubyte]


    lda #6
    sta 0,x

; goto__wolin_lab_whenEndLabel_0[adr]

    jmp __wolin_lab_whenEndLabel_0

; label__wolin_lab_whenLabel_1

__wolin_lab_whenLabel_1:

; letSP(0)<__wolin_reg6>[ubyte]=#1[ubyte]


    lda #1
    sta 0,x

; evaleqSP(1)<__wolin_reg5>[bool]=SP(2)<__wolin_reg4>[uword],SP(0)<__wolin_reg6>[ubyte]


    lda #0 // rozne
    sta 1,x
    lda 2+1,x
    bne +
    lda 2,x
    cmp 0,x
    bne +
    lda #1 // rowne
    sta 1,x
:

; bneSP(1)<__wolin_reg5>[bool]=#1[bool],__wolin_lab_whenLabel_2[adr]


    lda 1,x
    cmp #1
    bne __wolin_lab_whenLabel_2

; letSP(0)<__wolin_reg6>[ubyte]=#7[ubyte]


    lda #7
    sta 0,x

; goto__wolin_lab_whenEndLabel_0[adr]

    jmp __wolin_lab_whenEndLabel_0

; label__wolin_lab_whenLabel_2

__wolin_lab_whenLabel_2:

; letSP(0)<__wolin_reg6>[ubyte]=#9[ubyte]


    lda #9
    sta 0,x

; label__wolin_lab_whenEndLabel_0

__wolin_lab_whenEndLabel_0:

; letSP(4)<__wolin_reg3>[ubyte]=SP(0)<__wolin_reg6>[ubyte]


    lda 0,x
    sta 4,x

; freeSP<__wolin_reg6>,#1

    inx

; freeSP<__wolin_reg5>,#1

    inx

; freeSP<__wolin_reg4>,#2


    inx
    inx

; let__wolin_pl_qus_wolin_b<pl.qus.wolin.b>[ubyte]=SP(0)<__wolin_reg3>[ubyte]


    lda 0,x
    sta __wolin_pl_qus_wolin_b


; freeSP<__wolin_reg3>,#1

    inx

; freeSP<__wolin_reg2>,#1

    inx

; ret

    rts


Anyone care to check? ;)

EDIT:

I guess

Code:
    lda 1,x
    cmp #1
    bne __wolin_lab_whenLabel_2


could be:

Code:
    lda 1,x
    bne __wolin_lab_whenLabel_2


right?


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 26, 2019 9:19 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1927
Location: Sacramento, CA, USA
Those don't look equivalent to me ... in fact, almost opposite. Was the second BNE supposed to be a BEQ?

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 27, 2019 7:36 am 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
Indeed!

And now a complete Wolin program straight from the compiler!

This thing:

Code:
package pl.qus.wolin

var border: ubyte^53280
var d: uword = 0

fun main() {
    border = if(d == 0) 6
    else 9
}


compiles to:

Code:
; setupHEADER


;**********************************************
;*
;* BASIC header
;*
;* compile with:
;* cl65.exe -o assembler.prg -t c64 -C c64-asm.cfg assembler.s
;*
;**********************************************
            .org 2049
            .export LOADADDR = *
Bas10:      .word BasEnd
            .word 10   
            .byte 158 ; sys
            .byte " 2064"
            .byte 0
BasEnd:     .word 0
            .word 0
            ;


; setupSPF=251[ubyte],40959[uword]


; prepare function stack
__wolin_spf = 251 ; function stack ptr
__wolin_spf_top = 40959 ; function stack top
    lda #<__wolin_spf_top ; set function stack top
    sta __wolin_spf
    lda #>__wolin_spf_top
    sta __wolin_spf+1

; setupSP=143[ubyte]


; prepare program stack
__wolin_sp_top = 143 ; program stack top
    ldx #__wolin_sp_top ; set program stack top

; allocSP<__wolin_reg0>,#1

    dex

; letSP(0)<__wolin_reg0>[ubyte]=#0[ubyte]


    lda #0
    sta 0,x

; let__wolin_pl_qus_wolin_d<pl.qus.wolin.d>[uword]=SP(0)<__wolin_reg0>[ubyte]


    lda 0,x
    sta __wolin_pl_qus_wolin_d
    lda #0
    sta __wolin_pl_qus_wolin_d+1


; freeSP<__wolin_reg0>,#1

    inx

; label__wolin_pl_qus_wolin_main

__wolin_pl_qus_wolin_main:

; allocSP<__wolin_reg2>,#1

    dex

; allocSP<__wolin_reg3>,#1

    dex

; allocSP<__wolin_reg4>,#1

    dex

; allocSP<__wolin_reg5>,#2


    dex
    dex

; letSP(0)<__wolin_reg5>[uword]=__wolin_pl_qus_wolin_d<pl.qus.wolin.d>[uword]


    lda #<__wolin_pl_qus_wolin_d
    sta 0,x
    lda #>__wolin_pl_qus_wolin_d
    sta 0+1,x

; allocSP<__wolin_reg6>,#1

    dex

; letSP(0)<__wolin_reg6>[ubyte]=#0[ubyte]


    lda #0
    sta 0,x

; evaleqSP(3)<__wolin_reg4>[bool]=SP(1)<__wolin_reg5>[uword],SP(0)<__wolin_reg6>[ubyte]


    lda #0 ; rozne
    sta 3,x
    lda 1+1,x
    bne :+
    lda 1,x
    cmp 0,x
    bne :+
    lda #1 ; rowne
    sta 3,x
:

; freeSP<__wolin_reg6>,#1

    inx

; freeSP<__wolin_reg5>,#2


    inx
    inx

; allocSP<__wolin_reg7>,#1

    dex

; bneSP(1)<__wolin_reg4>[bool]=#1[bool],__wolin_lab_afterIfExpression_0<label_DO_else>[adr]


    lda 1,x
    beq __wolin_lab_afterIfExpression_0

; letSP(0)<__wolin_reg7>[ubyte]=#6[ubyte]


    lda #6
    sta 0,x

; goto__wolin_lab_afterWholeIf_0[adr]

    jmp __wolin_lab_afterWholeIf_0

; label__wolin_lab_afterIfExpression_0

__wolin_lab_afterIfExpression_0:

; letSP(0)<__wolin_reg7>[ubyte]=#9[ubyte]


    lda #9
    sta 0,x

; label__wolin_lab_afterWholeIf_0

__wolin_lab_afterWholeIf_0:

; letSP(2)<__wolin_reg3>[ubyte]=SP(0)<__wolin_reg7>[ubyte]


    lda 0,x
    sta 2,x

; freeSP<__wolin_reg7>,#1

    inx

; freeSP<__wolin_reg4>,#1

    inx

; let53280[ubyte]=SP(0)<__wolin_reg3>[ubyte]


    lda 0,x
    sta 53280


; freeSP<__wolin_reg3>,#1

    inx

; freeSP<__wolin_reg2>,#1

    inx

; ret

    rts

; label__wolin_indirect_jsr

__wolin_indirect_jsr:

; goto65535[adr]

    jmp 65535

; label__wolin_pl_qus_wolin_d

__wolin_pl_qus_wolin_d:

; alloc0[uword]

    .word 0



Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 02, 2019 3:36 pm 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
As an update - I've fixed function-local variables. Until now compiler treated them like they were package-global variables and allocated them statically. After the fix function-local variables get allocated on function call stack, as they should be, so recurrent functions are now possible. So this:

Code:
fun localTest(a: ubyte, b: ubyte): ubyte {
    val suma: ubyte

    suma = a + b

    return suma
}


becomes this pseudo asm:

Code:
// ****************************************
// funkcja: fun pl.qus.wolin.localTest(pl.qus.wolin.localTest.a: ubyte = 0 (), pl.qus.wolin.localTest.b: ubyte = 0 ()):ubyte
// ****************************************
label __wolin_pl_qus_wolin_localTest
alloc SP<__wolin_reg4>, #1 // For assignment left side
alloc SP<__wolin_reg5>, #1 // for value that gets assigned to left side
let SP(0)<__wolin_reg5>[ubyte] = SPF(3)<pl.qus.wolin.localTest.a>[ubyte] // simple id from var
alloc SP<__wolin_reg6>, #1 // RIGHT adding operator
let SP(0)<__wolin_reg6>[ubyte] = SPF(2)<pl.qus.wolin.localTest.b>[ubyte] // simple id from var
add SP(1)<__wolin_reg5>[ubyte] = SP(1)<__wolin_reg5>[ubyte], SP(0)<__wolin_reg6>[ubyte]
free SP<__wolin_reg6>, #1 // RIGHT adding operator
let SPF(1)<pl.qus.wolin.localTest..suma>[ubyte] = SP(0)<__wolin_reg5>[ubyte] // przez sprawdzacz typow - process assignment
free SP<__wolin_reg5>, #1 // for value that gets assigned to left side, type = ubyte
free SP<__wolin_reg4>, #1 // For assignment left side
alloc SP<__wolin_reg7>, #1 // for expression
let SP(0)<__wolin_reg7>[ubyte] = SPF(1)<pl.qus.wolin.localTest..suma>[ubyte] // simple id from var
let SPF(6)<returnValue>[ubyte] = SP(0)<__wolin_reg7>[ubyte] // przez sprawdzacz typow - jump expression
free SP<__wolin_reg7>, #1 // for expression
free SPF, #6 // free fn arguments and locals for pl.qus.wolin.localTest
ret


and gets translated to:

Code:
; label__wolin_pl_qus_wolin_localTest

__wolin_pl_qus_wolin_localTest:

; allocSP<__wolin_reg4>,#1

    dex

; allocSP<__wolin_reg5>,#1

    dex

; letSP(0)<__wolin_reg5>[ubyte]=SPF(3)<pl.qus.wolin.localTest.a>[ubyte]


    ldy #3
    lda (__wolin_spf),y
    sta 0,x


; allocSP<__wolin_reg6>,#1

    dex

; letSP(0)<__wolin_reg6>[ubyte]=SPF(2)<pl.qus.wolin.localTest.b>[ubyte]


    ldy #2
    lda (__wolin_spf),y
    sta 0,x


; addSP(1)<__wolin_reg5>[ubyte]=SP(1)<__wolin_reg5>[ubyte],SP(0)<__wolin_reg6>[ubyte]


    clc
    lda 1,x
    adc 0,x
    sta 1,x

; freeSP<__wolin_reg6>,#1

    inx

; letSPF(1)<pl.qus.wolin.localTest..suma>[ubyte]=SP(0)<__wolin_reg5>[ubyte]


    lda 0,x
    ldy #1
    sta (__wolin_spf),y

; freeSP<__wolin_reg5>,#1

    inx

; freeSP<__wolin_reg4>,#1

    inx

; allocSP<__wolin_reg7>,#1

    dex

; letSP(0)<__wolin_reg7>[ubyte]=SPF(1)<pl.qus.wolin.localTest..suma>[ubyte]


    ldy #1
    lda (__wolin_spf),y
    sta 0,x


; letSPF(6)<returnValue>[ubyte]=SP(0)<__wolin_reg7>[ubyte]


    lda 0,x
    ldy #6
    sta (__wolin_spf),y

; freeSP<__wolin_reg7>,#1

    inx

; freeSPF,#6


    clc
    lda __wolin_spf
    adc #6
    sta __wolin_spf
    lda __wolin_spf+1
    adc #0
    sta __wolin_spf+1

; ret

    rts


Of course implementing tail recurrency comes next ;)


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 02, 2019 5:30 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
Okay, let me point out a few obvious potential optimisations:
Code:
INX : INX : DEX -> INX ; lazy pointer management
LDA m : ADC #0 : STA m -> BCC + : INC m ; idiomatic for 16b += 8b operation
LDY #3 : … : LDY #2 : … : LDY #1 -> LDY #3 : … : DEY : … : DEY    ; same speed, fewer bytes if you know Y isn't changed meanwhile
STA 0,X : LDA 1,X : ADC 0,X -> ADC 1,X ; addition is commutative, lazy data movement
STA 0,X : LDA 0,X -> NOP ; lazy data movement
LDA (m),Y : STA 0,X : DEY : LDA (m),Y : ADC 0,X -> LDA (m),Y : DEY : ADC (m),Y ; most ALU ops get access to the ZP-indirect modes too!


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 03, 2019 1:18 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1927
Location: Sacramento, CA, USA
It's so easy for a human to sense the optimization potential. It's NOT so easy for a human to encode that sensibility into a compiler. ;-)

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 03, 2019 1:32 am 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
Sure, but these are common patterns that show up frequently in Wolin's output, and should mostly be easy to recognise at code generation time. By pointing them out, it becomes possible for the author to think about ways to incorporate them.

The two most obviously beneficial techniques I think would be lazy pointer management (coalescing runs of X increments and/or decrements) and lazy data movement (eliding unnecessary loads and stores, combining loads with ALU ops), which could potentially be peephole optimisations, or could be incorporated into the repertoire of idiomatic code segments.


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 03, 2019 8:42 am 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
Well, yes. Some of the optimizations can be done in pseudo asm, some can't (that's why I was asking about "optimizing assembler" some posts ago).

You might think that there's too many registers created, but that is due to all the magic things Kotlin can do, like everything is an expression, i.e.:

Code:
val divisionResult = try { a / b } catch (ex: DivisionByZero) { 666 }


and this magic comes from carefuly designed gramar. Unfortunately to make this gramar work I neeed to shuffle temporary registers a lot. Of course it doesn't mean they can't be optimized out eventually. But I digress.

Let me show you pseudo asm optimizations:

Chromatix wrote:
INX : INX : DEX -> INX ; lazy pointer management


This will be done on free/alloc level. Usualy a register(s) get(s) freed and new register(s) get(s) immediately allocated, i.e.:

Code:
free SP<>,#4 
alloc SP<>,#2


4 - 2 = 2 so this can be simplified to:

Code:
free SP<>,#2


or:

Code:
alloc SP,#1
alloc SP,#1


equals

Code:
alloc SP,#2


obviously.

Chromatix wrote:
LDA m : ADC #0 : STA m -> BCC + : INC m ; idiomatic for 16b += 8b operation


Would that be what you mean?

Code:
add ?dest[word] = ?src[word], #?val[byte] -> """
    clc
    lda {src}
    adc #{val}
    sta {dest}
    bcc +:
    inc {a}+1
:
"""


Chromatix wrote:
LDA (m),Y : STA 0,X : DEY : LDA (m),Y : ADC 0,X -> LDA (m),Y : DEY : ADC (m),Y ; most ALU ops get access to the ZP-indirect modes too!


I know, and it will get optimized easily, but during compile time due to how Kotlin grammar / temp register work, it needs to first copy functions stack register to temp register.

Now points below can't be really done in pseudo asm for various reasons (most of the time - I would need to keep track of what's happening in registers, some flow graphs perhaps)

Chromatix wrote:
STA 0,X : LDA 1,X : ADC 0,X -> ADC 1,X ; addition is commutative, lazy data movement


STA is probably a part of "put variable on operation stack" op, created by register flow logic:

Code:
let op_stack_reg = some_variable


while following LDA/ADC are probably part of add template:

Code:
add SP(?d)[ubyte] = SP(?s1)[ubyte], SP(?s2)[ubyte] -> """
    clc
    lda {s1},x
    adc {s2},x
    sta {d},x"""


Chromatix wrote:
LDY #3 : … : LDY #2 : … : LDY #1 -> LDY #3 : … : DEY : … : DEY ; same speed, fewer bytes if you know Y isn't changed meanwhile


While it is neat it might be not as useful as you may think. Such DEY/INY would occur only if 1) function arguments would be one byte 2) the programmer would access them in the very same order that they're placed on the stack... Not very likely probably.

Chromatix wrote:
STA 0,X : LDA 0,X -> NOP ; lazy data movement


Ah, yes. As these are again tail and head of two templates, there are times I can't find the logic to optimize them in pseudo-asm and "optimizing 6510 asm" would be handy.


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 03, 2019 2:10 pm 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
Meanwhile, after array code was broken by some other changes I fixed it again, and the code below does what it is supposed to do (if you know C64, you know what I mean...):

Code:
package pl.qus.wolin

var ekran: ubyte[]^1024

fun main() {

    ekran[0] = 65
    ekran[1] = 66
    ekran[998] = 66
    ekran[999] = 65
}


and then this:

Code:
package pl.qus.wolin

var border: ubyte^53280
var ekran: ubyte[]^1024

var i: uword

fun main() {
    i = 0

    while(i<1000) {
        ekran[i++] = 65
    }
}


so - Wolin is beginning to be actualy usable.

But I had to correct some silly errors in template code, not even sure why I made them...

I would really appreciate if any of you, 6502 wizards took a look at this:

https://github.com/ssuukk/wolin/blob/ma ... mplate.asm


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 03, 2019 8:42 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
Looking through your templates, it seems your "alloc/free SPF" templates are responsible for the ADC #0 instance I noticed. These should be replaced with a branch over DEC/INC.

I think what you refer to as "flow graphs" is what I mean by "lazy data movement". Once implemented, the ALU templates would no longer include LDA/STA, but the flow analysis would insert them only as strictly required to get the data where it's needed. As part of that, you will need to somehow divide your templates into categories which a: deliberately operate on the accumulator; b: clobber the accumulator as an incidental part of doing something else; c: do neither. This will determine where your flow analysis can and must operate.


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 04, 2019 6:01 am 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
OK, thanks. alloc SPF fixed.

The second thing you mention would be quite easy - I think all pseudo asm instructions (except allocs) trash A.


Top
 Profile  
Reply with quote  
PostPosted: Mon Aug 05, 2019 5:15 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
I draw a deliberate distinction between "clobber A" and "operate on A". The latter expects A to be loaded with one of the operands, to be passed the location of the other, and will normally leave a useful result in A to be saved and/or used subsequently. The former would be something like the following which I found in your templates, which does none of those things, only makes use of the A register and leaves it in a non-useful state:
Code:
alloc SP, #?count -> """
    txa
    sec
    sbc #{count}
    tax"""
So you have to distinguish this case from the ones which allocate only small amounts.

After that, you can take advantage of commutativity. Nearly all of the 6502's ALU operations are commutative, ie. it doesn't matter which operand is in A and which is in RAM. So if you have one of them already in A, you shouldn't save it to RAM only to load the other. The exception is SBC, and even then you can avoid the save-load cycle by substituting EOR #$FF : ADC.


Top
 Profile  
Reply with quote  
PostPosted: Mon Aug 05, 2019 7:50 pm 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
Yep, I know what you meant. But I am not sure how to implement it with current atchitecture...

Going back to objects. Object's constructor needs to:

1) obtain memory location to allocate new instance of the class it's making
2) initialize object fields to proper values.

Here's how I've done it in Wolin:

Code:
class Test {
    var x: ubyte = 10
    var y: ubyte = 20
}


Constructor pseudo asm:

Code:
label __wolin_pl_qus_wolin_Test

alloc SP<__wolin_reg4>, #2 // for returning this
alloc SPF, #6 // 2 uwords + return value (= start addr of new instance)
let SPF(2)[uword] = #3[uword] // this object occupies 3 bytes (x: ubyte, y: ubyte, class ID: ubyte)
let SPF(0)[uword] = #1[uword] // dummy parameter for alloc, maybe it will do something in the future
call __wolin_pl_qus_wolin_allocMem[adr]
let SP(0)<__wolin_reg4>[ptr] = SPF(0)<returnValue>[ptr]
free SPF <Any>, #2 // free return value of pl.qus.wolin.allocMem from call stack
let SPF(0)<pl.qus.wolin.Test.returnValue>[ptr] = SP(0)<__wolin_reg4>[ptr] // set constructor return value to allocated space start
setup HEAP = SP(0)<__wolin_reg4>[ptr] // setup ZP heap pointer for initializing object fields
free SP<__wolin_reg4>, #2 // for returning this
free SPF, #2 // free fn arguments and locals for pl.qus.wolin.Test - in the future move to the end, as we can have constructor params used in init
alloc SP<__wolin_reg5>, #1 // for var init expression
let SP(0)<__wolin_reg5>[ubyte] = #10[ubyte] // atomic ex
let HEAP(2)<pl.qus.wolin.Test.x>[ubyte] = SP(0)<__wolin_reg5>[ubyte] // set x on heap to 10
free SP<__wolin_reg5>, #1 // for var init expression
alloc SP<__wolin_reg6>, #1 // for var init expression
let SP(0)<__wolin_reg6>[ubyte] = #20[ubyte] // atomic ex
let HEAP(1)<pl.qus.wolin.Test.y>[ubyte] = SP(0)<__wolin_reg6>[ubyte] // set y on heap to 20
free SP<__wolin_reg6>, #1 // for var init expression// return from constructor
ret


And that gives:

Code:
; label__wolin_pl_qus_wolin_Test

__wolin_pl_qus_wolin_Test:

; allocSP<__wolin_reg4>,#2


    dex
    dex

; allocSPF,#6


    clc
    lda __wolin_spf
    sbc #6
    sta __wolin_spf
    bcs +:
    dec __wolin_spf+1
:

; letSPF(2)[uword]=#3[uword]


    ldy #2
    lda #<3
    sta (__wolin_spf),y
    iny
    lda #>3
    sta (__wolin_spf),y

; letSPF(0)[uword]=#1[uword]


    ldy #0
    lda #<1
    sta (__wolin_spf),y
    iny
    lda #>1
    sta (__wolin_spf),y

; call__wolin_pl_qus_wolin_allocMem[adr]

    jsr __wolin_pl_qus_wolin_allocMem

; letSP(0)<__wolin_reg4>[ptr]=SPF(0)<returnValue>[ptr]


     ldy #0
     lda (__wolin_spf),y
     sta 0,x
     iny
     lda (__wolin_spf),y
     sta 0+1,x

; freeSPF<Any>,#2


    clc
    lda __wolin_spf
    adc #2
    sta __wolin_spf
    bcc +:
    inc __wolin_spf+1
:

; letSPF(0)<pl.qus.wolin.Test.returnValue>[ptr]=SP(0)<__wolin_reg4>[ptr]


    ldy #0
    lda 0,x
    sta (__wolin_spf),y
    iny
    lda 0+1,x
    sta (__wolin_spf),y

; setupHEAP=SP(0)<__wolin_reg4>[ptr]


    lda 0,x
    sta __wolin_this_ptr
    lda 0+1,x
    sta __wolin_this_ptr+1

; freeSP<__wolin_reg4>,#2


    inx
    inx

; freeSPF,#2


    clc
    lda __wolin_spf
    adc #2
    sta __wolin_spf
    bcc +:
    inc __wolin_spf+1
:

; allocSP<__wolin_reg5>,#1

    dex

; letSP(0)<__wolin_reg5>[ubyte]=#10[ubyte]


    lda #10
    sta 0,x

; letHEAP(2)<pl.qus.wolin.Test.x>[ubyte]=SP(0)<__wolin_reg5>[ubyte]


    lda 0,x
    ldy #2
    sta (__wolin_this_ptr),y

; freeSP<__wolin_reg5>,#1

    inx

; allocSP<__wolin_reg6>,#1

    dex

; letSP(0)<__wolin_reg6>[ubyte]=#20[ubyte]


    lda #20
    sta 0,x

; letHEAP(1)<pl.qus.wolin.Test.y>[ubyte]=SP(0)<__wolin_reg6>[ubyte]


    lda 0,x
    ldy #1
    sta (__wolin_this_ptr),y

; freeSP<__wolin_reg6>,#1

    inx

; ret

    rts


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 11, 2019 7:21 am 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
News: calling class methods on class instance now works. As mentioned earlier - all class methods have intrinsic "this" argument handled by compiler that holds pointer to dereferenced instance. Here's an example with dummy allocMem function that would obviously always alloc the same memory location (30000) for all objects :D

Of course that's only start of the solution, as class methods should be called via jump table depending on real instance being derferenced. Currently bottom field of instance heap contains class id reserved for runtime class discrimination.

EDIT: fixed 6502 asm source.

Code:
package pl.qus.wolin

var border: ubyte^53280
var ekran: ubyte[]^1024

var i: uword = 0
var znak: ubyte = 0

fun allocMem(size: uword, count: uword): Any {
    return 30000
}

class Test {
    var x: ubyte = 3
    var y: ubyte = 7

    fun suma(): ubyte {
        return x+y
    }
}


fun main() {
    val testowa : Test

    testowa = Test()

    border = testowa.suma()
}


pseudoasm:

Code:
setup HEADER
setup SPF = 251[ubyte], 40959[uword] // call stack pointer at 251 = 40959
setup SP = 143[ubyte] // register stack top = 142
setup HEAP = 176[ubyte]
//  main function entry
goto __wolin_pl_qus_wolin_main[adr]


// ****************************************
// funkcja: fun pl.qus.wolin.allocMem(pl.qus.wolin.allocMem.size: uword = 0 (), pl.qus.wolin.allocMem.count: uword = 0 ()):Any
// ****************************************
label __wolin_pl_qus_wolin_allocMem
alloc SP<__wolin_reg1>, #2 // for expression
let SP(0)<__wolin_reg1>[ptr] = #30000[uword] // atomic ex
let SPF(4)<returnValue>[ptr] = SP(0)<__wolin_reg1>[ptr] // przez sprawdzacz typow - jump expression
free SP<__wolin_reg1>, #2 // for expression
free SPF, #4 // free fn arguments and locals for pl.qus.wolin.allocMem
ret


// ****************************************
// konstruktor: fun pl.qus.wolin.Test():pl.qus.wolin.Test
// ****************************************
label __wolin_pl_qus_wolin_Test

alloc SP<__wolin_reg2>, #2 // for returning this
alloc SPF, #6
let SPF(2)[uword] = #3[uword]
let SPF(0)[uword] = #1[uword]
call __wolin_pl_qus_wolin_allocMem[adr]
let SP(0)<__wolin_reg2>[ptr] = SPF(0)<returnValue>[ptr]
free SPF <Any>, #2 // free return value of pl.qus.wolin.allocMem from call stack
let SPF(0)<pl.qus.wolin.Test.returnValue>[ptr] = SP(0)<__wolin_reg2>[ptr] // przez sprawdzacz typow - zwrotka alloc do zwrotki konstruktora
setup HEAP = SP(0)<__wolin_reg2>[ptr]
free SP<__wolin_reg2>, #2 // for returning this
alloc SP<__wolin_reg3>, #1 // for var init expression
let SP(0)<__wolin_reg3>[ubyte] = #3[ubyte] // atomic ex
let HEAP(2)<pl.qus.wolin.Test.x>[ubyte] = SP(0)<__wolin_reg3>[ubyte] // podstawic wynik inicjalizacji expression do zmiennej pl.qus.wolin.Test.x
free SP<__wolin_reg3>, #1 // for var init expression
alloc SP<__wolin_reg4>, #1 // for var init expression
let SP(0)<__wolin_reg4>[ubyte] = #7[ubyte] // atomic ex
let HEAP(1)<pl.qus.wolin.Test.y>[ubyte] = SP(0)<__wolin_reg4>[ubyte] // podstawic wynik inicjalizacji expression do zmiennej pl.qus.wolin.Test.y
free SP<__wolin_reg4>, #1 // for var init expression
ret

// ****************************************
// funkcja: fun pl.qus.wolin.Test.suma(pl.qus.wolin.Test.suma.this: pl.qus.wolin.Test = 65535 ()):ubyte
// ****************************************
label __wolin_pl_qus_wolin_Test_suma
setup HEAP = this
alloc SP<__wolin_reg6>, #1 // for expression
let SP(0)<__wolin_reg6>[ubyte] = HEAP(2)<pl.qus.wolin.Test.x>[ubyte] // simple id from var
alloc SP<__wolin_reg7>, #1 // RIGHT adding operator
let SP(0)<__wolin_reg7>[ubyte] = HEAP(1)<pl.qus.wolin.Test.y>[ubyte] // simple id from var
add SP(1)<__wolin_reg6>[ubyte] = SP(1)<__wolin_reg6>[ubyte], SP(0)<__wolin_reg7>[ubyte]
free SP<__wolin_reg7>, #1 // RIGHT adding operator
let SPF(2)<returnValue>[ubyte] = SP(0)<__wolin_reg6>[ubyte] // przez sprawdzacz typow - jump expression
free SP<__wolin_reg6>, #1 // for expression
free SPF, #2 // free fn arguments and locals for pl.qus.wolin.Test.suma
ret

// ****************************************
// funkcja: fun pl.qus.wolin.main():unit
// ****************************************
label __wolin_pl_qus_wolin_main
alloc SP<__wolin_reg11>, #2 // For assignment left side
alloc SP<__wolin_reg12>, #2 // for value that gets assigned to left side
alloc SPF, #2
call __wolin_pl_qus_wolin_Test[adr]

let SP(0)<__wolin_reg12>[ptr] = SPF(0)<returnValue>[ptr]// copy return parameter - TODO sprawdzić co jeśli wywołanie funkcji było bez podstawienia!!!
free SPF <pl.qus.wolin.Test>, #2
let SPF(0)<pl.qus.wolin.main..testowa>[ptr] = SP(0)<__wolin_reg12>[ptr] // przez sprawdzacz typow - process assignment
free SP<__wolin_reg12>, #2 // for value that gets assigned to left side, type = pl.qus.wolin.Test
free SP<__wolin_reg11>, #2 // For assignment left side
alloc SP<__wolin_reg14>, #1 // For assignment left side
alloc SP<__wolin_reg15>, #1 // for value that gets assigned to left side
alloc SP<__wolin_reg16>, #2 // dereferenced var
let SP(0)<__wolin_reg16>[ptr] = SPF(0)<pl.qus.wolin.main..testowa>[ptr] // simple id from var
alloc SP<__wolin_reg17>, #1 // for right side of deref
alloc SPF, #3
let SPF(0)[ptr] = SP(1)<__wolin_reg16>[ptr]
call __wolin_pl_qus_wolin_Test_suma[adr]

let SP(0)<__wolin_reg17>[ubyte] = SPF(0)<returnValue>[ubyte]// copy return parameter - TODO sprawdzić co jeśli wywołanie funkcji było bez podstawienia!!!
free SPF <ubyte>, #1
free SP<__wolin_reg17>, #1 // for right side of deref
free SP<__wolin_reg16>, #2 // dereferenced var
let __wolin_pl_qus_wolin_result<pl.qus.wolin.result>[ubyte] = SP(0)<__wolin_reg15>[ubyte] // przez sprawdzacz typow - process assignment
free SP<__wolin_reg15>, #1 // for value that gets assigned to left side, type = ubyte
free SP<__wolin_reg14>, #1 // For assignment left side
free SPF, #2 // free fn arguments and locals for pl.qus.wolin.main
ret



// ****************************************
// LAMBDAS
// ****************************************


// ****************************************
// STATIC SPACE
// ****************************************
label __wolin_indirect_jsr
goto 65535[adr]
label __wolin_pl_qus_wolin_result
alloc 0[ubyte]  // pl.qus.wolin.result


Code:
; setupHEADER


;**********************************************
;*
;* BASIC header
;*
;* compile with:
;* cl65.exe -o assembler.prg -t c64 -C c64-asm.cfg -Ln labels.txt assembler.s
;*
;**********************************************
            .org 2049
            .export LOADADDR = *
Bas10:      .word BasEnd
            .word 10
            .byte 158 ; sys
            .byte " 2064"
            .byte 0
BasEnd:     .word 0
            .word 0
            ;


; setupSPF=251[ubyte],40959[uword]


; prepare function stack
__wolin_spf := 251 ; function stack ptr
__wolin_spf_top := 40959 ; function stack top
    lda #<__wolin_spf_top ; set function stack top
    sta __wolin_spf
    lda #>__wolin_spf_top
    sta __wolin_spf+1

; setupSP=143[ubyte]


; prepare program stack
__wolin_sp_top := 143 ; program stack top
    ldx #__wolin_sp_top ; set program stack top

; setupHEAP=176[ubyte]


__wolin_this_ptr := 176


; allocSP<__wolin_reg0>,#1

    dex

; letSP(0)<__wolin_reg0>[ubyte]=#0[ubyte]


    lda #0
    sta 0,x

; let__wolin_pl_qus_wolin_znak<pl.qus.wolin.znak>[ubyte]=SP(0)<__wolin_reg0>[ubyte]


    lda 0,x
    sta __wolin_pl_qus_wolin_znak


; freeSP<__wolin_reg0>,#1

    inx

; allocSP<__wolin_reg1>,#1

    dex

; letSP(0)<__wolin_reg1>[ubyte]=#0[ubyte]


    lda #0
    sta 0,x

; let__wolin_pl_qus_wolin_i<pl.qus.wolin.i>[uword]=SP(0)<__wolin_reg1>[ubyte]


    lda 0,x
    sta __wolin_pl_qus_wolin_i
    lda #0
    sta __wolin_pl_qus_wolin_i+1


; freeSP<__wolin_reg1>,#1

    inx

; goto__wolin_pl_qus_wolin_main[adr]

    jmp __wolin_pl_qus_wolin_main

; label__wolin_pl_qus_wolin_allocMem

__wolin_pl_qus_wolin_allocMem:

; allocSP<__wolin_reg2>,#2


    dex
    dex

; allocSP<__wolin_reg3>,#2


    dex
    dex

; letSP(0)<__wolin_reg3>[ptr]=#30000[uword]


    lda #<30000
    sta 0,x
    lda #>30000
    sta 0+1,x

; letSPF(4)<returnValue>[ptr]=SP(0)<__wolin_reg3>[ptr]


    ldy #4
    lda 0,x
    sta (__wolin_spf),y
    iny
    lda 0+1,x
    sta (__wolin_spf),y

; freeSP<__wolin_reg3>,#2


    inx
    inx

; freeSP<__wolin_reg2>,#2


    inx
    inx

; freeSPF,#4


    clc
    lda __wolin_spf
    adc #4
    sta __wolin_spf
    bcc :+
    inc __wolin_spf+1
:

; ret

    rts

; label__wolin_pl_qus_wolin_Test

__wolin_pl_qus_wolin_Test:

; allocSP<__wolin_reg4>,#2


    dex
    dex

; allocSPF,#6


    clc
    lda __wolin_spf
    sbc #6
    sta __wolin_spf
    bcs :+
    dec __wolin_spf+1
:

; letSPF(2)[uword]=#3[uword]


    ldy #2
    lda #<3
    sta (__wolin_spf),y
    iny
    lda #>3
    sta (__wolin_spf),y

; letSPF(0)[uword]=#1[uword]


    ldy #0
    lda #<1
    sta (__wolin_spf),y
    iny
    lda #>1
    sta (__wolin_spf),y

; call__wolin_pl_qus_wolin_allocMem[adr]

    jsr __wolin_pl_qus_wolin_allocMem

; letSP(0)<__wolin_reg4>[ptr]=SPF(0)<returnValue>[ptr]


     ldy #0
     lda (__wolin_spf),y
     sta 0,x
     iny
     lda (__wolin_spf),y
     sta 0+1,x

; freeSPF<Any>,#2


    clc
    lda __wolin_spf
    adc #2
    sta __wolin_spf
    bcc :+
    inc __wolin_spf+1
:

; letSPF(0)<pl.qus.wolin.Test.returnValue>[ptr]=SP(0)<__wolin_reg4>[ptr]


    ldy #0
    lda 0,x
    sta (__wolin_spf),y
    iny
    lda 0+1,x
    sta (__wolin_spf),y

; setupHEAP=SP(0)<__wolin_reg4>[ptr]


    lda 0,x
    sta __wolin_this_ptr
    lda 0+1,x
    sta __wolin_this_ptr+1

; freeSP<__wolin_reg4>,#2


    inx
    inx

; allocSP<__wolin_reg5>,#1

    dex

; letSP(0)<__wolin_reg5>[ubyte]=#3[ubyte]


    lda #3
    sta 0,x

; letHEAP(2)<pl.qus.wolin.Test.x>[ubyte]=SP(0)<__wolin_reg5>[ubyte]


    lda 0,x
    ldy #2
    sta (__wolin_this_ptr),y

; freeSP<__wolin_reg5>,#1

    inx

; allocSP<__wolin_reg6>,#1

    dex

; letSP(0)<__wolin_reg6>[ubyte]=#7[ubyte]


    lda #7
    sta 0,x

; letHEAP(1)<pl.qus.wolin.Test.y>[ubyte]=SP(0)<__wolin_reg6>[ubyte]


    lda 0,x
    ldy #1
    sta (__wolin_this_ptr),y

; freeSP<__wolin_reg6>,#1

    inx

; ret

    rts

; label__wolin_pl_qus_wolin_Test_suma

__wolin_pl_qus_wolin_Test_suma:

; setupHEAP=this


    ldy #0 ; this pointer from SPF to this pointer on ZP
    lda (__wolin_spf),y
    sta __wolin_this_ptr
    iny
    lda (__wolin_spf),y
    sta __wolin_this_ptr+1

; allocSP<__wolin_reg8>,#1

    dex

; letSP(0)<__wolin_reg8>[ubyte]=HEAP(2)<pl.qus.wolin.Test.x>[ubyte]


    ldy #2 ; assuming this ZP is set!
    lda (__wolin_this_ptr),y
    sta 0,x

; allocSP<__wolin_reg9>,#1

    dex

; letSP(0)<__wolin_reg9>[ubyte]=HEAP(1)<pl.qus.wolin.Test.y>[ubyte]


    ldy #1 ; assuming this ZP is set!
    lda (__wolin_this_ptr),y
    sta 0,x

; addSP(1)<__wolin_reg8>[ubyte]=SP(1)<__wolin_reg8>[ubyte],SP(0)<__wolin_reg9>[ubyte]


    clc
    lda 1,x
    adc 0,x
    sta 1,x

; freeSP<__wolin_reg9>,#1

    inx

; letSPF(2)<returnValue>[ubyte]=SP(0)<__wolin_reg8>[ubyte]


    lda 0,x
    ldy #2
    sta (__wolin_spf),y

; freeSP<__wolin_reg8>,#1

    inx

; freeSPF,#2


    clc
    lda __wolin_spf
    adc #2
    sta __wolin_spf
    bcc :+
    inc __wolin_spf+1
:

; ret

    rts

; label__wolin_pl_qus_wolin_main

__wolin_pl_qus_wolin_main:

; allocSP<__wolin_reg13>,#2


    dex
    dex

; allocSP<__wolin_reg14>,#2


    dex
    dex

; allocSPF,#2


    clc
    lda __wolin_spf
    sbc #2
    sta __wolin_spf
    bcs :+
    dec __wolin_spf+1
:

; call__wolin_pl_qus_wolin_Test[adr]

    jsr __wolin_pl_qus_wolin_Test

; letSP(0)<__wolin_reg14>[ptr]=SPF(0)<returnValue>[ptr]


     ldy #0
     lda (__wolin_spf),y
     sta 0,x
     iny
     lda (__wolin_spf),y
     sta 0+1,x

; freeSPF<pl.qus.wolin.Test>,#2


    clc
    lda __wolin_spf
    adc #2
    sta __wolin_spf
    bcc :+
    inc __wolin_spf+1
:

; letSPF(0)<pl.qus.wolin.main..testowa>[ptr]=SP(0)<__wolin_reg14>[ptr]


    ldy #0
    lda 0,x
    sta (__wolin_spf),y
    iny
    lda 0+1,x
    sta (__wolin_spf),y

; freeSP<__wolin_reg14>,#2


    inx
    inx

; freeSP<__wolin_reg13>,#2


    inx
    inx

; allocSP<__wolin_reg16>,#1

    dex

; allocSP<__wolin_reg17>,#1

    dex

; allocSP<__wolin_reg18>,#2


    dex
    dex

; letSP(0)<__wolin_reg18>[ptr]=SPF(0)<pl.qus.wolin.main..testowa>[ptr]


     ldy #0
     lda (__wolin_spf),y
     sta 0,x
     iny
     lda (__wolin_spf),y
     sta 0+1,x

; allocSP<__wolin_reg19>,#1

    dex

; allocSPF,#3


    clc
    lda __wolin_spf
    sbc #3
    sta __wolin_spf
    bcs :+
    dec __wolin_spf+1
:

; letSPF(0)[ptr]=SP(1)<__wolin_reg18>[ptr]


    ldy #0
    lda 1,x
    sta (__wolin_spf),y
    iny
    lda 1+1,x
    sta (__wolin_spf),y

; call__wolin_pl_qus_wolin_Test_suma[adr]

    jsr __wolin_pl_qus_wolin_Test_suma

; letSP(0)<__wolin_reg19>[ubyte]=SPF(0)<returnValue>[ubyte]


    ldy #0
    lda (__wolin_spf),y
    sta 0,x


; freeSPF<ubyte>,#1


    clc
    lda __wolin_spf
    adc #1
    sta __wolin_spf
    bcc :+
    inc __wolin_spf+1
:

; letSP(3)<__wolin_reg17>[ubyte]=SP(0)<__wolin_reg19>[ubyte]


    lda 0,x
    sta 3,x

; freeSP<__wolin_reg19>,#1

    inx

; freeSP<__wolin_reg18>,#2


    inx
    inx

; let53280[ubyte]=SP(0)<__wolin_reg17>[ubyte]


    lda 0,x
    sta 53280


; freeSP<__wolin_reg17>,#1

    inx

; freeSP<__wolin_reg16>,#1

    inx

; freeSPF,#2


    clc
    lda __wolin_spf
    adc #2
    sta __wolin_spf
    bcc :+
    inc __wolin_spf+1
:

; ret

    rts

; label__wolin_indirect_jsr

__wolin_indirect_jsr:

; goto65535[adr]

    jmp 65535

; label__wolin_pl_qus_wolin_znak

__wolin_pl_qus_wolin_znak:

; alloc0[ubyte]

    .byte 0

; label__wolin_pl_qus_wolin_i

__wolin_pl_qus_wolin_i:

; alloc0[uword]

    .word 0


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 31, 2019 8:52 pm 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
I was on vacations, so no updates, but last time I did something in Wolin was proper assignment left side handling. Until now it was pretty naive - it allowed either plain variable or array. But of course it mus handle more complex things like:

Code:
objectInstance.someField = 44


or:

Code:
objectArrayInstance[4].someField = 69


or even:

Code:
objectA.objectB[666].objectC[32].someField = 33


Which of course requres non-trivial logic. And it is indeed... non-trivial to code it...


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 171 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7, 8 ... 12  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 25 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: