jsrfar stuff

This commit is contained in:
Irmen de Jong 2024-11-05 00:15:40 +01:00
parent 5b1143bcb3
commit 459e9f8f3b
5 changed files with 146 additions and 74 deletions

View File

@ -54,7 +54,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
)
}
"c64" -> {
// a bit bloated because it has to retain the status flags and A register
asmgen.out("""
; start doing a jsr to another bank
php
pha
lda $01
@ -75,27 +77,11 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
+""")
}
"c128" -> {
// see https://cx16.dk/c128-kernal-routines/jsrfar.html
asmgen.out("""
sty $08
stx $07
sta $06
php
pla
sta $05
lda #$bank
ldy #>$subAsmName
ldx #<$subAsmName
sta $02
sty $03
stx $04
jsr c128.JSRFAR
lda $05
pha
lda $06
ldx $07
ldy $08
plp""")
jsr c128.x16jsrfar
.word $subAsmName ; ${sub.address!!.second.toHex()}
.byte $bank"""
)
}
else -> throw AssemblyError("callfar is not supported on the selected compilation target")
}

View File

@ -345,6 +345,56 @@ asmsub disable_basic() clobbers(A) {
}}
}
asmsub x16jsrfar() {
%asm {{
; setup a JSRFAR call (using X16 call convention)
; see https://cx16.dk/c128-kernal-routines/jsrfar.html
sty $08 ; save registers
stx $07
sta $06
php ; including PSR
pla
sta $05
pla ; get original return address
sta $fa ; and store it in a temp ZP pointer
pla
sta $fb
ldy #$01
lda ($fa),y ; grab low byte of target address
sta $04
iny
lda ($fa),y ; now the high byte
sta $03
iny
lda ($fa),y ; then the target bank
sta $02
; replace the original return address by it + 3 to skip the data bytes
clc
lda $fa
adc #3
sta $fa
lda $fb
adc #0
pha
lda $fa
pha
jsr c128.JSRFAR ; call kernal's jsrfar routine
lda $05 ; populate registers
pha
lda $06
ldx $07
ldy $08
plp
rts ; and return
}}
}
; ---- end of C128 specific system utility routines ----
}

View File

@ -165,10 +165,10 @@ internal fun Subroutine.hasRtsInAsm(checkOnlyLastInstruction: Boolean): Boolean
.filterIsInstance<InlineAssembly>()
if(checkOnlyLastInstruction) {
val lastAsm = asms.lastOrNull() ?: return false
val lastLine = lastAsm.assembly.lineSequence().map { it.trim() }.last {
val lastLine = lastAsm.assembly.lineSequence().map { it.trim() }.lastOrNull {
it.isNotBlank() && (!it.startsWith(';') || it.contains("!notreached!"))
}
if(lastLine.contains("!notreached!"))
if(lastLine?.contains("!notreached!")==true)
return true
val inlineAsm = InlineAssembly(" $lastLine", lastAsm.isIR, lastAsm.position)
return inlineAsm.hasReturnOrRts()

View File

@ -1,6 +1,8 @@
TODO
====
in translateFunctionCall(), make the JSRFAR bloat code for the C64 into a subroutine like the kernal JSRFAR on the X16
fix that on C64 when program ends halfway in basic address space $a000+ the sysinit code that banks out the basic Rom
is actually not reachable yet because it's at the end of the program in basic rom space?

View File

@ -1,66 +1,100 @@
%import textio
%zeropage basicsafe
%option no_sysinit
main {
romsub @bank 0 $4000 = routine(uword argument @AY) -> ubyte @A
romsub @bank 15 $ffd2 = character_out(ubyte char @A)
sub start() {
basic_area.routine1()
hiram_area.routine2()
sys.memcopy(&the_routine, $4000, 255)
; copy the kernal area routine to actual kernal address space $f800
sys.memcopy(&kernal_area.routine3, $f800, 255)
cx16.r0L = routine($2233)
txt.print("result=")
txt.print_ub(cx16.r0L)
txt.nl()
cx16.r0L = routine($3344)
txt.print("result=")
txt.print_ub(cx16.r0L)
txt.nl()
; how to call the routine using manual bank switching:
; c64.banks(%101) ; bank out kernal rom
; call($f800) ; call our routine
; c64.banks(%111) ; kernal back
; how to use prog8's automatic bank switching:
romsub @bank %101 $f800 = kernal_routine()
kernal_routine()
character_out('!')
character_out('\n')
}
}
kernal_area {
; this routine is actually copied to kernal address space first
; we cannot use CHROUT when the kernal is banked out so we write to the screen directly
asmsub routine3() {
asmsub the_routine(uword arg @AY) -> ubyte @A {
%asm {{
lda #<_message
ldy #>_message
sta $fe
sty $ff
ldy #0
- lda ($fe),y
beq +
sta $0400+240,y
iny
bne -
+ rts
_message
.enc 'screen'
.text "hello from kernal area $f800",0
.enc 'none'
; !notreached!
sty P8ZP_SCRATCH_REG
clc
adc P8ZP_SCRATCH_REG
rts
}}
}
}
basic_area $a000 {
sub routine1() {
txt.print("hello from basic rom area ")
txt.print_uwhex(&routine1, true)
txt.nl()
}
}
hiram_area $ca00 {
sub routine2() {
txt.print("hello from hiram area ")
txt.print_uwhex(&routine2, true)
txt.nl()
}
}
;main {
; sub start() {
; basic_area.routine1()
; hiram_area.routine2()
;
; ; copy the kernal area routine to actual kernal address space $f800
; sys.memcopy(&kernal_area.routine3, $f800, 255)
;
; ; how to call the routine using manual bank switching:
; ; c64.banks(%101) ; bank out kernal rom
; ; call($f800) ; call our routine
; ; c64.banks(%111) ; kernal back
;
; ; how to use prog8's automatic bank switching:
; romsub @bank %101 $f800 = kernal_routine()
;
; kernal_routine()
;
; txt.print("done!\n")
; }
;}
;
;kernal_area {
; ; this routine is actually copied to kernal address space first
; ; we cannot use CHROUT when the kernal is banked out so we write to the screen directly
; asmsub routine3() {
; %asm {{
; lda #<_message
; ldy #>_message
; sta $fe
; sty $ff
; ldy #0
;- lda ($fe),y
; beq +
; sta $0400+240,y
; iny
; bne -
;+ rts
;
;_message
; .enc 'screen'
; .text "hello from kernal area $f800",0
; .enc 'none'
; ; !notreached!
; }}
; }
;}
;
;
;basic_area $a000 {
; sub routine1() {
; txt.print("hello from basic rom area ")
; txt.print_uwhex(&routine1, true)
; txt.nl()
; }
;}
;
;hiram_area $ca00 {
; sub routine2() {
; txt.print("hello from hiram area ")
; txt.print_uwhex(&routine2, true)
; txt.nl()
; }
;}
;