diff --git a/codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt b/codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt index 1bcec08d8..90932a20d 100644 --- a/codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt +++ b/codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt @@ -15,7 +15,7 @@ class C64MachineDefinition: IMachineDefinition { override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE override val PROGRAM_LOAD_ADDRESS = 0x0801u - override val PROGRAM_TOP_ADDRESS = 0xbfffu + override val PROGRAM_TOP_ADDRESS = 0x9fffu // $bfff if basic rom is banked out override val BSSHIGHRAM_START = 0xc000u override val BSSHIGHRAM_END = 0xcfffu diff --git a/compiler/res/prog8lib/c64/syslib.p8 b/compiler/res/prog8lib/c64/syslib.p8 index 38581c222..4e2587ed3 100644 --- a/compiler/res/prog8lib/c64/syslib.p8 +++ b/compiler/res/prog8lib/c64/syslib.p8 @@ -307,6 +307,31 @@ c64 { ; ---- end of SID registers ---- +asmsub banks(ubyte banks @A) { + ; -- set the memory bank configuration + ; see https://www.c64-wiki.com/wiki/Bank_Switching + %asm {{ + and #%00000111 + sta P8ZP_SCRATCH_REG + sei + lda $01 + and #%11111000 + ora P8ZP_SCRATCH_REG + sta $01 + cli + rts + }} +} + +inline asmsub getbanks() -> ubyte @A { + ; -- get the current memory bank configuration + ; see https://www.c64-wiki.com/wiki/Bank_Switching + %asm {{ + lda $01 + and #%00000111 + }} +} + sub get_vic_memory_base() -> uword { ; one of the 4 possible banks. $0000/$4000/$8000/$c000. diff --git a/compiler/test/TestCompilerOnExamples.kt b/compiler/test/TestCompilerOnExamples.kt index 9a6c6a482..05d003353 100644 --- a/compiler/test/TestCompilerOnExamples.kt +++ b/compiler/test/TestCompilerOnExamples.kt @@ -74,6 +74,7 @@ class TestCompilerOnExamplesC64: FunSpec({ val onlyC64 = cartesianProduct( listOf( "balloonflight", + "banking", "bdmusic", "bdmusic-irq", "charset", @@ -116,6 +117,7 @@ class TestCompilerOnExamplesCx16: FunSpec({ "sprites/dragons", "zsmkit/demo1", "zsmkit/demo2", + "banking/program", "amiga", "audioroutines", "automatons", diff --git a/docs/source/targetsystem.rst b/docs/source/targetsystem.rst index 8d9d4cd02..4c558de9c 100644 --- a/docs/source/targetsystem.rst +++ b/docs/source/targetsystem.rst @@ -51,6 +51,7 @@ Memory map for the C64 and the X16 This is the default memory map of the 64 Kb addressable memory for those two systems. Both systems have ways to alter the memory map and/or to switch memory banks, but that is not shown here. +See :ref:`banking` for details about that. .. image:: memorymap.svg diff --git a/docs/source/technical.rst b/docs/source/technical.rst index 8c77308bc..e84afe8f5 100644 --- a/docs/source/technical.rst +++ b/docs/source/technical.rst @@ -31,10 +31,19 @@ ROM/RAM bank selection ---------------------- On certain systems prog8 provides support for managing the ROM or RAM banks that are active. -For example, on the Commander X16, you can use ``cx16.getrombank()`` to get the active ROM bank, -and ``cx16.rombank(10)`` to make rom bank 10 active. Likewise, ``cx16.getrambank()`` to get the active RAM bank, -and ``cx16.rambank(10)`` to make ram bank 10 active. This is explicit manual banking control. +======= ============================================= =========== +system get banks (returns byte) set banks +======= ============================================= =========== +c64 ``c64.getbanks()`` ``c64.banks(x)`` +c128 *TODO* *TODO* +cx16 ``cx16.getrombank()`` , ``cx16.getrambank()`` ``cx16.rombank(x)`` , ``cx16.rambank(x)`` +other N/A N/A +======= ============================================= =========== + +Calling a subroutine in another memory bank can be done by using the ``callfar`` or ``callfar2`` builtin functions. + +When you are using the routines above, you are doing explicit manual banks control. However, Prog8 also provides something more sophisticated than this, when dealing with banked subroutines: External subroutines defined with ``romsub`` can have a non-standard ROM or RAM bank specified as well. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index aad76d5bb..eab791475 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,14 +1,22 @@ TODO ==== +on the C64, if not using floats, disable basic ROM in startup to gain another 8Kb of RAM. Make sure to set memtop to $bfff in this case or $9fff when basic is banked in +update the memory map picture, and add footnote that BASIC ROM is usually banked out. ALso mention this in the 'banking' chapter + add support for banked romsubs on the C64 as well (banks basic/kernal rom in/out) +make @bank accept a variable as well to make it dynamic + rename 'romsub' to 'extsub' ? keep romsub as alias? for releasenotes: gfx2.width and gfx2.height got renamed as gfx_lores.WIDTH/HEIGHT or gfx_hires4.WIDTH/HEIGTH constants. Screen mode routines also renamed. regenerate symbol dump files +improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine AND there is separate setup logic going on before calling it. +Make up our mind! Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc. + Improve register load order in subroutine call args assignments: in certain situations, the "wrong" order of evaluation of function call arguments is done which results in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!) diff --git a/examples/c64/banking.p8 b/examples/c64/banking.p8 new file mode 100644 index 000000000..908d5cee5 --- /dev/null +++ b/examples/c64/banking.p8 @@ -0,0 +1,45 @@ +%import string +%import textio +%option no_sysinit +%zeropage basicsafe + +; some bank switching on the C64. See https://www.c64-wiki.com/wiki/Bank_Switching + +main { + sub start() { + ; copy basic rom to ram and replace ready prompt + sys.memcopy($a000, $a000, $2000) + void string.copy(iso:"HELLO!\r", $a378) + + txt.print("8 bytes at $f000 (kernal rom):\n") + for cx16.r0 in $f000 to $f007 { + txt.print_ubhex(@(cx16.r0), false) + txt.spc() + } + txt.nl() + + ; store some other data in the RAM below those kernal ROM locations + ; switch off kernal rom to see those bytes + ; we cannot print during this time and the IRQ has to be disabled temporarily as well. + void string.copy("hello !?", $f000) + sys.set_irqd() + c64.banks(%101) ; switch off roms + ubyte[8] buffer + sys.memcopy($f000, &buffer, 8) + c64.banks(%111) ; kernal rom back on + sys.clear_irqd() + txt.print("8 bytes at $f000 (ram this time):\n") + for cx16.r0L in buffer { + txt.print_ubhex(cx16.r0L, false) + txt.spc() + } + txt.nl() + + + ; we can switch off the basic rom now, but this is not persistent after program exit + c64.banks(%110) + + ; ...so we print a message for the user to do it manually to see the changed prompt. + txt.print("\ntype: poke 1,54\nto switch off basic rom :-)\n") + } +} diff --git a/examples/test.p8 b/examples/test.p8 index 2e891b26d..908d5cee5 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,18 +1,45 @@ +%import string %import textio %option no_sysinit %zeropage basicsafe +; some bank switching on the C64. See https://www.c64-wiki.com/wiki/Bank_Switching + main { - romsub @bank 10 $C09F = audio_init() - romsub @bank 5 $A000 = hiram_routine() - sub start() { - ; put an rts in hiram bank 5 to not crash - cx16.rambank(5) - @($a000) = $60 - cx16.rambank(0) + ; copy basic rom to ram and replace ready prompt + sys.memcopy($a000, $a000, $2000) + void string.copy(iso:"HELLO!\r", $a378) - audio_init() - hiram_routine() + txt.print("8 bytes at $f000 (kernal rom):\n") + for cx16.r0 in $f000 to $f007 { + txt.print_ubhex(@(cx16.r0), false) + txt.spc() + } + txt.nl() + + ; store some other data in the RAM below those kernal ROM locations + ; switch off kernal rom to see those bytes + ; we cannot print during this time and the IRQ has to be disabled temporarily as well. + void string.copy("hello !?", $f000) + sys.set_irqd() + c64.banks(%101) ; switch off roms + ubyte[8] buffer + sys.memcopy($f000, &buffer, 8) + c64.banks(%111) ; kernal rom back on + sys.clear_irqd() + txt.print("8 bytes at $f000 (ram this time):\n") + for cx16.r0L in buffer { + txt.print_ubhex(cx16.r0L, false) + txt.spc() + } + txt.nl() + + + ; we can switch off the basic rom now, but this is not persistent after program exit + c64.banks(%110) + + ; ...so we print a message for the user to do it manually to see the changed prompt. + txt.print("\ntype: poke 1,54\nto switch off basic rom :-)\n") } }