added c64.banks() and c64.getbanks() and c64 banking example

This commit is contained in:
Irmen de Jong 2024-11-04 04:28:27 +01:00
parent c11a52b278
commit e514eeba17
8 changed files with 130 additions and 13 deletions

View File

@ -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

View File

@ -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.

View File

@ -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",

View File

@ -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

View File

@ -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.

View File

@ -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!)

45
examples/c64/banking.p8 Normal file
View File

@ -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")
}
}

View File

@ -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")
}
}