diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt index f43365b99..8ca6286b2 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt @@ -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") } diff --git a/compiler/res/prog8lib/c128/syslib.p8 b/compiler/res/prog8lib/c128/syslib.p8 index c228a9c0a..5115e19a9 100644 --- a/compiler/res/prog8lib/c128/syslib.p8 +++ b/compiler/res/prog8lib/c128/syslib.p8 @@ -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 ---- } diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index 00d22de3a..61b5efcd9 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -165,10 +165,10 @@ internal fun Subroutine.hasRtsInAsm(checkOnlyLastInstruction: Boolean): Boolean .filterIsInstance() 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() diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 7f366d170..ae752cbd5 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -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? diff --git a/examples/test.p8 b/examples/test.p8 index 07ab1acaa..1e71f1208 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -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() +; } +;} +;