6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Sep 20, 2024 5:44 am

All times are UTC




Post new topic Reply to topic  [ 171 posts ]  Go to page Previous  1 ... 5, 6, 7, 8, 9, 10, 11, 12  Next
Author Message
PostPosted: Fri Jan 17, 2020 9:35 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8510
Location: Southern California
qus wrote:
I forgot to mention that before but there was another reason for creating Wolin. An argument with some guy on C64 forum that you can open the border ONLY from assembler. He was unable to provide a reason for this ridiculous statement, though.

Was it that there's too much to do in a short time (because of the speed of the scanning beam which won't wait for you)? Or did he think that interrupts can only be serviced in assembly language (which of course is not true)?

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Sat Jan 18, 2020 3:08 pm 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
drogon wrote:
qus wrote:
Update.

I forgot to mention that before but there was another reason for creating Wolin. An argument with some guy on C64 forum that you can open the border ONLY from assembler. He was unable to provide a reason for this ridiculous statement, though.


Hehe... Well done! I love this sort of stuff!

But no different, really, from being able to point an interrupt (if that's what it takes, I know nothing about the C64 hardware) to a function in C - it's done daily in just about every CPU that supports C ... So if not C, then anything else that can do the right thing at function entry/exit...

-Gordon


Yeah. But the guy knew better...

GARTHWILSON wrote:
qus wrote:
I forgot to mention that before but there was another reason for creating Wolin. An argument with some guy on C64 forum that you can open the border ONLY from assembler. He was unable to provide a reason for this ridiculous statement, though.

Was it that there's too much to do in a short time (because of the speed of the scanning beam which won't wait for you)? Or did he think that interrupts can only be serviced in assembly language (which of course is not true)?


Well, it's hard to tell really, as he didn't elaborate. I can only suspect he was along the line "compilers produce too complicated and too bloated code for it to work..."

Anyway. Trying to implement a simple raster proggy form here:

https://www.c64-wiki.com/wiki/Raster_interrupt

Wolin:

Code:
package pl.qus.wolin

var interruptRoutineVector: uword^0x314 // this is C64 raster interrupt vector
var cia1InerruptCtrlReg: ubyte^0xDC0D
var vicScreenCtrlReg1: ubyte^0xD011
var vicRasterLine: ubyte^0xD012
var vicInterruptStatusReg: ubyte^0xd019
var vicInterruptCtrlReg: ubyte^0xd01a
var vicBorder: ubyte^53280

fun rasterProc() {
   vicBorder = 7 // Turn screen frame yellow
   // Empty loop that "does nothing" for a little under a half millisecond
   vicBorder = 0 // Switch frame color back to black
    vicInterruptStatusReg = 0 // "Acknowledge" the interrupt by clearing the VIC's interrupt flag. (ASL)
    return@0xEA31 // JMP $EA31   Jump into KERNAL's standard interrupt service routine to handle keyboard scan, cursor display etc.
}

fun main() {
    cia1InerruptCtrlReg = 127 // "Switch off" interrupts signals from CIA-1 (bity 0-6 ustawione na 1 przestawia na 0)
    //vicScreenCtrlReg1 &= 127  // vicScreenCtrlReg1 and to, co powyżej // Clear most significant bit in VIC's raster register
    vicRasterLine = 210 // Set the raster line number where interrupt should occur
    interruptRoutineVector = rasterProc // Set the interrupt vector to point to interrupt service routine
    vicInterruptCtrlReg = 1 // Enable raster interrupt signals from VIC
}


Notice new syntax - return@addr that tells the function to goto addr instead of returning from subroutine, so the compiler generates this pseudo asm, without "ret" pseudo-op:

Code:
function __wolin_pl_qus_wolin_rasterProc
let 53280[ubyte] = #7[ubyte]
let 53280[ubyte] = #0[ubyte]
let 53273[ubyte] = #0[ubyte]
goto 59953


Which gives:

Code:
__wolin_pl_qus_wolin_rasterProc:

; let53280[ubyte]=#7[ubyte]


    lda #7
    sta 53280

; let53280[ubyte]=#0[ubyte]


    lda #0
    sta 53280

; let53273[ubyte]=#0[ubyte]


    lda #0
    sta 53273

; goto59953[adr]

    jmp 59953


