Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"cSpell.words": [
"endlogical"
]
}
34 changes: 25 additions & 9 deletions modules/_scripts/makebuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,24 @@

import os
import re
import sys
from pathlib import Path
from typing import TextIO

# regex for exported labels
export_re = re.compile(r"^\s*Export_(\w+)\s*:\s*(;.*)?$")


PAGE2_BEGIN = """
.section page2
.logical * + $2000
"""

PAGE2_END = """
.endlogical
.send page2
"""


def main(*, module_name: str, build_dir: Path, page: int = 1) -> None:
"""Generate a single assembly build file for the specified module."""
if not build_dir.exists():
Expand Down Expand Up @@ -59,26 +69,28 @@ def process_source(path: Path, out: TextIO, *, page: int = 1) -> list[str]:
if page == 2:
normalized = " ".join(line.split())
if normalized == ".section code":
out.write("\t\t.section page2\n")
out.write("\t\t.logical * + $2000\n")
out.write(PAGE2_BEGIN)
continue
elif normalized == ".send code":
out.write("\t\t.here\n")
out.write("\t\t.send page2\n")
out.write(PAGE2_END)
continue

out.write(f"{line.rstrip()}\n")

return exports


def dump_exports(build_dir: Path, module_name: str, exports: list[str], *, page: int = 1) -> None:
def dump_exports(
build_dir: Path, module_name: str, exports: list[str], *, page: int = 1
) -> None:
"""Save module exports to the corresponding `.exports` file."""
if not exports:
return

suffix = "_p2" if page == 2 else ""
with open(build_dir / (module_name + suffix + ".exports"), "w", encoding="utf-8") as out:
with open(
build_dir / (module_name + suffix + ".exports"), "w", encoding="utf-8"
) as out:
for export in exports:
out.write(f"{export}\n")

Expand Down Expand Up @@ -109,8 +121,12 @@ def collect_sources(module_name: str) -> list[Path]:

parser = argparse.ArgumentParser(description="Build module assembly file")
parser.add_argument("module_name", help="Name of the module to build")
parser.add_argument("build_dir", nargs="?", default=".build", help="Build output directory")
parser.add_argument("--page", type=int, default=1, choices=[1, 2], help="Module page (1 or 2)")
parser.add_argument(
"build_dir", nargs="?", default=".build", help="Build output directory"
)
parser.add_argument(
"--page", type=int, default=1, choices=[1, 2], help="Module page (1 or 2)"
)
args = parser.parse_args()

