From 85e87dfe2e5b22b668f0ce37ecd3bd50d6943d8f Mon Sep 17 00:00:00 2001
From: Irmen de Jong <irmen@razorvine.net>
Date: Sun, 3 Nov 2024 21:00:39 +0100
Subject: [PATCH] consolidate @rombank and @rambank into just @bank

---
 codeCore/src/prog8/code/SymbolTable.kt        |   3 +-
 codeCore/src/prog8/code/ast/AstPrinter.kt     |   5 +-
 codeCore/src/prog8/code/ast/AstStatements.kt  |   7 +-
 .../codegen/cpu6502/FunctionCallAsmGen.kt     |  26 ++--
 .../codegen/cpu6502/ProgramAndVarsGen.kt      |   4 +-
 .../codegen/intermediate/ExpressionGen.kt     |   8 +-
 codeGenIntermediate/test/TestVmCodeGen.kt     |   2 +-
 compiler/res/prog8lib/cx16/syslib.p8          | 112 +++++++++---------
 .../compiler/astprocessing/AstChecker.kt      |   7 +-
 .../astprocessing/IntermediateAstMaker.kt     |   2 +-
 .../src/prog8/ast/AstToSourceTextConverter.kt |   5 +-
 compilerAst/src/prog8/ast/SymbolDumper.kt     |   5 +-
 .../src/prog8/ast/antlr/Antlr2Kotlin.kt       |   8 +-
 .../prog8/ast/expressions/AstExpressions.kt   |   3 +-
 .../src/prog8/ast/statements/AstStatements.kt |   4 +-
 docs/source/syntaxreference.rst               |   4 +-
 docs/source/technical.rst                     |   6 +-
 docs/source/todo.rst                          |   6 +-
 examples/test.p8                              |   9 +-
 parser/antlr/Prog8ANTLR.g4                    |   5 +-
 syntax-files/IDEA/Prog8.xml                   |   2 +-
 syntax-files/Vim/prog8.vim                    |   2 +-
 22 files changed, 105 insertions(+), 130 deletions(-)

diff --git a/codeCore/src/prog8/code/SymbolTable.kt b/codeCore/src/prog8/code/SymbolTable.kt
index 3f251c77c..835240dce 100644
--- a/codeCore/src/prog8/code/SymbolTable.kt
+++ b/codeCore/src/prog8/code/SymbolTable.kt
@@ -1,6 +1,5 @@
 package prog8.code
 
-import prog8.code.ast.PtAsmSub
 import prog8.code.ast.PtNode
 import prog8.code.ast.PtProgram
 import prog8.code.core.*
@@ -258,7 +257,7 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, val retur
 
 
 class StRomSub(name: String,
-               val address: PtAsmSub.Address?,      // null in case of asmsub, specified in case of romsub
+               val address: Pair<UByte?, UInt>?,      // null in case of asmsub, specified in case of romsub.   bank, address.
                val parameters: List<StRomSubParameter>,
                val returns: List<StRomSubParameter>,
                astNode: PtNode) :
