Skip to content

Commit b2e99a2

Browse files
mbrukneragurtovoy
authored andcommitted
feat: banked program memory, FRE() function, array region, boot ROM layout
- Program memory uses MMU slot 1 banking: up to 32 × 8KB pages (~256KB) - FRE(0) = free program memory, FRE(-1) = free variable/string space, FRE(-2) = free array space - Array/ALLOC storage moved to dedicated $4000-$7FFF (16KB) - F256Header + headerdata moved to boot section in slot 3 - 5th ROM block (sb05.bin) for module code page 2
1 parent 5431177 commit b2e99a2

50 files changed

Lines changed: 937 additions & 499 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

modules/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ all:
2323
$(Q)$(CCOPY) ..$(S)source$(S)common$(S)generated$(S)kwdtext.dat tokeniser$(S)__kwdtext.asm
2424
$(Q)$(PYTHON) _scripts$(S)makebuild.py tokeniser
2525

26-
$(Q)$(PYTHON) _scripts$(S)makebuild.py graphics $(BUILD_DIR)
26+
$(Q)$(PYTHON) _scripts$(S)makebuild.py graphics $(BUILD_DIR) --page 2
2727
$(Q)$(PYTHON) _scripts$(S)makebuild.py hardware $(BUILD_DIR)
2828
$(Q)$(PYTHON) _scripts$(S)makebuild.py kernel $(BUILD_DIR)
29-
$(Q)$(PYTHON) _scripts$(S)makebuild.py sound $(BUILD_DIR)
29+
$(Q)$(PYTHON) _scripts$(S)makebuild.py sound $(BUILD_DIR) --page 2
3030

3131
$(Q)$(PYTHON) _scripts$(S)makeexport.py >$(BUILD_DIR)$(S)_exports.module.asm
3232

modules/_scripts/makebuild.py

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@
1919
export_re = re.compile(r"^\s*Export_(\w+)\s*:\s*(;.*)?$")
2020

2121

22-
def main(*, module_name: str, build_dir: Path) -> None:
22+
def main(*, module_name: str, build_dir: Path, page: int = 1) -> None:
2323
"""Generate a single assembly build file for the specified module."""
2424
if not build_dir.exists():
2525
build_dir.mkdir(parents=True, exist_ok=True)
2626

2727
sources = collect_sources(module_name)
28-
output_path = build_dir / (module_name + ".module.asm")
28+
29+
if page == 2:
30+
output_path = build_dir / (module_name + "_p2.module.asm")
31+
else:
32+
output_path = build_dir / (module_name + ".module.asm")
2933

3034
with open(output_path, "w", encoding="utf-8") as out:
3135
# Write the output assembly file header
@@ -36,14 +40,14 @@ def main(*, module_name: str, build_dir: Path) -> None:
3640
for src in sources:
3741
# Scan each source file line-by-line, accumulating its exports
3842
# and writing its contents to the output.
39-
file_exports = process_source(src, out)
43+
file_exports = process_source(src, out, page=page)
4044
module_exports.extend(file_exports)
4145

4246
# Dump the collected exports to a file
43-
dump_exports(build_dir, module_name, module_exports)
47+
dump_exports(build_dir, module_name, module_exports, page=page)
4448

4549

46-
def process_source(path: Path, out: TextIO) -> list[str]:
50+
def process_source(path: Path, out: TextIO, *, page: int = 1) -> list[str]:
4751
"""Process a single source file, extracting exports and writing to output."""
4852
exports: list[str] = []
4953

@@ -52,17 +56,29 @@ def process_source(path: Path, out: TextIO) -> list[str]:
5256
if m := export_re.match(line):
5357
exports.append(m.group(1))
5458

59+
if page == 2:
60+
normalized = " ".join(line.split())
61+
if normalized == ".section code":
62+
out.write("\t\t.section page2\n")
63+
out.write("\t\t.logical * + $6000\n")
64+
continue
65+
elif normalized == ".send code":
66+
out.write("\t\t.here\n")
67+
out.write("\t\t.send page2\n")
68+
continue
69+
5570
out.write(f"{line.rstrip()}\n")
5671

5772
return exports
5873

5974

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

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

@@ -89,14 +105,16 @@ def collect_sources(module_name: str) -> list[Path]:
89105

90106

91107
if __name__ == "__main__":
92-
if len(sys.argv) < 2:
93-
print(
94-
"Usage: python3 makebuild.py <module_name> [<build_directory>]",
95-
file=sys.stderr,
96-
)
97-
sys.exit(1)
108+
import argparse
109+
110+
parser = argparse.ArgumentParser(description="Build module assembly file")
111+
parser.add_argument("module_name", help="Name of the module to build")
112+
parser.add_argument("build_dir", nargs="?", default=".build", help="Build output directory")
113+
parser.add_argument("--page", type=int, default=1, choices=[1, 2], help="Module page (1 or 2)")
114+
args = parser.parse_args()
98115

99116
main(
100-
module_name=sys.argv[1],
101-
build_dir=Path(sys.argv[2] if len(sys.argv) > 2 else ".build"),
117+
module_name=args.module_name,
118+
build_dir=Path(args.build_dir),
119+
page=args.page,
102120
)