I will probably add special "interrupt" annotation to functions, so they "rti" instead of "rts" + will complain if the function has arguments/return values.


Top
 Profile  
Reply with quote  
PostPosted: Mon Jan 20, 2020 3:46 pm 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
So, gentelmen (hoping it's non-woke forum...) first working raster proggy in Wolin:

Code:
package pl.qus.wolin

// raster interrupt colour band taken from:
// https://gist.github.com/bremensaki/8f33cd7d67b78377881c7eb7147c0f32

var interruptRoutineVector: uword^0x314 // this is C64 raster interrupt vector
var cia1InerruptCtrlReg: ubyte^0xDC0D
var vicScreenCtrlReg1: ubyte^0xD011
var vicRasterLine: ubyte^0xD012
var vicInterruptStatusReg: ubyte^0xd019
var vicInterruptCtrlReg: ubyte^0xd01a
var vicBorder: ubyte^53280
var vicBackground: ubyte^53281
var i: ubyte
var maskInterrupts: bool^CPU.I

fun clearScreen^0xe544()

interrupt fun backgroundToBlue() {
    interruptRoutineVector = backgroundToWhite
    vicRasterLine = 140
    vicBorder = 6
    vicBackground = 6
    vicInterruptStatusReg = 0xff
    return@0xea31
}

interrupt fun backgroundToWhite() {
    interruptRoutineVector = backgroundToBlue
    vicRasterLine = 160
    vicBorder = 1
    vicBackground = 1
    vicInterruptStatusReg = 0xff
    return@0xea31
}

fun main() {
    clearScreen()
    vicBorder = 6                                    // Init screen and border to blue
    vicBackground = 6
    maskInterrupts = true                            // Suspend interrupts during init
    cia1InerruptCtrlReg = 0x7f                       // Disable CIA
    vicInterruptCtrlReg := 1                         // Enable raster interrupts
    vicScreenCtrlReg1 &= 0x7f                        // High bit of raster line cleared, we're only working within single byte ranges
    vicRasterLine = 140                              // We want an interrupt at the top line
    interruptRoutineVector = backgroundToBlue        // IRQ vector addresses
    maskInterrupts = false                           // Enable interrupts again

    do {} while (true)                               // Eternal do-nothing loop, we're done.
}


Of course
Code:
    vicScreenCtrlReg1 .= 128                         // High bit of raster line cleared, we're only working within single byte ranges


would work too!

It compiles to:

Code:

BasEnd:     .word 0
            .word 0
            ;


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


; prepare function stack
__wolin_spf := 251 ; function stack ptr
__wolin_spf_hi := 251+1 ; function stack ptr

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

; setupSP=114[ubyte]


; prepare program stack
__wolin_sp_top := 114 ; program stack top
__wolin_sp_top_hi := 114+1 ; program stack top
    ldx #__wolin_sp_top ; set program stack top

; setupHEAP=176[ubyte]


__wolin_this_ptr := 176
__wolin_this_ptr_hi := 176+1


; allocSPF,#0

 

; call__wolin_pl_qus_wolin_main[adr]

    jsr __wolin_pl_qus_wolin_main

; ret

    rts

; function__wolin_pl_qus_wolin_backgroundToBlue

__wolin_pl_qus_wolin_backgroundToBlue:

; let788[uword]=__wolin_pl_qus_wolin_backgroundToWhite[uword]


    lda #<__wolin_pl_qus_wolin_backgroundToWhite
    sta 788
    lda #>__wolin_pl_qus_wolin_backgroundToWhite
    sta 788+1


; let53266[ubyte]=#140[ubyte]


    lda #140
    sta 53266

; let53280[ubyte]=#6[ubyte]


    lda #6
    sta 53280

; let53281[ubyte]=#6[ubyte]


    lda #6
    sta 53281

; let53273[ubyte]=#255[ubyte]


    lda #255
    sta 53273

; goto59953[adr]

    jmp 59953

; function__wolin_pl_qus_wolin_backgroundToWhite

__wolin_pl_qus_wolin_backgroundToWhite:

; let788[uword]=__wolin_pl_qus_wolin_backgroundToBlue[uword]


    lda #<__wolin_pl_qus_wolin_backgroundToBlue
    sta 788
    lda #>__wolin_pl_qus_wolin_backgroundToBlue
    sta 788+1


; let53266[ubyte]=#160[ubyte]


    lda #160
    sta 53266

; let53280[ubyte]=#1[ubyte]


    lda #1
    sta 53280

; let53281[ubyte]=#1[ubyte]


    lda #1
    sta 53281

; let53273[ubyte]=#255[ubyte]


    lda #255
    sta 53273

; goto59953[adr]

    jmp 59953

; function__wolin_pl_qus_wolin_main

__wolin_pl_qus_wolin_main:

; allocSPF,#0

 

; call58692[adr]

    jsr 58692

; let53280[ubyte]=#6[ubyte]


    lda #6
    sta 53280

; let53281[ubyte]=#6[ubyte]


    lda #6
    sta 53281

; letCPU.I[bool]=#1[bool]


    sei


; let56333[ubyte]=#127[ubyte]


    lda #127
    sta 56333

; bit53274[ubyte]=#1[ubyte],#1[bool]


    lda 53274
    ora 1
    sta 53274


; and53265[ubyte]=#127[ubyte]


    lda 53265
    and #127
    sta 53265


; let53266[ubyte]=#140[ubyte]


    lda #140
    sta 53266

; let788[uword]=__wolin_pl_qus_wolin_backgroundToBlue[uword]


    lda #<__wolin_pl_qus_wolin_backgroundToBlue
    sta 788
    lda #>__wolin_pl_qus_wolin_backgroundToBlue
    sta 788+1


; letCPU.I[bool]=#0[bool]


    cli


; allocSP<__wolin_reg63>,#1

    dex

; label__wolin_lab_loopStart_1

__wolin_lab_loopStart_1:

; letSP(0)<__wolin_reg63>[bool]=#1[bool]


    lda #1
    sta 0,x

; beqSP(0)<__wolin_reg63>[bool]=#1[bool],__wolin_lab_loopStart_1<label_po_if>[uword]


    lda 0,x
    bne __wolin_lab_loopStart_1

; label__wolin_lab_loopEnd_1

__wolin_lab_loopEnd_1:

; freeSP<__wolin_reg63>,#1

    inx

; ret

    rts

; label__wolin_indirect_jsr

__wolin_indirect_jsr:

; goto65535[adr]

    jmp 65535

; label__wolin_pl_qus_wolin_i

__wolin_pl_qus_wolin_i:

; alloc0[ubyte]

    .byte 0


Try it on your C64 or emulator!


Top
 Profile  
Reply with quote  
PostPosted: Sat Jan 25, 2020 4:26 pm 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
Aaaawright. Having border open and some sprite there I went back to calling system/kernal procs. Someone suggested stubs, but since I'm still hoping I can make Wolin behave in multi-threaded environment I found such solution clumsy. So...

Code:
package pl.qus.wolin

fun save^0xffd8(zpPointerToStart: ubyte^CPU.A, endAddr: uword^CPU.XY): bool^CPU.C // C == 1 means error occured
fun chrin^0xffcf(): ubyte^CPU.A

val wynik: bool

/*********************************************************
* Main function
**********************************************************/

fun main() {
    wynik = save(10,12345)
}


Since calculating final values of calling args may require Wolin stack (X), which may overwrite our argument located at X, and the routine may trash X anyway, we have to do some smart shuffling...
Will be called like this:

Code:
function __wolin_pl_qus_wolin_main
alloc SPF, #1 // alloc function return value
save SP // push "our" stack pointer (X) to CPU stack
// here we could have some calculations made on Wolin stack and we would be pushingmZP stack regs instead
// also further arguments may overwrite A, X and Y required for function call so...
save #10[ubyte] // we push our final value to CPU stack
// here we could have some other calculations on arguments that would overwrite A na change X, Y
save #12345[uword] // so we push final value to CPU stack
restore CPU.XY[uword] // restore x, y
restore CPU.A[ubyte] // restore a (note - argument processing is always reshuffled with A going last, because storing X and Y on 6502 requires A)
call 65496[adr] // with A,X and Y set we can run!
let SPF(0)<pl.qus.wolin.save.__returnValue>[bool] = CPU.C[bool] // set return bool value to contents of C flag
restore SP // restore Wolin SP (x)
let __wolin_pl_qus_wolin_wynik<pl.qus.wolin.wynik>[bool] = SPF(0)<pl.qus.wolin.save.__returnValue>[bool] // set wynik to our bool
free SPF<pl.qus.wolin.save.__returnValue>, #1
ret


Tadaaam!

And final code:

Code:
; setupHEADER


;**********************************************
;*
;* BASIC header
;*
;* compile with:
;* cl65.exe -o assembler.prg -t c64 -C c64-asm.cfg -g -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_hi := 251+1 ; function stack ptr

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

; setupSP=114[ubyte]


; prepare program stack
__wolin_sp_top := 114 ; program stack top
__wolin_sp_top_hi := 114+1 ; program stack top
    ldx #__wolin_sp_top ; set program stack top

; setupHEAP=176[ubyte]


__wolin_this_ptr := 176
__wolin_this_ptr_hi := 176+1


; allocSPF,#0

 

; call__wolin_pl_qus_wolin_main[adr]

    jsr __wolin_pl_qus_wolin_main

; ret

    rts

; function__wolin_pl_qus_wolin_main

__wolin_pl_qus_wolin_main:

; allocSPF,#1


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

; saveSP


    txa
    pha

; save#10[ubyte]


    lda #10
    pha


; save#12345[uword]


    lda #<12345
    pha
    lda #>12345
    pha


; restoreCPU.XY[uword]


    pla
    tay
    pla
    tax


; restoreCPU.A[ubyte]


    pla

; call65496[adr]

    jsr 65496

; letSPF(0)<pl.qus.wolin.save.__returnValue>[bool]=CPU.C[bool]


    ldy #0
    lda #1
    bcs :+
    lda #0
:
    sta (__wolin_spf),y



; restoreSP


    pla
    tax

; let__wolin_pl_qus_wolin_wynik<pl.qus.wolin.wynik>[bool]=SPF(0)<pl.qus.wolin.save.__returnValue>[bool]


    ldy #0
    lda (__wolin_spf),y
    sta __wolin_pl_qus_wolin_wynik


; freeSPF<pl.qus.wolin.save.__returnValue>,#1


    clc
    lda __wolin_spf
    adc #1
    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_wynik

__wolin_pl_qus_wolin_wynik:

; alloc0[bool]

    .byte 0


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 05, 2020 5:23 pm 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
Optimization can bring surprises. I.e. registers get shifted behind their deallocs:

Code:
alloc SP<__wolin_reg10>, #2
let SP(0)<__wolin_reg10>[ubyte*] = #1024[uword]
add __wolin_pl_qus_wolin_i<pl.qus.wolin.i>[uword] = __wolin_pl_qus_wolin_i<pl.qus.wolin.i>[uword], #1[uword]
add SP(0)<__wolin_reg10>[ubyte*] = SP(0)<__wolin_reg10>[ubyte*], &*__wolin_pl_qus_wolin_i<pl.qus.wolin.i>[uword]
free SP<__wolin_reg10>, #2
add __wolin_pl_qus_wolin_chr<pl.qus.wolin.chr>[ubyte] = __wolin_pl_qus_wolin_chr<pl.qus.wolin.chr>[ubyte], #1[ubyte]
let &SP(-2)<__wolin_reg10>[ubyte*] = &*__wolin_pl_qus_wolin_chr<pl.qus.wolin.chr>[ubyte] <------- oooops reg10 is levitating above the stack!


So I will be working on moving alloc and free around, it will come in handy when I get to loop optimization!

Additionaly, I changed reference substitution logic in optimizer to late resolving. I.e. before when I was substituting pointer reg into deref (&) argument, I would immediately cancel out * and & making it a normal, direct variable. But that meant the possibly useful & (if aonther substitution should occur) was lost and gone, and I would end up with unecessary pointers. That's why you can see "&*variable" in some places, I am going to cancel them out after no more substitutions are possible.

And lastly - I'm doing optimizations from top (newest regs) to bottom (oldert regs) which seems to work better...


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 04, 2020 11:47 am 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
Today I finally managed to implement last optimization I though of before. Not sure how to call them, but they work like this:

If there's some operation that has register A as target:

op A = whatever

And later on A register is put into B:

let B = A

Then first line can be optimized to:

op B = whatever
// let B = A removed

Thus a simple function:

Code:
fun suma(a: ubyte, b: ubyte): ubyte {
    return a+b
}


Which compiles to bloated:

Code:
// ****************************************
// funkcja: fun pl.qus.wolin.suma(pl.qus.wolin.suma.a: ubyte = 0 () location = null, null, pl.qus.wolin.suma.b: ubyte = 0 () location = null, null):ubyte
// ****************************************
function __wolin_pl_qus_wolin_suma
alloc SP<__wolin_reg1>, #1 // for expression
alloc SP<__wolin_reg2>, #2 // LEFT adding operator
let SP(0)<__wolin_reg2>[ubyte*] = *SPF(1)<pl.qus.wolin.suma.a>[ubyte] // przez sprawdzacz typow - simple id from var
// switchType to:ubyte by type from pl.qus.wolin.suma.a
// top type already set: __wolin_reg2: ubyte* = 0 (LEFT adding operator) location = null, null
alloc SP<__wolin_reg3>, #2 // RIGHT adding operator
let SP(0)<__wolin_reg3>[ubyte*] = *SPF(0)<pl.qus.wolin.suma.b>[ubyte] // przez sprawdzacz typow - simple id from var
// switchType to:ubyte by type from pl.qus.wolin.suma.b
// top type already set: __wolin_reg3: ubyte* = 0 (RIGHT adding operator) location = null, null
add &SP(4)<__wolin_reg1>[ubyte] = &SP(2)<__wolin_reg2>[ubyte*], &SP(0)<__wolin_reg3>[ubyte*]
free SP<__wolin_reg3>, #2 // RIGHT adding operator
free SP<__wolin_reg2>, #2 // LEFT adding operator
// top type already set: __wolin_reg1: ubyte = 0 (for expression) location = null, null
let SPF(2)<pl.qus.wolin.suma.__returnValue>[ubyte] = &SP(0)<__wolin_reg1>[ubyte] // przez sprawdzacz typow - jump expression
// switchType to:ubyte by return expression
// top type already set: __wolin_reg1: ubyte = 0 (for expression) location = null, null
free SP<__wolin_reg1>, #1 // for expression
free SPF<pl.qus.wolin.suma.__fnargs>, #2 // free fn arguments and locals for pl.qus.wolin.suma
// ***** fnDeclFreeStackAndRet usuwanie zwrotki pl.qus.wolin.suma ze stosu
// freeing call stack
// return from function body
ret


Now gets optimized to:

Code:
function __wolin_pl_qus_wolin_suma
add SPF(2)<pl.qus.wolin.suma.__returnValue>[ubyte] = SPF(1)<pl.qus.wolin.suma.a>[ubyte], SPF(0)<pl.qus.wolin.suma.b>[ubyte]
free SPF<pl.qus.wolin.suma.__fnargs>, #2
ret



How about that?


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 04, 2020 12:00 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
Nice! Would we call that renaming?


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 04, 2020 12:52 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
This could be considered a combination of register allocation with common subexpression elimination, more usually carried out via a transformation to Single Static Assignment form.

Whatever it's called, it's definitely effective at cutting out a lot of cruft.


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 04, 2020 2:03 pm 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
Yes, yes! Something along these lines, like "retrospective register subsitution" ;)


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 06, 2020 6:18 am 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
This morning I checked if C gets cleared automatically on add/sub, just to be sure (but I guess it was pretty obvious since nobody here roasted me about doing CLC before ADC) and it obviously doesn't, but that made me wonder - why did they design it this way? Setting C on addition/substraction overflow doesn't cost additional cycles, right? So why didn't they want to spare us cycles wasted on CLC and always set or clear C accordingly?