diff --git a/codeCore/src/prog8/code/ast/AstPrinter.kt b/codeCore/src/prog8/code/ast/AstPrinter.kt
index 248b3a558..8cb5d82a9 100644
--- a/codeCore/src/prog8/code/ast/AstPrinter.kt
+++ b/codeCore/src/prog8/code/ast/AstPrinter.kt
@@ -92,9 +92,8 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
                 if(node.address == null) {
                     str + "asmsub ${node.name}($params) $clobbers $returns"
                 } else {
-                    val rombank = if(node.address.rombank!=null) "@rombank ${node.address.rombank}" else ""
-                    val rambank = if(node.address.rambank!=null) "@rambank ${node.address.rambank}" else ""
-                    str + "romsub $rombank $rambank ${node.address.address.toHex()} = ${node.name}($params) $clobbers $returns"
+                    val bank = if(node.address.first!=null) "@bank ${node.address.first}" else ""
+                    str + "romsub $bank ${node.address.second.toHex()} = ${node.name}($params) $clobbers $returns"
                 }
             }
             is PtBlock -> {
diff --git a/codeCore/src/prog8/code/ast/AstStatements.kt b/codeCore/src/prog8/code/ast/AstStatements.kt
index e8fefbed4..a7dd6f346 100644
--- a/codeCore/src/prog8/code/ast/AstStatements.kt
+++ b/codeCore/src/prog8/code/ast/AstStatements.kt
@@ -10,16 +10,13 @@ sealed interface IPtSubroutine {
 
 class PtAsmSub(
     name: String,
-    val address: Address?,
+    val address: Pair<UByte?, UInt>?,       // bank, address
     val clobbers: Set<CpuRegister>,
     val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
     val returns: List<Pair<RegisterOrStatusflag, DataType>>,
     val inline: Boolean,
     position: Position
-) : PtNamedNode(name, position), IPtSubroutine {
-
-    class Address(val rombank: UByte?, val rambank: UByte?, val address: UInt)
-}
+) : PtNamedNode(name, position), IPtSubroutine
 
 
 class PtSub(
diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt
index 0b486ff29..3555cb98d 100644
--- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt
+++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt
@@ -40,30 +40,20 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
                 sub.children.forEach { asmgen.translate(it as PtInlineAssembly) }
                 asmgen.out("  \t; inlined routine end: ${sub.name}")
             } else {
-                val rombank = sub.address?.rombank
-                val rambank = sub.address?.rambank
-                if(rombank==null && rambank==null)
+                val bank = sub.address?.first
+                if(bank==null)
                     asmgen.out("  jsr  $subAsmName")
                 else {
                     when(asmgen.options.compTarget.name) {
                         "cx16" -> {
-                            if(rambank!=null) {
-                                // JSRFAR can jump to a banked RAM address as well!
-                                asmgen.out("""
-                                    jsr cx16.JSRFAR
-                                    .word  $subAsmName    ; ${sub.address!!.address.toHex()}
-                                    .byte  $rambank"""
-                                )
-                            } else {
-                                asmgen.out("""
-                                    jsr cx16.JSRFAR
-                                    .word  $subAsmName    ; ${sub.address!!.address.toHex()}
-                                    .byte  $rombank"""
-                                )
-                            }
+                            // JSRFAR can jump to a banked RAM address as well!
+                            asmgen.out("""
+                                jsr cx16.JSRFAR
+                                .word  $subAsmName    ; ${sub.address!!.second.toHex()}
+                                .byte  $bank"""
+                            )
                         }
                         "c128" -> {
-                            val bank = rambank ?: rombank!!
                             // see https://cx16.dk/c128-kernal-routines/jsrfar.html
                             asmgen.out("""
                                 sty	 $08
diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt
index 67448b7de..87d67525d 100644
--- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt
+++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt
@@ -781,8 +781,8 @@ internal class ProgramAndVarsGen(
             .forEach { asmsub ->
                 asmsub as PtAsmSub
                 val address = asmsub.address!!
-                val bank = if(address.rombank!=null) "; @rombank ${address.rombank}" else if(address.rambank!=null) "; @rambank ${address.rambank}" else ""
-                asmgen.out("  ${asmsub.name} = ${address.address.toHex()} $bank")
+                val bank = if(address.first!=null) "; @bank ${address.first}" else ""
+                asmgen.out("  ${asmsub.name} = ${address.second.toHex()} $bank")
             }
     }
 
diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
index 0e57a450c..87ba4be88 100644
--- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
+++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
@@ -645,10 +645,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
                         IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
                     else {
                         val address = callTarget.address!!
-                        if(address.rombank==null && address.rambank==null) {
+                        if(address.first==null) {
                             IRInstruction(
                                 Opcode.CALL,
-                                address = address.address.toInt(),
+                                address = address.second.toInt(),
                                 fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
                         }
                         else {
@@ -767,10 +767,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
                 IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegisters))
             else {
                 val address = callTarget.address!!
-                if(address.rombank==null && address.rambank==null) {
+                if(address.first==null) {
                     IRInstruction(
                         Opcode.CALL,
-                        address = address.address.toInt(),
+                        address = address.second.toInt(),
                         fcallArgs = FunctionCallArgs(argRegisters, returnRegisters)
                     )
                 }
diff --git a/codeGenIntermediate/test/TestVmCodeGen.kt b/codeGenIntermediate/test/TestVmCodeGen.kt
index a896efb09..0a6ed57dd 100644
--- a/codeGenIntermediate/test/TestVmCodeGen.kt
+++ b/codeGenIntermediate/test/TestVmCodeGen.kt
@@ -534,7 +534,7 @@ class TestVmCodeGen: FunSpec({
         val codegen = VmCodeGen()
         val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
         val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
-        val romsub = PtAsmSub("routine", PtAsmSub.Address(null, null, 0x5000u), setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
+        val romsub = PtAsmSub("routine", null to 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
         block.add(romsub)
         val sub = PtSub("start", emptyList(), null, Position.DUMMY)
         val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8
index 08bc0da4d..0e5b5dbb9 100644
--- a/compiler/res/prog8lib/cx16/syslib.p8
+++ b/compiler/res/prog8lib/cx16/syslib.p8
@@ -484,62 +484,62 @@ romsub $C006 = x16edit_loadfile_options(ubyte firstbank @X, ubyte lastbank @Y, s
                 uword disknumberAndColors @R3, uword headerAndStatusColors @R4) clobbers(A,X,Y)
 
 ; Audio (rom bank 10)
-romsub @rombank 10  $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc     ; (re)initialize both vera PSG and YM audio chips
-romsub @rombank 10  $C000 = bas_fmfreq(ubyte channel @A, uword freq @XY, bool noretrigger @Pc) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C003 = bas_fmnote(ubyte channel @A, ubyte note @X, ubyte fracsemitone @Y, bool noretrigger @Pc) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C006 = bas_fmplaystring(ubyte length @A, str string @XY) clobbers(A,X,Y)
-romsub @rombank 10  $C009 = bas_fmvib(ubyte speed @A, ubyte depth @X) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C00C = bas_playstringvoice(ubyte channel @A) clobbers(Y)
-romsub @rombank 10  $C00F = bas_psgfreq(ubyte voice @A, uword freq @XY) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C012 = bas_psgnote(ubyte voice @A, ubyte note @X, ubyte fracsemitone @Y) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C015 = bas_psgwav(ubyte voice @A, ubyte waveform @X) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C018 = bas_psgplaystring(ubyte length @A, str string @XY) clobbers(A,X,Y)
-romsub @rombank 10  $C08D = bas_fmchordstring(ubyte length @A, str string @XY) clobbers(A,X,Y)
-romsub @rombank 10  $C090 = bas_psgchordstring(ubyte length @A, str string @XY) clobbers(A,X,Y)
-romsub @rombank 10  $C01B = notecon_bas2fm(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
-romsub @rombank 10  $C01E = notecon_bas2midi(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
-romsub @rombank 10  $C021 = notecon_bas2psg(ubyte note @X, ubyte fracsemitone @Y) clobbers(A) -> uword @XY, bool @Pc
-romsub @rombank 10  $C024 = notecon_fm2bas(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
-romsub @rombank 10  $C027 = notecon_fm2midi(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
-romsub @rombank 10  $C02A = notecon_fm2psg(ubyte note @X, ubyte fracsemitone @Y) clobbers(A) -> uword @XY, bool @Pc
-romsub @rombank 10  $C02D = notecon_freq2bas(uword freqHz @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
-romsub @rombank 10  $C030 = notecon_freq2fm(uword freqHz @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
-romsub @rombank 10  $C033 = notecon_freq2midi(uword freqHz @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
-romsub @rombank 10  $C036 = notecon_freq2psg(uword freqHz @XY) clobbers(A) -> uword @XY, bool @Pc
-romsub @rombank 10  $C039 = notecon_midi2bas(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
-romsub @rombank 10  $C03C = notecon_midi2fm(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
-romsub @rombank 10  $C03F = notecon_midi2psg(ubyte note @X, ubyte fracsemitone @Y) clobbers(A) -> uword @XY, bool @Pc
-romsub @rombank 10  $C042 = notecon_psg2bas(uword freq @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
-romsub @rombank 10  $C045 = notecon_psg2fm(uword freq @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
-romsub @rombank 10  $C048 = notecon_psg2midi(uword freq @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
-romsub @rombank 10  $C04B = psg_init() clobbers(A,X,Y)               ; (re)init Vera PSG
-romsub @rombank 10  $C04E = psg_playfreq(ubyte voice @A, uword freq @XY) clobbers(A,X,Y)
-romsub @rombank 10  $C051 = psg_read(ubyte offset @X, bool cookedVol @Pc) clobbers(Y) -> ubyte @A
-romsub @rombank 10  $C054 = psg_setatten(ubyte voice @A, ubyte attenuation @X) clobbers(A,X,Y)
-romsub @rombank 10  $C057 = psg_setfreq(ubyte voice @A, uword freq @XY) clobbers(A,X,Y)
-romsub @rombank 10  $C05A = psg_setpan(ubyte voice @A, ubyte panning @X) clobbers(A,X,Y)
-romsub @rombank 10  $C05D = psg_setvol(ubyte voice @A, ubyte volume @X) clobbers(A,X,Y)
-romsub @rombank 10  $C060 = psg_write(ubyte value @A, ubyte offset @X) clobbers(Y)
-romsub @rombank 10  $C0A2 = psg_write_fast(ubyte value @A, ubyte offset @X) clobbers(Y)
-romsub @rombank 10  $C093 = psg_getatten(ubyte voice @A) clobbers(Y) -> ubyte @X
-romsub @rombank 10  $C096 = psg_getpan(ubyte voice @A) clobbers(Y) -> ubyte @X
-romsub @rombank 10  $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc              ; (re)init YM chip
-romsub @rombank 10  $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc    ; load default YM patches
-romsub @rombank 10  $C069 = ym_loadpatch(ubyte channel @A, uword patchOrAddress @XY, bool what @Pc) clobbers(A,X,Y)
-romsub @rombank 10  $C06C = ym_loadpatchlfn(ubyte channel @A, ubyte lfn @X) clobbers(X,Y) -> ubyte @A, bool @Pc
-romsub @rombank 10  $C06F = ym_playdrum(ubyte channel @A, ubyte note @X) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C072 = ym_playnote(ubyte channel @A, ubyte kc @X, ubyte kf @Y, bool notrigger @Pc) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C075 = ym_setatten(ubyte channel @A, ubyte attenuation @X) clobbers(Y) -> bool @Pc
-romsub @rombank 10  $C078 = ym_setdrum(ubyte channel @A, ubyte note @X) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C07B = ym_setnote(ubyte channel @A, ubyte kc @X, ubyte kf @Y) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C07E = ym_setpan(ubyte channel @A, ubyte panning @X) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C081 = ym_read(ubyte register @X, bool cooked @Pc) clobbers(Y) -> ubyte @A, bool @Pc
-romsub @rombank 10  $C084 = ym_release(ubyte channel @A) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C087 = ym_trigger(ubyte channel @A, bool noRelease @Pc) clobbers(A,X,Y) -> bool @Pc
-romsub @rombank 10  $C08A = ym_write(ubyte value @A, ubyte register @X) clobbers(Y) -> bool @Pc
-romsub @rombank 10  $C099 = ym_getatten(ubyte channel @A) clobbers(Y) -> ubyte @X
-romsub @rombank 10  $C09C = ym_getpan(ubyte channel @A) clobbers(Y) -> ubyte @X
-romsub @rombank 10  $C0A5 = ym_get_chip_type() clobbers(X) -> ubyte @A
+romsub @bank 10  $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc     ; (re)initialize both vera PSG and YM audio chips
+romsub @bank 10  $C000 = bas_fmfreq(ubyte channel @A, uword freq @XY, bool noretrigger @Pc) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C003 = bas_fmnote(ubyte channel @A, ubyte note @X, ubyte fracsemitone @Y, bool noretrigger @Pc) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C006 = bas_fmplaystring(ubyte length @A, str string @XY) clobbers(A,X,Y)
+romsub @bank 10  $C009 = bas_fmvib(ubyte speed @A, ubyte depth @X) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C00C = bas_playstringvoice(ubyte channel @A) clobbers(Y)
+romsub @bank 10  $C00F = bas_psgfreq(ubyte voice @A, uword freq @XY) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C012 = bas_psgnote(ubyte voice @A, ubyte note @X, ubyte fracsemitone @Y) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C015 = bas_psgwav(ubyte voice @A, ubyte waveform @X) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C018 = bas_psgplaystring(ubyte length @A, str string @XY) clobbers(A,X,Y)
+romsub @bank 10  $C08D = bas_fmchordstring(ubyte length @A, str string @XY) clobbers(A,X,Y)
+romsub @bank 10  $C090 = bas_psgchordstring(ubyte length @A, str string @XY) clobbers(A,X,Y)
+romsub @bank 10  $C01B = notecon_bas2fm(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
+romsub @bank 10  $C01E = notecon_bas2midi(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
+romsub @bank 10  $C021 = notecon_bas2psg(ubyte note @X, ubyte fracsemitone @Y) clobbers(A) -> uword @XY, bool @Pc
+romsub @bank 10  $C024 = notecon_fm2bas(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
+romsub @bank 10  $C027 = notecon_fm2midi(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
+romsub @bank 10  $C02A = notecon_fm2psg(ubyte note @X, ubyte fracsemitone @Y) clobbers(A) -> uword @XY, bool @Pc
+romsub @bank 10  $C02D = notecon_freq2bas(uword freqHz @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
+romsub @bank 10  $C030 = notecon_freq2fm(uword freqHz @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
+romsub @bank 10  $C033 = notecon_freq2midi(uword freqHz @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
+romsub @bank 10  $C036 = notecon_freq2psg(uword freqHz @XY) clobbers(A) -> uword @XY, bool @Pc
+romsub @bank 10  $C039 = notecon_midi2bas(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
+romsub @bank 10  $C03C = notecon_midi2fm(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
+romsub @bank 10  $C03F = notecon_midi2psg(ubyte note @X, ubyte fracsemitone @Y) clobbers(A) -> uword @XY, bool @Pc
+romsub @bank 10  $C042 = notecon_psg2bas(uword freq @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
+romsub @bank 10  $C045 = notecon_psg2fm(uword freq @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
+romsub @bank 10  $C048 = notecon_psg2midi(uword freq @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
+romsub @bank 10  $C04B = psg_init() clobbers(A,X,Y)               ; (re)init Vera PSG
+romsub @bank 10  $C04E = psg_playfreq(ubyte voice @A, uword freq @XY) clobbers(A,X,Y)
+romsub @bank 10  $C051 = psg_read(ubyte offset @X, bool cookedVol @Pc) clobbers(Y) -> ubyte @A
+romsub @bank 10  $C054 = psg_setatten(ubyte voice @A, ubyte attenuation @X) clobbers(A,X,Y)
+romsub @bank 10  $C057 = psg_setfreq(ubyte voice @A, uword freq @XY) clobbers(A,X,Y)
+romsub @bank 10  $C05A = psg_setpan(ubyte voice @A, ubyte panning @X) clobbers(A,X,Y)
+romsub @bank 10  $C05D = psg_setvol(ubyte voice @A, ubyte volume @X) clobbers(A,X,Y)
+romsub @bank 10  $C060 = psg_write(ubyte value @A, ubyte offset @X) clobbers(Y)
+romsub @bank 10  $C0A2 = psg_write_fast(ubyte value @A, ubyte offset @X) clobbers(Y)
+romsub @bank 10  $C093 = psg_getatten(ubyte voice @A) clobbers(Y) -> ubyte @X
+romsub @bank 10  $C096 = psg_getpan(ubyte voice @A) clobbers(Y) -> ubyte @X
+romsub @bank 10  $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc              ; (re)init YM chip
+romsub @bank 10  $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc    ; load default YM patches
+romsub @bank 10  $C069 = ym_loadpatch(ubyte channel @A, uword patchOrAddress @XY, bool what @Pc) clobbers(A,X,Y)
+romsub @bank 10  $C06C = ym_loadpatchlfn(ubyte channel @A, ubyte lfn @X) clobbers(X,Y) -> ubyte @A, bool @Pc
+romsub @bank 10  $C06F = ym_playdrum(ubyte channel @A, ubyte note @X) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C072 = ym_playnote(ubyte channel @A, ubyte kc @X, ubyte kf @Y, bool notrigger @Pc) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C075 = ym_setatten(ubyte channel @A, ubyte attenuation @X) clobbers(Y) -> bool @Pc
+romsub @bank 10  $C078 = ym_setdrum(ubyte channel @A, ubyte note @X) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C07B = ym_setnote(ubyte channel @A, ubyte kc @X, ubyte kf @Y) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C07E = ym_setpan(ubyte channel @A, ubyte panning @X) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C081 = ym_read(ubyte register @X, bool cooked @Pc) clobbers(Y) -> ubyte @A, bool @Pc
+romsub @bank 10  $C084 = ym_release(ubyte channel @A) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C087 = ym_trigger(ubyte channel @A, bool noRelease @Pc) clobbers(A,X,Y) -> bool @Pc
+romsub @bank 10  $C08A = ym_write(ubyte value @A, ubyte register @X) clobbers(Y) -> bool @Pc
+romsub @bank 10  $C099 = ym_getatten(ubyte channel @A) clobbers(Y) -> ubyte @X
+romsub @bank 10  $C09C = ym_getpan(ubyte channel @A) clobbers(Y) -> ubyte @X
+romsub @bank 10  $C0A5 = ym_get_chip_type() clobbers(X) -> ubyte @A
 
 ; extapi call numbers
 const ubyte  EXTAPI_clear_status = $01
diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt
index 9dc79b252..a42e5e772 100644
--- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt
+++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt
@@ -377,11 +377,8 @@ internal class AstChecker(private val program: Program,
         if(uniqueNames.size!=subroutine.parameters.size)
             err("parameter names must be unique")
 
-        val rambank = subroutine.asmAddress?.rambank
-        val rombank = subroutine.asmAddress?.rombank
-        if(rambank!=null && rambank>255u)
-            err("bank must be 0 to 255")
-        if(rombank!=null && rombank>255u)
+        val bank = subroutine.asmAddress?.first
+        if(bank!=null && bank>255u)
             err("bank must be 0 to 255")
         if(subroutine.inline && subroutine.asmAddress!=null)
             throw FatalAstException("romsub cannot be inline")
diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt
index 3b030df3c..1eca43a5a 100644
--- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt
+++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt
@@ -466,7 +466,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
 
     private fun transformAsmSub(srcSub: Subroutine): PtAsmSub {
         val params = srcSub.asmParameterRegisters.zip(srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.position) })
-        val asmAddr = if(srcSub.asmAddress==null) null else PtAsmSub.Address(srcSub.asmAddress!!.rombank?.toUByte(), srcSub.asmAddress!!.rambank?.toUByte(), srcSub.asmAddress!!.address)
+        val asmAddr = if(srcSub.asmAddress==null) null else srcSub.asmAddress!!.first to srcSub.asmAddress!!.second
         val sub = PtAsmSub(srcSub.name,
             asmAddr,
             srcSub.asmClobbers,
diff --git a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt
index ff031dea6..567da5c61 100644
--- a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt
+++ b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt
@@ -181,9 +181,8 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
             output("inline ")
         if(subroutine.isAsmSubroutine) {
             if(subroutine.asmAddress!=null) {
-                val rombank = if(subroutine.asmAddress.rombank!=null) "@rombank ${subroutine.asmAddress.rombank}" else ""
-                val rambank = if(subroutine.asmAddress.rambank!=null) "@rambank ${subroutine.asmAddress.rambank}" else ""
-                output("romsub $rombank $rambank ${subroutine.asmAddress.address.toHex()} = ${subroutine.name} (")
+                val bank = if(subroutine.asmAddress.first!=null) "@bank ${subroutine.asmAddress.first}" else ""
+                output("romsub $bank ${subroutine.asmAddress.second.toHex()} = ${subroutine.name} (")
             }
             else
                 output("asmsub ${subroutine.name} (")
diff --git a/compilerAst/src/prog8/ast/SymbolDumper.kt b/compilerAst/src/prog8/ast/SymbolDumper.kt
index ae5bb2fea..11a66b4c1 100644
--- a/compilerAst/src/prog8/ast/SymbolDumper.kt
+++ b/compilerAst/src/prog8/ast/SymbolDumper.kt
@@ -173,9 +173,8 @@ private class SymbolDumper(val skipLibraries: Boolean): IAstVisitor {
             }
         }
         if(subroutine.asmAddress!=null) {
-            val rombank = if(subroutine.asmAddress.rombank!=null) "@rombank ${subroutine.asmAddress.rombank}" else ""
-            val rambank = if(subroutine.asmAddress.rambank!=null) "@rambank ${subroutine.asmAddress.rambank}" else ""
-            output("$rombank $rambank = ${subroutine.asmAddress.address.toHex()}")
+            val bank = if(subroutine.asmAddress.first!=null) "@bank ${subroutine.asmAddress.first}" else ""
+            output("$bank = ${subroutine.asmAddress.second.toHex()}")
         }
 
         output("\n")
diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt
index 8f8769cb6..37a69feed 100644
--- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt
+++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt
@@ -177,13 +177,11 @@ private fun AsmsubroutineContext.toAst(): Subroutine {
 
 private fun RomsubroutineContext.toAst(): Subroutine {
     val subdecl = asmsub_decl().toAst()
-    val rombank = rombankspec()?.integerliteral()?.toAst()?.number?.toUInt()
-    val rambank = rambankspec()?.integerliteral()?.toAst()?.number?.toUInt()
-    val addr = integerliteral().toAst().number.toUInt()
-    val address = Subroutine.Address(rombank, rambank, addr)
+    val bank = bank?.toAst()?.number?.toUInt()?.toUByte()
+    val addr = address.toAst().number.toUInt()
     return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
             subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
-            subdecl.asmClobbers, address, true, inline = false, statements = mutableListOf(), position = toPosition()
+            subdecl.asmClobbers, bank to addr, true, inline = false, statements = mutableListOf(), position = toPosition()
     )
 }
 
diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt
index f51c07cc7..05b1b2e9a 100644
--- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt
+++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt
@@ -442,8 +442,7 @@ data class AddressOf(var identifier: IdentifierReference, var arrayIndex: ArrayI
         }
         val targetAsmAddress = (target as? Subroutine)?.asmAddress
         if(targetAsmAddress!=null) {
-            if(targetAsmAddress.rombank==null && targetAsmAddress.rambank==null)
-                return NumericLiteral(DataType.UWORD, targetAsmAddress.address.toDouble(), position)
+            return NumericLiteral(DataType.UWORD, targetAsmAddress.second.toDouble(), position)
         }
         return null
     }
diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt
index 871e2cea6..6a67f03d4 100644
--- a/compilerAst/src/prog8/ast/statements/AstStatements.kt
+++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt
@@ -806,7 +806,7 @@ class Subroutine(override val name: String,
                  val asmParameterRegisters: List<RegisterOrStatusflag>,
                  val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
                  val asmClobbers: Set<CpuRegister>,
-                 val asmAddress: Address?,
+                 val asmAddress: Pair<UByte?, UInt>?,       //  bank, address
                  val isAsmSubroutine: Boolean,
                  var inline: Boolean,
                  var hasBeenInlined: Boolean=false,
@@ -846,8 +846,6 @@ class Subroutine(override val name: String,
     override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
     override fun toString() =
         "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)"
-
-    class Address(val rombank: UInt?, val rambank: UInt?, val address: UInt)
 }
 
 open class SubroutineParameter(val name: String,
diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst
index 3bb232428..f82bbf3ad 100644
--- a/docs/source/syntaxreference.rst
+++ b/docs/source/syntaxreference.rst
@@ -793,8 +793,8 @@ This defines the ``LOAD`` subroutine at memory address $FFD5, taking arguments i
 and returning stuff in several registers as well. The ``clobbers`` clause is used to signify to the compiler
 what CPU registers are clobbered by the call instead of being unchanged or returning a meaningful result value.
 
-**Banks:** it is possible to declare a non-standard ROM or RAM bank that the routine is living in, with ``@rombank`` or ``@rambank`` like this:
-``romsub @rombank 10  $C09F = audio_init()`` to define a routine at $C09F in ROM bank 10.
+**Banks:** it is possible to declare a non-standard ROM or RAM bank that the routine is living in, with ``@bank`` like this:
+``romsub @bank 10  $C09F = audio_init()`` to define a routine at $C09F in bank 10.
 See :ref:`banking` for more information.
 
 .. note::
diff --git a/docs/source/technical.rst b/docs/source/technical.rst
index 41ff59049..fadb46d5d 100644
--- a/docs/source/technical.rst
+++ b/docs/source/technical.rst
@@ -41,10 +41,10 @@ External subroutines defined with ``romsub`` can have a non-standard ROM or RAM
 The compiler will then transparently change a call to this routine so that the correct bank is activated
 automatically before the normal jump to the subroutine (and switched back on return). The programmer doesn't
 have to bother anymore with setting/resetting the banks manually, or having the program crash because
-the routine is called in the wrong bank!  You define such a routine by adding ``@rombank <bank>`` or ``@rambank <bank>``
+the routine is called in the wrong bank!  You define such a routine by adding ``@bank <bank>``
 to the romsub subroutine definition. This specifies the bank number where the subroutine is located in::
 
-    romsub @rombank 10  $C09F = audio_init()
+    romsub @bank 10  $C09F = audio_init()
 
 When you then call this routine in your program as usual, the compiler will no longer generate a simple JSR instruction to the
 routine. Instead it will generate a piece of code that automatically switches the ROM or RAM bank to the
@@ -56,7 +56,7 @@ Other compilation targets don't have banking or prog8 doesn't yet support automa
 Notice that the symbol for this routine in the assembly source code will still be defined as usual.
 The bank number is not translated into assembly (only as a comment)::
 
-	p8s_audio_init = $c09f ; @rombank 10
+	p8s_audio_init = $c09f ; @bank 10
 
 .. caution::
     Calls with automatic bank switching like this are not safe to use from IRQ handlers. Don't use them there.
diff --git a/docs/source/todo.rst b/docs/source/todo.rst
index ca40c9629..e2f4fee02 100644
--- a/docs/source/todo.rst
+++ b/docs/source/todo.rst
@@ -1,16 +1,14 @@
 TODO
 ====
 
-consolidate @rombank and @rambank into just @bank
-
 add example for cx16 that compiles and loads libraries in different ram banks and calls romsub from rom and ram banks automatically
 
-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.
-
 add support for banked romsubs on the C64 as well  (banks basic/kernal rom in/out)
 
 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 register load order in subroutine call args assignments:
diff --git a/examples/test.p8 b/examples/test.p8
index cc12099d3..2e891b26d 100644
--- a/examples/test.p8
+++ b/examples/test.p8
@@ -3,10 +3,15 @@
 %zeropage basicsafe
 
 main {
-    romsub @rombank 10  $C09F = audio_init()
-    romsub @rambank 22  $A000 = hiram_routine()
+    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)
+
         audio_init()
         hiram_routine()
     }
diff --git a/parser/antlr/Prog8ANTLR.g4 b/parser/antlr/Prog8ANTLR.g4
index 69a520b0f..97116a6dc 100644
--- a/parser/antlr/Prog8ANTLR.g4
+++ b/parser/antlr/Prog8ANTLR.g4
@@ -294,12 +294,9 @@ asmsubroutine :
     ;
 
 romsubroutine :
-    'romsub' (rombankspec | rambankspec)? integerliteral '=' asmsub_decl
+    'romsub' ('@bank' bank=integerliteral)? address=integerliteral '=' asmsub_decl
     ;
 
-rombankspec : '@rombank' integerliteral;
-rambankspec : '@rambank' integerliteral;
-
 asmsub_decl : identifier '(' asmsub_params? ')' asmsub_clobbers? asmsub_returns? ;
 
 asmsub_params :  asmsub_param (',' EOL? asmsub_param)* ;
diff --git a/syntax-files/IDEA/Prog8.xml b/syntax-files/IDEA/Prog8.xml
index f5d3611aa..432d67853 100644
--- a/syntax-files/IDEA/Prog8.xml
+++ b/syntax-files/IDEA/Prog8.xml
@@ -12,7 +12,7 @@
       <option name="HAS_STRING_ESCAPES" value="true" />
     </options>
     <keywords keywords="&amp;;-&gt;;@;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
-    <keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@nozp;@requirezp;@shared;@split;@zp;atascii:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
+    <keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@bank;@nozp;@requirezp;@shared;@split;@zp;atascii:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
     <keywords3 keywords="bool;byte;const;float;str;ubyte;uword;void;word" />
     <keywords4 keywords="abs;call;callfar;callfar2;clamp;cmp;defer;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt" />
   </highlighting>
diff --git a/syntax-files/Vim/prog8.vim b/syntax-files/Vim/prog8.vim
index 5ab0df0bc..f8a03a9d1 100644
--- a/syntax-files/Vim/prog8.vim
+++ b/syntax-files/Vim/prog8.vim
@@ -43,7 +43,7 @@ syn region prog8ArrayType matchgroup=prog8Type
             \ start="\<\%(u\?byte\|u\?word\|float\|str\|bool\)\[" end="\]"
             \ transparent
 syn keyword prog8StorageClass const
-syn match prog8StorageClass "\(^\|\s\)\(@zp\|@shared\|@split\|@nozp\|@requirezp\|@align64\|@alignword\|@alignpage\)\>"
+syn match prog8StorageClass "\(^\|\s\)\(@zp\|@bank\|@shared\|@split\|@nozp\|@requirezp\|@align64\|@alignword\|@alignpage\)\>"
 
 syn region prog8Block start="{" end="}" transparent
 syn region prog8Expression start="(" end=")" transparent