use bio use std const strdup = {s var buf = std.slalloc(s.len) std.slcp(buf, s) -> buf } const upcase = {s var i var buf = std.mksb() for i = 0; i < s.len; i++ std.sbputc(buf, std.toupper((s[i] : char))) ;; -> std.sbfin(buf) } const readline = {line var cmt = std.strfind(line, ";") match cmt | `std.Some (pos): line = line[:pos] | `std.None: ;; if line.len == 0 -> (`std.None, [][:]) ;; var parts = std.strtok(line) if std.isspace((line[0] : char)) -> (`std.None, parts) else -> (`std.Some(parts[0]), parts[1:]) ;; } type literal = union `Number int `Register int `Label byte[:] `DerefAddr int `DerefLabel byte[:] `Invalid ;; const getnum = {s -> std.intparsebase((s[1:] : byte[:]), 16) } const parselit = {s if s.len < 1 -> `Invalid elif s[0] == ('#' : byte) match getnum(s) |`std.Some(n): -> `Number(n) |`std.None: -> `Invalid ;; elif s[0] == ('v' : byte) || s[0] == ('V' : byte) match getnum(s) |`std.Some(n): -> `Register(n) |`std.None: -> `Invalid ;; elif s[0] == ('[' : byte) match getnum(s[:s.len-1]) |`std.Some(n): -> `DerefAddr(n) |`std.None: -> `DerefLabel(s[:s.len-1]) ;; else -> `Label(s) ;; } const emitinstr = {p var b = `std.None var mark = `std.None if p.len < 1 -> `std.Ok(b, mark) ;; match upcase(p[0]) | "CLS": b = `std.Some([0x00, 0xe0]) | "RET": b = `std.Some([0x00, 0xee]) | "SYS": if p.len < 2 -> `std.Err("SYS missing subroutine address") ;; match parselit(p[1]) | `Number(n): b = `std.Some([0x00 | (n & 0x0f00), (n & 0xff)]) | `Label(l): b = `std.Some([0x00, 0x00]) mark = `std.Some(strdup(l)) | _: -> `std.Err(std.fmt("invalid argument to SYS: {}", p[1])) ;; | "CALL": if p.len < 2 -> `std.Err("CALL missing subroutine address") ;; match parselit(p[1]) | `Number(n): b = `std.Some([0x20 | (n & 0x0f00), (n & 0xff)]) | `Label(l): b = `std.Some([0x20, 0x00]) mark = `std.Some(strdup(l)) | _: -> `std.Err(std.fmt("invalid argument to CALL: {}", p[1])) ;; | "JP": if p.len < 2 -> `std.Err("JP missing address") elif p.len == 2 match parselit(p[1]) | `Number(n): b = `std.Some([0x10 | (n & 0x0f00), (n & 0xff)]) | `Label(l): b = `std.Some([0x10, 0x00]) mark = `std.Some(strdup(l)) | _: -> `std.Err(std.fmt("invalid argument to JP: {}", p[1])) ;; else match (parselit(p[1]), parselit(p[2])) | (`Register(0), `Number(n)): b = `std.Some([0xb0 | (n & 0x0f00), (n & 0xff)]) | (`Register(0), `Label(l)): b = `std.Some([0xb0, 0x00]) mark = `std.Some(strdup(l)) | _: -> `std.Err(std.fmt("invalid arguments to JP: {}, {}", p[1], p[2])) ;; ;; | "SE": if p.len < 3 -> `std.Err("SE missing arguments") ;; match (parselit(p[1]), parselit(p[2])) | (`Register(m), `Number(n)): b = `std.Some([0x30 | (m & 0x0f), (n & 0xff)]) | (`Register(m), `Register(n)): b = `std.Some([0x50 | (m & 0x0f), (n & 0x0f) << 1]) | _: -> `std.Err(std.fmt("invalid arguments to SE: {}, {}", p[1], p[2])) ;; | "SNE": if p.len < 3 -> `std.Err("SNE missing arguments") ;; match (parselit(p[1]), parselit(p[2])) | (`Register(m), `Number(n)): b = `std.Some([0x40 | (m & 0x0f), (n & 0xff)]) | (`Register(m), `Register(n)): b = `std.Some([0x90 | (m & 0x0f), (n & 0x0f) << 1]) | _: -> `std.Err(std.fmt("invalid arguments to SNE: {}, {}", p[1], p[2])) ;; | "SKP": if p.len < 2 -> `std.Err("SKP missing address") ;; match parselit(p[1]) | `Register(n): b = `std.Some([0xe0 | (n & 0x0f), 0x9e]) | _: -> `std.Err(std.fmt("invalid argument to SKP: {}", p[1])) ;; | _: -> `std.Err(std.fmt("unrecognized instruction {}", p[0])) ;; -> `std.Ok((b, mark)) } const die_emit = {msg, lc std.die(std.fmt("{} on line {}\n", msg, lc)) } const die_rewrite = {l, lc const s = "ref to undefined label '{}' on line {}\n" std.die(std.fmt(s, l, lc)) } /* convert `arr` to a byte[:] slice */ const itob = {arr var i var ret = std.slalloc(arr.len) for i = 0; i < arr.len; i++; ret[i] = ((arr[i] & 0xff) : byte) ;; -> ret } const main = { var fd = bio.mkfile(std.In, bio.Rd) var src = bio.byline(fd) var lc = 0 var offset = 0 var buf = [][:] var q = [][:] var labels = std.mkht() var res for line : src ; match readline(line) | (`std.None, i): res = emitinstr(i) | (`std.Some (label), i): res = emitinstr(i) std.htput(labels, strdup(label), offset) ;; match res | `std.Err(msg): die_emit(msg, lc) | `std.Ok((bytes, marker)): match marker | `std.Some(label): q = std.slpush(&q, (label, offset, lc)) | `std.None: ;; match bytes | `std.Some(arr): buf = std.sljoin(&buf, arr[:]) offset += 2 | `std.None: ;; ;; lc++ ;; for p : q ; match p | (l, buf_pos, linum): match std.htget(labels, l) | `std.None: die_rewrite(l, linum) | `std.Some(addr): buf[buf_pos] |= addr & 0x0f00 buf[buf_pos + 1] |= addr & 0xff ;; ;; ;; std.write(std.Out, itob(buf)) bio.free(fd) }