modules/_scripts/makeexport.py

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,26 @@ def main(*, build_dir: Path) -> None:
1818

1919
print(f"PagingEnabled = {1 if paging else 0}")
2020

21+
# Map module page number to MMU slot increments from slot 5 default (N+2):
22+
# page 1 (sb04 = N+3): 1 increment past default
23+
# page 2 (sb05 = N+4): 2 increments past default
24+
page_to_incs = {1: 1, 2: 2}
25+
2126
for module in exports:
22-
print(f"\t.if {module}Integrated == 1")
23-
for routine in exports[module]:
27+
page = exports[module]["page"]
28+
routines = exports[module]["routines"]
29+
module_name = exports[module]["name"]
30+
incs = page_to_incs[page]
31+
print(f"\t.if {module_name}Integrated == 1")
32+
for routine in routines:
2433
print(f"{routine}:")
2534
if paging:
26-
print("\tinc 8+5")
35+
for _ in range(incs):
36+
print("\tinc 8+5")
2737
print(f"\tjsr\tExport_{routine}")
2838
print("\tphp")
29-
print("\tdec 8+5")
39+
for _ in range(incs):
40+
print("\tdec 8+5")
3041
print("\tplp")
3142
print("\trts")
3243
else:
@@ -35,19 +46,33 @@ def main(*, build_dir: Path) -> None:
3546
print("\t.endif")
3647

3748

38-
def read_exports(build_dir: Path) -> dict[str, list[str]]:
39-
"""Read all `.exports` files from the build directory."""
40-
all_exports: dict[str, list[str]] = {}
49+
def read_exports(build_dir: Path) -> dict[str, dict]:
50+
"""Read all `.exports` files from the build directory.
51+
52+
Files named `<module>_p2.exports` are treated as page 2 exports
53+
(requiring double inc/dec 8+5). All others are page 1.
54+
"""
55+
all_exports: dict[str, dict] = {}
4156

4257
if not build_dir.exists():
4358
return all_exports
4459

4560
# Find all .exports files in the build directory
4661
for file_path in build_dir.glob("*.exports"):
4762
if module_exports := read_module_exports(file_path):
48-
module_name = file_path.stem # filename without extension
63+
stem = file_path.stem # filename without extension
64+
if stem.endswith("_p2"):
65+
module_name = stem[:-3] # strip _p2 suffix
66+
page = 2
67+
else:
68+
module_name = stem
69+
page = 1
4970
module_exports.sort()
50-
all_exports[module_name] = module_exports
71+
all_exports[stem] = {
72+
"name": module_name,
73+
"page": page,
74+
"routines": module_exports,
75+
}
5176

5277
return all_exports
5378

modules/hardware/dos.asm

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
; ************************************************************************************************
2+
; ************************************************************************************************
3+
;
4+
; Name: dos.asm
5+
; Purpose: DOS boot (module code)
6+
; Created: 13th January 2023
7+
; Author: Paul Robson (paul@robsons.org.uk)
8+
;
9+
; ************************************************************************************************
10+
; ************************************************************************************************
11+
12+
.section code
13+
14+
Export_EXTBootXA:
15+
pha
16+
phx
17+
jsr IsDestructiveActionOK
18+
plx
19+
pla
20+
bcc _action_ok
21+
jmp WarmStart
22+
23+
_action_ok:
24+
sta zTemp0+0
25+
stx zTemp0+1
26+
27+
ldx #0
28+
ldy #0
29+
30+
_copy_next_string:
31+
tya
32+
clc
33+
adc #<ArgumentStrings
34+
sta ArgumentArray,x
35+
inx
36+
lda #>ArgumentStrings
37+
adc #0
38+
sta ArgumentArray,x
39+
inx
40+
41+
_copy_string:
42+
lda (zTemp0),y
43+
beq _copy_done
44+
cmp #' '
45+
beq _skip_spaces
46+
sta ArgumentStrings,y
47+
iny
48+
bra _copy_string
49+
50+
_skip_spaces:
51+
lda #0
52+
sta ArgumentStrings,y
53+
iny
54+
lda (zTemp0),y
55+
beq _copy_done
56+
cmp #' '
57+
beq _skip_spaces
58+
bra _copy_next_string
59+
60+
_copy_done:
61+
lda #0
62+
sta ArgumentStrings,y
63+
64+
stx kernel.args.extlen
65+
66+
stz ArgumentArray,x
67+
stz ArgumentArray+1,x
68+
69+
lda #<ArgumentArray
70+
sta kernel.args.ext
71+
lda #>ArgumentArray
72+
sta kernel.args.ext+1
73+
74+
lda #<ArgumentStrings
75+
sta kernel.args.buf
76+
lda #>ArgumentStrings
77+
sta kernel.args.buf+1
78+
79+
jsr kernel.RunNamed
80+
81+
jsr ResetTokenBuffer
82+
.error_noprogram
83+
84+
jmp WarmStart
85+
86+
.send code

0 commit comments

Comments
 (0)