kimdos 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kimdos/__init__.py +1 -0
- kimdos/bench.py +97 -0
- kimdos/board.py +58 -0
- kimdos/build.py +75 -0
- kimdos/c/Dockerfile +1 -0
- kimdos/c/include/kimdos.h +86 -0
- kimdos/c/kim1/crt.s +8 -0
- kimdos/c/kim1/link.ld +25 -0
- kimdos/c/src/con.s +36 -0
- kimdos/c/src/dir.c +43 -0
- kimdos/c/src/dos.c +150 -0
- kimdos/c/src/iec.s +573 -0
- kimdos/c/src/kdint.h +9 -0
- kimdos/cli.py +111 -0
- kimdos/clib.py +21 -0
- kimdos/host.py +298 -0
- kimdos/iec.py +786 -0
- kimdos/images/kimdos-0400.bin +0 -0
- kimdos/images/kimdos-0400.lbl +211 -0
- kimdos/images/kimdos-0400.ptp +128 -0
- kimdos/images/kimdos-2000.bin +0 -0
- kimdos/images/kimdos-2000.lbl +211 -0
- kimdos/images/kimdos-2000.ptp +128 -0
- kimdos/images/kimdos-C000.bin +0 -0
- kimdos/images/kimdos-C000.lbl +211 -0
- kimdos/images/kimdos-C000.ptp +128 -0
- kimdos/sim.py +252 -0
- kimdos-0.2.0.dist-info/METADATA +98 -0
- kimdos-0.2.0.dist-info/RECORD +33 -0
- kimdos-0.2.0.dist-info/WHEEL +5 -0
- kimdos-0.2.0.dist-info/entry_points.txt +2 -0
- kimdos-0.2.0.dist-info/licenses/LICENSE +201 -0
- kimdos-0.2.0.dist-info/top_level.txt +1 -0
kimdos/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""KIM-1 DOS test oracle: serial bus drive model and KIM-1 machine simulator."""
|
kimdos/bench.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Throughput and JiffyDOS timing margins of the 6502 library, simulated."""
|
|
2
|
+
|
|
3
|
+
from tqdm import tqdm
|
|
4
|
+
|
|
5
|
+
from .iec import JIFFY_AVR_EARLY, JIFFY_AVR_LATE, JIFFY_LPC, Drive
|
|
6
|
+
from .sim import RETURN, Kim
|
|
7
|
+
|
|
8
|
+
TIMINGS = {"lpc": JIFFY_LPC, "avr-early": JIFFY_AVR_EARLY, "avr-late": JIFFY_AVR_LATE}
|
|
9
|
+
BUF = 0x4000
|
|
10
|
+
NAME = 0x3F00
|
|
11
|
+
SIZES = (1016, 2032)
|
|
12
|
+
OPS = ("read", "write", "load")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _kim(image, files, **kw):
|
|
16
|
+
kim = Kim(image.binary, image.org, drives=Drive(files=files, **kw))
|
|
17
|
+
return kim, kim.drive
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _call(kim, image, name, **kw):
|
|
21
|
+
r = kim.call(image[name], max_cycles=100_000_000, **kw)
|
|
22
|
+
assert r.reason == RETURN, name
|
|
23
|
+
return r
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _setnam(kim, image, name):
|
|
27
|
+
kim.write(NAME, name)
|
|
28
|
+
_call(kim, image, "dos_setnam", a=len(name), x=NAME & 0xFF, y=NAME >> 8)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _range(kim, image, beg, end):
|
|
32
|
+
kim.write(image["dos_beg"], beg.to_bytes(2, "little") + end.to_bytes(2, "little"))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def op_cycles(image, op, n, **kw):
|
|
36
|
+
"""CPU cycles for one library operation moving n bytes: dos_read of an
|
|
37
|
+
open file, dos_write to one, or a whole dos_load (open, transfer, close)."""
|
|
38
|
+
data = bytes(i * 7 & 0xFF for i in range(n))
|
|
39
|
+
kim, drive = _kim(image, {b"F": b"\x00\x40" + data}, **kw)
|
|
40
|
+
if op == "load":
|
|
41
|
+
_setnam(kim, image, b"F")
|
|
42
|
+
c = _call(kim, image, "dos_load").cycles
|
|
43
|
+
assert kim.read(BUF, n) == data
|
|
44
|
+
else:
|
|
45
|
+
_setnam(kim, image, b"F" if op == "read" else b"G,S,W")
|
|
46
|
+
_call(kim, image, "dos_open", a=2)
|
|
47
|
+
if op == "write":
|
|
48
|
+
kim.write(BUF, data)
|
|
49
|
+
_range(kim, image, BUF, BUF + n + 2 if op == "read" else BUF + n)
|
|
50
|
+
c = _call(kim, image, "dos_" + op, a=2).cycles
|
|
51
|
+
_call(kim, image, "dos_close", a=2)
|
|
52
|
+
got = kim.read(BUF, n + 2)[2:] if op == "read" else drive.files[b"G"]
|
|
53
|
+
assert got == data, op
|
|
54
|
+
assert not drive.errors, drive.errors
|
|
55
|
+
return c
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def per_byte(image, op, **kw):
|
|
59
|
+
"""Cycles per byte of op, fixed costs cancelled over two sizes."""
|
|
60
|
+
lo, hi = (op_cycles(image, op, n, **kw) for n in SIZES)
|
|
61
|
+
return (hi - lo) / (SIZES[1] - SIZES[0])
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def margin_ok(image, margin, **kw):
|
|
65
|
+
"""Whether a load, a save and a status read stay margin us clear of
|
|
66
|
+
every device edge and sample point."""
|
|
67
|
+
data = bytes(range(256)) * 2
|
|
68
|
+
kim, drive = _kim(image, {b"F": b"\x00\x40" + data}, margin=margin, **kw)
|
|
69
|
+
_setnam(kim, image, b"F")
|
|
70
|
+
_call(kim, image, "dos_load")
|
|
71
|
+
_setnam(kim, image, b"G")
|
|
72
|
+
_range(kim, image, BUF, BUF + len(data))
|
|
73
|
+
_call(kim, image, "dos_save")
|
|
74
|
+
_call(kim, image, "dos_status")
|
|
75
|
+
return not drive.errors and drive.files.get(b"G", b"")[2:] == data
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def max_margin(image, step=0.25, **kw):
|
|
79
|
+
"""Largest margin, in steps of step us, at which margin_ok holds."""
|
|
80
|
+
m = 0
|
|
81
|
+
while margin_ok(image, m + step, **kw):
|
|
82
|
+
m += step
|
|
83
|
+
return m
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def report(image, latencies=(0, 1, 2)):
|
|
87
|
+
"""Throughput rows (op, standard, Jiffy) and margin rows (timing,
|
|
88
|
+
latency, margin), with progress bars."""
|
|
89
|
+
rows = []
|
|
90
|
+
for op in tqdm(OPS, desc="throughput"):
|
|
91
|
+
rows.append((op, per_byte(image, op, jiffy=None), per_byte(image, op)))
|
|
92
|
+
cases = [(t, lat) for t in TIMINGS for lat in latencies]
|
|
93
|
+
margins = [
|
|
94
|
+
(t, lat, max_margin(image, jiffy=TIMINGS[t], latency=lat))
|
|
95
|
+
for t, lat in tqdm(cases, desc="margins")
|
|
96
|
+
]
|
|
97
|
+
return rows, margins
|
kimdos/board.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Drive the KIMDOS shell on a real board over its TTY line."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import sys
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from kimtape import Monitor, Port, Tape
|
|
8
|
+
|
|
9
|
+
PROMPT = b"\r\n>"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Board:
|
|
13
|
+
"""A KIM-1 running the KIMDOS shell, reached through the monitor."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, dev, baud=1200, trace=False):
|
|
16
|
+
self.port = Port(dev, baud, trace=trace)
|
|
17
|
+
self.mon = Monitor(self.port, log=sys.stderr)
|
|
18
|
+
|
|
19
|
+
def install(self, ptp, org):
|
|
20
|
+
"""Load a tape image with the monitor, verify it and start the shell."""
|
|
21
|
+
self.mon.sync()
|
|
22
|
+
tape = Tape("kimdos", ptp)
|
|
23
|
+
for _ in range(3):
|
|
24
|
+
self.mon.load(tape)
|
|
25
|
+
if self.mon.verify(tape):
|
|
26
|
+
break
|
|
27
|
+
else:
|
|
28
|
+
raise RuntimeError("image did not verify")
|
|
29
|
+
self.start(org)
|
|
30
|
+
|
|
31
|
+
def start(self, org):
|
|
32
|
+
"""Run the shell at org and wait for its prompt."""
|
|
33
|
+
self.mon.sync()
|
|
34
|
+
self.port.take()
|
|
35
|
+
self.mon.go(org)
|
|
36
|
+
self.wait_prompt(10)
|
|
37
|
+
|
|
38
|
+
def wait_prompt(self, timeout):
|
|
39
|
+
"""Collect output up to the next shell prompt."""
|
|
40
|
+
deadline = time.time() + timeout
|
|
41
|
+
while PROMPT not in self.port.rx and time.time() < deadline:
|
|
42
|
+
self.port.poll(0.1)
|
|
43
|
+
if PROMPT not in self.port.rx:
|
|
44
|
+
raise TimeoutError(Port.show(bytes(self.port.rx)))
|
|
45
|
+
out = bytes(self.port.rx)
|
|
46
|
+
self.port.take()
|
|
47
|
+
return out[: out.index(PROMPT)]
|
|
48
|
+
|
|
49
|
+
def command(self, line, timeout=60):
|
|
50
|
+
"""Type a shell command and return its output without the echo."""
|
|
51
|
+
self.port.take()
|
|
52
|
+
self.port.write(line.encode() + b"\r")
|
|
53
|
+
out = self.wait_prompt(timeout).decode("ascii", "replace")
|
|
54
|
+
return re.sub(r"^.*?\r\n", "", out, count=1, flags=re.S).strip("\r\n")
|
|
55
|
+
|
|
56
|
+
def close(self):
|
|
57
|
+
"""Release the serial line."""
|
|
58
|
+
self.port.close()
|
kimdos/build.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Build the 6502 image with cc65 and read back its symbols."""
|
|
2
|
+
|
|
3
|
+
import fcntl
|
|
4
|
+
import subprocess
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
ROOT = Path(__file__).resolve().parents[2]
|
|
9
|
+
IMAGES = Path(__file__).resolve().parent / "images"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class Image:
|
|
14
|
+
"""A linked KIMDOS image: binary, tape, load address and symbol table."""
|
|
15
|
+
|
|
16
|
+
binary: bytes
|
|
17
|
+
ptp: Path
|
|
18
|
+
labels: dict
|
|
19
|
+
org: int
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def load(cls, stem):
|
|
23
|
+
"""Read the .bin, .ptp and .lbl files an ld65 build left at stem."""
|
|
24
|
+
stem = Path(stem)
|
|
25
|
+
labels = read_labels(stem.with_suffix(".lbl"))
|
|
26
|
+
return cls(
|
|
27
|
+
stem.with_suffix(".bin").read_bytes(),
|
|
28
|
+
stem.with_suffix(".ptp"),
|
|
29
|
+
labels,
|
|
30
|
+
labels["__RAM_START__"],
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def __getitem__(self, name):
|
|
34
|
+
return self.labels[name]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def read_labels(path):
|
|
38
|
+
"""Parse an ld65 -Ln (VICE) label file into {name: address}."""
|
|
39
|
+
labels = {}
|
|
40
|
+
for line in Path(path).read_text(encoding="ascii").splitlines():
|
|
41
|
+
_, addr, name = line.split()
|
|
42
|
+
labels[name.lstrip(".")] = int(addr, 16)
|
|
43
|
+
return labels
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _make(build_dir, *args):
|
|
47
|
+
build_dir.mkdir(parents=True, exist_ok=True)
|
|
48
|
+
with open(build_dir / ".lock", "w", encoding="ascii") as lock:
|
|
49
|
+
fcntl.flock(lock, fcntl.LOCK_EX)
|
|
50
|
+
subprocess.run(
|
|
51
|
+
["make", "-s", "-C", str(ROOT), "BUILD=%s" % build_dir, *args],
|
|
52
|
+
check=True,
|
|
53
|
+
stdout=subprocess.DEVNULL,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def build_c():
|
|
58
|
+
"""Build the C library and its test program (llvm-mos in docker) under the
|
|
59
|
+
repository's build directory, which docker can mount; return kdtest.bin."""
|
|
60
|
+
build_dir = ROOT / "build"
|
|
61
|
+
_make(build_dir, "clib")
|
|
62
|
+
return (build_dir / "c" / "kdtest.bin").read_bytes()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def build(org=0x2000, build_dir=None):
|
|
66
|
+
"""Run make for an image at org and return it."""
|
|
67
|
+
build_dir = Path(build_dir or ROOT / "build")
|
|
68
|
+
_make(build_dir, "ORG=%04X" % org)
|
|
69
|
+
return Image.load(build_dir / ("kimdos-%04X" % org))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def packaged(org=0x2000):
|
|
73
|
+
"""The prebuilt shell image at org shipped in the wheel ($0400, $2000,
|
|
74
|
+
$C000; `make images` in a checkout)."""
|
|
75
|
+
return Image.load(IMAGES / ("kimdos-%04X" % org))
|
kimdos/c/Dockerfile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
FROM ghcr.io/anarkiwi/docker-mos-llvm-sdk:23.3.0
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* KIMDOS: Commodore serial bus disk access for the KIM-1, llvm-mos C API. */
|
|
2
|
+
#ifndef KIMDOS_H
|
|
3
|
+
#define KIMDOS_H
|
|
4
|
+
|
|
5
|
+
#include <stdint.h>
|
|
6
|
+
|
|
7
|
+
#ifdef __cplusplus
|
|
8
|
+
extern "C" {
|
|
9
|
+
#endif
|
|
10
|
+
|
|
11
|
+
/* assembly routines: they never call back into C */
|
|
12
|
+
#define KD_ASM __attribute__((leaf))
|
|
13
|
+
|
|
14
|
+
#define ST_WTO 0x01 /* a listener did not acknowledge a byte */
|
|
15
|
+
#define ST_RTO 0x02 /* the talker sent nothing */
|
|
16
|
+
#define ST_VER 0x10 /* verify mismatch */
|
|
17
|
+
#define ST_EOI 0x40 /* the last byte read was the end of the data */
|
|
18
|
+
#define ST_DNP 0x80 /* no device answered */
|
|
19
|
+
|
|
20
|
+
extern uint8_t iec_st; /* status bits collected by the bus primitives */
|
|
21
|
+
/* bit 0: offer JiffyDOS (default 1); 0 forces the standard protocol.
|
|
22
|
+
* bit 7: set by iec_listen/iec_talk when the device answered */
|
|
23
|
+
extern uint8_t iec_jiffy;
|
|
24
|
+
extern uint8_t kd_dev; /* device number, default 8 */
|
|
25
|
+
|
|
26
|
+
/* bus primitives (asm, same semantics as asm/lib/iec.s) */
|
|
27
|
+
KD_ASM void iec_listen(uint8_t dev);
|
|
28
|
+
KD_ASM void iec_second(uint8_t sa);
|
|
29
|
+
KD_ASM void iec_talk(uint8_t dev);
|
|
30
|
+
KD_ASM void iec_tksa(uint8_t sa);
|
|
31
|
+
KD_ASM void iec_ciout(uint8_t b);
|
|
32
|
+
KD_ASM uint8_t iec_acptr(void);
|
|
33
|
+
KD_ASM void iec_unlsn(void);
|
|
34
|
+
KD_ASM void iec_untlk(void);
|
|
35
|
+
/* after TALK and secondary 0x61 to a device in iec_jiffy bit 7: the rest of
|
|
36
|
+
* the file in JiffyDOS LOAD mode to addr up; returns one past the end */
|
|
37
|
+
KD_ASM uint16_t iec_jload(uint16_t addr);
|
|
38
|
+
|
|
39
|
+
/* DOS layer: functions returning uint8_t return 0 on success, else nonzero
|
|
40
|
+
* (iec_st bits or a DOS error code as documented) */
|
|
41
|
+
uint8_t kd_open(uint8_t ch, const void *name, uint8_t len);
|
|
42
|
+
void kd_close(uint8_t ch);
|
|
43
|
+
uint8_t kd_chkin(uint8_t ch); /* TALK ch */
|
|
44
|
+
uint8_t kd_chkout(uint8_t ch); /* LISTEN ch */
|
|
45
|
+
int16_t kd_getb(void); /* next byte, -1 once past the end (EOI seen or RTO) */
|
|
46
|
+
/* TALK, read up to n bytes or to EOI, UNTALK; returns count; can be called
|
|
47
|
+
* again to continue */
|
|
48
|
+
uint16_t kd_read(uint8_t ch, void *buf, uint16_t n);
|
|
49
|
+
/* LISTEN, write, UNLISTEN; returns count */
|
|
50
|
+
uint16_t kd_write(uint8_t ch, const void *buf, uint16_t n);
|
|
51
|
+
/* command on channel 15 (binary safe) */
|
|
52
|
+
uint8_t kd_cmd(const void *cmd, uint8_t len);
|
|
53
|
+
/* reads channel 15 into buf (48 bytes, zero terminated, CR stripped); returns
|
|
54
|
+
* the numeric code, 0xFF if no device */
|
|
55
|
+
uint8_t kd_status(char *buf);
|
|
56
|
+
/* sd2iec "P" + chr(0x60|ch) + 4-byte little-endian offset, then kd_status;
|
|
57
|
+
* returns its code */
|
|
58
|
+
uint8_t kd_seek(uint8_t ch, uint32_t offset);
|
|
59
|
+
|
|
60
|
+
/* name raw PETSCII, zero terminated, quotes stripped; type e.g. "PRG" */
|
|
61
|
+
typedef struct {
|
|
62
|
+
char name[17];
|
|
63
|
+
char type[4];
|
|
64
|
+
uint16_t blocks;
|
|
65
|
+
} kd_dirent;
|
|
66
|
+
/* opens "$"+pattern on channel 0 and skips the load address and header line;
|
|
67
|
+
* 0 ok */
|
|
68
|
+
uint8_t kd_dir_open(const void *pattern, uint8_t len);
|
|
69
|
+
/* 1 with the next file entry, 0 at the end (BLOCKS FREE line or end) */
|
|
70
|
+
uint8_t kd_dir_next(kd_dirent *e);
|
|
71
|
+
void kd_dir_close(void);
|
|
72
|
+
/* addr NULL: to the file's own address; *end = one past last byte; 0 ok */
|
|
73
|
+
uint8_t kd_load(const void *name, uint8_t len, uint8_t *addr, uint16_t *end);
|
|
74
|
+
uint8_t kd_save(const void *name, uint8_t len, const uint8_t *beg,
|
|
75
|
+
const uint8_t *end);
|
|
76
|
+
|
|
77
|
+
/* console via the KIM-1 monitor TTY */
|
|
78
|
+
KD_ASM void kd_putc(uint8_t c); /* OUTCH */
|
|
79
|
+
KD_ASM uint8_t kd_getc(void); /* GETCH, 7-bit */
|
|
80
|
+
KD_ASM void kd_puts(const char *s);
|
|
81
|
+
|
|
82
|
+
#ifdef __cplusplus
|
|
83
|
+
}
|
|
84
|
+
#endif
|
|
85
|
+
|
|
86
|
+
#endif
|
kimdos/c/kim1/crt.s
ADDED
kimdos/c/kim1/link.ld
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* KIM-1 llvm-mos platform: a RAM image at $2000, the monitor's zero page
|
|
2
|
+
* ($EF-$FF) left alone, a 1K soft stack after the image. Programs may use
|
|
3
|
+
* __kim_end up to $FEFF as free RAM. */
|
|
4
|
+
|
|
5
|
+
__rc0 = 0x00;
|
|
6
|
+
INCLUDE imag-regs.ld
|
|
7
|
+
ASSERT(__rc31 == 0x1f, "Inconsistent zero page map.")
|
|
8
|
+
|
|
9
|
+
MEMORY {
|
|
10
|
+
zp : ORIGIN = __rc31 + 1, LENGTH = 0xEF - (__rc31 + 1)
|
|
11
|
+
ram (rw) : ORIGIN = 0x2000, LENGTH = 0xDF00
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
REGION_ALIAS("c_readonly", ram)
|
|
15
|
+
REGION_ALIAS("c_writeable", ram)
|
|
16
|
+
|
|
17
|
+
SECTIONS { INCLUDE c.ld }
|
|
18
|
+
|
|
19
|
+
__stack = ALIGN(__heap_start + 0x400, 0x100);
|
|
20
|
+
__kim_end = __stack;
|
|
21
|
+
ASSERT(__kim_end <= 0xFF00, "Image and stack do not fit below $FF00.")
|
|
22
|
+
|
|
23
|
+
INPUT(-lexit-custom -linit-stack -lzero-bss)
|
|
24
|
+
|
|
25
|
+
OUTPUT_FORMAT { TRIM(ram) }
|
kimdos/c/src/con.s
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
; Console over the KIM-1 monitor's TTY routines. OUTCH and GETCH clobber
|
|
2
|
+
; A, X, Y and the monitor's zero page ($EF-$FF), none of which the llvm-mos
|
|
3
|
+
; calling convention asks a callee to preserve.
|
|
4
|
+
|
|
5
|
+
OUTCH = 0x1EA0
|
|
6
|
+
GETCH = 0x1E5A
|
|
7
|
+
|
|
8
|
+
.globl kd_putc, kd_getc, kd_puts
|
|
9
|
+
|
|
10
|
+
.section .text.kd_putc,"ax",@progbits
|
|
11
|
+
kd_putc:
|
|
12
|
+
jmp OUTCH
|
|
13
|
+
|
|
14
|
+
.section .text.kd_getc,"ax",@progbits
|
|
15
|
+
kd_getc:
|
|
16
|
+
jsr GETCH
|
|
17
|
+
and #0x7F
|
|
18
|
+
rts
|
|
19
|
+
|
|
20
|
+
; String pointer in __rc2/__rc3, advanced past 256-byte pages as it goes.
|
|
21
|
+
.section .text.kd_puts,"ax",@progbits
|
|
22
|
+
kd_puts:
|
|
23
|
+
ldy #0
|
|
24
|
+
1: lda (__rc2),y
|
|
25
|
+
beq 2f
|
|
26
|
+
sty savy
|
|
27
|
+
jsr OUTCH
|
|
28
|
+
ldy savy
|
|
29
|
+
iny
|
|
30
|
+
bne 1b
|
|
31
|
+
inc __rc3
|
|
32
|
+
bne 1b
|
|
33
|
+
2: rts
|
|
34
|
+
|
|
35
|
+
.section .bss.kd_puts,"aw",@nobits
|
|
36
|
+
savy: .zero 1
|
kimdos/c/src/dir.c
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#include "kdint.h"
|
|
2
|
+
|
|
3
|
+
/* Next listing byte; 0 past the end, which also ends a line or the listing. */
|
|
4
|
+
__attribute__((noinline)) static uint8_t next(void) {
|
|
5
|
+
int16_t c = kd_getb();
|
|
6
|
+
return c < 0 ? 0 : c;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
uint8_t kd_dir_open(const void *pattern, uint8_t len) {
|
|
10
|
+
if (kd_openp(0, '$', pattern, len) & ST_DNP || kd_chkin(0) & ST_DNP)
|
|
11
|
+
return iec_st;
|
|
12
|
+
for (uint8_t n = 6; n; --n)
|
|
13
|
+
next();
|
|
14
|
+
while (next())
|
|
15
|
+
;
|
|
16
|
+
iec_untlk();
|
|
17
|
+
return iec_st & ~ST_EOI;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
uint8_t kd_dir_next(kd_dirent *e) {
|
|
21
|
+
uint8_t q = 0, n = 0, t = 0, c;
|
|
22
|
+
if (kd_chkin(0) & ST_DNP)
|
|
23
|
+
return 0;
|
|
24
|
+
if (next() | next()) {
|
|
25
|
+
c = next();
|
|
26
|
+
e->blocks = c | next() << 8;
|
|
27
|
+
while ((c = next())) {
|
|
28
|
+
if (c == '"')
|
|
29
|
+
++q;
|
|
30
|
+
else if (q == 1) {
|
|
31
|
+
if (n < 16)
|
|
32
|
+
e->name[n++] = c;
|
|
33
|
+
} else if (q == 2 && c != ' ' && c != '*' && t < 3)
|
|
34
|
+
e->type[t++] = c;
|
|
35
|
+
}
|
|
36
|
+
e->name[n] = 0;
|
|
37
|
+
e->type[t] = 0;
|
|
38
|
+
}
|
|
39
|
+
iec_untlk();
|
|
40
|
+
return q > 1;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
void kd_dir_close(void) { kd_close(0); }
|
kimdos/c/src/dos.c
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#include "kdint.h"
|
|
2
|
+
|
|
3
|
+
#define ERRMAX 47
|
|
4
|
+
|
|
5
|
+
uint8_t kd_dev = 8;
|
|
6
|
+
|
|
7
|
+
static uint8_t listen_sa(uint8_t sa) {
|
|
8
|
+
iec_st = 0;
|
|
9
|
+
iec_listen(kd_dev);
|
|
10
|
+
if (!(iec_st & ST_DNP))
|
|
11
|
+
iec_second(sa);
|
|
12
|
+
return iec_st;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static void send(const uint8_t *p, uint8_t n) {
|
|
16
|
+
while (n--)
|
|
17
|
+
iec_ciout(*p++);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static const uint8_t *put(const uint8_t *p, const uint8_t *end) {
|
|
21
|
+
while (!iec_st && p < end)
|
|
22
|
+
iec_ciout(*p++);
|
|
23
|
+
iec_unlsn();
|
|
24
|
+
return p;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
uint8_t kd_openp(uint8_t ch, uint8_t pre, const uint8_t *name, uint8_t len) {
|
|
28
|
+
if (listen_sa(0xF0 | ch) & ST_DNP)
|
|
29
|
+
return iec_st;
|
|
30
|
+
if (pre)
|
|
31
|
+
iec_ciout(pre);
|
|
32
|
+
send(name, len);
|
|
33
|
+
iec_unlsn();
|
|
34
|
+
return iec_st;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
uint8_t kd_open(uint8_t ch, const void *name, uint8_t len) {
|
|
38
|
+
return kd_openp(ch, 0, name, len);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
void kd_close(uint8_t ch) {
|
|
42
|
+
listen_sa(0xE0 | ch);
|
|
43
|
+
iec_unlsn();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
uint8_t kd_chkout(uint8_t ch) { return listen_sa(0x60 | ch); }
|
|
47
|
+
|
|
48
|
+
__attribute__((noinline)) uint8_t kd_chkin(uint8_t ch) {
|
|
49
|
+
iec_st = 0;
|
|
50
|
+
iec_talk(kd_dev);
|
|
51
|
+
iec_tksa(0x60 | ch);
|
|
52
|
+
return iec_st;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
__attribute__((noinline)) int16_t kd_getb(void) {
|
|
56
|
+
if (iec_st & (ST_EOI | ST_RTO))
|
|
57
|
+
return -1;
|
|
58
|
+
uint8_t b = iec_acptr();
|
|
59
|
+
return iec_st & ST_RTO ? -1 : b;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
uint16_t kd_read(uint8_t ch, void *buf, uint16_t n) {
|
|
63
|
+
uint8_t *p = buf;
|
|
64
|
+
uint16_t i = 0;
|
|
65
|
+
if (!(kd_chkin(ch) & ST_DNP)) {
|
|
66
|
+
for (int16_t c; i != n && (c = kd_getb()) >= 0;)
|
|
67
|
+
p[i++] = c;
|
|
68
|
+
iec_untlk();
|
|
69
|
+
}
|
|
70
|
+
return i;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
uint16_t kd_write(uint8_t ch, const void *buf, uint16_t n) {
|
|
74
|
+
const uint8_t *p = buf;
|
|
75
|
+
if (kd_chkout(ch) & ST_DNP)
|
|
76
|
+
return 0;
|
|
77
|
+
return put(p, p + n) - p;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
uint8_t kd_cmd(const void *cmd, uint8_t len) {
|
|
81
|
+
if (!(kd_chkout(15) & ST_DNP)) {
|
|
82
|
+
send(cmd, len);
|
|
83
|
+
iec_unlsn();
|
|
84
|
+
}
|
|
85
|
+
return iec_st;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
uint8_t kd_status(char *buf) {
|
|
89
|
+
uint8_t i = 0;
|
|
90
|
+
int16_t c;
|
|
91
|
+
buf[0] = buf[1] = 0;
|
|
92
|
+
if (kd_chkin(15) & ST_DNP)
|
|
93
|
+
return 0xFF;
|
|
94
|
+
while ((c = kd_getb()) >= 0)
|
|
95
|
+
if (c != '\r' && i != ERRMAX)
|
|
96
|
+
buf[i++] = c;
|
|
97
|
+
buf[i] = 0;
|
|
98
|
+
iec_untlk();
|
|
99
|
+
return (buf[0] & 15) * 10 + (buf[1] & 15);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
uint8_t kd_seek(uint8_t ch, uint32_t offset) {
|
|
103
|
+
uint8_t cmd[6] = {'P', 0x60 | ch, offset,
|
|
104
|
+
offset >> 8, offset >> 16, offset >> 24};
|
|
105
|
+
char st[ERRMAX + 1];
|
|
106
|
+
kd_cmd(cmd, sizeof cmd);
|
|
107
|
+
return kd_status(st);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
uint8_t kd_load(const void *name, uint8_t len, uint8_t *addr, uint16_t *end) {
|
|
111
|
+
uint8_t st;
|
|
112
|
+
if (!(kd_open(0, name, len) & ST_DNP) && !(kd_chkin(0) & ST_DNP)) {
|
|
113
|
+
uint8_t lo = kd_getb();
|
|
114
|
+
uint16_t at = lo | (uint8_t)kd_getb() << 8;
|
|
115
|
+
if (!(iec_st & ST_RTO)) {
|
|
116
|
+
uint8_t *p = addr ? addr : (uint8_t *)at;
|
|
117
|
+
if (iec_jiffy & 0x80 && !iec_st && *(const uint8_t *)name != '$') {
|
|
118
|
+
iec_untlk();
|
|
119
|
+
iec_talk(kd_dev);
|
|
120
|
+
iec_tksa(0x61);
|
|
121
|
+
if (!iec_st)
|
|
122
|
+
p = (uint8_t *)iec_jload((uint16_t)p);
|
|
123
|
+
} else {
|
|
124
|
+
for (int16_t c; (c = kd_getb()) >= 0;)
|
|
125
|
+
*p++ = c;
|
|
126
|
+
}
|
|
127
|
+
if (end)
|
|
128
|
+
*end = (uint16_t)p;
|
|
129
|
+
}
|
|
130
|
+
iec_untlk();
|
|
131
|
+
}
|
|
132
|
+
st = iec_st;
|
|
133
|
+
kd_close(0);
|
|
134
|
+
iec_st = st;
|
|
135
|
+
return st & ~ST_EOI;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
uint8_t kd_save(const void *name, uint8_t len, const uint8_t *beg,
|
|
139
|
+
const uint8_t *end) {
|
|
140
|
+
uint8_t st;
|
|
141
|
+
if (kd_open(1, name, len) & ST_DNP || kd_chkout(1) & ST_DNP)
|
|
142
|
+
return iec_st;
|
|
143
|
+
iec_ciout((uint16_t)beg);
|
|
144
|
+
iec_ciout((uint16_t)beg >> 8);
|
|
145
|
+
put(beg, end);
|
|
146
|
+
st = iec_st;
|
|
147
|
+
kd_close(1);
|
|
148
|
+
iec_st = st;
|
|
149
|
+
return st;
|
|
150
|
+
}
|