Files
snark13 c71e249a4e Add full compiler toolchain, libc, examples and reference docs
First substantive commit: the entire Sprinter C compiler tree on top of
the bare README+gitignore initial commit.

What's in here:
  bin/sprinter-cc        — driver script invoking SDCC + linker + mkexe
  libc/                  — Sprinter-specific libc layer over ESTEX/BIOS
                           (conio, gfx, io, mem, stdio + headers)
  runtime/               — crt0 variants (default/small/banked/minimal)
                           + heap + bank trampolines
  toolchain/             — mkexe (SprintEXE packer, C + tests)
  examples/              — 30 demo programs (gfx, file I/O, env, time, …)
  lib/Makefile           — builds the libc archive (sprinter.lib)
  docs/                  — converted Sprinter manuals + asm reference samples
  third_party/           — solid-c reference compiler dump + sdcc setup script
  release_docs/          — packaging / release notes

gitignore overhaul:
  • Drop dangerous blanket patterns: *.asm (would hide docs/samples/*.asm)
    and *.exe (case-insensitive match was hiding third_party/solid-c/*.EXE
    on macOS APFS).  Replaced with examples/*/*.{asm,exe,…} and lib/*.lib.
  • Restore tracking of toolchain/mkexe/tests/{one,big}.bin — those are
    INPUT fixtures, not build outputs.
  • Collapse the duplicated SDCC/C/Sdcc sections into one section per
    concern (build outputs / vendored / OS-junk).
  • Add .sprinter-cc-*/, build/ (catches lib/build/ too), .claude/.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 16:13:21 +03:00

159 lines
3.8 KiB
C

/*
* open / creat / close — ESTEX file-handle primitives.
*
* The interesting part is the open() flag state machine. We expose POSIX
* flag bits (O_RDONLY/O_WRONLY/O_RDWR + O_CREAT/O_EXCL/O_TRUNC/O_APPEND)
* and dispatch onto the three ESTEX entry points:
*
* $11 OPEN — open existing file
* $0A CREATE — create (truncate if exists), return new handle
* $0B CREATE_NEW — create only if file does not exist
*
* Three private __naked wrappers do the raw RST 10h calls; the public
* open() / creat() / close() are plain C orchestrators.
*
* IX is saved across every RST 10h. Failures set errno and return -1.
*/
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
/* ---- raw ESTEX wrappers ---------------------------------------------- */
/* ESTEX $11 OPEN: A=mode (1=R, 2=W, 0=R/W), HL=path → A=handle, CF=err. */
static int _estex_open_raw(const char *path, int posix_mode) __naked
{
(void)path; (void)posix_mode;
__asm
push ix
;; HL = path, DE = posix_mode. Translate to ESTEX numbering.
ld a, e
and a, #0x03
ld c, #1
or a, a
jr Z, _oopen_real
ld c, #2
dec a
jr Z, _oopen_real
ld c, #0
_oopen_real:
ld a, c
ld c, #0x11 ; ESTEX OPEN
rst #0x10
pop ix
jr c, _oopen_err
ld e, a
ld d, #0
ret
_oopen_err:
call __errno_set
ld de, #-1
ret
__endasm;
}
/* ESTEX $0A CREATE: A=attr, HL=path → A=handle, CF=err.
* Truncates an existing file. */
static int _estex_create_raw(const char *path) __naked
{
(void)path;
__asm
push ix
xor a, a ; A = 0 (normal attribute)
ld c, #0x0A
rst #0x10
pop ix
jr c, _ocreat_err
ld e, a
ld d, #0
ret
_ocreat_err:
call __errno_set
ld de, #-1
ret
__endasm;
}
/* ESTEX $0B CREATE_NEW: A=attr, HL=path → A=handle, CF=err.
* Fails (errno=EEXIST) if file already exists. */
static int _estex_create_new_raw(const char *path) __naked
{
(void)path;
__asm
push ix
xor a, a
ld c, #0x0B
rst #0x10
pop ix
jr c, _ocreatn_err
ld e, a
ld d, #0
ret
_ocreatn_err:
call __errno_set
ld de, #-1
ret
__endasm;
}
/* ---- public surface --------------------------------------------------- */
int open(const char *path, int flags)
{
int fd;
if (flags & O_CREAT) {
if (flags & O_EXCL) {
/* Must not already exist. */
fd = _estex_create_new_raw(path);
} else if (flags & O_TRUNC) {
/* Always create or truncate. */
fd = _estex_create_raw(path);
} else {
/* Open if it exists, otherwise create. */
fd = _estex_open_raw(path, flags);
if (fd < 0 && errno == ENOENT) {
fd = _estex_create_raw(path);
}
}
} else {
fd = _estex_open_raw(path, flags);
}
if (fd < 0) {
return -1;
}
if (flags & O_APPEND) {
/* Position at end of file so future writes append. */
(void)lseek(fd, 0L, SEEK_END);
}
return fd;
}
int creat(const char *path, int mode)
{
(void)mode; /* Sprinter has no per-file permission bits */
return open(path, O_WRONLY | O_CREAT | O_TRUNC);
}
int close(int fd) __naked
{
(void)fd;
__asm
push ix
ld a, l
ld c, #0x12
rst #0x10
pop ix
jr c, _oclose_err
ld de, #0
ret
_oclose_err:
call __errno_set
ld de, #-1
ret
__endasm;
}