1
0
mirror of https://github.com/rkujawa/rk65c02.git synced 2026-03-11 03:16:11 +00:00
Files
rk65c02/examples/tinyos.c
Radosław Kujawa d3bafd5892 Add Tiny OS example: scheduler with extended physical RAM
- examples/tinyos.c: host with 64K system RAM, 3x32KB task regions above 64K
  (bus_device_add_phys), MMU translate (virtual 32KB per task), console at
  , cooperative yield ( + JMP cd /home/rkujawa/repos/rk65c02 && git commit --trailer "Made-with: Cursor" -m "Add Tiny OS example: scheduler with extended physical RAM

- examples/tinyos.c: host with 64K system RAM, 3x32KB task regions above 64K
  (bus_device_add_phys), MMU translate (virtual 32KB per task), console at
  $DE00, cooperative yield ($FF00 + JMP $1000) and WAI + idle_wait + IRQ
  yield, vectors at $FFFC-$FFFF
- examples/tinyos_kernel.s: entry at $8000, IRQ handler at $8010 (JMP $1000)
- examples/tinyos_task.s: per-task loop printing task id, round-robin yield,
  optional WAI, STP after 3 runs
- Makefile: tinyos target, run-tinyos with timeout
- README.md, doc/MMU.md: document Tiny OS (64K virtual + expanded physical)
- .gitignore: /examples/tinyos"000) and WAI + idle_wait + IRQ
  yield, vectors at -
- examples/tinyos_kernel.s: entry at 000, IRQ handler at 010 (JMP cd /home/rkujawa/repos/rk65c02 && git commit --trailer "Made-with: Cursor" -m "Add Tiny OS example: scheduler with extended physical RAM

- examples/tinyos.c: host with 64K system RAM, 3x32KB task regions above 64K
  (bus_device_add_phys), MMU translate (virtual 32KB per task), console at
  $DE00, cooperative yield ($FF00 + JMP $1000) and WAI + idle_wait + IRQ
  yield, vectors at $FFFC-$FFFF
- examples/tinyos_kernel.s: entry at $8000, IRQ handler at $8010 (JMP $1000)
- examples/tinyos_task.s: per-task loop printing task id, round-robin yield,
  optional WAI, STP after 3 runs
- Makefile: tinyos target, run-tinyos with timeout
- README.md, doc/MMU.md: document Tiny OS (64K virtual + expanded physical)
- .gitignore: /examples/tinyos"000)
- examples/tinyos_task.s: per-task loop printing task id, round-robin yield,
  optional WAI, STP after 3 runs
- Makefile: tinyos target, run-tinyos with timeout
- README.md, doc/MMU.md: document Tiny OS (64K virtual + expanded physical)
- .gitignore: /examples/tinyos

Made-with: Cursor
2026-03-09 22:11:41 +01:00

226 lines
6.1 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Tiny OS Example — Host program (task scheduler with extended physical RAM)
*
* Demonstrates 64K virtual address space with 512KB physical: first 64KB is
* system RAM (kernel, vectors, MMIO); three tasks each have 32KB in extended
* physical space (0x10000, 0x18000, 0x20000). Virtual $0000$7FFF is
* per-task (ZP, stack, code); $8000$FFFF is shared. Tasks yield cooperatively
* (write next id to $FF00, JMP $1000) or via WAI (host idle_wait picks next
* task, updates MMU, asserts IRQ; handler JMP $1000).
*
* Build: make tinyos tinyos_kernel.rom tinyos_task.rom
* Run: ./tinyos (or: make run-tinyos)
*
* Expected: tasks 0, 1, 2 print to console in round-robin; cooperative and
* IRQ-driven switches; eventual STP and PASS.
*/
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bus.h"
#include "device.h"
#include "device_ram.h"
#include "rk65c02.h"
/* -------------------------------------------------------------------------
* Physical layout (512KB conceptual; we use 64K system + 3×32K task regions)
* 0x00000 0x0FFFF System RAM (kernel at $8000, vectors $FFFC$FFFF,
* yield $FF00, current task $FF01, console MMIO $DE00)
* 0x10000 0x17FFF Task 0 private (32KB)
* 0x18000 0x1FFFF Task 1 private (32KB)
* 0x20000 0x27FFF Task 2 private (32KB)
*
* Virtual layout
* $0000 $7FFF Per-task → physical 0x10000 + (current_task * 0x8000)
* $8000 $FFFF Shared (identity to physical $8000$FFFF)
* ------------------------------------------------------------------------- */
#define TASK_SIZE 0x8000u
#define TASK_ENTRY 0x1000u
#define PHYS_TASK0_BASE 0x10000u
#define PHYS_TASK1_BASE 0x18000u
#define PHYS_TASK2_BASE 0x20000u
#define YIELD_REG 0xFF00u
#define CURRENT_TASK_REG 0xFF01u
#define KERNEL_START 0x8000u
#define IRQ_HANDLER 0x8010u
#define CONSOLE_BASE 0xDE00u
#define NUM_TASKS 3u
struct task_state {
uint8_t current_task; /* 0, 1, or 2 */
};
static uint8_t
console_read_1(void *dev, uint16_t doff)
{
(void)dev;
(void)doff;
return 0;
}
static void
console_write_1(void *dev, uint16_t doff, uint8_t val)
{
struct task_state *ts;
(void)doff;
ts = (struct task_state *)((device_t *)dev)->config;
if (ts != NULL)
printf("[%u] %c", (unsigned)ts->current_task, (char)val);
else
putchar((char)val);
}
static device_t console_device = {
.name = "console",
.size = 16,
.read_1 = console_read_1,
.write_1 = console_write_1,
.finish = NULL,
.config = NULL,
.aux = NULL
};
static rk65c02_mmu_result_t
tinyos_translate(rk65c02emu_t *e, uint16_t vaddr, rk65c02_mmu_access_t access,
void *ctx)
{
struct task_state *ts = (struct task_state *)ctx;
rk65c02_mmu_result_t r = {
.ok = true,
.paddr = (uint32_t)vaddr,
.perms = RK65C02_MMU_PERM_R | RK65C02_MMU_PERM_W | RK65C02_MMU_PERM_X,
.fault_code = 0,
.no_fill_tlb = false,
};
(void)access;
if (vaddr < TASK_SIZE) {
if (vaddr == TASK_ENTRY) {
ts->current_task = bus_read_1(e->bus, YIELD_REG) % NUM_TASKS;
bus_write_1(e->bus, CURRENT_TASK_REG, ts->current_task);
}
switch (ts->current_task) {
case 0:
r.paddr = PHYS_TASK0_BASE + vaddr;
break;
case 1:
r.paddr = PHYS_TASK1_BASE + vaddr;
break;
case 2:
r.paddr = PHYS_TASK2_BASE + vaddr;
break;
default:
r.paddr = PHYS_TASK0_BASE + vaddr;
break;
}
r.no_fill_tlb = true;
}
return r;
}
static void
on_idle_wait(rk65c02emu_t *e, void *ctx)
{
struct task_state *ts = (struct task_state *)ctx;
uint8_t next;
unsigned int p;
next = (ts->current_task + 1) % NUM_TASKS;
bus_write_1(e->bus, YIELD_REG, next);
bus_write_1(e->bus, CURRENT_TASK_REG, next);
ts->current_task = next;
rk65c02_mmu_begin_update(e);
for (p = 0; p < 0x80; p++)
rk65c02_mmu_mark_changed_vpage(e, (uint8_t)p);
rk65c02_mmu_end_update(e);
rk65c02_assert_irq(e);
}
int
main(void)
{
struct task_state ts = { .current_task = 0 };
rk65c02emu_t e;
bus_t b;
device_t *sys_ram;
device_t *task0_ram;
device_t *task1_ram;
device_t *task2_ram;
b = bus_init();
/* System RAM: 0-$FFFE (0xFFFF bytes) + $FF00-$FFFF (256 bytes). */
sys_ram = device_ram_init(0xFFFF);
bus_device_add(&b, sys_ram, 0);
bus_device_add(&b, device_ram_init(0x100), 0xFF00);
console_device.config = &ts;
bus_device_add(&b, &console_device, CONSOLE_BASE);
task0_ram = device_ram_init(TASK_SIZE);
task1_ram = device_ram_init(TASK_SIZE);
task2_ram = device_ram_init(TASK_SIZE);
bus_device_add_phys(&b, task0_ram, PHYS_TASK0_BASE);
bus_device_add_phys(&b, task1_ram, PHYS_TASK1_BASE);
bus_device_add_phys(&b, task2_ram, PHYS_TASK2_BASE);
if (!bus_load_file(&b, KERNEL_START, "tinyos_kernel.rom")) {
fprintf(stderr, "tinyos: cannot load tinyos_kernel.rom\n");
bus_finish(&b);
return 1;
}
if (!bus_load_file_phys(&b, PHYS_TASK0_BASE + TASK_ENTRY,
"tinyos_task.rom")) {
fprintf(stderr, "tinyos: cannot load tinyos_task.rom at task 0\n");
bus_finish(&b);
return 1;
}
if (!bus_load_file_phys(&b, PHYS_TASK1_BASE + TASK_ENTRY,
"tinyos_task.rom")) {
fprintf(stderr, "tinyos: cannot load tinyos_task.rom at task 1\n");
bus_finish(&b);
return 1;
}
if (!bus_load_file_phys(&b, PHYS_TASK2_BASE + TASK_ENTRY,
"tinyos_task.rom")) {
fprintf(stderr, "tinyos: cannot load tinyos_task.rom at task 2\n");
bus_finish(&b);
return 1;
}
bus_write_1(&b, 0xFFFC, (uint8_t)(KERNEL_START & 0xFF));
bus_write_1(&b, 0xFFFD, (uint8_t)(KERNEL_START >> 8));
bus_write_1(&b, 0xFFFE, (uint8_t)(IRQ_HANDLER & 0xFF));
bus_write_1(&b, 0xFFFF, (uint8_t)(IRQ_HANDLER >> 8));
e = rk65c02_init(&b);
e.regs.SP = 0xFF;
e.regs.PC = KERNEL_START;
assert(rk65c02_mmu_set(&e, tinyos_translate, &ts, NULL, NULL, true, false));
bus_write_1(&b, YIELD_REG, 0);
bus_write_1(&b, CURRENT_TASK_REG, 0);
rk65c02_idle_wait_set(&e, on_idle_wait, &ts);
rk65c02_start(&e);
if (e.stopreason != STP) {
fprintf(stderr, "FAIL: stop reason is %s (expected STP)\n",
rk65c02_stop_reason_string(e.stopreason));
bus_finish(&b);
return 1;
}
printf("\nPASS: Tiny OS tasks ran (cooperative + IRQ yield), stopped with STP.\n");
bus_finish(&b);
return 0;
}