Ha! And coming back to assembler optimizer... This would be probably always safe:

BCS xxxx
CLC
LDA
ADC

optimized to:

BCS xxxx
LDA
ADC


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 06, 2020 6:26 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8510
Location: Southern California
qus wrote:
why did they design it this way?

It's necessary for multi-byte arithmetic.

As I look back over your other recent posts, I think I should point out that subtraction will normally start with SEC, not CLC.

Explanations are in all 65xx programming manuals.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 06, 2020 6:47 am 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
GARTHWILSON wrote:
qus wrote:
why did they design it this way?

It's necessary for multi-byte arithmetic.


Heh, right. I forgot that one :D

GARTHWILSON wrote:
qus wrote:
why did they design it this way?

As I look back over your other recent posts, I think I should point out that subtraction will normally start with SEC, not CLC.

Explanations are in all 65xx programming manuals.


Well, I see this in my template:

Code:
sub ?dest[uword] = ?src[uword], #?val[uword] -> """
    sec
    lda {src}
    sbc #<{val}
    sta {dest}
    lda {src}+1
    sbc #>{val}
    sta {dest}+1
"""


So if I CLC before SBC then it must be an error somewhere else...

EDIT - ah, yes! It's in alloc SPE/SPF! That would explode!


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 06, 2020 9:49 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
GARTHWILSON wrote:
qus wrote:
why did they design it this way?

It's necessary for multi-byte arithmetic.

As I look back over your other recent posts, I think I should point out that subtraction will normally start with SEC, not CLC.

Explanations are in all 65xx programming manuals.

Some other processors of the same era (like the 6800 and 8080) had separate ADD and SUB instructions which don't take the carry as input but produce a carry output. So on a 6800 a multibyte add starts with an ADD and then uses ADC for subsequent bytes.

These processors can do this as they have very few addressing modes, only 4 on the 6800 and 2 on the 8080 (register A-L and HL indirect - immediate is handed separately). The 6502 has 8 addressing modes for ADC while the 65C02 has 9. Just having ADC and SBC and using CLC/SEC minimises the total number of opcodes used for these operations leaving room for more addressing modes on other instructions.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 14, 2020 2:20 pm 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
While I am stuck with many things that unlike OO are really hard to implement, I've added some very basic string support. So, this is a piece of code that sends a command to C64 disk drive:

Code:
package pl.qus.wolin

fun chkin^0xFFC6(lfn: ubyte^CPU.X): bool^CPU.C
fun getin^0xFFE4(): bool^CPU.A
fun readst^0xFFB7(): ubyte^CPU.A
fun clrchn^0xFFCC()
fun setlfs^0xFFBA(lfn: ubyte^CPU.A, device: ubyte^CPU.X, sa: ubyte^CPU.Y)
fun setnam^0xFFBD(fnameLen: ubyte^CPU.A, name: string^CPU.XY)
fun kopen^0xFFC0()

fun openCommandChannel(lfn: ubyte, device: ubyte, command: string) {
    setlfs(lfn, device, 15)
    setnam(10, command)
    kopen()
}

fun main() {
    openCommandChannel(15,8,"DUPA")
}


To pseudo asm:

Code:
setup HEADER
setup SPF = 251[ubyte], 40959[uword]
setup SP = 114[ubyte]
setup HEAP = 176[ubyte]
alloc SPF, #0
call __wolin_pl_qus_wolin_main[uword]
endfunction

function __wolin_pl_qus_wolin_openCommandChannel
alloc SPF, #0
save SP
save SPF(3)<pl.qus.wolin.openCommandChannel.lfn>[ubyte]
save SPF(2)<pl.qus.wolin.openCommandChannel.device>[ubyte]
save #15[ubyte]
restore CPU.Y[ubyte]
restore CPU.X[ubyte]
restore CPU.A[ubyte]
call 65466[uword]
restore SP
alloc SPF, #0
save SP
save #10[ubyte]
save SPF(0)<pl.qus.wolin.openCommandChannel.command>[ubyte*]
restore CPU.XY[ubyte*]
restore CPU.A[ubyte]
call 65469[uword]
restore SP
alloc SPF, #0
save SP
call 65472[uword]
restore SP
free SPF<pl.qus.wolin.openCommandChannel.__fnargs>, #4
endfunction

function __wolin_pl_qus_wolin_main
alloc SPF, #4
let SPF(3)[ubyte] = #15[ubyte]
let SPF(2)[ubyte] = #8[ubyte]
let SPF(0)[ubyte*] = #__wolin_lab_stringConst_0[uword]
call __wolin_pl_qus_wolin_openCommandChannel[uword]
endfunction
string __wolin_lab_stringConst_0[uword] = $"DUPA"


And finally to our favorite CPU:

Code:
; setupHEADER


;**********************************************
;*
;* BASIC header
;*
;* compile with:
;* cl65.exe -o assembler.prg -t c64 -C c64-asm.cfg -g -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_hi := 251+1 ; function stack ptr

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

; setupSP=114[ubyte]


; prepare program stack
__wolin_sp_top := 114 ; program stack top
__wolin_sp_top_hi := 114+1 ; program stack top
    ldx #__wolin_sp_top ; set program stack top

; setupHEAP=176[ubyte]


__wolin_this_ptr := 176
__wolin_this_ptr_hi := 176+1


; allocSPF,#0

 

; call__wolin_pl_qus_wolin_main[uword]

    jsr __wolin_pl_qus_wolin_main

; endfunction

    rts

; function__wolin_pl_qus_wolin_openCommandChannel

__wolin_pl_qus_wolin_openCommandChannel:

; allocSPF,#0

 

; saveSP


    txa
    pha

; saveSPF(3)<pl.qus.wolin.openCommandChannel.lfn>[ubyte]


    ldy #3
    lda (__wolin_spf),y
    pha


; saveSPF(2)<pl.qus.wolin.openCommandChannel.device>[ubyte]


    ldy #2
    lda (__wolin_spf),y
    pha


; save#15[ubyte]


    lda #15
    pha


; restoreCPU.Y[ubyte]


    pla
    tay

; restoreCPU.X[ubyte]


    pla
    tax

; restoreCPU.A[ubyte]


    pla

; call65466[uword]

    jsr 65466

; restoreSP


    pla
    tax

; allocSPF,#0

 

; saveSP


    txa
    pha

; save#10[ubyte]


    lda #10
    pha


; saveSPF(0)<pl.qus.wolin.openCommandChannel.command>[ubyte*]


    ldy #0
    lda (__wolin_spf),y
    pha
    iny
    lda (__wolin_spf),y
    pha


; restoreCPU.XY[ubyte*]


    pla
    tay
    pla
    tax


; restoreCPU.A[ubyte]


    pla

; call65469[uword]

    jsr 65469

; restoreSP


    pla
    tax

; allocSPF,#0

 

; saveSP


    txa
    pha

; call65472[uword]

    jsr 65472

; restoreSP


    pla
    tax

; freeSPF<pl.qus.wolin.openCommandChannel.__fnargs>,#4


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

; endfunction

    rts

; function__wolin_pl_qus_wolin_main

__wolin_pl_qus_wolin_main:

; allocSPF,#4


    sec
    lda __wolin_spf
    sbc #4
    sta __wolin_spf
    bcs :+
    dec __wolin_spf+1
:

; letSPF(3)[ubyte]=#15[ubyte]


    ldy #3
    lda #15
    sta (__wolin_spf),y

; letSPF(2)[ubyte]=#8[ubyte]


    ldy #2
    lda #8
    sta (__wolin_spf),y

; letSPF(0)[ubyte*]=#__wolin_lab_stringConst_0[uword]


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

; call__wolin_pl_qus_wolin_openCommandChannel[uword]

    jsr __wolin_pl_qus_wolin_openCommandChannel

; endfunction

    rts

; string__wolin_lab_stringConst_0[uword]=$"DUPA"


__wolin_lab_stringConst_0:
    .str "DUPA"


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 16, 2020 1:06 pm 
Offline

Joined: Sat Apr 20, 2019 5:31 pm
Posts: 104
Anyone able to help me out? I'm looking for code to do this:

Code:
fun print(what: string) {
    val fromArray: ubyte

    fromArray = what[3]

}


This gets translated into:

Code:
let SPF(0)<pl.qus.wolin.print..fromArray>[ubyte] = &SPF(1)<pl.qus.wolin.print.what>[ubyte*], #3[ubyte]


meaning: "assign value stored at (pointer on functionStack(1)+#3) to functionStack(0)"

I access function stack by:

Code:
ldy #stack_position
lda/sta (__wolin_spf), y


So I need to somehow:

1) get address that is stored in two bytes on function stack

2) index it by val (or worst case - add val to it)

3) store the byta that sits in 2) at position d on function stack like this

Code:
ldy #{d}
sta (__wolin_spf), y


Since this is the "fast" array (single byte val indexed by single byte) I was hoping I can use standard X/Y indexing to do this, but I guess it won't be possible in such case, where I keep the array address on function sack, but only if the array is global and attached to some absolute memory location... Unless you have some solution for this case?


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

All times are UTC


Who is online

Users browsing this forum: No registered users and 10 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: