summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/Makefile3
-rw-r--r--usr.bin/kstat/Makefile6
-rw-r--r--usr.bin/kstat/kstat.c73
-rw-r--r--usr.bin/login/login.c3
-rw-r--r--usr.bin/nerve/Makefile6
-rw-r--r--usr.bin/nerve/nerve.c377
-rw-r--r--usr.bin/oasm/Makefile7
-rw-r--r--usr.bin/oasm/emit.c258
-rw-r--r--usr.bin/oasm/include/oasm/emit.h112
-rw-r--r--usr.bin/oasm/include/oasm/lex.h156
-rw-r--r--usr.bin/oasm/include/oasm/log.h48
-rw-r--r--usr.bin/oasm/include/oasm/parse.h37
-rw-r--r--usr.bin/oasm/include/oasm/state.h55
-rw-r--r--usr.bin/oasm/lex.c314
-rw-r--r--usr.bin/oasm/log.c81
-rw-r--r--usr.bin/oasm/oasm.c73
-rw-r--r--usr.bin/oasm/parse.c202
-rw-r--r--usr.bin/oemu/Makefile7
-rw-r--r--usr.bin/oemu/cpu.c146
-rw-r--r--usr.bin/oemu/emu.c127
-rw-r--r--usr.bin/oemu/include/oemu/cpu.h69
-rw-r--r--usr.bin/oemu/include/oemu/osmx64.h82
-rw-r--r--usr.bin/oemu/include/oemu/types.h39
-rw-r--r--usr.bin/osh/osh.c135
-rw-r--r--usr.bin/whoami/Makefile6
-rw-r--r--usr.bin/whoami/whoami.c38
26 files changed, 2416 insertions, 44 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index e84831a..b190492 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -20,3 +20,6 @@ all:
make -C readcore/ $(ARGS)
make -C login/ $(ARGS)
make -C sleep/ $(ARGS)
+ make -C kstat/ $(ARGS)
+ make -C nerve/ $(ARGS)
+ make -C whoami/ $(ARGS)
diff --git a/usr.bin/kstat/Makefile b/usr.bin/kstat/Makefile
new file mode 100644
index 0000000..ccceb3c
--- /dev/null
+++ b/usr.bin/kstat/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/kstat:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/kstat/kstat.c b/usr.bin/kstat/kstat.c
new file mode 100644
index 0000000..853bc8b
--- /dev/null
+++ b/usr.bin/kstat/kstat.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/sched.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static void
+get_sched_stat(void)
+{
+ struct sched_stat stat;
+ struct sched_cpu *cpu;
+ int fd;
+
+ fd = open("/ctl/sched/stat", O_RDONLY);
+ if (fd < 0) {
+ printf("failed to get sched stat\n");
+ return;
+ }
+ if (read(fd, &stat, sizeof(stat)) < 0) {
+ printf("failed to read sched stat\n");
+ return;
+ }
+
+ close(fd);
+ printf("-------------------------------\n");
+ printf("Number of tasks: %d\n", stat.nproc);
+ printf("Number of cores online: %d\n", stat.ncpu);
+ printf("Scheduler quantum: %d usec\n", stat.quantum_usec);
+ printf("-------------------------------\n");
+
+ /*
+ * Log out some per-cpu information
+ */
+ for (int i = 0; i < stat.ncpu; ++i) {
+ cpu = &stat.cpus[i];
+ printf("[cpu %d]: %d switches\n", i, cpu->nswitch);
+ }
+}
+
+int
+main(void)
+{
+ get_sched_stat();
+ return 0;
+}
diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c
index 93b08ed..5b21303 100644
--- a/usr.bin/login/login.c
+++ b/usr.bin/login/login.c
@@ -104,6 +104,7 @@ check_user(char *alias, char *hash, char *entry)
short have_uid = 0;
short have_shell = 0;
uid_t uid = -1;
+ pid_t shell_pid;
if (alias == NULL || entry == NULL) {
return -EINVAL;
@@ -172,7 +173,7 @@ check_user(char *alias, char *hash, char *entry)
setuid(uid);
shell_argv[0] = shell_path;
- spawn(shell_argv[0], shell_argv, envp, SPAWN_WAIT);
+ shell_pid = spawn(shell_argv[0], shell_argv, envp, 0);
return 0;
}
diff --git a/usr.bin/nerve/Makefile b/usr.bin/nerve/Makefile
new file mode 100644
index 0000000..cc0fd91
--- /dev/null
+++ b/usr.bin/nerve/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/nerve:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/nerve/nerve.c b/usr.bin/nerve/nerve.c
new file mode 100644
index 0000000..75a19be
--- /dev/null
+++ b/usr.bin/nerve/nerve.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <sys/console.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdint.h>
+
+/* Verb numeric defs (see string defs) */
+#define VERB_UNKNOWN -1
+#define VERB_POKE 0x0000
+#define VERB_PEEK 0x0001
+
+/* Verb string defs (see numeric defs) */
+#define SVERB_POKE "poke"
+#define SVERB_PEEK "peek"
+
+/* Nerve numeric defs (see string defs) */
+#define NERVE_UNKNOWN -1
+#define NERVE_CONSATTR 0x0000
+#define NERVE_CONSFEAT 0x0001
+
+/* Nerve string defs (see numeric defs) */
+#define SNERVE_CONSATTR "consattr"
+#define SNERVE_CONSFEAT "consfeat"
+
+/* Misc defines */
+#define NERVE_PACKET_LEN 16
+
+struct verb_handler;
+
+static int poke_nerve(const char *nerve, struct verb_handler *h);
+static int peek_nerve(const char *nerve, struct verb_handler *h);
+static int nerve_to_def(const char *nerve);
+
+/*
+ * Contains verb handlers called when a verb
+ * (e.g., 'poke') is matched.
+ */
+struct verb_handler {
+ int(*run)(const char *nerve, struct verb_handler *h);
+ char **argv;
+ size_t argc;
+};
+
+/*
+ * Holds information that may be sent down
+ * my nerves.
+ *
+ * Example: nerve poke <x> 1 0 1
+ * * * *
+ * +--------+ / / /
+ * | meow | <------+ / /
+ * |--------| / /
+ * | foo | <------+ /
+ * |--------| /
+ * | foobar | <------+
+ * +--------+
+ * packet
+ */
+struct nerve_payload {
+ uint32_t packet[NERVE_PACKET_LEN];
+ uint16_t len;
+};
+
+/*
+ * Verb handler table, when a verb is matched,
+ * its respective handler is called.
+ */
+static struct verb_handler verbtab[] = {
+ { poke_nerve },
+ { peek_nerve }
+};
+
+/*
+ * Print list of available options as well as
+ * information about the program.
+ */
+static void
+help(void)
+{
+ printf(
+ "nerve: usage: nerve <verb> [ .. data ..]\n"
+ "verb 'poke': Poke a control (/ctl) nerve\n"
+ "???????????????? NERVES ????????????????\n"
+ "consattr: Console attributes\n"
+ "consfeat: Console features\n"
+ );
+}
+
+/*
+ * The user gets to send data down my nerves through
+ * a nerve payload. This function acquires the nerve
+ * payload. Please don't hurt me.
+ *
+ * @argc: Number of arguments within argv
+ * @argv: Argument vector
+ * @res: Where the payload goes
+ */
+static int
+get_nerve_payload(int argc, char *argv[], struct nerve_payload *res)
+{
+ char *payload_str;
+ uint32_t datum;
+
+ /* Do we have a nerve payload? */
+ if (argc < 4) {
+ printf("[!] missing nerve payload\n");
+ return -1;
+ }
+
+ /* Reset fields */
+ res->len = 0;
+ memset(res->packet, 0, sizeof(res->packet));
+
+ /* Start grabbing bytes */
+ for (int i = 3; i < argc; ++i) {
+ if (res->len >= NERVE_PACKET_LEN) {
+ printf("[*] truncated packet\n");
+ break;
+ }
+ payload_str = argv[i];
+ datum = atoi(payload_str);
+ res->packet[res->len++] = datum;
+ }
+
+ return 0;
+}
+
+/*
+ * Peek at a control nerve located in /ctl/
+ *
+ * @nerve: Name of nerve to peek at
+ * @h: Verb handler, instance of self
+ *
+ * Returns less than zero if the nerve does
+ * not match.
+ */
+static int
+peek_nerve(const char *nerve, struct verb_handler *h)
+{
+ int error, nerve_idx = -1;
+
+ if (nerve == NULL || h == NULL) {
+ return -EINVAL;
+ }
+
+ /* Grab the nerve table index */
+ nerve_idx = nerve_to_def(nerve);
+ if (nerve_idx == NERVE_UNKNOWN) {
+ printf("[&^]: This is not my nerve.\n");
+ return -1;
+ }
+
+ switch (nerve_idx) {
+ case NERVE_CONSATTR:
+ {
+ struct console_attr c;
+ int fd;
+
+ fd = open("/ctl/console/attr", O_RDONLY);
+ read(fd, &c, sizeof(c));
+ printf("(cursx=%d, cursy=%d)\n", c.cursor_x, c.cursor_y);
+ close(fd);
+ break;
+ }
+ case NERVE_CONSFEAT:
+ {
+ struct console_feat f;
+ int fd;
+
+ fd = open("/ctl/console/feat", O_RDONLY);
+ read(fd, &f, sizeof(f));
+ printf("ansi_esc=%d\n", f.ansi_esc);
+ printf("show_curs=%d\n", f.show_curs);
+ close(fd);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Poke a control nerve located in /ctl/
+ *
+ * @nerve: Name of the nerve (e.g., consattr)
+ * @h: Verb handler, instance of self
+ *
+ * Returns less than zero if the nerve does not
+ * match.
+ */
+static int
+poke_nerve(const char *nerve, struct verb_handler *h)
+{
+ struct nerve_payload payload;
+ int error, nerve_idx = -1;
+
+ if (nerve == NULL || h == NULL) {
+ return -EINVAL;
+ }
+
+ /* Grab the nerve table index */
+ nerve_idx = nerve_to_def(nerve);
+ if (nerve_idx == NERVE_UNKNOWN) {
+ printf("[&^]: This is not my nerve.\n");
+ return -1;
+ }
+
+ /* Grab the payload passed by the user */
+ error = get_nerve_payload(h->argc, h->argv, &payload);
+ if (error < 0) {
+ printf("[!] nerve error\n");
+ return -1;
+ }
+
+ switch (nerve_idx) {
+ case NERVE_CONSATTR:
+ {
+ struct console_attr c;
+ int fd;
+
+ c.cursor_x = payload.packet[0];
+ c.cursor_y = payload.packet[1];
+
+ fd = open("/ctl/console/attr", O_WRONLY);
+ write(fd, &c, sizeof(c));
+ close(fd);
+ break;
+ }
+ case NERVE_CONSFEAT:
+ {
+ struct console_feat f;
+ int fd;
+
+ f.ansi_esc = payload.packet[0] & 0xFF;
+ f.show_curs = payload.packet[1] & 0xFF;
+
+ fd = open("/ctl/console/feat", O_WRONLY);
+ write(fd, &f, sizeof(f));
+ close(fd);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+/*
+ * Convert a nerve name into a numeric nerve
+ * definition
+ *
+ * @nerve: Nerve name to convert
+ */
+static int
+nerve_to_def(const char *nerve)
+{
+ /*
+ * Now we need to parse the nerve string
+ * and see if it matches with anything
+ * that we know.
+ */
+ switch (*nerve) {
+ case 'c':
+ if (strcmp(nerve, SNERVE_CONSATTR) == 0) {
+ return NERVE_CONSATTR;
+ } else if (strcmp(nerve, SNERVE_CONSFEAT) == 0) {
+ return NERVE_CONSFEAT;
+ }
+ }
+
+ return NERVE_UNKNOWN;
+}
+
+/*
+ * Convert a string verb, passed in through the command
+ * line, into a numeric definition
+ *
+ * @verb: String verb
+ */
+static int
+verb_to_def(const char *verb)
+{
+ if (verb == NULL) {
+ return -EINVAL;
+ }
+
+ /*
+ * Parse the verb and try to match it against
+ * a constant.
+ *
+ * XXX: Here we are first matching the first character
+ * before we match the entire verb as that is more
+ * efficient than scanning each entire string until
+ * one matches.
+ */
+ switch (*verb) {
+ case 'p':
+ if (strcmp(verb, SVERB_POKE) == 0) {
+ return VERB_POKE;
+ }
+ if (strcmp(verb, SVERB_PEEK) == 0) {
+ return VERB_PEEK;
+ }
+ default:
+ printf("[!] bad verb \"%s\"\n", verb);
+ return VERB_UNKNOWN;
+ }
+
+ return VERB_UNKNOWN;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct verb_handler *verbd;
+ int verb;
+
+ if (argc < 2) {
+ help();
+ return -1;
+ }
+
+ verb = verb_to_def(argv[1]);
+ if (verb < 0) {
+ return -1;
+ }
+
+ /* Make sure the arguments match */
+ switch (verb) {
+ case VERB_POKE:
+ if (argc < 3) {
+ printf("[!] missing nerve name\n");
+ help();
+ return -1;
+ }
+ break;
+ }
+
+ verbd = &verbtab[verb];
+ verbd->argv = argv;
+ verbd->argc = argc;
+ return verbd->run(argv[2], verbd);
+}
diff --git a/usr.bin/oasm/Makefile b/usr.bin/oasm/Makefile
new file mode 100644
index 0000000..a83aaab
--- /dev/null
+++ b/usr.bin/oasm/Makefile
@@ -0,0 +1,7 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+CFLAGS = -Iinclude/
+
+$(ROOT)/base/usr/bin/oasm:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS) $(CFLAGS)
diff --git a/usr.bin/oasm/emit.c b/usr.bin/oasm/emit.c
new file mode 100644
index 0000000..801951e
--- /dev/null
+++ b/usr.bin/oasm/emit.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <oasm/emit.h>
+#include <oasm/log.h>
+#include <stdlib.h>
+#include <string.h>
+
+static inline void
+emit_bytes(struct emit_state *state, void *p, size_t len)
+{
+ write(state->out_fd, p, len);
+}
+
+/*
+ * Convert an IR register to an OSMX64
+ * valid register value that can be encoded
+ * into the instruction.
+ */
+static inline reg_t
+ir_to_reg(tt_t ir)
+{
+ switch (ir) {
+ case TT_X0: return OSMX64_R_X0;
+ case TT_X1: return OSMX64_R_X1;
+ case TT_X2: return OSMX64_R_X2;
+ case TT_X3: return OSMX64_R_X3;
+ case TT_X4: return OSMX64_R_X4;
+ case TT_X5: return OSMX64_R_X5;
+ case TT_X6: return OSMX64_R_X6;
+ case TT_X7: return OSMX64_R_X7;
+ case TT_X8: return OSMX64_R_X8;
+ case TT_X9: return OSMX64_R_X9;
+ case TT_X10: return OSMX64_R_X10;
+ case TT_X11: return OSMX64_R_X11;
+ case TT_X12: return OSMX64_R_X12;
+ case TT_X13: return OSMX64_R_X13;
+ case TT_X14: return OSMX64_R_X14;
+ case TT_X15: return OSMX64_R_X15;
+ }
+
+ return OSMX64_R_BAD;
+}
+
+/*
+ * Encode a MOV instruction
+ *
+ * mov [r], [r/imm]
+ *
+ * Returns the next token on success,
+ * otherwise NULL.
+ */
+static struct oasm_token *
+emit_encode_mov(struct emit_state *state, struct oasm_token *tok)
+{
+ inst_t curinst;
+ reg_t rd;
+
+ if (state == NULL || tok == NULL) {
+ return NULL;
+ }
+
+ /* Next token should be a register */
+ tok = TAILQ_NEXT(tok, link);
+ if (tok == NULL) {
+ return NULL;
+ }
+ if (!tok_is_xreg(tok->type)) {
+ oasm_err("[emit error]: bad 'mov' order\n");
+ return NULL;
+ }
+
+ rd = ir_to_reg(tok->type);
+ if (rd == OSMX64_R_BAD) {
+ oasm_err("[emit error]: got bad reg in 'mov'\n");
+ return NULL;
+ }
+
+
+ /* Next token should be an IMM */
+ tok = TAILQ_NEXT(tok, link);
+ if (tok == NULL) {
+ oasm_err("[emit error]: bad 'mov' order\n");
+ return NULL;
+ }
+ if (tok->type != TT_IMM) {
+ oasm_err("[emit error]: expected <imm>\n");
+ return NULL;
+ }
+
+ curinst.opcode = OSMX64_MOV_IMM;
+ curinst.rd = rd;
+ curinst.imm = tok->imm;
+ emit_bytes(state, &curinst, sizeof(curinst));
+ return TAILQ_NEXT(tok, link);
+}
+
+/*
+ * Encode a INC/DEC instruction
+ *
+ * inc/dec [r]
+ *
+ * Returns the next token on success,
+ * otherwise NULL.
+ */
+static struct oasm_token *
+emit_encode_incdec(struct emit_state *state, struct oasm_token *tok)
+{
+ inst_t curinst;
+ reg_t rd;
+ uint8_t opcode = OSMX64_INC;
+ char *inst_str = "inc";
+
+ if (state == NULL || tok == NULL) {
+ return NULL;
+ }
+
+ if (tok->type == TT_DEC) {
+ inst_str = "dec";
+ opcode = OSMX64_DEC;
+ }
+
+ /* Next token should be a register */
+ tok = TAILQ_NEXT(tok, link);
+ if (tok == NULL) {
+ return NULL;
+ }
+ if (!tok_is_xreg(tok->type)) {
+ oasm_err("[emit error]: bad '%s' order\n", inst_str);
+ return NULL;
+ }
+
+ rd = ir_to_reg(tok->type);
+ if (rd == OSMX64_R_BAD) {
+ oasm_err("[emit error]: got bad reg in '%s'\n", inst_str);
+ return NULL;
+ }
+
+ curinst.opcode = opcode;
+ curinst.rd = rd;
+ curinst.unused = 0;
+ emit_bytes(state, &curinst, sizeof(curinst));
+ return TAILQ_NEXT(tok, link);
+}
+
+int
+emit_osxm64(struct emit_state *state, struct oasm_token *tp)
+{
+ struct oasm_token *toknew;
+
+ if (state == NULL || tp == NULL) {
+ return -EINVAL;
+ }
+
+ /*
+ * We need to create a copy of the object as the
+ * caller will likely end up destroying it.
+ */
+ toknew = malloc(sizeof(*toknew));
+ if (toknew == NULL) {
+ return -ENOMEM;
+ }
+
+ memcpy(toknew, tp, sizeof(*toknew));
+ TAILQ_INSERT_TAIL(&state->ir, toknew, link);
+ return 0;
+}
+
+int
+emit_init(struct emit_state *state)
+{
+ state->last_token = TT_UNKNOWN;
+ state->is_init = 1;
+ TAILQ_INIT(&state->ir);
+ return 0;
+}
+
+int
+emit_destroy(struct emit_state *state)
+{
+ struct oasm_token *curtok, *last = NULL;
+
+ TAILQ_FOREACH(curtok, &state->ir, link) {
+ if (last != NULL) {
+ free(last);
+ last = NULL;
+ }
+ if (curtok->raw != NULL) {
+ free(curtok->raw);
+ }
+
+ last = curtok;
+ }
+
+ /* Clean up any last objects */
+ if (last != NULL) {
+ free(last);
+ }
+
+ return 0;
+}
+
+int
+emit_process(struct oasm_state *oasm, struct emit_state *emit)
+{
+ struct oasm_token *curtok;
+ tt_t last_tok;
+
+ if (!emit->is_init) {
+ return -1;
+ }
+
+ emit->out_fd = oasm->out_fd;
+ curtok = TAILQ_FIRST(&emit->ir);
+ while (curtok != NULL) {
+ switch (curtok->type) {
+ case TT_MOV:
+ curtok = emit_encode_mov(emit, curtok);
+ break;
+ case TT_INC:
+ case TT_DEC:
+ curtok = emit_encode_incdec(emit, curtok);
+ break;
+ default:
+ curtok = TAILQ_NEXT(curtok, link);
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/usr.bin/oasm/include/oasm/emit.h b/usr.bin/oasm/include/oasm/emit.h
new file mode 100644
index 0000000..f4b4f77
--- /dev/null
+++ b/usr.bin/oasm/include/oasm/emit.h
@@ -0,0 +1,112 @@
+/* Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _EMIT_H_
+#define _EMIT_H_
+
+#include <sys/queue.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <oasm/lex.h>
+#include <oasm/state.h>
+
+/*
+ * The OSMX64 architecture has 32-bit instructions
+ * that are encoded in the following manner:
+ *
+ * - [0:7]: Opcode
+ * - [11:8]: Register
+ * - [31:12]: Reserved
+ *
+ * The values below define various operation
+ * codes.
+ */
+#define OSMX64_NOP 0x00 /* No-operation */
+#define OSMX64_ADD 0x01 /* Add operation */
+#define OSMX64_SUB 0x02 /* Sub operation */
+#define OSMX64_MUL 0x03 /* Multiply operation */
+#define OSMX64_DIV 0x04 /* Divide operation */
+#define OSMX64_INC 0x05 /* Increment operation */
+#define OSMX64_DEC 0x06 /* Decrement operation */
+#define OSMX64_OR 0x07 /* Bitwise OR operation */
+#define OSMX64_XOR 0x08 /* Bitwise XOR operation */
+#define OSMX64_AND 0x09 /* Bitwise AND operation */
+#define OSMX64_NOT 0x10 /* Bitwise NOT operation */
+#define OSMX64_SLL 0x11 /* Shift left logical operation */
+#define OSMX64_SRL 0x12 /* Shift right logical operation */
+#define OSMX64_MOV_IMM 0x13 /* Data move operation from IMM */
+
+/*
+ * OSMX64 register definitions
+ */
+#define OSMX64_R_X0 0x00
+#define OSMX64_R_X1 0x01
+#define OSMX64_R_X2 0x02
+#define OSMX64_R_X3 0x03
+#define OSMX64_R_X4 0x04
+#define OSMX64_R_X5 0x05
+#define OSMX64_R_X6 0x06
+#define OSMX64_R_X7 0x07
+#define OSMX64_R_X8 0x08
+#define OSMX64_R_X9 0x09
+#define OSMX64_R_X10 0x0A
+#define OSMX64_R_X11 0x0B
+#define OSMX64_R_X12 0x0C
+#define OSMX64_R_X13 0x0D
+#define OSMX64_R_X14 0x0E
+#define OSMX64_R_X15 0x0F
+#define OSMX64_R_BAD 0xFF
+
+typedef uint8_t reg_t;
+typedef uint16_t imm_t;
+
+/*
+ * OSMX64 instruction
+ */
+typedef struct {
+ uint8_t opcode;
+ uint8_t rd;
+ union {
+ uint16_t imm;
+ uint16_t unused;
+ };
+} inst_t;
+
+struct emit_state {
+ tt_t last_token;
+ uint8_t is_init : 1;
+ int out_fd;
+ TAILQ_HEAD(, oasm_token) ir;
+};
+
+int emit_init(struct emit_state *state);
+int emit_destroy(struct emit_state *state);
+int emit_process(struct oasm_state *oasm, struct emit_state *emit);
+int emit_osxm64(struct emit_state *state, struct oasm_token *tp);
+
+#endif /* !_EMIT_H_ */
diff --git a/usr.bin/oasm/include/oasm/lex.h b/usr.bin/oasm/include/oasm/lex.h
new file mode 100644
index 0000000..28ad52c
--- /dev/null
+++ b/usr.bin/oasm/include/oasm/lex.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OASM_LEX_H_
+#define _OASM_LEX_H_
+
+#include <sys/queue.h>
+#include <sys/cdefs.h>
+#include <stdint.h>
+
+struct oasm_state;
+
+#define __XN_REGS \
+ TT_X0, \
+ TT_X1, \
+ TT_X2, \
+ TT_X3, \
+ TT_X4, \
+ TT_X5, \
+ TT_X6, \
+ TT_X7, \
+ TT_X8, \
+ TT_X9, \
+ TT_X10, \
+ TT_X11, \
+ TT_X12, \
+ TT_X13, \
+ TT_X14, \
+ TT_X15
+
+#define __FN_REGS \
+ TT_F0, \
+ TT_F1, \
+ TT_F2, \
+ TT_F3, \
+ TT_F4, \
+ TT_F5, \
+ TT_F6, \
+ TT_F7
+
+#define __DN_REGS \
+ TT_D0, \
+ TT_D1, \
+ TT_D2, \
+ TT_D3, \
+ TT_D4, \
+ TT_D5, \
+ TT_D6, \
+ TT_D7
+
+#define __VN_REGS \
+ TT_V0, \
+ TT_V1, \
+ TT_V2, \
+ TT_V3, \
+ TT_V4, \
+ TT_V5, \
+ TT_V6, \
+ TT_V7
+
+/*
+ * Token type definitions
+ */
+typedef enum {
+ TT_UNKNOWN, /* Unknown token */
+
+ /* Arithmetic instructions */
+ TT_ADD, /* 'add' */
+ TT_SUB, /* 'sub' */
+ TT_MUL, /* 'mul' */
+ TT_DIV, /* 'div' */
+
+ /* Register ops */
+ TT_MOV, /* 'mov' */
+ TT_INC, /* 'inc' */
+ TT_DEC, /* 'dec' */
+ TT_IMM, /* #<n> */
+
+ /* Register sets */
+ __XN_REGS, /* x0-x15 */
+ __FN_REGS, /* f0-f7 */
+ __DN_REGS, /* d0-d7 */
+ __VN_REGS, /* v0-v7 */
+
+ /* Symbols */
+ TT_COMMA, /* ',' */
+} tt_t;
+
+struct oasm_token {
+ tt_t type;
+ uint8_t is_reg : 1;
+ uint16_t imm;
+ char *raw;
+ TAILQ_ENTRY(oasm_token) link;
+};
+
+int lex_tok(struct oasm_state *state, struct oasm_token *ttp);
+
+
+/*
+ * Check if a token is an X<n> register.
+ * Returns true on match.
+ */
+__always_inline static inline bool
+tok_is_xreg(tt_t tok)
+{
+ switch (tok) {
+ case TT_X0:
+ case TT_X1:
+ case TT_X2:
+ case TT_X3:
+ case TT_X4:
+ case TT_X5:
+ case TT_X6:
+ case TT_X7:
+ case TT_X8:
+ case TT_X9:
+ case TT_X10:
+ case TT_X11:
+ case TT_X12:
+ case TT_X13:
+ case TT_X14:
+ case TT_X15:
+ return true;
+ }
+
+ return false;
+}
+
+#endif /* !_OASM_LEX_H_ */
diff --git a/usr.bin/oasm/include/oasm/log.h b/usr.bin/oasm/include/oasm/log.h
new file mode 100644
index 0000000..330c273
--- /dev/null
+++ b/usr.bin/oasm/include/oasm/log.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OASM_LOG_H_
+#define _OASM_LOG_H_
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <stdio.h>
+
+#define ERROR_COLOR "\033[31;40m"
+#define WARN_COLOR "\033[35;40m"
+
+void __oasm_debug(const char *fmt, ...);
+void __oasm_err(const char *fmt, ...);
+void __oasm_warn(const char *fmt, ...);
+
+#define oasm_debug(...) __oasm_debug(__VA_ARGS__)
+#define oasm_err(...) __oasm_err(__VA_ARGS__)
+#define oasm_warn(...) __oasm_warn(__VA_ARGS__)
+
+#endif /* !_OASM_LOG_H_ */
diff --git a/usr.bin/oasm/include/oasm/parse.h b/usr.bin/oasm/include/oasm/parse.h
new file mode 100644
index 0000000..04962e7
--- /dev/null
+++ b/usr.bin/oasm/include/oasm/parse.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OASM_PARSE_H_
+#define _OASM_PARSE_H_
+
+#include <oasm/state.h>
+
+void parse_enter(struct oasm_state *state);
+
+#endif /* !_OASM_PARSE_H_ */
diff --git a/usr.bin/oasm/include/oasm/state.h b/usr.bin/oasm/include/oasm/state.h
new file mode 100644
index 0000000..5f58144
--- /dev/null
+++ b/usr.bin/oasm/include/oasm/state.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OASM_STATE_H_
+#define _OASM_STATE_H_
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <oasm/lex.h>
+
+/*
+ * OASM state:
+ *
+ * @filename: Filname of unit we are parsing
+ * @in_fd: Input file descriptor
+ * @out_fd: Resulting binary output file descriptor
+ * @line: Current line number
+ * @last: Last token
+ */
+struct oasm_state {
+ char *filename;
+ int in_fd;
+ int out_fd;
+ off_t line;
+ tt_t last;
+};
+
+#endif /* !_OASM_STATE_H_ */
diff --git a/usr.bin/oasm/lex.c b/usr.bin/oasm/lex.c
new file mode 100644
index 0000000..f8427e0
--- /dev/null
+++ b/usr.bin/oasm/lex.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <oasm/state.h>
+#include <oasm/lex.h>
+#include <oasm/log.h>
+
+#define is_num(c) ((c) >= '0' && (c) <= '9')
+
+static char putback = '\0';
+
+/* Instruction mnemonic strings */
+#define S_IMN_MOV "mov"
+#define S_IMN_ADD "add"
+#define S_IMN_SUB "sub"
+#define S_IMN_DIV "div"
+#define S_IMN_INC "inc"
+#define S_IMN_DEC "dec"
+
+/*
+ * Returns 0 if a char is counted as a
+ * skippable token. Otherwise, -1
+ */
+static inline int
+lex_skippable(struct oasm_state *state, char c)
+{
+ switch (c) {
+ case ' ': return 0;
+ case '\f': return 0;
+ case '\t': return 0;
+ case '\r': return 0;
+ case '\n':
+ ++state->line;
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * For cleaning up allocated sources
+ * during error conditions
+ *
+ * @p: Memory to free
+ */
+static inline void
+lex_try_free(void *p)
+{
+ if (p != NULL) {
+ free(p);
+ }
+}
+
+/*
+ * Put back a token to grab later
+ *
+ * @c: Character to put back
+ */
+static inline char
+lex_putback(char c)
+{
+ putback = c;
+ return c;
+}
+
+/*
+ * Grab a character from the input file
+ * descriptor.
+ */
+static char
+lex_cin(struct oasm_state *state)
+{
+ char retval;
+
+ if (putback != '\0') {
+ retval = putback;
+ putback = '\0';
+ return retval;
+ }
+
+ if (read(state->in_fd, &retval, 1) <= 0) {
+ return '\0';
+ }
+ return retval;
+}
+
+/*
+ * Nom an operation, directive or any kind
+ * of raw string (unquoted/builtin) and return
+ * memory allocated by strdup() pointing to the
+ * string.
+ *
+ * @state: OASM state pointer
+ * @res: Resulting string
+ *
+ * Returns 0 on success. Greater than zero
+ * value of the last character if a comma or
+ * space was not buffered.
+ */
+static int
+lex_nomstr(struct oasm_state *state, char **res)
+{
+ char buf[256];
+ int retval = 0, n = 0;
+ int tmp;
+
+ memset(buf, 0, sizeof(buf));
+
+ /*
+ * We are filling the buffer containing
+ * the operation or directive.
+ *
+ * Keep going until we hit a space or comman (^)
+ * Examples of such strings (everything in '[]'):
+ *
+ * [mov] [x0], [#1]
+ * ^ ^
+ */
+ while ((tmp = lex_cin(state)) != 0) {
+ if (tmp == ' ' || tmp == ',') {
+ retval = tmp;
+ break;
+ }
+ if (tmp == '\n') {
+ ++state->line;
+ retval = tmp;
+ break;
+
+ }
+
+ buf[n++] = tmp;
+ }
+
+ *res = strdup(buf);
+ return retval;
+}
+
+static tt_t
+token_arith(char *p)
+{
+ if (strcmp(p, S_IMN_MOV) == 0) {
+ return TT_MOV;
+ } else if (strcmp(p, S_IMN_INC) == 0) {
+ return TT_INC;
+ } else if (strcmp(p, S_IMN_DEC) == 0) {
+ return TT_DEC;
+ } else if (strcmp(p, S_IMN_ADD) == 0) {
+ return TT_ADD;
+ } else if (strcmp(p, S_IMN_SUB) == 0) {
+ return TT_SUB;
+ } else if (strcmp(p, S_IMN_DIV) == 0) {
+ return TT_DIV;
+ }
+
+ return TT_UNKNOWN;
+}
+
+static tt_t
+token_xreg(char *p)
+{
+ int num;
+
+ if (p[0] != 'x') {
+ return TT_UNKNOWN;
+ }
+
+ if (!is_num(p[1])) {
+ return TT_UNKNOWN;
+ }
+
+ num = atoi(&p[1]);
+ switch (num) {
+ case 0: return TT_X0;
+ case 1: return TT_X1;
+ case 2: return TT_X2;
+ case 3: return TT_X3;
+ case 4: return TT_X4;
+ case 5: return TT_X5;
+ case 6: return TT_X6;
+ case 7: return TT_X7;
+ case 8: return TT_X8;
+ case 9: return TT_X9;
+ case 10: return TT_X10;
+ case 11: return TT_X11;
+ case 12: return TT_X12;
+ case 13: return TT_X13;
+ case 14: return TT_X14;
+ case 15: return TT_X15;
+ }
+
+ return TT_UNKNOWN;
+}
+
+static tt_t
+token_operand(char *p)
+{
+ /* Is this a numeric constant? */
+ if (p[0] == '#') {
+ return TT_IMM;
+ }
+
+ return TT_UNKNOWN;
+}
+
+static tt_t
+token_reg(char *p)
+{
+ tt_t tok;
+
+ if ((tok = token_xreg(p)) != TT_UNKNOWN) {
+ return tok;
+ }
+
+ return TT_UNKNOWN;
+}
+
+int
+lex_tok(struct oasm_state *state, struct oasm_token *ttp)
+{
+ char *p = NULL;
+ char c = ' ';
+ int tmp;
+ tt_t tok;
+
+ if (state == NULL || ttp == NULL) {
+ return -EINVAL;
+ }
+
+ /*
+ * Grab characters. If they are skippable,
+ * don't use them.
+ */
+ while (lex_skippable(state, c) == 0) {
+ if ((c = lex_cin(state)) == 0) {
+ return -1;
+ }
+ }
+
+ switch (c) {
+ case '\n':
+ ++state->line;
+ return 0;
+ case '\0':
+ return -1;
+ case ',':
+ return TT_COMMA;
+ default:
+ ttp->type = TT_UNKNOWN;
+ ttp->raw = NULL;
+
+ lex_putback(c);
+ lex_nomstr(state, &p);
+
+ /* Arithmetic operation? */
+ if ((tok = token_arith(p)) != TT_UNKNOWN) {
+ ttp->type = tok;
+ ttp->raw = p;
+ return 0;
+ }
+
+ /* Register? */
+ if ((tok = token_reg(p)) != TT_UNKNOWN) {
+ ttp->is_reg = 1;
+ ttp->type = tok;
+ ttp->raw = p;
+ return 0;
+ }
+
+ /* Immediate operand? */
+ if ((tok = token_operand(p)) != TT_UNKNOWN) {
+ if (tok == TT_IMM) {
+ ttp->imm = atoi(&p[1]);
+ }
+
+ ttp->type = tok;
+ ttp->raw = p;
+ return 0;
+ }
+ oasm_err("bad token \"%s\"\n", p);
+ lex_try_free(p);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/usr.bin/oasm/log.c b/usr.bin/oasm/log.c
new file mode 100644
index 0000000..c408865
--- /dev/null
+++ b/usr.bin/oasm/log.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <oasm/log.h>
+#include <oasm/state.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+/* TODO FIXME: Use stdarg.h */
+#define __va_start(ap, fmt) __builtin_va_start(ap, fmt)
+#define __va_end(ap) __builtin_va_end(ap)
+
+extern struct oasm_state g_state;
+
+void
+oasm_debug(const char *fmt, ...)
+{
+ char buf[512];
+ va_list ap;
+ int ret;
+
+ __va_start(ap, fmt);
+ ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+ printf("[debug]: %s\033[0m", buf);
+ printf("\033[0m");
+ __va_end(ap);
+}
+
+void
+oasm_err(const char *fmt, ...)
+{
+ char buf[512];
+ va_list ap;
+ int ret;
+
+ __va_start(ap, fmt);
+ ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+ printf(ERROR_COLOR "error: %s\033[0m", buf);
+ printf("%s: line %d\n", g_state.filename, g_state.line);
+ __va_end(ap);
+}
+
+void
+oasm_warn(const char *fmt, ...)
+{
+ char buf[512];
+ va_list ap;
+ int ret;
+
+ __va_start(ap, fmt);
+ ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+ printf(WARN_COLOR "warning: %s\033[0m", buf);
+ printf("line %d\n", g_state.filename, g_state.line);
+ __va_end(ap);
+}
diff --git a/usr.bin/oasm/oasm.c b/usr.bin/oasm/oasm.c
new file mode 100644
index 0000000..6c37778
--- /dev/null
+++ b/usr.bin/oasm/oasm.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <oasm/state.h>
+#include <oasm/parse.h>
+#define OASM_DBG
+#include <oasm/log.h>
+
+struct oasm_state g_state;
+
+static void
+oasm_start(struct oasm_state *state)
+{
+ state->line = 1;
+ parse_enter(state);
+}
+
+int
+main(int argc, char **argv)
+{
+ if (argc < 3) {
+ printf("oasm: usage: oasm <file> <output>\n");
+ return -1;
+ }
+
+ g_state.in_fd = open(argv[1], O_RDONLY);
+ if (g_state.in_fd < 0) {
+ printf("could not open \"%s\"\n", argv[1]);
+ return -1;
+ }
+
+ g_state.out_fd = open(argv[2], O_CREAT | O_WRONLY);
+ if (g_state.out_fd < 0) {
+ printf("could not open output \"%s\"\n", argv[2]);
+ close(g_state.in_fd);
+ return -1;
+ }
+
+ g_state.filename = argv[1];
+ oasm_start(&g_state);
+ close(g_state.in_fd);
+ close(g_state.out_fd);
+ return 0;
+}
diff --git a/usr.bin/oasm/parse.c b/usr.bin/oasm/parse.c
new file mode 100644
index 0000000..7a855ca
--- /dev/null
+++ b/usr.bin/oasm/parse.c
@@ -0,0 +1,202 @@
+/* Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <oasm/emit.h>
+#include <oasm/state.h>
+#include <oasm/lex.h>
+#include <oasm/parse.h>
+#include <oasm/log.h>
+
+static struct emit_state emit_state;
+static const char *tokstr[] = {
+ [ TT_UNKNOWN] = "bad",
+ [ TT_ADD ] = "add",
+ [ TT_SUB ] = "sub",
+ [ TT_MUL ] = "mul",
+ [ TT_DIV ] = "div",
+ [ TT_COMMA ] = ",",
+ [ TT_INC ] = "inc",
+ [ TT_DEC ] = "dec",
+ [ TT_MOV ] = "mov",
+ [ TT_IMM ] = "<imm>",
+
+ /* X<n> registers */
+ [ TT_X0 ] = "x0",
+ [ TT_X1 ] = "x1",
+ [ TT_X2 ] = "x2",
+ [ TT_X3 ] = "x3",
+ [ TT_X4 ] = "x4",
+ [ TT_X5 ] = "x5",
+ [ TT_X6 ] = "x6",
+ [ TT_X7 ] = "x7",
+ [ TT_X8 ] = "x8",
+ [ TT_X9 ] = "x9",
+ [ TT_X10 ] = "x10",
+ [ TT_X11 ] = "x11",
+ [ TT_X12 ] = "x12",
+ [ TT_X13 ] = "x13",
+ [ TT_X14 ] = "x14",
+ [ TT_X15 ] = "x15",
+
+ /* V<n> registers */
+ [ TT_F0 ] = "v0",
+ [ TT_F1 ] = "v1",
+ [ TT_F2 ] = "v2",
+ [ TT_F3 ] = "v3",
+ [ TT_F4 ] = "v4",
+ [ TT_F5 ] = "v5",
+ [ TT_F6 ] = "v6",
+ [ TT_F7 ] = "v7",
+
+ /* D<n> registers */
+ [ TT_D0 ] = "d0",
+ [ TT_D1 ] = "d1",
+ [ TT_D2 ] = "d2",
+ [ TT_D3 ] = "d3",
+ [ TT_D4 ] = "d4",
+ [ TT_D5 ] = "d5",
+ [ TT_D6 ] = "d6",
+ [ TT_D7 ] = "d7",
+
+ /* V<n> registers */
+ [ TT_V0 ] = "v0",
+ [ TT_V1 ] = "v1",
+ [ TT_V2 ] = "v2",
+ [ TT_V3 ] = "v3",
+ [ TT_V4 ] = "v4",
+ [ TT_V5 ] = "v5",
+ [ TT_V6 ] = "v6",
+ [ TT_V7 ] = "v7",
+};
+
+static int
+parse_reg(struct oasm_state *state, struct oasm_token *tok)
+{
+ const char *p;
+
+ /* Valid instructions that go with regs */
+ switch (state->last) {
+ case TT_MOV:
+ case TT_DEC:
+ case TT_INC:
+ state->last = tok->type;
+ break;
+ default:
+ p = tokstr[state->last];
+ oasm_err("bad instruction '%s' for regop\n", p);
+ return -1;
+ }
+
+ if (!tok_is_xreg(tok->type)) {
+ p = tokstr[tok->type];
+ oasm_err("bad register \"%s\"\n", p);
+ return -1;
+ }
+
+ state->last = tok->type;
+ emit_osxm64(&emit_state, tok);
+ return 0;
+}
+
+static int
+parse_imm(struct oasm_token *tok, tt_t last)
+{
+ return 0;
+}
+
+static int
+parse_tok(struct oasm_state *state, struct oasm_token *tok)
+{
+ const char *p;
+ int error;
+
+ switch (tok->type) {
+ case TT_MOV:
+ state->last = tok->type;
+ emit_osxm64(&emit_state, tok);
+ break;
+ case TT_DEC:
+ case TT_INC:
+ state->last = tok->type;
+ emit_osxm64(&emit_state, tok);
+ break;
+ case TT_IMM:
+ p = tokstr[state->last];
+ if (!tok_is_xreg(state->last)) {
+ printf("expected X<n> but got %s\n", p);
+ return -1;
+ }
+ emit_osxm64(&emit_state, tok);
+ break;
+ default:
+ if (!tok->is_reg) {
+ oasm_err("syntax error\n");
+ return -1;
+ }
+
+ error = parse_reg(state, tok);
+ if (error < 0) {
+ return error;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+void
+parse_enter(struct oasm_state *state)
+{
+ struct oasm_token tok;
+ const char *type, *raw;
+ int error = 0;
+
+ emit_init(&emit_state);
+
+ for (;;) {
+ error = lex_tok(state, &tok);
+ if (error < 0) {
+ break;
+ }
+
+ if (parse_tok(state, &tok) < 0) {
+ break;
+ }
+
+ type = tokstr[tok.type];
+ raw = tok.raw;
+ oasm_debug("got token type %s (%s)\n", type, raw);
+ }
+
+ /* Process then destroy the emit state */
+ emit_process(state, &emit_state);
+ emit_destroy(&emit_state);
+}
diff --git a/usr.bin/oemu/Makefile b/usr.bin/oemu/Makefile
new file mode 100644
index 0000000..366208c
--- /dev/null
+++ b/usr.bin/oemu/Makefile
@@ -0,0 +1,7 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+CFLAGS = -Iinclude/
+
+$(ROOT)/base/usr/bin/oemu:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS) $(CFLAGS)
diff --git a/usr.bin/oemu/cpu.c b/usr.bin/oemu/cpu.c
new file mode 100644
index 0000000..61568f2
--- /dev/null
+++ b/usr.bin/oemu/cpu.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <oemu/cpu.h>
+#include <oemu/types.h>
+#include <oemu/osmx64.h>
+
+/*
+ * Decode the INST_MOV_IMM instruction
+ *
+ * @cpu: CPU that is executing
+ * @inst: Instruction dword
+ */
+static void
+cpu_mov_imm(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'mov'\n");
+ return;
+ }
+
+ regs->xreg[inst->rd] = inst->imm;
+ printf("#%d -> x%d\n", inst->imm, inst->rd);
+}
+
+/*
+ * Decode the INST_INC instruction
+ *
+ * @cpu: CPU that is executing
+ * @inst: Instruction dword
+ */
+static void
+cpu_inc(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ imm_t imm;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'mov'\n");
+ return;
+ }
+
+ imm = regs->xreg[inst->rd]++;
+ printf("INC X%d [%x], new=%x\n", inst->rd,
+ imm, regs->xreg[inst->rd]);
+}
+
+/*
+ * Decode the INST_DEC instruction
+ *
+ * @cpu: CPU that is executing
+ * @inst: Instruction dword
+ */
+static void
+cpu_dec(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ imm_t imm;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'mov'\n");
+ return;
+ }
+
+ imm = regs->xreg[inst->rd]--;
+ printf("DEC X%d [%x], new=%x\n", inst->rd,
+ imm, regs->xreg[inst->rd]);
+}
+
+/*
+ * Reset a CPU to a default state
+ */
+void
+cpu_reset(struct oemu_cpu *cpu)
+{
+ struct cpu_regs *regs;
+
+ regs = &cpu->regs;
+ regs->ip = 0;
+ memset(regs->xreg, 0x0, sizeof(regs->xreg));
+}
+
+/*
+ * Main instruction execution loop.
+ */
+void
+cpu_kick(struct oemu_cpu *cpu, struct sysmem *mem)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ inst_t *inst;
+ uint8_t *memp = mem->mem;
+
+ for (;;) {
+ inst = (inst_t *)&memp[regs->ip];
+
+ switch (inst->opcode) {
+ case INST_MOV_IMM:
+ cpu_mov_imm(cpu, inst);
+ break;
+ case INST_INC:
+ cpu_inc(cpu, inst);
+ break;
+ case INST_DEC:
+ cpu_dec(cpu, inst);
+ break;
+ }
+
+ if (regs->ip >= MEMORY_SIZE) {
+ break;
+ }
+
+ regs->ip += sizeof(*inst);
+ }
+}
diff --git a/usr.bin/oemu/emu.c b/usr.bin/oemu/emu.c
new file mode 100644
index 0000000..1b4280b
--- /dev/null
+++ b/usr.bin/oemu/emu.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <oemu/cpu.h>
+
+static struct oemu_cpu core_0;
+struct sysmem g_mem;
+
+static void
+help(void)
+{
+ printf(
+ "OSMORA OSMX64 Emulator\n"
+ "usage: oemu <binary file>\n"
+ );
+}
+
+/*
+ * Allocate and initialize platform
+ * memory.
+ */
+static int
+mem_init(void)
+{
+ printf("allocating 0x%x bytes of memory\n", MEMORY_SIZE);
+ g_mem.mem_size = MEMORY_SIZE;
+ g_mem.mem = malloc(MEMORY_SIZE);
+ if (g_mem.mem == NULL) {
+ printf("failed to allocate memory\n");
+ return -1;
+ }
+}
+
+/*
+ * Load a program specified by a path into
+ * memory for execution.
+ */
+static int
+program_load(const char *path, paddr_t loadoff)
+{
+ void *mem = g_mem.mem;
+ size_t size;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ printf("failed to open \"%s\"\n", path);
+ return -ENOENT;
+ }
+
+ /* Grab the size of the file */
+ size = lseek(fd, 0, SEEK_END);
+ lseek(fd, loadoff, SEEK_SET);
+ printf("loading size %d\n", size);
+
+ /* Is it too big? */
+ if (size >= g_mem.mem_size) {
+ printf("program too big !! (memsize=%x)\n", g_mem.mem_size);
+ close(fd);
+ return -1;
+ }
+
+ printf("read data into %p\n", mem);
+ printf("read %d bytes\n", read(fd, mem, size));
+ close(fd);
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ if (argc < 2) {
+ help();
+ return -1;
+ }
+
+ /* Initialize memory */
+ if (mem_init() < 0) {
+ return -1;
+ }
+
+ /* Put the CPU in a known state */
+ cpu_reset(&core_0);
+
+ /*
+ * Load the program and send the little guy off
+ * to start nomming those 32-bit instructions
+ */
+ if (program_load(argv[1], 0x00000000) < 0) {
+ return -1;
+ }
+ cpu_kick(&core_0, &g_mem);
+ free(g_mem.mem);
+ return 0;
+}
diff --git a/usr.bin/oemu/include/oemu/cpu.h b/usr.bin/oemu/include/oemu/cpu.h
new file mode 100644
index 0000000..df4cc80
--- /dev/null
+++ b/usr.bin/oemu/include/oemu/cpu.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OEMU_CPU_H_
+#define _OEMU_CPU_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <oemu/types.h>
+
+#define MEMORY_SIZE 512
+
+/*
+ * System memory
+ *
+ * @mem: Data
+ * @mem_size: Memory size max
+ */
+struct sysmem {
+ void *mem;
+ size_t mem_size;
+};
+
+/*
+ * CPU register state
+ *
+ * @xreg: X<n>
+ * @ip: Instruction pointer
+ */
+struct cpu_regs {
+ reg_t xreg[16];
+ reg_t ip;
+};
+
+struct oemu_cpu {
+ struct cpu_regs regs;
+};
+
+void cpu_reset(struct oemu_cpu *cpu);
+void cpu_kick(struct oemu_cpu *cpu, struct sysmem *mem);
+
+#endif /* !_OEMU_CPU_H_ */
diff --git a/usr.bin/oemu/include/oemu/osmx64.h b/usr.bin/oemu/include/oemu/osmx64.h
new file mode 100644
index 0000000..d7ea8b1
--- /dev/null
+++ b/usr.bin/oemu/include/oemu/osmx64.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OEMU_OSMX64_H_
+#define _OEMU_OSMX64_H_
+
+#include <stdint.h>
+
+/* Opcodes */
+#define INST_NOP 0x00 /* No-operation */
+#define INST_ADD 0x01 /* Add operation */
+#define INST_SUB 0x02 /* Sub operation */
+#define INST_MUL 0x03 /* Multiply operation */
+#define INST_DIV 0x04 /* Divide operation */
+#define INST_INC 0x05 /* Increment operation */
+#define INST_DEC 0x06 /* Decrement operation */
+#define INST_OR 0x07 /* Bitwise OR operation */
+#define INST_XOR 0x08 /* Bitwise XOR operation */
+#define INST_AND 0x09 /* Bitwise AND operation */
+#define INST_NOT 0x10 /* Bitwise NOT operation */
+#define INST_SLL 0x11 /* Shift left logical operation */
+#define INST_SRL 0x12 /* Shift right logical operation */
+#define INST_MOV_IMM 0x13 /* Data move operation from IMM */
+
+/* Registers */
+#define REG_X0 0x00
+#define REG_X1 0x01
+#define REG_X2 0x02
+#define REG_X3 0x03
+#define REG_X4 0x04
+#define REG_X5 0x05
+#define REG_X6 0x06
+#define REG_X7 0x07
+#define REG_X8 0x08
+#define REG_X9 0x09
+#define REG_X10 0x0A
+#define REG_X11 0x0B
+#define REG_X12 0x0C
+#define REG_X13 0x0D
+#define REG_X14 0x0E
+#define REG_X15 0x0F
+#define REG_BAD 0xFF
+
+/*
+ * OSMX64 instruction format
+ */
+typedef struct {
+ uint8_t opcode;
+ uint8_t rd;
+ union {
+ uint16_t imm;
+ uint16_t unused;
+ };
+} inst_t;
+
+#endif /* !_OEMU_OSMX64_H_ */
diff --git a/usr.bin/oemu/include/oemu/types.h b/usr.bin/oemu/include/oemu/types.h
new file mode 100644
index 0000000..0769c84
--- /dev/null
+++ b/usr.bin/oemu/include/oemu/types.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OEMU_TYPES_H_
+#define _OEMU_TYPES_H_
+
+#include <sys/types.h>
+
+typedef uint64_t reg_t;
+typedef uintptr_t paddr_t;
+typedef uint16_t imm_t;
+
+#endif /* !_OEMU_TYPES_H_ */
diff --git a/usr.bin/osh/osh.c b/usr.bin/osh/osh.c
index 5bcd2e2..af7f4ab 100644
--- a/usr.bin/osh/osh.c
+++ b/usr.bin/osh/osh.c
@@ -30,7 +30,9 @@
#include <sys/types.h>
#include <sys/cdefs.h>
#include <sys/reboot.h>
+#include <sys/errno.h>
#include <sys/spawn.h>
+#include <sys/wait.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdbool.h>
@@ -38,7 +40,9 @@
#include <string.h>
#include <stdio.h>
+#define is_printable(C) ((C) >= 32 && (C) <= 126)
#define is_ascii(C) ((C) >= 0 && (C) <= 128)
+
#define COMMENT '@'
#define WELCOME \
":::::::::::::::::::::::::::::::::::::::\n" \
@@ -63,7 +67,6 @@
#define PROMPT "[%s::osmora]~ "
static char buf[64];
-static uint8_t buf_i;
static int running;
static int bell_fd;
static bool bs_bell = true; /* Beep on backspace */
@@ -81,6 +84,15 @@ struct builtin_cmd {
void (*func)(int argc, char *argv[]);
};
+/*
+ * Results after parsing a command
+ *
+ * @bg: Run command in background
+ */
+struct parse_state {
+ uint8_t bg : 1;
+};
+
static struct builtin_cmd cmds[] = {
{"help",cmd_help},
{"exit",cmd_exit},
@@ -143,7 +155,7 @@ cmd_bell(int argc, char *argv[])
}
static int
-parse_args(char *input, char *argv[], int max_args)
+parse_args(char *input, char *argv[], int max_args, struct parse_state *p)
{
int argc = 0;
@@ -152,6 +164,10 @@ parse_args(char *input, char *argv[], int max_args)
return 0;
}
+ /* setup default state */
+ p->bg = 0;
+
+ /* parse loop */
while (*input != '\0') {
/* skip leading spaces */
while (*input == ' ') {
@@ -168,6 +184,11 @@ parse_args(char *input, char *argv[], int max_args)
break;
}
+ /* run in background? */
+ if (*input == '&') {
+ p->bg = 1;
+ }
+
if (argc < max_args) {
argv[argc++] = input; /* mark start of the argument */
}
@@ -191,14 +212,18 @@ parse_args(char *input, char *argv[], int max_args)
return argc;
}
-static char *
+/*
+ * Grab a string from stdin and return
+ * the resulting offset within the input
+ * buffer we are at.
+ */
+static uint8_t
getstr(void)
{
char c;
int input;
uint32_t beep_payload;
-
- buf_i = 0;
+ uint8_t buf_i = 0;
/*
* Prepare the beep payload @ 500 Hz
@@ -221,7 +246,7 @@ getstr(void)
if (c == '\n') {
buf[buf_i] = '\0';
putchar('\n');
- return buf;
+ return buf_i;
}
/* handle backspaces and DEL */
@@ -232,7 +257,7 @@ getstr(void)
} else if (bell_fd > 0 && bs_bell) {
write(bell_fd, &beep_payload, sizeof(beep_payload));
}
- } else if (is_ascii(c) && buf_i < sizeof(buf) - 1) {
+ } else if (is_printable(c) && buf_i < sizeof(buf) - 1) {
/* write to fd and add to buffer */
buf[buf_i++] = c;
putchar(c);
@@ -250,16 +275,11 @@ builtin_run(struct builtin_cmd *cmd, int argc, char *argv[])
}
static int
-cmd_run(const char *input, int argc, char *argv[], bool wait)
+cmd_run(const char *input, int argc, char *argv[])
{
char bin_path[512];
char *envp[1] = { NULL };
- int error, spawn_flags = 0;
-
- /* Should we wait or daemonize? */
- if (wait) {
- spawn_flags |= SPAWN_WAIT;
- }
+ pid_t child;
/*
* If we can access the raw input as a file, try to
@@ -268,11 +288,11 @@ cmd_run(const char *input, int argc, char *argv[], bool wait)
* path directly into the console.
*/
if (access(input, F_OK) == 0) {
- error = spawn(input, argv, envp, spawn_flags);
- if (error < 0) {
- return error;
+ child = spawn(input, argv, envp, 0);
+ if (child < 0) {
+ return child;
}
- return 0;
+ return child;
}
snprintf(bin_path, sizeof(bin_path), "/usr/bin/%s", input);
@@ -282,11 +302,11 @@ cmd_run(const char *input, int argc, char *argv[], bool wait)
return -1;
}
- if ((error = spawn(bin_path, argv, envp, spawn_flags)) < 0) {
- return error;
+ if ((child = spawn(bin_path, argv, envp, 0)) < 0) {
+ return child;
}
- return 0;
+ return child;
}
/*
@@ -295,27 +315,29 @@ cmd_run(const char *input, int argc, char *argv[], bool wait)
* @input: Command input
* @argc: Argument count
* @argv: Argument vector
- * @wait: If false, program will be daemonized
*/
-static void
-command_match(const char *input, int argc, char *argv[], bool wait)
+static int
+command_match(const char *input, int argc, char *argv[])
{
int found = 0;
int i;
+ pid_t child = -1;
for (i = 0; cmds[i].name != NULL; i++) {
if (strcmp(input, cmds[i].name) == 0) {
builtin_run(&cmds[i], argc, argv);
found = 1;
- break;
}
}
if (found == 0) {
- if (cmd_run(input, argc, argv, wait) < 0) {
+ if ((child = cmd_run(input, argc, argv)) < 0) {
puts("Unrecognized command");
+ return -1;
}
}
+
+ return child;
}
static void
@@ -329,11 +351,45 @@ script_skip_comment(int fd)
}
}
+/*
+ * Parse a single line typed in from the
+ * user.
+ *
+ * @input: Input line
+ */
+static int
+parse_line(char *input)
+{
+ int argc;
+ char *argv[16];
+ struct parse_state state = {0};
+ pid_t child;
+
+ /* Ensure the aux vector is zeored */
+ memset(argv, 0, sizeof(argv));
+
+ /*
+ * Grab args from the user, there should be
+ * at least one.
+ */
+ argc = parse_args(input, argv, sizeof(argv), &state);
+ if (argc == 0) {
+ return -EAGAIN;
+ }
+
+ child = command_match(input, argc, argv);
+ if (child > 0 && !state.bg) {
+ waitpid(child, NULL, 0);
+ }
+
+ return 0;
+}
+
static int
open_script(const char *pathname)
{
int fd, argc, buf_i = 0;
- char c, *input, *argv[16];
+ char c, *input;
char buf[256];
fd = open(pathname, O_RDONLY);
@@ -360,11 +416,7 @@ open_script(const char *pathname)
if (c == '\n') {
buf[buf_i] = '\0';
- argc = parse_args(buf, argv, sizeof(argv));
- command_match(buf, argc, argv, true);
-
- argv[0] = NULL;
- argv[1] = NULL;
+ parse_line(buf);
buf_i = 0;
continue;
}
@@ -379,35 +431,32 @@ main(int argc, char **argv)
{
int found, prog_argc;
int stdout_fd;
- char *input, *prog_argv[16], *p;
+ uint8_t buf_i;
+ char *p;
char c;
+ pid_t child;
if (argc > 1) {
return open_script(argv[1]);
}
- buf_i = 0;
running = 1;
- found = 0;
bell_fd = open("/dev/beep", O_WRONLY);
-
puts(WELCOME);
+
while (running) {
- memset(prog_argv, 0, sizeof(prog_argv));
printf(PROMPT, getlogin());
- input = getstr();
- if (input[0] == '\0') {
+ buf_i = getstr();
+ if (buf[0] == '\0') {
continue;
}
- prog_argc = parse_args(input, prog_argv, sizeof(prog_argv));
- if (prog_argc == 0) {
+ buf[buf_i] = '\0';
+ if (parse_line(buf) < 0) {
continue;
}
- command_match(input, prog_argc, prog_argv, true);
- found = 0;
buf[0] = '\0';
}
return 0;
diff --git a/usr.bin/whoami/Makefile b/usr.bin/whoami/Makefile
new file mode 100644
index 0000000..ced9ae2
--- /dev/null
+++ b/usr.bin/whoami/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/whoami:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/whoami/whoami.c b/usr.bin/whoami/whoami.c
new file mode 100644
index 0000000..c3adcf0
--- /dev/null
+++ b/usr.bin/whoami/whoami.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ printf("%s\f\n", getlogin());
+ return 0;
+}