Wolin - a minimal Kotlin-like language compiler for 65xx
- GARTHWILSON
- Forum Moderator
- Posts: 8773
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
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.
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?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
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.
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.
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
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.
Anyway. Trying to implement a simple raster proggy form here:
https://www.c64-wiki.com/wiki/Raster_interrupt
Wolin:
Code: Select all
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
}
Code: Select all
function __wolin_pl_qus_wolin_rasterProc
let 53280[ubyte] = #7[ubyte]
let 53280[ubyte] = #0[ubyte]
let 53273[ubyte] = #0[ubyte]
goto 59953
Code: Select all
__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
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
So, gentelmen (hoping it's non-woke forum...) first working raster proggy in Wolin:
Of course
would work too!
It compiles to:
Try it on your C64 or emulator!
Code: Select all
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.
}
Code: Select all
vicScreenCtrlReg1 .= 128 // High bit of raster line cleared, we're only working within single byte rangesIt compiles to:
Code: Select all
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
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
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...
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:
Tadaaam!
And final code:
Code: Select all
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)
}
Will be called like this:
Code: Select all
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
And final code:
Code: Select all
; 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
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
Optimization can bring surprises. I.e. registers get shifted behind their deallocs:
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...
Code: Select all
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!
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...
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
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:
Which compiles to bloated:
Now gets optimized to:
How about that?
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: Select all
fun suma(a: ubyte, b: ubyte): ubyte {
return a+b
}
Code: Select all
// ****************************************
// 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
Code: Select all
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?
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
Nice! Would we call that renaming?
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
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.
Whatever it's called, it's definitely effective at cutting out a lot of cruft.
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
Yes, yes! Something along these lines, like "retrospective register subsitution" 
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
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
Ha! And coming back to assembler optimizer... This would be probably always safe:
BCS xxxx
CLC
LDA
ADC
optimized to:
BCS xxxx
LDA
ADC
- GARTHWILSON
- Forum Moderator
- Posts: 8773
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
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.
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?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
GARTHWILSON wrote:
qus wrote:
why did they design it this way?
GARTHWILSON wrote:
qus wrote:
why did they design it this way?
Explanations are in all 65xx programming manuals.
Code: Select all
sub ?dest[uword] = ?src[uword], #?val[uword] -> """
sec
lda {src}
sbc #<{val}
sta {dest}
lda {src}+1
sbc #>{val}
sta {dest}+1
"""
EDIT - ah, yes! It's in alloc SPE/SPF! That would explode!
- BitWise
- In Memoriam
- Posts: 996
- Joined: 02 Mar 2004
- Location: Berkshire, UK
- Contact:
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
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.
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
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
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
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:
To pseudo asm:
And finally to our favorite CPU:
Code: Select all
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")
}
Code: Select all
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"
Code: Select all
; 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"
Re: Wolin - a minimal Kotlin-like language compiler for 65xx
Anyone able to help me out? I'm looking for code to do this:
This gets translated into:
meaning: "assign value stored at (pointer on functionStack(1)+#3) to functionStack(0)"
I access function stack by:
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
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?
Code: Select all
fun print(what: string) {
val fromArray: ubyte
fromArray = what[3]
}
Code: Select all
let SPF(0)<pl.qus.wolin.print..fromArray>[ubyte] = &SPF(1)<pl.qus.wolin.print.what>[ubyte*], #3[ubyte]
I access function stack by:
Code: Select all
ldy #stack_position
lda/sta (__wolin_spf), y
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: Select all
ldy #{d}
sta (__wolin_spf), y