Skip to content

Latest commit

Β 

History

History
141 lines (108 loc) Β· 6.72 KB

CONTRACT.md

File metadata and controls

141 lines (108 loc) Β· 6.72 KB

CONTRACT β€” tish compiler ⇄ chuggie framework

This file pins the interface between the two tracks so they move independently:

  • Compiler track (in the tish repo /Users/a_/Projects/tish/tish): the Gba emit mode, the portable runtime facade tishlang_runtime_gba, typed Rust-scalar lowering, typed externs, the asset: import scheme, and async emission.
  • Framework track (this repo): tish-agb bindings, tish-gba-game-engine, macros, the host asset importer, and the example games.

Any change to the items below is a breaking change and must be updated in lockstep on both sides. Committed .d.tish files + a CI drift check enforce this.

Status legend: βœ… locked & proven in P0 Β· πŸ”· locked by design Β· πŸ•“ lands in a later phase.


1. Generated-crate shape (what --target gba emits) πŸ”·

The Gba emit mode generates a no_std binary crate:

#![no_std]
#![no_main]
extern crate alloc;
use tishlang_runtime::Arc;   // = alloc::rc::Rc<T> (facade alias) β€” absorbs Arc::from/clone
// ...alloc imports, prelude `use tishlang_runtime::{...}`...

fn run() -> Result<(), Box<dyn core::error::Error>> { /* program body */ }   // πŸ•“ P5: async fn

#[agb::entry]
fn agb_main(gba: agb::Gba) -> ! {
    tishlang_runtime::gba::init(gba);   // stash peripherals for binding crates
    let _ = run();                       // πŸ•“ P5: tishlang_runtime::gba::block_on(run())
    tishlang_runtime::gba::halt()
}
  • Runtime crate name: the Cargo dependency is tishlang_runtime = { package = "tishlang_runtime_gba", path = ... }. The rename makes every emitted absolute tishlang_runtime::… path resolve to the GBA facade with no codegen edits.
  • Panic/halt: gba::halt() -> ! loops on agb::halt().

2. Prelude surface tishlang_runtime_gba must export πŸ”·

Name-for-name, everything codegen.rs emits in its use tishlang_runtime::{...} list: Value, VmRef, ObjectMap, ObjectData, PropMap, TishError, the math/json/uri/string/array/object/number/symbol/collection/typedarray helpers, and console_log & co (β†’ agb::println!). Plus:

  • pub type Arc<T> = alloc::rc::Rc<T>;
  • pub type Fixed = agb_fixnum::Num<i32, 8>; (typed-numeric target type, Β§5)
  • pub mod gba (Β§4).
  • Capped-out capabilities (fetch/serve/fs/http/timers/ws/tty/pty) are compile_error! stubs so missing features read as "not available on GBA".

Portable Value core (proven P0a): Number(f64) | Str(Rc<str>) | Bool | Null | Array(Rc<RefCell<Vec<Value>>>) | Object(Rc<RefCell<ObjectMap>>) | Function(Rc<dyn Fn(&[Value]) -> Value>) | Promise(Rc<dyn TishPromise>) | Opaque(Rc<dyn TishOpaque>). Single-threaded Rc (NOT Arc; send-values OFF). ObjectMap hashes with rustc_hash::FxBuildHasher β€” NOT foldhash/ahash (both need atomics; see docs/findings/P0-findings.md).

3. cargo: binding ABI πŸ”·

Current implementation: exports are hand-written in Rust and declared in crates/*/tish.d.tish. There is no #[tish_export] macro or compiler bindgen scan in this tree today β€” the .d.tish files are the typed ABI contract the tish compiler reads at build time.

  • Binding crates (tish-agb, tish-gba-game-engine) expose idiomatic Rust fns plus matching declare fn entries in tish.d.tish.
  • Classifiable param/return types at the ABI boundary: i8 u8 i16 u16 i32 u32 bool, Fixed, &str/String, (), Option<T> of those, and Vector2D<Fixed> (destructured to a (x: Fixed, y: Fixed) scalar pair). Anything else falls back to the explicit fn(args: &[Value]) -> Value convention, which crates MAY still hand-write where dynamic data is the point (e.g. spawn(componentConfigs), defineComponent(def)).
  • Future direction: a #[tish_export] marker + compiler bindgen that auto-generates the .d.tish declaration and boxed shim remains compatible with this contract but is not implemented yet.

4. tishlang_runtime_gba::gba module πŸ”· / πŸ•“

  • init(gba: agb::Gba) β€” stash peripherals (single-core, critical-section RefCells). βœ… shape proven.
  • halt() -> !. βœ…
  • Peripheral accessors for binding crates (graphics/input/mixer/timers/save/rng).
  • Hooks: hooks::register_pre_commit(fn) β€” tish-agb's frame driver and tish-gba-game-engine's pipeline install here; run once per frame before commit().
  • Executor πŸ•“ P5: block_on(fut), tish-level spawn/cancel, and WakeCondition { Ready, NextFrame, AtFrame(u32), Buttons(u16), ChannelDone, Flag(FlagId) }. All wakes at frame boundaries in spawn order β‡’ deterministic.

5. Numeric model πŸ”·

  • Annotations may name concrete Rust scalars: i8 u8 i16 u16 i32 u32 bool and fixed (≙ Fixed = agb_fixnum::Num<i32,8>). Explicit : number = f64 (soft-float; exact JS semantics).
  • Unannotated number in Gba mode: integral-inferred β†’ i32, fractional β†’ fixed.
  • Value::Number stays f64 on the dynamic path (lossless fixed↔f64 boxing; JSON/formatting unchanged).

6. Async πŸ•“ P5

Phase 1 (P2–P4): synchronous frame loop; await in Gba mode is a compile error (not a silent sync fallback). Phase 2 (P5): tish async fn β†’ Rust async fn, await β†’ .await, Value::Promise(Rc<dyn TishPromise>) bridges a Pin<Box<dyn Future<Output=Value>>>. Framework awaitables: video.frame(), time.waitFrames(n), input.buttonPress(mask), engine tween/dialogue/walkTo.

7. asset: imports πŸ•“ P3

import { player } from "asset:gfx/sprites.aseprite" β€” compile-time file check; Gba codegen emits include_aseprite! / include_background_gfx! / include_wav! into the generated crate and registers each imported name as an i32 handle with the binding crate's asset table before run(). Aseprite tags are animation handles. The build step copies the project assets/ tree into the build dir (mtime-diffed) so proc-macro paths resolve.

8. ECS decision (P0c) βœ…

tish-gba-game-engine uses a custom SoA/archetype store, not hecs (hecs's spin + foldhash need atomics unavailable on thumbv4t). Component set is closed (Β§ engine).


Workspace host/target split

.cargo/config.toml sets thumbv4t-none-eabi as the default target, so plain workspace members build for GBA. Host-only tooling must NOT be a plain member:

  • tish-agb-macros / tish-gba-game-engine-macros are proc-macro crates β†’ cargo builds them for the host automatically. βœ… safe as members.
  • The host LDtk importer (tish-gba-assets) is consumed only as a [build-dependencies] entry of example games β†’ cargo builds it for the host. It must live outside the default GBA members (its own tools/ workspace, or [patch]/path build-dep) so cargo build never tries to cross-compile it.

Until those crates exist, the workspace member list stays minimal.