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
22 changes: 14 additions & 8 deletions source/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ ASFLAGS += '-D MONITOR_ADDRESS=$(DOLLAR)$(LMONITOR)' '-D LOCKOUT_ADDRESS=$(DOLLA
-L $(BUILDDIR)debug$(S)basic.lst -l $(BUILDDIR)debug$(S)basic.lbl

MODULES = +hardware +tokeniser +graphics +kernel:2 +sound:2
BUILD_OUT = $(BUILDDIR)basic.rom
SCRIPTDIR = scripts$(S)
MAME_PATH=../../mame
KERNEL_PATH=../../F256_MicroKernel
Expand Down Expand Up @@ -74,10 +73,20 @@ build: prelim
$(call cleandir,$(BUILDDIR)debug$(S))

@echo "Compiling assembly"
$(Q)$(ASM) $(ASFLAGS) _basic.asm -o $(BUILD_OUT)
$(Q)$(ASM) -b $(ASFLAGS) _basic.asm \
--output-section boot -o $(BUILDDIR)sb01.bin \
--output-section resident_shared -o $(BUILDDIR)sb02.bin \
--output-section resident_swappable -o $(BUILDDIR)sb03.bin \
--output-section page1 -o $(BUILDDIR)sb04.bin \
--output-section page2 -o $(BUILDDIR)sb05.bin \
--output-section zeropage -o $(BUILDDIR)zeropage.ram \
--output-section zeropref -o $(BUILDDIR)zeropref.ram \
--output-section arguments -o $(BUILDDIR)arguments.ram \
--output-section storage -o $(BUILDDIR)storage.ram

@echo "Checking constraints"
$(Q)$(PYTHON) $(SCRIPTDIR)verifyblank.py $(BUILDDIR)

@echo "Splitting ROM"
$(Q)$(PYTHON) $(SCRIPTDIR)splitrom.py $(BUILDDIR)

#
# Scripts run in advance generating tables etc.
Expand Down Expand Up @@ -123,7 +132,7 @@ build_release:
$(call cleandir,$(OUTPUTDIR)debug$(S))

@echo "Copying build artifacts"
$(Q)$(CCOPY) $(BUILDDIR)*.* $(OUTPUTDIR)
$(Q)$(CCOPY) $(BUILDDIR)*.bin $(OUTPUTDIR)
$(Q)$(CCOPY) $(BUILDDIR)debug$(S)*.* $(OUTPUTDIR)debug

#
Expand Down Expand Up @@ -170,9 +179,6 @@ paging:
#
# Test on real hardware
#
run: build
foenixmgr binary --address 5000 $(BUILD_OUT)

flash: build
foenixmgr flash-bulk build/basic.csv

Expand Down
3 changes: 2 additions & 1 deletion source/common/aa.system/00start.asm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
; Start up code
;;

; ---------------------------------------------------------------------------------------------- ;
.section boot

;;
Expand All @@ -25,7 +26,7 @@ KernelHeader:

.send boot


; ---------------------------------------------------------------------------------------------- ;
.section code

;;
Expand Down
101 changes: 95 additions & 6 deletions source/common/aa.system/01common.inc
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,17 @@ MaxPages = 32 ; max logical pages (256KB program space)
ArrayStart = $4000
ArrayEnd = $8000
;
; Build address of ROM (slots 4-5, $8000-$BFFF)
; Boot section at $6000 (slot 3, remapped to RAM after boot)
;
BootStart = $6000
CodeStart = $8000
;
; Resident shared code (slot 4, $8000-$9FFF)
;
ResidentSharedStart = $8000
;
; Resident swappable code (slot 5, $A000-$BFFF)
;
ResidentSwappableStart = $A000
;
; Start of variable/string space. This is a fixed location in memory.
;
Expand Down Expand Up @@ -84,6 +90,9 @@ MaxLineSize = 80
;
; ************************************************************************************************

;;
; RAM storage, declarations/dynamic data only, cannot contain statically initialized data
;;
* = ZeroPageMandatory ; *must* be in zero page
.dsection zeropage

Expand All @@ -96,11 +105,91 @@ MaxLineSize = 80
* = MemoryStorage ; doesn't matter if zero page or not
.dsection storage

* = $4000 ; page 2 modules at $4000 (output before boot)

; ROM image sections: code and static data
;
; $4000-$5FFF: module page 2 code (`page2` section)
; $6000-$7FFF: KernelHeader + headerdata (`boot` section)
; $8000-$9FFF: resident code 1 (`resident_shared` section)
; $A000-$BFFF: resident code 2 (`resident_swappable` section)
; $C000-$DFFF: module page 1 code (`page1` section)
;
; Banking/visibility rules:
;
; - `resident_shared` is always visible. Banking thunks, interrupt handlers,
; and code/data needed by paged modules live here.
;
; - `resident_swappable` is unavailable while `page1` is mapped.
;
; - `page2` hides array RAM at $6000-$7FFF while executing.
;
; - `page1` has the full array storage ($4000-$7FFF) visible, but only if it
; doesn't have `page2` code in its call stack.
;
; - Calls from outside `page1` or `page2` must go through the corresponding
; banking thunk.
;
; - A `page1` thunk may only be entered while `resident_swappable` is mapped.
; Consequently, `page1` cannot be re-entered through a thunk, including
; through a `page1` -> `page2` -> `page1` call chain.
;
; - `page2` may call `page1` through a thunk iff `page1` is not already
; mapped. The called code cannot access array RAM at $6000-$7FFF because
; `page2` still occupies slot 3.


