c71e249a4e
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>
159 lines
3.8 KiB
C
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;
|
|
}
|