add iacore's zig implementation
FossilOrigin-Name: 9e5619d102e37ddc606e41788e397f091eaf28c77414a05e5c0cae477ac6cc31
This commit is contained in:
parent
1f6fbafe63
commit
a34c08b74f
7 changed files with 1589 additions and 0 deletions
|
@ -34,6 +34,11 @@
|
||||||
|
|
||||||
- new nga implementations
|
- new nga implementations
|
||||||
|
|
||||||
|
- nga-zig
|
||||||
|
|
||||||
|
- added a zig implementation from iacore under vm/libnga-zig
|
||||||
|
- build with: zig build -Doptimize=ReleaseFast -p ~/.local
|
||||||
|
|
||||||
- nga-c
|
- nga-c
|
||||||
|
|
||||||
- use UTF32 internally (translating to/from UTF8 externally)
|
- use UTF32 internally (translating to/from UTF8 externally)
|
||||||
|
|
18
vm/libnga-zig/LICENSE
Normal file
18
vm/libnga-zig/LICENSE
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
awawawawawa
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software
|
||||||
|
for any purpose with or without fee is hereby granted, provided
|
||||||
|
that the copyright notice and this permission notice appear in
|
||||||
|
all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
|
||||||
|
ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
|
||||||
|
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||||
|
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||||
|
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||||
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||||
|
OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
src/image.c is copied from vm/nga-c/image.c from the official retro repository (https://git.sr.ht/~crc_/retroforth)
|
51
vm/libnga-zig/LICENSE-RETRO
Normal file
51
vm/libnga-zig/LICENSE-RETRO
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
RETRO is a personal, minimalistic Forth
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software
|
||||||
|
for any purpose with or without fee is hereby granted, provided
|
||||||
|
that the copyright notice and this permission notice appear in
|
||||||
|
all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
|
||||||
|
ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
|
||||||
|
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||||
|
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||||
|
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||||
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||||
|
OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
RETRO 12 is:
|
||||||
|
|
||||||
|
Copyright (c) 2008 - 2023, Charles Childers
|
||||||
|
|
||||||
|
Portions of the code derive from RETRO 11, which was:
|
||||||
|
|
||||||
|
Copyright (c) 2008 - 2016, Charles Childers
|
||||||
|
Copyright (c) 2012 - 2013, Michal J Wallace
|
||||||
|
Copyright (c) 2009 - 2011, Luke Parrish
|
||||||
|
Copyright (c) 2009 - 2010, JGL
|
||||||
|
Copyright (c) 2010 - 2011, Marc Simpson
|
||||||
|
Copyright (c) 2011 - 2012, Oleksandr Kozachuk
|
||||||
|
Copyright (c) 2010, Jay Skeer
|
||||||
|
Copyright (c) 2010, Greg Copeland
|
||||||
|
Copyright (c) 2011, Aleksej Saushev
|
||||||
|
Copyright (c) 2011, Foucist
|
||||||
|
Copyright (c) 2011, Erturk Kocalar
|
||||||
|
Copyright (c) 2011, Kenneth Keating
|
||||||
|
Copyright (c) 2011, Ashley Feniello
|
||||||
|
Copyright (c) 2011, Peter Salvi
|
||||||
|
Copyright (c) 2011, Christian Kellermann
|
||||||
|
Copyright (c) 2011, Jorge Acereda
|
||||||
|
Copyright (c) 2011, Remy Moueza
|
||||||
|
Copyright (c) 2012, John M Harrison
|
||||||
|
Copyright (c) 2012, Todd Thomas
|
||||||
|
Copyright (c) 2022, Rick Carlino
|
||||||
|
|
||||||
|
The Free Pascal implementation in vm/nga-pascal is:
|
||||||
|
|
||||||
|
Copyright (c) 2016, Rob Judd
|
||||||
|
|
||||||
|
The Nim implementation in vm/nga-nim is:
|
||||||
|
|
||||||
|
Copyright (c) 2021, Jorge Acereda
|
79
vm/libnga-zig/build.zig
Normal file
79
vm/libnga-zig/build.zig
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
// Although this function looks imperative, note that its job is to
|
||||||
|
// declaratively construct a build graph that will be executed by an external
|
||||||
|
// runner.
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard optimization options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||||
|
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const entry = .{ .path = "src/retro.zig" };
|
||||||
|
|
||||||
|
_ = b.addModule("nga", .{
|
||||||
|
.source_file = entry,
|
||||||
|
});
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "retro-zig",
|
||||||
|
// In this case the main source file is merely a path, however, in more
|
||||||
|
// complicated build scripts, this could be a generated file.
|
||||||
|
.root_source_file = entry,
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
exe.disable_sanitize_c = true;
|
||||||
|
exe.addCSourceFile(.{ .file = .{ .path = "src/image.c" }, .flags = &.{} });
|
||||||
|
exe.addIncludePath(.{ .path = "src" });
|
||||||
|
|
||||||
|
// This declares intent for the executable to be installed into the
|
||||||
|
// standard location when the user invokes the "install" step (the default
|
||||||
|
// step when running `zig build`).
|
||||||
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
// This *creates* a Run step in the build graph, to be executed when another
|
||||||
|
// step is evaluated that depends on it. The next line below will establish
|
||||||
|
// such a dependency.
|
||||||
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
|
||||||
|
// By making the run step depend on the install step, it will be run from the
|
||||||
|
// installation directory rather than directly from within the cache directory.
|
||||||
|
// This is not necessary, however, if the application depends on other installed
|
||||||
|
// files, this ensures they will be present and in the expected location.
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
// This allows the user to pass arguments to the application in the build
|
||||||
|
// command itself, like this: `zig build run -- arg1 arg2 etc`
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This creates a build step. It will be visible in the `zig build --help` menu,
|
||||||
|
// and can be selected like this: `zig build run`
|
||||||
|
// This will evaluate the `run` step rather than the default, which is "install".
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
|
// Creates a step for unit testing. This only builds the test executable
|
||||||
|
// but does not run it.
|
||||||
|
const unit_tests = b.addTest(.{
|
||||||
|
.root_source_file = entry,
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_unit_tests = b.addRunArtifact(unit_tests);
|
||||||
|
|
||||||
|
// Similar to creating the run step earlier, this exposes a `test` step to
|
||||||
|
// the `zig build --help` menu, providing a way for the user to request
|
||||||
|
// running the unit tests.
|
||||||
|
const test_step = b.step("test", "Run unit tests");
|
||||||
|
test_step.dependOn(&run_unit_tests.step);
|
||||||
|
}
|
15
vm/libnga-zig/readme.md
Normal file
15
vm/libnga-zig/readme.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
nga VM for retroforth, in Zig.
|
||||||
|
|
||||||
|
It's faster than the official C VM somehow.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
This project is developed with Zig 0.11.0.
|
||||||
|
|
||||||
|
```
|
||||||
|
# Build and run
|
||||||
|
zig build run
|
||||||
|
|
||||||
|
# Install to ~/.local/bin/retro-zig
|
||||||
|
zig build -Doptimize=ReleaseFast -p ~/.local
|
||||||
|
```
|
1064
vm/libnga-zig/src/image.c
Normal file
1064
vm/libnga-zig/src/image.c
Normal file
File diff suppressed because it is too large
Load diff
357
vm/libnga-zig/src/retro.zig
Normal file
357
vm/libnga-zig/src/retro.zig
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const CELL = i32;
|
||||||
|
pub const CELL_MIN = std.math.minInt(CELL) + 1;
|
||||||
|
pub const CELL_MAX = std.math.maxInt(CELL) - 1;
|
||||||
|
const CELL_1: CELL = -1;
|
||||||
|
const CELL_0: CELL = 0;
|
||||||
|
|
||||||
|
fn cell_lsb(data: CELL) u8 {
|
||||||
|
return @truncate(@as(u32, @bitCast(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Amount of RAM.
|
||||||
|
pub const IMAGE_SIZE = 524288;
|
||||||
|
/// Max address stack depth
|
||||||
|
pub const ADDRESSES = 128;
|
||||||
|
/// Max data stack depth
|
||||||
|
pub const STACK_DEPTH = 32;
|
||||||
|
|
||||||
|
// zig fmt: off
|
||||||
|
pub const Inst = enum(u8) {
|
||||||
|
no_, li_, du_, dr_, sw_, pu_, po_, ju_,
|
||||||
|
ca_, cc_, re_, eq_, ne_, lt_, gt_, fe_,
|
||||||
|
st_, ad_, su_, mu_, di_, an_, or_, xo_,
|
||||||
|
sh_, zr_, ha_, ie_, iq_, ii_,
|
||||||
|
};
|
||||||
|
// zig fmt: on
|
||||||
|
|
||||||
|
pub const VM = struct {
|
||||||
|
/// Data Stack pointer
|
||||||
|
sp: CELL = 0,
|
||||||
|
/// Return Stack pointer
|
||||||
|
rp: CELL = 0,
|
||||||
|
/// Instruction pointer
|
||||||
|
ip: CELL = 0,
|
||||||
|
/// The data stack
|
||||||
|
data: [STACK_DEPTH]CELL = .{0} ** STACK_DEPTH,
|
||||||
|
/// The address stack
|
||||||
|
address: [ADDRESSES]CELL = .{0} ** ADDRESSES,
|
||||||
|
/// Image Memory
|
||||||
|
memory: [IMAGE_SIZE]CELL = .{0} ** IMAGE_SIZE,
|
||||||
|
|
||||||
|
fin: std.fs.File,
|
||||||
|
fout: std.fs.File,
|
||||||
|
|
||||||
|
pub fn init() VM {
|
||||||
|
return .{
|
||||||
|
.fin = std.io.getStdIn(),
|
||||||
|
.fout = std.io.getStdOut(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper functions
|
||||||
|
|
||||||
|
/// Top item on stack
|
||||||
|
pub fn TOS(__: *VM) *CELL {
|
||||||
|
return &__.data[@intCast(__.sp)];
|
||||||
|
}
|
||||||
|
/// Next Top item on stack
|
||||||
|
pub fn NOS(__: *VM) *CELL {
|
||||||
|
return &__.data[@intCast(__.sp - 1)];
|
||||||
|
}
|
||||||
|
/// Top item on address stack
|
||||||
|
pub fn TORS(__: *VM) *CELL {
|
||||||
|
return &__.address[@intCast(__.rp)];
|
||||||
|
}
|
||||||
|
/// direct memory access
|
||||||
|
pub fn MEM(__: *VM, addr: CELL) *CELL {
|
||||||
|
return &__.memory[@intCast(addr)];
|
||||||
|
}
|
||||||
|
pub fn stack_push(__: *VM, value: CELL) void {
|
||||||
|
__.sp += 1;
|
||||||
|
__.TOS().* = value;
|
||||||
|
}
|
||||||
|
pub fn stack_pop(__: *VM) CELL {
|
||||||
|
const r = __.TOS().*;
|
||||||
|
__.dr_();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn halt(__: *VM) void {
|
||||||
|
@setCold(true);
|
||||||
|
__.ip = IMAGE_SIZE;
|
||||||
|
}
|
||||||
|
fn not_halted(__: VM) bool {
|
||||||
|
return __.ip < IMAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute_from(__: *VM, initial_ip: CELL) void {
|
||||||
|
__.rp = 1;
|
||||||
|
__.ip = initial_ip;
|
||||||
|
while (__.not_halted()) {
|
||||||
|
const opcode = __.MEM(__.ip).*;
|
||||||
|
__.process_opcode_bundle(opcode);
|
||||||
|
if (__.rp == 0)
|
||||||
|
// __.halt();
|
||||||
|
break;
|
||||||
|
__.ip += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_opcode_bundle(__: *@This(), opcodes: CELL) void {
|
||||||
|
const opcode: u32 = @bitCast(opcodes);
|
||||||
|
__.handle_instruction(@enumFromInt(opcode & 0xFF));
|
||||||
|
__.handle_instruction(@enumFromInt((opcode >> 8) & 0xFF));
|
||||||
|
__.handle_instruction(@enumFromInt((opcode >> 16) & 0xFF));
|
||||||
|
__.handle_instruction(@enumFromInt((opcode >> 24) & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// instruction/io handler type
|
||||||
|
pub fn handle_instruction(__: *VM, inst: Inst) void {
|
||||||
|
switch (inst) {
|
||||||
|
inline else => |tag| @field(VM, @tagName(tag))(__),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn no_(__: *VM) void {
|
||||||
|
_ = __;
|
||||||
|
}
|
||||||
|
pub fn li_(__: *VM) void {
|
||||||
|
__.ip += 1;
|
||||||
|
__.stack_push(__.MEM(__.ip).*);
|
||||||
|
}
|
||||||
|
pub fn du_(__: *VM) void {
|
||||||
|
__.stack_push(__.TOS().*);
|
||||||
|
}
|
||||||
|
pub fn dr_(__: *VM) void {
|
||||||
|
__.TOS().* = 0; // sus: cound set to undefined
|
||||||
|
if (__.sp == 0) { // stack exhausted
|
||||||
|
__.halt();
|
||||||
|
} else {
|
||||||
|
__.sp -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn sw_(__: *VM) void {
|
||||||
|
std.mem.swap(CELL, __.TOS(), __.NOS());
|
||||||
|
}
|
||||||
|
pub fn pu_(__: *VM) void {
|
||||||
|
__.rp += 1;
|
||||||
|
__.TORS().* = __.stack_pop();
|
||||||
|
}
|
||||||
|
pub fn po_(__: *VM) void {
|
||||||
|
__.stack_push(__.TORS().*);
|
||||||
|
__.rp -= 1;
|
||||||
|
}
|
||||||
|
pub fn ju_(__: *VM) void {
|
||||||
|
__.ip = __.stack_pop() - 1;
|
||||||
|
}
|
||||||
|
pub fn ca_(__: *VM) void {
|
||||||
|
__.rp += 1;
|
||||||
|
__.TORS().* = __.ip;
|
||||||
|
__.ip = __.stack_pop() - 1;
|
||||||
|
}
|
||||||
|
pub fn cc_(__: *VM) void {
|
||||||
|
const a = __.stack_pop();
|
||||||
|
const b = __.stack_pop();
|
||||||
|
if (b != 0) {
|
||||||
|
__.rp += 1;
|
||||||
|
__.TORS().* = __.ip;
|
||||||
|
__.ip = a - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn re_(__: *VM) void {
|
||||||
|
__.ip = __.TORS().*;
|
||||||
|
__.rp -= 1;
|
||||||
|
}
|
||||||
|
pub fn eq_(__: *VM) void {
|
||||||
|
const cond = (__.NOS().* == __.TOS().*);
|
||||||
|
__.NOS().* = if (cond) CELL_1 else CELL_0;
|
||||||
|
__.dr_();
|
||||||
|
}
|
||||||
|
pub fn ne_(__: *VM) void {
|
||||||
|
const cond = (__.NOS().* != __.TOS().*);
|
||||||
|
__.NOS().* = if (cond) CELL_1 else CELL_0;
|
||||||
|
__.dr_();
|
||||||
|
}
|
||||||
|
pub fn lt_(__: *VM) void {
|
||||||
|
const cond = (__.NOS().* < __.TOS().*);
|
||||||
|
__.NOS().* = if (cond) CELL_1 else CELL_0;
|
||||||
|
__.dr_();
|
||||||
|
}
|
||||||
|
pub fn gt_(__: *VM) void {
|
||||||
|
const cond = (__.NOS().* > __.TOS().*);
|
||||||
|
__.NOS().* = if (cond) CELL_1 else CELL_0;
|
||||||
|
__.dr_();
|
||||||
|
}
|
||||||
|
pub fn fe_(__: *VM) void {
|
||||||
|
const tos = __.TOS().*;
|
||||||
|
__.TOS().* = switch (tos) {
|
||||||
|
-1 => __.sp - 1,
|
||||||
|
-2 => __.rp,
|
||||||
|
-3 => IMAGE_SIZE,
|
||||||
|
-4 => CELL_MIN,
|
||||||
|
-5 => CELL_MAX,
|
||||||
|
else => __.MEM(tos).*,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn st_(__: *VM) void {
|
||||||
|
const tos = __.stack_pop();
|
||||||
|
const nos = __.stack_pop();
|
||||||
|
if (tos >= 0 and tos <= IMAGE_SIZE) {
|
||||||
|
__.MEM(tos).* = nos;
|
||||||
|
} else {
|
||||||
|
__.halt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn ad_(__: *VM) void {
|
||||||
|
__.NOS().* +%= __.TOS().*;
|
||||||
|
__.dr_();
|
||||||
|
}
|
||||||
|
pub fn su_(__: *VM) void {
|
||||||
|
__.NOS().* -%= __.TOS().*;
|
||||||
|
__.dr_();
|
||||||
|
}
|
||||||
|
pub fn mu_(__: *VM) void {
|
||||||
|
__.NOS().* *%= __.TOS().*;
|
||||||
|
__.dr_();
|
||||||
|
}
|
||||||
|
pub fn di_(__: *VM) void {
|
||||||
|
// sus: division by zero is UB from the view of VM | handle this error
|
||||||
|
const tos = __.TOS().*;
|
||||||
|
const nos = __.NOS().*;
|
||||||
|
__.TOS().* = @divTrunc(nos, tos);
|
||||||
|
__.NOS().* = @rem(nos, tos);
|
||||||
|
}
|
||||||
|
pub fn an_(__: *VM) void {
|
||||||
|
__.NOS().* &= __.TOS().*;
|
||||||
|
__.dr_();
|
||||||
|
}
|
||||||
|
pub fn or_(__: *VM) void {
|
||||||
|
__.NOS().* |= __.TOS().*;
|
||||||
|
__.dr_();
|
||||||
|
}
|
||||||
|
pub fn xo_(__: *VM) void {
|
||||||
|
__.NOS().* ^= __.TOS().*;
|
||||||
|
__.dr_();
|
||||||
|
}
|
||||||
|
pub fn sh_(__: *VM) void {
|
||||||
|
const tos = __.TOS().*;
|
||||||
|
const nos = __.NOS().*;
|
||||||
|
__.NOS().* = if (tos < 0)
|
||||||
|
nos << @intCast(-tos)
|
||||||
|
else
|
||||||
|
// signed shift
|
||||||
|
if (nos < 0 and tos > 0)
|
||||||
|
nos >> @intCast(tos) | ~(~@as(CELL, 0) >> @intCast(tos))
|
||||||
|
else
|
||||||
|
nos >> @intCast(tos);
|
||||||
|
__.dr_();
|
||||||
|
}
|
||||||
|
pub fn zr_(__: *VM) void {
|
||||||
|
if (__.TOS().* == 0) {
|
||||||
|
__.dr_();
|
||||||
|
__.ip = __.TORS().*;
|
||||||
|
__.rp -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn ha_(__: *VM) void {
|
||||||
|
__.halt();
|
||||||
|
}
|
||||||
|
pub fn ie_(__: *VM) void {
|
||||||
|
__.stack_push(io.devices.len);
|
||||||
|
}
|
||||||
|
pub fn iq_(__: *VM) void {
|
||||||
|
const tos = __.stack_pop();
|
||||||
|
switch (tos) {
|
||||||
|
inline 0...io.devices.len - 1 => |i| {
|
||||||
|
const device = io.devices[i];
|
||||||
|
__.stack_push(device.type);
|
||||||
|
__.stack_push(device.version);
|
||||||
|
},
|
||||||
|
else => @panic("iq | accessing invalid device"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn ii_(__: *VM) void {
|
||||||
|
const tos = __.stack_pop();
|
||||||
|
switch (tos) {
|
||||||
|
inline 0...io.devices.len - 1 => |i| {
|
||||||
|
const device = io.devices[i];
|
||||||
|
device.handler(__);
|
||||||
|
},
|
||||||
|
else => @panic("ii | accessing invalid device"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Handler = @TypeOf(VM.no_);
|
||||||
|
|
||||||
|
pub const io = struct {
|
||||||
|
pub const DeviceDesc = struct {
|
||||||
|
type: CELL,
|
||||||
|
version: CELL,
|
||||||
|
handler: Handler,
|
||||||
|
};
|
||||||
|
pub const devices = [_]DeviceDesc{
|
||||||
|
.{
|
||||||
|
.type = 0,
|
||||||
|
.version = 0,
|
||||||
|
.handler = putchar,
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.type = 0,
|
||||||
|
.version = 1,
|
||||||
|
.handler = getchar,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
pub fn putchar(__: *VM) void {
|
||||||
|
const char = cell_lsb(__.TOS().*);
|
||||||
|
__.fout.writeAll(&.{char}) catch @panic("sus: handle this error");
|
||||||
|
__.dr_();
|
||||||
|
}
|
||||||
|
pub fn getchar(__: *VM) void {
|
||||||
|
const maybe_char = _: {
|
||||||
|
var buf: [1]u8 = undefined;
|
||||||
|
const nread = __.fin.readAll(&buf) catch @panic("sus: handle this error");
|
||||||
|
if (nread != buf.len) // eof
|
||||||
|
break :_ null;
|
||||||
|
break :_ buf[0];
|
||||||
|
};
|
||||||
|
if (maybe_char) |char|
|
||||||
|
__.stack_push(char)
|
||||||
|
else // on eof
|
||||||
|
__.halt(); // sus: could do better here
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test "VM dry test" {
|
||||||
|
var vm = VM.init();
|
||||||
|
const memory: [*]u8 = @ptrCast(@alignCast(&vm.memory));
|
||||||
|
memory[0] = @intFromEnum(VM.Inst.li_);
|
||||||
|
memory[1] = 0;
|
||||||
|
vm.execute_from(0);
|
||||||
|
try std.testing.expectEqual(vm.ip, 524288);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern const ngaImageCells: c_int;
|
||||||
|
pub const ngaImage: [*c]c_int = @extern([*c]c_int, .{
|
||||||
|
.name = "ngaImage",
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer _ = gpa.deinit();
|
||||||
|
|
||||||
|
const a = gpa.allocator();
|
||||||
|
var vm = VM.init();
|
||||||
|
if (std.os.argv.len > 1) {
|
||||||
|
var p = std.ChildProcess.init(&.{ "retro-unu", std.mem.span(std.os.argv[1]) }, a);
|
||||||
|
p.stdin_behavior = .Ignore;
|
||||||
|
p.stdout_behavior = .Pipe;
|
||||||
|
try p.spawn();
|
||||||
|
// _ = try p.wait();
|
||||||
|
vm.fin = p.stdout.?;
|
||||||
|
}
|
||||||
|
@memcpy(vm.memory[0..@intCast(ngaImageCells)], ngaImage[0..@intCast(ngaImageCells)]);
|
||||||
|
vm.execute_from(0);
|
||||||
|
}
|
Loading…
Reference in a new issue