; Page 2 section at $4000
;
; Mapped to `BootStart` (slot 3) when called and therefore cannot
; directly access higher half of the RAM/array data at $6000-$7FFF

* = $4000
.logical BootStart
.dsection page2
.endlogical

; define the corresponding namespace
Page2 .namespace
.endnamespace

* = BootStart ; boot section at $6000 (KernelHeader + headerdata)

; Boot section at $6000 (KernelHeader + headerdata)
;
; RAM (array/`alloc` storage in slot 3) is mapped here after boot and
; page 2 code is mapped here when called

* = BootStart
.dsection boot

* = CodeStart ; main code at $8000
.dsection code

; Shared resident code at $8000
;
; The only code section that stays resident in CPU memory throughout
; the lifetime of the interpreter

* = ResidentSharedStart ; shared resident code at $8000
.dsection resident_shared


; Swappable resident code at $A000
;
; Page 1 modules are mapped here on demand

* = ResidentSwappableStart
.dsection resident_swappable


; Page 1 section at $C000
;
; Mapped to `ResidentSwappableStart` when called and therefore cannot
; depend on `resident_swappable` routines

* = $C000
.logical ResidentSwappableStart
.dsection page1
.endlogical

; define the corresponding namespace
Page1 .namespace
.endnamespace

2 changes: 1 addition & 1 deletion source/common/aa.system/_dataoverflowchecks.asm
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@
.send storage

.section boot
.cwarn * > CodeStart, "BROKEN BUILD: boot section overflowed into code by ", * - CodeStart," bytes"
.cwarn * > ResidentSharedStart, "BROKEN BUILD: boot section overflowed into resident code by ", * - ResidentSharedStart," bytes"
.send boot
4 changes: 4 additions & 0 deletions source/common/aa.system/_slot3banking.asm
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
; buffer overflows).
;;

; Page 1 modules use slot 5 (inc/dec 8+5).
; Page 2 modules use slot 3 (save/restore 8+3 with depth counter).


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

Slot3Init:
Expand Down
4 changes: 2 additions & 2 deletions source/modules/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ all:
$(Q)$(MAKE) -B -C hardware$(S)startup

$(Q)$(CCOPY) ..$(S)common$(S)generated$(S)kwdtext.dat tokeniser$(S)__kwdtext.asm
$(Q)$(PYTHON) _scripts$(S)makebuild.py tokeniser

$(Q)$(PYTHON) _scripts$(S)makebuild.py tokeniser $(BUILD_DIR)
$(Q)$(PYTHON) _scripts$(S)makebuild.py graphics $(BUILD_DIR)
$(Q)$(PYTHON) _scripts$(S)makebuild.py hardware $(BUILD_DIR)
$(Q)$(PYTHON) _scripts$(S)makebuild.py kernel $(BUILD_DIR) --page 2
$(Q)$(PYTHON) _scripts$(S)makebuild.py sound $(BUILD_DIR) --page 2

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

clean:
$(Q)$(MAKE) -C graphics clean
Expand Down
46 changes: 17 additions & 29 deletions source/modules/_scripts/makebuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,22 @@
from pathlib import Path
from typing import TextIO

from pages import Page, PAGE1, PAGE2

# 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():
build_dir.mkdir(parents=True, exist_ok=True)

sources = collect_sources(module_name)

if page == 2:
output_path = build_dir / (module_name + "_p2.module.asm")
else:
output_path = build_dir / (module_name + ".module.asm")
output_page = PAGE2 if page == 2 else PAGE1
output_path = build_dir / output_page.name / (module_name + ".module.asm")
output_path.parent.mkdir(exist_ok=True)

with open(output_path, "w", encoding="utf-8") as out:
# Write the output assembly file header
Expand All @@ -50,14 +40,14 @@ def main(*, module_name: str, build_dir: Path, page: int = 1) -> None:
for src in sources:
# Scan each source file line-by-line, accumulating its exports
# and writing its contents to the output.
file_exports = process_source(src, out, page=page)
file_exports = process_source(src, out, page=output_page)
module_exports.extend(file_exports)

# Dump the collected exports to a file
dump_exports(build_dir, module_name, module_exports, page=page)
dump_exports(build_dir, module_name, module_exports, page=output_page)


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

Expand All @@ -66,30 +56,28 @@ def process_source(path: Path, out: TextIO, *, page: int = 1) -> list[str]:
if m := export_re.match(line):
exports.append(m.group(1))

if page == 2:
normalized = " ".join(line.split())
if normalized == ".section code":
out.write(PAGE2_BEGIN)
continue
elif normalized == ".send code":
out.write(PAGE2_END)
continue
normalized = " ".join(line.split())
if normalized == ".section code":
out.write(page.begin())
continue
elif normalized == ".send code":
out.write(page.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
build_dir: Path, module_name: str, exports: list[str], *, page: Page
) -> 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"
build_dir / page.name / (module_name + ".exports"), "w", encoding="utf-8"
) as out:
for export in exports:
out.write(f"{export}\n")
Expand Down
Loading