main(
Expand Down
81 changes: 27 additions & 54 deletions modules/_scripts/makeexport.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,48 +22,7 @@ def main(*, build_dir: Path) -> None:
print(f"PagingEnabled = {1 if paging else 0}")

has_page2 = any(exports[m]["page"] == 2 for m in exports)

if paging and has_page2:
# These routines are included inside an existing .section code block
# in 00start.asm, so no .section/.send wrappers are needed here.
# Slot3ModulePage/Slot3Depth/Slot3Saved are declared in 04data.inc
# (placed before numberBuffer/decimalBuffer to survive buffer overflows).
print("")
print("; --- Slot 3 module bank switching ---")
print("")
print("Slot3Init:")
print("\tstz Slot3Depth")
print("\tlda 8+4")
print("\tclc")
print("\tadc #3")
print("\tsta Slot3ModulePage")
print("\trts")
print("")
print("Slot3BankIn:")
print("\tphy")
print("\tpha")
print("\tinc Slot3Depth")
print("\tlda Slot3Depth")
print("\tcmp #1")
print("\tbne +")
print("\tldy 8+3")
print("\tsty Slot3Saved")
print("\tldy Slot3ModulePage")
print("\tsty 8+3")
print("+\tpla")
print("\tply")
print("\trts")
print("")
print("Slot3BankOut:")
print("\tpha")
print("\tphy")
print("\tdec Slot3Depth")
print("\tbne +")
print("\tldy Slot3Saved")
print("\tsty 8+3")
print("+\tply")
print("\tpla")
print("\trts")
print(f"HasPage2 = {1 if has_page2 else 0}")

for module in exports:
page = exports[module]["page"]
Expand All @@ -74,25 +33,39 @@ def main(*, build_dir: Path) -> None:
print(f"{routine}:")
if paging:
if page == 1:
print("\tinc 8+5")
print(f"\tjsr\tExport_{routine}")
print("\tphp")
print("\tdec 8+5")
print("\tplp")
print("\trts")
print(page1_thunk(routine))
elif page == 2:
print("\tjsr Slot3BankIn")
print(f"\tjsr\tExport_{routine}")
print("\tphp")
print("\tjsr Slot3BankOut")
print("\tplp")
print("\trts")
print(page2_thunk(routine))
else:
raise RuntimeError(f"Unknown page #{page}")
else:
print(f"\tjmp\tExport_{routine}")

print("\t.endif")


def page1_thunk(routine: str) -> str:
return f"""
inc 8+5
jsr Export_{routine}
php
dec 8+5
plp
rts
"""


def page2_thunk(routine: str) -> str:
return f"""
jsr Slot3BankIn
jsr Export_{routine}
php
jsr Slot3BankOut
plp
rts
"""


def read_exports(build_dir: Path) -> dict[str, dict]:
"""Read all `.exports` files from the build directory.

Expand Down
24 changes: 20 additions & 4 deletions source/_basic.asm
Original file line number Diff line number Diff line change
Expand Up @@ -159,16 +159,25 @@


.section code
; issuing a warning instead of an error so one can examine output listing for details
.cwarn * > $C000, "BROKEN BUILD: non-paged code overflows into kernel-reserved I/O space ($C000-$DFFF) by ", * - $C000," bytes"

StartModuleCode:
.if PagingEnabled==1
* = $A000
.offs $2000
.endif
.if PagingEnabled==1
* = $A000
.offs $2000
.endif
.send code

.include "../modules/.build/hardware.module.asm"
.include "../modules/.build/tokeniser.module.asm"
.include "../modules/.build/graphics.module.asm"

.section code
; issuing a warning instead of an error so one can examine output listing for details
.cwarn * > $C000, "BROKEN BUILD: page 1 code overflows into kernel-reserved I/O space ($C000-$DFFF) by ", * - $C000," bytes"
.send code

; --- Startup banner data in boot section ($6000-$7FFF) ---
.include "../modules/hardware/startup/.build/banner.dat"

Expand All @@ -180,3 +189,10 @@ StartModuleCode:
.include "./common/generated/_errortext_p2.asm"
.include "../modules/.build/kernel_p2.module.asm"
.include "../modules/.build/sound_p2.module.asm"

.section page2
.logical * + $2000
; issuing a warning instead of an error so one can examine output listing for details
.cwarn * > $8000, "BROKEN BUILD: page 2 code overflows into SuperBASIC ROM space ($8000-$BFFF) by ", * - $8000," bytes"
.endlogical
.send page2
4 changes: 3 additions & 1 deletion source/common/aa.system/00start.asm
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ KernelHeader:
;;
Boot: jmp Start
.include "../../../modules/.build/_exports.module.asm"
.include "./_slot3banking.asm"

Start: ldx #$FF ; stack reset
txs
Expand All @@ -45,11 +46,12 @@ Start: ldx #$FF ; stack reset

jsr EXTInitialize ; hardware initialization
jsr EXTShowStartupBanner ; reads banner data from slot 3
jsr DisplayBannerText ; print hardware, ROM & build info

lda #3 ; remap slot 3 to RAM page 3
sta $0008+3 ; (frees $6000-$7FFF for arrays/temp buffers)

jsr DisplayBannerText ; print hardware, ROM & build info

lda 0 ; turn on editing of MMU LUT
ora #$80
sta 0
Expand Down
48 changes: 48 additions & 0 deletions source/common/aa.system/_slot3banking.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
;;
; Slot 3 module bank switching
;
; Note that slot 3 (page 2) banking is about 4x slower than slot 5 (page 1),
; ~29 cycles vs ~112 cycles per thunk.
;
; `Slot3ModulePage`, `Slot3Depth`, and `Slot3Saved` are declared in
; `04data.inc` (placed before `numberBuffer`/`decimalBuffer` to survive
; buffer overflows).
;;

.if PagingEnabled==1 && HasPage2==1

Slot3Init:
stz Slot3Depth
lda 8+4
clc
adc #3
sta Slot3ModulePage
rts

Slot3BankIn:
phy
pha
inc Slot3Depth
lda Slot3Depth
cmp #1
bne +
ldy 8+3
sty Slot3Saved
ldy Slot3ModulePage
sty 8+3
+ pla
ply
rts

Slot3BankOut:
pha
phy
dec Slot3Depth
bne +
ldy Slot3Saved
sty 8+3
+ ply
pla
rts

.endif
1 change: 1 addition & 0 deletions source/common/generated/_errortext.asm
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
;
; This is automatically generated.
;

.section code
ErrorText:
.text "Break",0
Expand Down
9 changes: 5 additions & 4 deletions source/common/generated/_errortext_p2.asm
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
;
; This is automatically generated.
;
.section page2
.logical * + $2000

.section page2
.logical * + $2000
ErrorText:
.text "Break",0
.text "Syntax error",0
Expand Down Expand Up @@ -35,5 +36,5 @@ ErrorText:
.text "Too many parameters",0
.text "Formula too complex",0
.text "Initialization error",0
.here
.send page2
.endlogical
.send page2
Loading