Up-to-date with main as of

c7d3a410ab

Merge remote-tracking branch 'upstream/main' into d6/subprocess-may2024
This commit is contained in:
~d6 2024-05-09 09:13:03 -04:00
commit a8119fb818
11 changed files with 499 additions and 448 deletions

6
etc/hello.tal Normal file
View File

@ -0,0 +1,6 @@
%square { DUP MUL ?{ } }
|100
#12 square
BRK

56
etc/mmu.tal Normal file
View File

@ -0,0 +1,56 @@
|00 @System &vector $2 &expansion $2 &wst $1 &rst $1 &metadata $2 &r $2 &g $2 &b $2 &debug $1 &state $1
|100
@on-reset ( -> )
;buf <pstr>
#2018 DEO
;buf <pstr>
#0a18 DEO
( | copy left )
;buf #0006 ADD2 ;mmu-cpyl/a STA2
;buf #0003 ADD2 ;mmu-cpyl/b STA2
;mmu-cpyl .System/expansion DEO2
;buf <pstr>
#2018 DEO
;res1 <pstr>
#0a18 DEO
( | copy right )
;buf #0003 ADD2 ;mmu-cpyr/a STA2
;buf #0009 ADD2 ;mmu-cpyr/b STA2
;mmu-cpyr .System/expansion DEO2
;buf <pstr>
#2018 DEO
;res2 <pstr>
#0a18 DEO
( | copy left )
;buf #0009 ADD2 ;mmu-cpyl/a STA2
;buf #0006 ADD2 ;mmu-cpyl/b STA2
;mmu-cpyl .System/expansion DEO2
;buf <pstr>
#2018 DEO
;res3 <pstr>
#0a18 DEO
( | memset )
;buf #0003 ADD2 ;mmu-fill/a STA2
;mmu-fill .System/expansion DEO2
;buf <pstr>
#2018 DEO
;res4 <pstr>
#0a18 DEO
BRK
@<pstr> ( str* -- )
LDAk #18 DEO
INC2 LDAk ?<pstr>
POP2 JMP2r
@buf [ "......[hello]..... $1 ]
@res1 [ "...[hello]o]...... $1 ]
@res2 [ "...[hello[hello].. $1 ]
@res3 [ "...[he[hello]lo].. $1 ]
@res4 [ "...-------------.. $1 ]
@mmu-fill [ 00 000d 0000 &a $2 2d ]
@mmu-cpyl [ 01 0007 0000 &a $2 0000 &b $2 ]
@mmu-cpyr [ 02 0007 0000 &a $2 0000 &b $2 ]

View File

@ -5,24 +5,26 @@ EMU_src=${CLI_src} src/devices/screen.c src/devices/controller.c src/devices/mou
RELEASE_flags=-DNDEBUG -O2 -g0 -s RELEASE_flags=-DNDEBUG -O2 -g0 -s
DEBUG_flags=-std=c89 -D_POSIX_C_SOURCE=199309L -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined DEBUG_flags=-std=c89 -D_POSIX_C_SOURCE=199309L -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined
.PHONY: all debug dest run test install uninstall format clean .PHONY: all debug dest run test install uninstall format clean archive
all: dest bin/uxnasm bin/uxncli bin/uxn11 all: dest bin/uxnasm bin/uxncli bin/uxn11
dest: dest:
@ mkdir -p bin @ mkdir -p bin
run: all bin/uxnasm bin/uxncli bin/uxn11 run: all bin/uxnasm bin/uxncli bin/uxn11
@ ./bin/uxn11 @ bin/uxn11
test: bin/uxnasm bin/uxncli bin/uxn11 test: bin/uxnasm bin/uxncli bin/uxn11
@ ./bin/uxnasm -v && ./bin/uxncli -v && ./bin/uxn11 -v @ bin/uxnasm -v && ./bin/uxncli -v && ./bin/uxn11 -v
@ ./bin/uxnasm etc/opctest.tal bin/opctest.rom @ bin/uxnasm etc/opctest.tal bin/opctest.rom
@ ./bin/uxncli bin/opctest.rom @ bin/uxncli bin/opctest.rom
install: all bin/uxnasm bin/uxncli bin/uxn11 install: all bin/uxnasm bin/uxncli bin/uxn11
@ cp bin/uxn11 bin/uxnasm bin/uxncli ~/bin/ @ cp bin/uxn11 bin/uxnasm bin/uxncli ~/bin/
uninstall: uninstall:
@ rm -f ~/bin/uxn11 ~/bin/uxnasm ~/bin/uxncli @ rm -f ~/bin/uxn11 ~/bin/uxnasm ~/bin/uxncli
format: format:
@ clang-format -i src/uxnasm.c src/uxncli.c src/uxn11.c src/devices/* @ clang-format -i src/uxnasm.c src/uxncli.c src/uxn11.c src/devices/*
archive:
@ cp src/uxnasm.c ../oscean/etc/uxnasm.c.txt
clean: clean:
@ rm -fr bin @ rm -fr bin

View File

@ -346,6 +346,109 @@ console_listen(Uxn *u, int i, int argc, char **argv)
} }
} }
/* static void */
/* start_fork_pty(Uint8 *d) */
/* { */
/* int fd = -1; */
/* pid_t pid = forkpty(&fd, NULL, NULL, NULL); */
/* if(pid < 0) { /\* failure *\/ */
/* d[0x6] = 0xff; */
/* fprintf(stderr, "fork failure\n"); */
/* } else if(pid == 0) { /\* child *\/ */
/* setenv("TERM", "ansi", 1); */
/* execvp(fork_args[0], fork_args); */
/* d[0x6] = 0xff; */
/* fprintf(stderr, "exec failure\n"); */
/* } else { /\*parent*\/ */
/* child_pid = pid; */
/* pty_fd = fd; */
/* ioctl(fd, TIOCSWINSZ, &ws); */
/* saved_in = dup(0); */
/* saved_out = dup(1); */
/* dup2(fd, 0); */
/* dup2(fd, 1); */
/* } */
/* } */
/* static void */
/* start_fork_pipe(Uint8 *d) */
/* { */
/* pid_t pid; */
/* if(child_mode & 0x01) { */
/* /\* parent writes to child's stdin *\/ */
/* if(pipe(to_child_fd) == -1) { */
/* d[0x6] = 0xff; */
/* fprintf(stderr, "pipe error: to child\n"); */
/* return; */
/* } */
/* } */
/* if(child_mode & 0x06) { */
/* /\* parent reads from child's stdout and/or stderr *\/ */
/* if(pipe(from_child_fd) == -1) { */
/* d[0x6] = 0xff; */
/* fprintf(stderr, "pipe error: from child\n"); */
/* return; */
/* } */
/* } */
/* pid = fork(); */
/* if(pid < 0) { /\* failure *\/ */
/* d[0x6] = 0xff; */
/* fprintf(stderr, "fork failure\n"); */
/* } else if(pid == 0) { /\* child *\/ */
/* if(child_mode & 0x01) { */
/* dup2(to_child_fd[0], 0); */
/* close(to_child_fd[1]); */
/* } */
/* if(child_mode & 0x06) { */
/* if(child_mode & 0x02) dup2(from_child_fd[1], 1); */
/* if(child_mode & 0x04) dup2(from_child_fd[1], 2); */
/* close(from_child_fd[0]); */
/* } */
/* execvp(fork_args[0], fork_args); */
/* d[0x6] = 0xff; */
/* fprintf(stderr, "exec failure\n"); */
/* } else { /\*parent*\/ */
/* child_pid = pid; */
/* if(child_mode & 0x01) { */
/* saved_out = dup(1); */
/* dup2(to_child_fd[1], 1); */
/* close(to_child_fd[0]); */
/* } */
/* if(child_mode & 0x06) { */
/* saved_in = dup(0); */
/* dup2(from_child_fd[0], 0); */
/* close(from_child_fd[1]); */
/* } */
/* } */
/* } */
/* static void */
/* kill_child(Uint8 *d, int options) */
/* { */
/* int wstatus; */
/* if(child_pid) { */
/* kill(child_pid, 9); */
/* if(waitpid(child_pid, &wstatus, options)) { */
/* d[0x6] = 1; */
/* d[0x7] = WEXITSTATUS(wstatus); */
/* clean_after_child(); */
/* } */
/* } */
/* } */
/* static void */
/* start_fork(Uxn *u, Uint8 *d) */
/* { */
/* fflush(stderr); */
/* kill_child(d, 0); */
/* child_mode = d[0x5]; */
/* parse_args(u, d); */
/* if(child_mode >= 0x80) */
/* start_fork_pty(d); */
/* else */
/* start_fork_pipe(d); */
/* } */
Uint8 Uint8
console_dei(Uxn *u, Uint8 addr) console_dei(Uxn *u, Uint8 addr)
{ {

View File

@ -10,8 +10,6 @@ WITH REGARD TO THIS SOFTWARE.
*/ */
#define FILE_VERSION 1 #define FILE_VERSION 1
#define FILE_DEIMASK 0x0000
#define FILE_DEOMASK 0xa260
#define POLYFILEY 2 #define POLYFILEY 2
#define DEV_FILE0 0xa #define DEV_FILE0 0xa

View File

@ -29,23 +29,17 @@ mouse_up(Uxn *u, Uint8 *d, Uint8 mask)
void void
mouse_pos(Uxn *u, Uint8 *d, Uint16 x, Uint16 y) mouse_pos(Uxn *u, Uint8 *d, Uint16 x, Uint16 y)
{ {
*(d + 2) = x >> 8; *(d + 2) = x >> 8, *(d + 3) = x;
*(d + 3) = x; *(d + 4) = y >> 8, *(d + 5) = y;
*(d + 4) = y >> 8;
*(d + 5) = y;
uxn_eval(u, PEEK2(d)); uxn_eval(u, PEEK2(d));
} }
void void
mouse_scroll(Uxn *u, Uint8 *d, Uint16 x, Uint16 y) mouse_scroll(Uxn *u, Uint8 *d, Uint16 x, Uint16 y)
{ {
*(d + 0xa) = x >> 8; *(d + 0xa) = x >> 8, *(d + 0xb) = x;
*(d + 0xb) = x; *(d + 0xc) = -y >> 8, *(d + 0xd) = -y;
*(d + 0xc) = -y >> 8;
*(d + 0xd) = -y;
uxn_eval(u, PEEK2(d)); uxn_eval(u, PEEK2(d));
*(d + 0xa) = 0; *(d + 0xa) = 0, *(d + 0xb) = 0;
*(d + 0xb) = 0; *(d + 0xc) = 0, *(d + 0xd) = 0;
*(d + 0xc) = 0;
*(d + 0xd) = 0;
} }

View File

@ -10,8 +10,6 @@ WITH REGARD TO THIS SOFTWARE.
*/ */
#define MOUSE_VERSION 1 #define MOUSE_VERSION 1
#define MOUSE_DEIMASK 0x0000
#define MOUSE_DEOMASK 0x0000
void mouse_down(Uxn *u, Uint8 *d, Uint8 mask); void mouse_down(Uxn *u, Uint8 *d, Uint8 mask);
void mouse_up(Uxn *u, Uint8 *d, Uint8 mask); void mouse_up(Uxn *u, Uint8 *d, Uint8 mask);

View File

@ -44,17 +44,12 @@ system_load(Uxn *u, char *filename)
} }
static void static void
system_print(Stack *s, char *name) system_print(Stack *s)
{ {
Uint8 i; Uint8 i;
fprintf(stderr, "%s ", name); for(i = s->ptr - 7; i != (Uint8)(s->ptr + 1); i++)
for(i = 0; i < 9; i++) { fprintf(stderr, "%02x%c", s->dat[i], i == 0 ? '|' : ' ');
Uint8 pos = s->ptr - 4 + i; fprintf(stderr, "< \n");
fprintf(stderr, !pos ? "[%02x]" : i == 4 ? "<%02x>" :
" %02x ",
s->dat[pos]);
}
fprintf(stderr, "\n");
} }
int int
@ -68,8 +63,8 @@ system_error(char *msg, const char *err)
void void
system_inspect(Uxn *u) system_inspect(Uxn *u)
{ {
system_print(&u->wst, "wst"); fprintf(stderr, "WST "), system_print(&u->wst);
system_print(&u->rst, "rst"); fprintf(stderr, "RST "), system_print(&u->rst);
} }
int int
@ -100,17 +95,6 @@ system_boot(Uxn *u, Uint8 *ram, char *rom)
return 1; return 1;
} }
static void
system_expansion_copy(Uxn *u, Uint8 *ram, Uint16 addr)
{
Uint16 i, length = PEEK2(ram + addr + 1);
Uint16 a_page = PEEK2(ram + addr + 1 + 2), a_addr = PEEK2(ram + addr + 1 + 4);
Uint16 b_page = PEEK2(ram + addr + 1 + 6), b_addr = PEEK2(ram + addr + 1 + 8);
int src = (a_page % RAM_PAGES) * 0x10000, dst = (b_page % RAM_PAGES) * 0x10000;
for(i = 0; i < length; i++)
ram[dst + (Uint16)(b_addr + i)] = ram[src + (Uint16)(a_addr + i)];
}
static int static int
uuid_eq(Uint8 *m, Uint8 uuid[16]) uuid_eq(Uint8 *m, Uint8 uuid[16])
{ {
@ -160,10 +144,31 @@ system_deo(Uxn *u, Uint8 *d, Uint8 port)
case 0x3: case 0x3:
ram = u->ram; ram = u->ram;
addr = PEEK2(d + 2); addr = PEEK2(d + 2);
if(ram[addr] == 0x1) if(ram[addr] == 0x0) {
system_expansion_copy(u, ram, addr); Uint8 value = ram[addr + 7];
else if(ram[addr] == 0x3) Uint16 i, length = PEEK2(ram + addr + 1);
Uint16 dst_page = PEEK2(ram + addr + 3), dst_addr = PEEK2(ram + addr + 5);
int dst = (dst_page % RAM_PAGES) * 0x10000;
for(i = 0; i < length; i++)
ram[dst + (Uint16)(dst_addr + i)] = value;
} else if(ram[addr] == 0x1) {
Uint16 i, length = PEEK2(ram + addr + 1);
Uint16 a_page = PEEK2(ram + addr + 3), a_addr = PEEK2(ram + addr + 5);
Uint16 b_page = PEEK2(ram + addr + 7), b_addr = PEEK2(ram + addr + 9);
int src = (a_page % RAM_PAGES) * 0x10000, dst = (b_page % RAM_PAGES) * 0x10000;
for(i = 0; i < length; i++)
ram[dst + (Uint16)(b_addr + i)] = ram[src + (Uint16)(a_addr + i)];
} else if(ram[addr] == 0x2) {
Uint16 i, length = PEEK2(ram + addr + 1);
Uint16 a_page = PEEK2(ram + addr + 3), a_addr = PEEK2(ram + addr + 5);
Uint16 b_page = PEEK2(ram + addr + 7), b_addr = PEEK2(ram + addr + 9);
int src = (a_page % RAM_PAGES) * 0x10000, dst = (b_page % RAM_PAGES) * 0x10000;
for(i = length - 1; i != 0xffff; i--)
ram[dst + (Uint16)(b_addr + i)] = ram[src + (Uint16)(a_addr + i)];
} else if(ram[addr] == 0x3)
system_expansion_uxn38_device(u, ram, addr); system_expansion_uxn38_device(u, ram, addr);
else
fprintf(stderr, "Unknown Expansion Command 0x%02x\n", ram[addr]);
break; break;
case 0x4: case 0x4:
u->wst.ptr = d[4]; u->wst.ptr = d[4];

View File

@ -164,7 +164,7 @@ emu_event(Uxn *u)
XPutImage(display, window, DefaultGC(display, 0), ximage, 0, 0, PAD, PAD, w, h); XPutImage(display, window, DefaultGC(display, 0), ximage, 0, 0, PAD, PAD, w, h);
} break; } break;
case ClientMessage: case ClientMessage:
emu_end(u); u->dev[0x0f] = 0x80;
break; break;
case KeyPress: { case KeyPress: {
KeySym sym; KeySym sym;
@ -191,6 +191,10 @@ emu_event(Uxn *u)
mouse_scroll(u, &u->dev[0x90], 0, 1); mouse_scroll(u, &u->dev[0x90], 0, 1);
else if(e->button == 5) else if(e->button == 5)
mouse_scroll(u, &u->dev[0x90], 0, -1); mouse_scroll(u, &u->dev[0x90], 0, -1);
else if(e->button == 6)
mouse_scroll(u, &u->dev[0x90], 1, 0);
else if(e->button == 7)
mouse_scroll(u, &u->dev[0x90], -1, 0);
else else
mouse_down(u, &u->dev[0x90], 0x1 << (e->button - 1)); mouse_down(u, &u->dev[0x90], 0x1 << (e->button - 1));
} break; } break;
@ -242,7 +246,7 @@ display_init(void)
} }
static int static int
emu_run(Uxn *u, char *rom) emu_run(Uxn *u)
{ {
int i = 1, n; int i = 1, n;
int *child_fd_outs; int *child_fd_outs;
@ -298,7 +302,7 @@ main(int argc, char **argv)
int i = 1; int i = 1;
char *rom; char *rom;
if(i != argc && argv[i][0] == '-' && argv[i][1] == 'v') { if(i != argc && argv[i][0] == '-' && argv[i][1] == 'v') {
fprintf(stdout, "Uxn11 - Varvara Emulator, 22 Feb 2023.\n"); fprintf(stdout, "Uxn11 - Varvara Emulator, 19 Mar 2024.\n");
i++; i++;
} }
rom = i == argc ? "boot.rom" : argv[i++]; rom = i == argc ? "boot.rom" : argv[i++];
@ -314,7 +318,7 @@ main(int argc, char **argv)
u.dev[0x17] = argc - i; u.dev[0x17] = argc - i;
if(uxn_eval(&u, PAGE_PROGRAM)) { if(uxn_eval(&u, PAGE_PROGRAM)) {
console_listen(&u, i, argc, argv); console_listen(&u, i, argc, argv);
emu_run(&u, boot_rom); emu_run(&u);
} }
return emu_end(&u); return emu_end(&u);
} }

View File

@ -1,7 +1,7 @@
#include <stdio.h> #include <stdio.h>
/* /*
Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick Copyright (c) 2021-2024 Devine Lu Linvega, Andrew Alderwick
Permission to use, copy, modify, and distribute this software for any Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@ -11,43 +11,25 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
#define TRIM 0x0100 /* clang-format off */
#define LENGTH 0x10000
#define PAGE 0x0100
typedef unsigned char Uint8; typedef unsigned char Uint8;
typedef signed char Sint8; typedef signed char Sint8;
typedef unsigned short Uint16; typedef unsigned short Uint16;
typedef struct { int line; char *path; } Context;
typedef struct { char *name, rune, *data; Uint16 addr, refs, line; } Item;
typedef struct { static int ptr, length;
char name[0x40], items[0x40][0x40]; static char token[0x40], scope[0x40], lambda[0x05];
Uint8 len; static char dict[0x8000], *dictnext = dict;
} Macro; static Uint8 data[0x10000], lambda_stack[0x100], lambda_ptr, lambda_len;
static Uint16 labels_len, refs_len, macro_len;
typedef struct { static Item labels[0x400], refs[0x1000], macros[0x100];
char name[0x40];
Uint16 addr, refs;
} Label;
typedef struct {
char name[0x40], rune;
Uint16 addr;
} Reference;
typedef struct {
Uint8 data[LENGTH];
Uint8 lambda_stack[0x100], lambda_ptr, lambda_count;
char scope[0x40], lambda[0x10];
unsigned int ptr, length;
Uint16 label_len, macro_len, refs_len;
Label labels[0x400];
Macro macros[0x100];
Reference refs[0x800];
} Program;
Program p;
/* clang-format off */
static char *runes = "|$@&,_.-;=!?#\"%~";
static char *hexad = "0123456789abcdef";
static char ops[][4] = { static char ops[][4] = {
"LIT", "INC", "POP", "NIP", "SWP", "ROT", "DUP", "OVR", "LIT", "INC", "POP", "NIP", "SWP", "ROT", "DUP", "OVR",
"EQU", "NEQ", "GTH", "LTH", "JMP", "JCN", "JSR", "STH", "EQU", "NEQ", "GTH", "LTH", "JMP", "JCN", "JSR", "STH",
@ -55,54 +37,48 @@ static char ops[][4] = {
"ADD", "SUB", "MUL", "DIV", "AND", "ORA", "EOR", "SFT" "ADD", "SUB", "MUL", "DIV", "AND", "ORA", "EOR", "SFT"
}; };
static char *runes = "|$@&,_.-;=!?#\"%~"; static int find(char *s, char t) { int i = 0; char c; while((c = *s++)) { if(c == t) return i; i++; } return -1; }
static int shex(char *s) { int d, n = 0; char c; while((c = *s++)) { d = find(hexad, c); if(d < 0) return d; n = n << 4, n |= d; } return n; }
static int scmp(char *a, char *b, int len) { int i = 0; while(a[i] == b[i]) if(!a[i] || ++i >= len) return 1; return 0; }
static char *copy(char *src, char *dst, char c) { while(*src && *src != c) *dst++ = *src++; *dst++ = 0; return dst; }
static char *save(char *s, char c) { char *o = dictnext; while((*dictnext++ = *s++) && *s); *dictnext++ = c; return o; }
static char *join(char *a, char j, char *b) { char *res = dictnext; save(a, j), save(b, 0); return res; }
static int scmp(char *a, char *b, int len) { int i = 0; while(a[i] == b[i]) if(!a[i] || ++i >= len) return 1; return 0; } /* string compare */ #define ishex(x) (shex(x) >= 0)
static int sihx(char *s) { int i = 0; char c; while((c = s[i++])) if(!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f')) return 0; return i > 1; } /* string is hexadecimal */ #define isopc(x) (findopcode(x) || scmp(x, "BRK", 4))
static int shex(char *s) { int n = 0, i = 0; char c; while((c = s[i++])) if(c >= '0' && c <= '9') n = n * 16 + (c - '0'); else if(c >= 'a' && c <= 'f') n = n * 16 + 10 + (c - 'a'); return n; } /* string to num */ #define isinvalid(x) (!x[0] || ishex(x) || isopc(x) || find(runes, x[0]) >= 0)
static int slen(char *s) { int i = 0; while(s[i]) i++; return i; } /* string length */ #define writeshort(x) (writebyte(x >> 8, ctx) && writebyte(x & 0xff, ctx))
static int spos(char *s, char c) { Uint8 i = 0, j; while((j = s[i++])) if(j == c) return i; return -1; } /* character position */ #define findlabel(x) finditem(x, labels, labels_len)
static char *scpy(char *src, char *dst, int len) { int i = 0; while((dst[i] = src[i]) && i < len - 2) i++; dst[i + 1] = '\0'; return dst; } /* string copy */ #define findmacro(x) finditem(x, macros, macro_len)
static char *scat(char *dst, const char *src) { char *ptr = dst + slen(dst); while(*src) *ptr++ = *src++; *ptr = '\0'; return dst; } /* string cat */ #define error_top(id, msg) !printf("%s: %s\n", id, msg)
#define error_asm(id) !printf("%s: %s in @%s, %s:%d.\n", id, token, scope, ctx->path, ctx->line)
#define error_ref(id) !printf("%s: %s, %s:%d\n", id, r->name, r->data, r->line)
/* clang-format on */ /* clang-format on */
static int parse(char *w, FILE *f); static int parse(char *w, FILE *f, Context *ctx);
static int
error(const char *name, const char *msg)
{
fprintf(stderr, "%s: %s\n", name, msg);
return 0;
}
static char * static char *
sublabel(char *src, char *scope, char *name) push(char *s, char c)
{ {
if(slen(scope) + slen(name) >= 0x3f) { char *d = dict;
error("Sublabel length too long", name); for(d = dict; d < dictnext; d++) {
return NULL; char *ss = s, *dd = d, a, b;
while((a = *dd++) == (b = *ss++))
if(!a && !b) return d;
} }
return scat(scat(scpy(scope, src, 0x40), "/"), name); return save(s, c);
} }
static Macro * static Item *
findmacro(char *name) finditem(char *name, Item *list, int len)
{ {
int i; int i;
for(i = 0; i < p.macro_len; i++) if(name[0] == '&')
if(scmp(p.macros[i].name, name, 0x40)) name = join(scope, '/', name + 1);
return &p.macros[i]; for(i = 0; i < len; i++)
return NULL; if(scmp(list[i].name, name, 0x40))
} return &list[i];
static Label *
findlabel(char *name)
{
int i;
for(i = 0; i < p.label_len; i++)
if(scmp(p.labels[i].name, name, 0x40))
return &p.labels[i];
return NULL; return NULL;
} }
@ -111,19 +87,18 @@ findopcode(char *s)
{ {
int i; int i;
for(i = 0; i < 0x20; i++) { for(i = 0; i < 0x20; i++) {
int m = 0; int m = 3;
if(!scmp(ops[i], s, 3)) if(!scmp(ops[i], s, 3)) continue;
continue; if(!i) i |= (1 << 7);
if(!i) i |= (1 << 7); /* force keep for LIT */ while(s[m]) {
while(s[3 + m]) { if(s[m] == '2')
if(s[3 + m] == '2') i |= (1 << 5);
i |= (1 << 5); /* mode: short */ else if(s[m] == 'r')
else if(s[3 + m] == 'r') i |= (1 << 6);
i |= (1 << 6); /* mode: return */ else if(s[m] == 'k')
else if(s[3 + m] == 'k') i |= (1 << 7);
i |= (1 << 7); /* mode: keep */
else else
return 0; /* failed to match */ return 0;
m++; m++;
} }
return i; return i;
@ -132,402 +107,312 @@ findopcode(char *s)
} }
static int static int
makemacro(char *name, FILE *f) walkcomment(FILE *f, Context *ctx)
{ {
Macro *m; char c;
char word[0x40]; int depth = 1;
if(findmacro(name)) while(f && fread(&c, 1, 1, f)) {
return error("Macro duplicate", name); if(c == 0xa) ctx->line++;
if(sihx(name) && slen(name) % 2 == 0) if(c == '(') depth++;
return error("Macro name is hex number", name); if(c == ')' && --depth < 1) return 1;
if(findopcode(name) || scmp(name, "BRK", 4) || !slen(name)) }
return error("Macro name is invalid", name); return error_asm("Comment incomplete");
if(p.macro_len == 0x100) }
return error("Macros limit exceeded", name);
m = &p.macros[p.macro_len++]; static int
scpy(name, m->name, 0x40); walkmacro(Item *m, Context *ctx)
while(fscanf(f, "%63s", word) == 1) { {
if(word[0] == '{') continue; unsigned char c;
if(word[0] == '}') break; char *dataptr = m->data, *cptr = token;
if(word[0] == '%') while((c = *dataptr++)) {
return error("Macro error", name); if(c < 0x21) {
if(m->len >= 0x40) *cptr++ = 0x00;
return error("Macro size exceeded", name); if(token[0] && !parse(token, NULL, ctx)) return 0;
scpy(word, m->items[m->len++], 0x40); cptr = token;
} else
*cptr++ = c;
} }
return 1; return 1;
} }
static int static int
isrune(char c) walkfile(FILE *f, Context *ctx)
{ {
char cc, *r = runes; unsigned char c;
while((cc = *r++)) char *cptr = token;
if(c == cc) return 1; while(f && fread(&c, 1, 1, f)) {
return 0; if(c < 0x21) {
*cptr++ = 0x00;
if(token[0] && !parse(token, f, ctx)) return 0;
if(c == 0xa) ctx->line++;
cptr = token;
} else if(cptr - token < 0x3f)
*cptr++ = c;
else
return error_asm("Token too long");
} }
*cptr++ = 0;
static int return parse(token, f, ctx);
makelabel(char *name)
{
Label *l;
if(findlabel(name))
return error("Label duplicate", name);
if(sihx(name) && (slen(name) == 2 || slen(name) == 4))
return error("Label name is hex number", name);
if(findopcode(name) || scmp(name, "BRK", 4) || !slen(name))
return error("Label name is invalid", name);
if(isrune(name[0]))
return error("Label name is runic", name);
if(p.label_len == 0x400)
return error("Labels limit exceeded", name);
l = &p.labels[p.label_len++];
l->addr = p.ptr;
l->refs = 0;
scpy(name, l->name, 0x40);
return 1;
} }
static char * static char *
makelambda(int id) makelambda(int id)
{ {
scpy("lambda", p.lambda, 0x07); lambda[0] = (char)0xce;
p.lambda[6] = '0' + (id >> 0x4); lambda[1] = (char)0xbb;
p.lambda[7] = '0' + (id & 0xf); lambda[2] = hexad[id >> 0x4];
return p.lambda; lambda[3] = hexad[id & 0xf];
return lambda;
} }
static int static int
makereference(char *scope, char *label, char rune, Uint16 addr) makemacro(char *name, FILE *f, Context *ctx)
{ {
char subw[0x40], parent[0x40]; int depth = 0;
Reference *r; char c;
if(p.refs_len >= 0x800) Item *m;
return error("References limit exceeded", label); if(macro_len >= 0x100) return error_asm("Macros limit exceeded");
r = &p.refs[p.refs_len++]; if(isinvalid(name)) return error_asm("Macro is invalid");
if(findmacro(name)) return error_asm("Macro is duplicate");
m = &macros[macro_len++];
m->name = push(name, 0);
m->data = dictnext;
while(f && fread(&c, 1, 1, f) && c != '{')
if(c == 0xa) ctx->line++;
while(f && fread(&c, 1, 1, f)) {
if(c == 0xa) ctx->line++;
if(c == '%') return error_asm("Macro nested");
if(c == '{') depth++;
if(c == '}' && --depth) break;
if(c == '(' && !walkcomment(f, ctx))
return 0;
else
*dictnext++ = c;
}
*dictnext++ = 0;
return 1;
}
static int
makelabel(char *name, int setscope, Context *ctx)
{
Item *l;
if(name[0] == '&')
name = join(scope, '/', name + 1);
if(labels_len >= 0x400) return error_asm("Labels limit exceeded");
if(isinvalid(name)) return error_asm("Label invalid");
if(findlabel(name)) return error_asm("Label duplicate");
l = &labels[labels_len++];
l->name = push(name, 0);
l->addr = ptr;
l->refs = 0;
if(setscope) copy(name, scope, '/');
return 1;
}
static int
makeref(char *label, char rune, Uint16 addr, Context *ctx)
{
Item *r;
if(refs_len >= 0x1000) return error_asm("References limit exceeded");
r = &refs[refs_len++];
if(label[0] == '{') { if(label[0] == '{') {
p.lambda_stack[p.lambda_ptr++] = p.lambda_count; lambda_stack[lambda_ptr++] = lambda_len;
scpy(makelambda(p.lambda_count++), r->name, 0x40); r->name = push(makelambda(lambda_len++), 0);
} else if(label[0] == '&' || label[0] == '/') { } else if(label[0] == '&' || label[0] == '/') {
if(!sublabel(subw, scope, label + 1)) r->name = join(scope, '/', label + 1);
return error("Invalid sublabel", label); } else
scpy(subw, r->name, 0x40); r->name = push(label, 0);
} else {
int pos = spos(label, '/');
if(pos > 0) {
Label *l;
if((l = findlabel(scpy(label, parent, pos))))
l->refs++;
}
scpy(label, r->name, 0x40);
}
r->rune = rune; r->rune = rune;
r->addr = addr; r->addr = addr;
r->line = ctx->line;
r->data = ctx->path;
return 1; return 1;
} }
static int static int
writebyte(Uint8 b) writepad(char *w, Context *ctx)
{ {
if(p.ptr < TRIM) Item *l;
return error("Writing in zero-page", ""); int rel = w[0] == '$' ? ptr : 0;
else if(p.ptr > 0xffff) if(ishex(w + 1)) {
return error("Writing after the end of RAM", ""); ptr = shex(w + 1) + rel;
else if(p.ptr < p.length) return 1;
return error("Memory overwrite", ""); }
p.data[p.ptr++] = b; if((l = findlabel(w + 1))) {
p.length = p.ptr; ptr = l->addr + rel;
return 1;
}
return error_asm("Padding invalid");
}
static int
writebyte(Uint8 b, Context *ctx)
{
if(ptr < PAGE)
return error_asm("Writing zero-page");
else if(ptr >= 0x10000)
return error_asm("Writing outside memory");
else if(ptr < length)
return error_asm("Writing rewind");
data[ptr++] = b;
if(b)
length = ptr;
return 1; return 1;
} }
static int static int
writeopcode(char *w) writehex(char *w, Context *ctx)
{ {
return writebyte(findopcode(w)); if(*w == '#')
writebyte(findopcode("LIT") | !!(++w)[2] << 5, ctx);
if(w[1] && !w[2])
return writebyte(shex(w), ctx);
else if(w[3] && !w[4])
return writeshort(shex(w));
else
return error_asm("Hexadecimal invalid");
} }
static int static int
writeshort(Uint16 s, int lit) writestring(char *w, Context *ctx)
{ {
if(lit) char c;
if(!writebyte(findopcode("LIT2"))) return 0; while((c = *(w++)))
return writebyte(s >> 8) && writebyte(s & 0xff); if(!writebyte(c, ctx)) return error_asm("String invalid");
return 1;
} }
static int static int
writelitbyte(Uint8 b) assemble(char *filename)
{
return writebyte(findopcode("LIT")) && writebyte(b);
}
static int
doinclude(const char *filename)
{ {
FILE *f; FILE *f;
char w[0x40]; int res = 0;
Context ctx;
ctx.line = 1;
ctx.path = push(filename, 0);
if(!(f = fopen(filename, "r"))) if(!(f = fopen(filename, "r")))
return error("Include missing", filename); return error_top("Source missing", filename);
while(fscanf(f, "%63s", w) == 1) res = walkfile(f, &ctx);
if(!parse(w, f))
return error("Unknown token", w);
fclose(f); fclose(f);
return 1; return res;
} }
static int static int
parse(char *w, FILE *f) parse(char *w, FILE *f, Context *ctx)
{ {
int i; Item *m;
char word[0x40], subw[0x40], c;
Label *l;
Macro *m;
if(slen(w) >= 63)
return error("Invalid token", w);
switch(w[0]) { switch(w[0]) {
case '(': /* comment */ case 0x0: return 1;
if(slen(w) != 1) fprintf(stderr, "-- Malformed comment: %s\n", w); case '(': return walkcomment(f, ctx);
i = 1; /* track nested comment depth */ case '%': return makemacro(w + 1, f, ctx);
while(fscanf(f, "%63s", word) == 1) { case '@': return makelabel(w + 1, 1, ctx);
if(slen(word) != 1) case '&': return makelabel(w, 0, ctx);
continue; case '}': return makelabel(makelambda(lambda_stack[--lambda_ptr]), 0, ctx);
else if(word[0] == '(') case '#': return writehex(w, ctx);
i++; case '_': return makeref(w + 1, w[0], ptr, ctx) && writebyte(0xff, ctx);
else if(word[0] == ')' && --i < 1) case ',': return makeref(w + 1, w[0], ptr + 1, ctx) && writebyte(findopcode("LIT"), ctx) && writebyte(0xff, ctx);
break; case '-': return makeref(w + 1, w[0], ptr, ctx) && writebyte(0xff, ctx);
} case '.': return makeref(w + 1, w[0], ptr + 1, ctx) && writebyte(findopcode("LIT"), ctx) && writebyte(0xff, ctx);
break; case ':': printf("Deprecated rune %s, use =%s\n", w, w + 1); /* fall-through */
case '~': /* include */ case '=': return makeref(w + 1, w[0], ptr, ctx) && writeshort(0xffff);
if(!doinclude(w + 1)) case ';': return makeref(w + 1, w[0], ptr + 1, ctx) && writebyte(findopcode("LIT2"), ctx) && writeshort(0xffff);
return error("Invalid include", w); case '?': return makeref(w + 1, w[0], ptr + 1, ctx) && writebyte(0x20, ctx) && writeshort(0xffff);
break; case '!': return makeref(w + 1, w[0], ptr + 1, ctx) && writebyte(0x40, ctx) && writeshort(0xffff);
case '%': /* macro */ case '"': return writestring(w + 1, ctx);
if(!makemacro(w + 1, f)) case '~': return !assemble(w + 1) ? error_asm("Include missing") : 1;
return error("Invalid macro", w); case '$':
break; case '|': return writepad(w, ctx);
case '|': /* pad-absolute */
if(sihx(w + 1))
p.ptr = shex(w + 1);
else if(w[1] == '&') {
if(!sublabel(subw, p.scope, w + 2) || !(l = findlabel(subw)))
return error("Invalid sublabel", w);
p.ptr = l->addr;
} else {
if(!(l = findlabel(w + 1)))
return error("Invalid label", w);
p.ptr = l->addr;
}
break;
case '$': /* pad-relative */
if(sihx(w + 1))
p.ptr += shex(w + 1);
else if(w[1] == '&') {
if(!sublabel(subw, p.scope, w + 2) || !(l = findlabel(subw)))
return error("Invalid sublabel", w);
p.ptr += l->addr;
} else {
if(!(l = findlabel(w + 1)))
return error("Invalid label", w);
p.ptr += l->addr;
}
break;
case '@': /* label */
if(!makelabel(w + 1))
return error("Invalid label", w);
i = 0;
while(w[i + 1] != '/' && i < 0x3e && (p.scope[i] = w[i + 1]))
i++;
p.scope[i] = '\0';
break;
case '&': /* sublabel */
if(!sublabel(subw, p.scope, w + 1) || !makelabel(subw))
return error("Invalid sublabel", w);
break;
case '#': /* literals hex */
if(sihx(w + 1) && slen(w) == 3)
return writelitbyte(shex(w + 1));
else if(sihx(w + 1) && slen(w) == 5)
return writeshort(shex(w + 1), 1);
else
return error("Invalid hex literal", w);
break;
case '_': /* raw byte relative */
makereference(p.scope, w + 1, w[0], p.ptr);
return writebyte(0xff);
case ',': /* literal byte relative */
makereference(p.scope, w + 1, w[0], p.ptr + 1);
return writelitbyte(0xff);
case '-': /* raw byte absolute */
makereference(p.scope, w + 1, w[0], p.ptr);
return writebyte(0xff);
case '.': /* literal byte zero-page */
makereference(p.scope, w + 1, w[0], p.ptr + 1);
return writelitbyte(0xff);
case '=': /* raw short absolute */
makereference(p.scope, w + 1, w[0], p.ptr);
return writeshort(0xffff, 0);
case ';': /* literal short absolute */
makereference(p.scope, w + 1, w[0], p.ptr + 1);
return writeshort(0xffff, 1);
case '?': /* JCI */
makereference(p.scope, w + 1, w[0], p.ptr + 1);
return writebyte(0x20) && writeshort(0xffff, 0);
case '!': /* JMI */
makereference(p.scope, w + 1, w[0], p.ptr + 1);
return writebyte(0x40) && writeshort(0xffff, 0);
case '"': /* raw string */
i = 0;
while((c = w[++i]))
if(!writebyte(c)) return 0;
break;
case '}': /* lambda end */
if(!makelabel(makelambda(p.lambda_stack[--p.lambda_ptr])))
return error("Invalid label", w);
break;
case '[': case '[':
case ']': case ']': return 1;
if(slen(w) == 1) break; /* else fallthrough */
default:
/* opcode */
if(findopcode(w) || scmp(w, "BRK", 4))
return writeopcode(w);
/* raw byte */
else if(sihx(w) && slen(w) == 2)
return writebyte(shex(w));
/* raw short */
else if(sihx(w) && slen(w) == 4)
return writeshort(shex(w), 0);
/* macro */
else if((m = findmacro(w))) {
for(i = 0; i < m->len; i++)
if(!parse(m->items[i], f))
return 0;
return 1;
} else {
makereference(p.scope, w, ' ', p.ptr + 1);
return writebyte(0x60) && writeshort(0xffff, 0);
} }
} if(ishex(w)) return writehex(w, ctx);
return 1; if(isopc(w)) return writebyte(findopcode(w), ctx);
if((m = findmacro(w))) return walkmacro(m, ctx);
return makeref(w, ' ', ptr + 1, ctx) && writebyte(0x60, ctx) && writeshort(0xffff);
} }
static int static int
resolve(void) resolve(char *filename)
{ {
Label *l; int i, rel;
int i; if(!length) return error_top("Output empty", filename);
Uint16 a; for(i = 0; i < refs_len; i++) {
for(i = 0; i < p.refs_len; i++) { Item *r = &refs[i], *l = findlabel(r->name);
Reference *r = &p.refs[i]; Uint8 *rom = data + r->addr;
if(!l) return error_ref("Label unknown");
switch(r->rune) { switch(r->rune) {
case '_': case '_':
case ',': case ',':
if(!(l = findlabel(r->name))) *rom = rel = l->addr - r->addr - 2;
return error("Unknown relative reference", r->name); if((Sint8)data[r->addr] != rel)
p.data[r->addr] = (Sint8)(l->addr - r->addr - 2); return error_ref("Relative reference too far");
if((Sint8)p.data[r->addr] != (l->addr - r->addr - 2))
return error("Relative reference is too far", r->name);
l->refs++;
break; break;
case '-': case '-':
case '.': case '.':
if(!(l = findlabel(r->name))) *rom = l->addr;
return error("Unknown zero-page reference", r->name);
p.data[r->addr] = l->addr & 0xff;
l->refs++;
break; break;
case ':':
case '=': case '=':
case ';': case ';':
if(!(l = findlabel(r->name))) *rom++ = l->addr >> 8, *rom = l->addr;
return error("Unknown absolute reference", r->name);
p.data[r->addr] = l->addr >> 0x8;
p.data[r->addr + 1] = l->addr & 0xff;
l->refs++;
break; break;
case '?': case '?':
case '!': case '!':
default: default:
if(!(l = findlabel(r->name))) rel = l->addr - r->addr - 2;
return error("Unknown absolute reference", r->name); *rom++ = rel >> 8, *rom = rel;
a = l->addr - r->addr - 2;
p.data[r->addr] = a >> 0x8;
p.data[r->addr + 1] = a & 0xff;
l->refs++;
break; break;
} }
l->refs++;
} }
return 1; return 1;
} }
static int static int
assemble(FILE *f) build(char *rompath)
{
char w[0x40];
p.ptr = 0x100;
scpy("on-reset", p.scope, 0x40);
while(fscanf(f, "%62s", w) == 1)
if(slen(w) > 0x3d || !parse(w, f))
return error("Invalid token", w);
return resolve();
}
static void
review(char *filename)
{ {
int i; int i;
for(i = 0; i < p.label_len; i++) FILE *dst, *dstsym;
if(p.labels[i].name[0] >= 'A' && p.labels[i].name[0] <= 'Z') char *sympath = join(rompath, '.', "sym");
continue; /* Ignore capitalized labels(devices) */ /* rom */
else if(!p.labels[i].refs) if(!(dst = fopen(rompath, "wb")))
fprintf(stderr, "-- Unused label: %s\n", p.labels[i].name); return !error_top("Output file invalid", rompath);
fprintf(stderr, for(i = 0; i < labels_len; i++)
if(labels[i].name[0] - 'A' > 25 && !labels[i].refs)
printf("-- Unused label: %s\n", labels[i].name);
fwrite(data + PAGE, length - PAGE, 1, dst);
printf(
"Assembled %s in %d bytes(%.2f%% used), %d labels, %d macros.\n", "Assembled %s in %d bytes(%.2f%% used), %d labels, %d macros.\n",
filename, rompath,
p.length - TRIM, length - PAGE,
(p.length - TRIM) / 652.80, (length - PAGE) / 652.80,
p.label_len, labels_len,
p.macro_len); macro_len);
/* sym */
if(!(dstsym = fopen(sympath, "w")))
return !error_top("Symbols file invalid", sympath);
for(i = 0; i < labels_len; i++) {
Uint8 hb = labels[i].addr >> 8, lb = labels[i].addr;
char c, d = 0, *name = labels[i].name;
fwrite(&hb, 1, 1, dstsym);
fwrite(&lb, 1, 1, dstsym);
while((c = *name++)) fwrite(&c, 1, 1, dstsym);
fwrite(&d, 1, 1, dstsym);
} }
fclose(dst), fclose(dstsym);
static void return 1;
writesym(char *filename)
{
int i;
char symdst[0x60];
FILE *fp;
if(slen(filename) > 0x60 - 5)
return;
fp = fopen(scat(scpy(filename, symdst, slen(filename) + 1), ".sym"), "w");
if(fp != NULL) {
for(i = 0; i < p.label_len; i++) {
Uint8 hb = p.labels[i].addr >> 8, lb = p.labels[i].addr & 0xff;
fwrite(&hb, 1, 1, fp);
fwrite(&lb, 1, 1, fp);
fwrite(p.labels[i].name, slen(p.labels[i].name) + 1, 1, fp);
}
}
fclose(fp);
} }
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
FILE *src, *dst; ptr = PAGE;
if(argc == 1) copy("on-reset", scope, 0);
return error("usage", "uxnasm [-v] input.tal output.rom"); if(argc == 2 && scmp(argv[1], "-v", 2)) return !printf("Uxnasm - Uxntal Assembler, 13 Apr 2024.\n");
if(argv[1][0] == '-' && argv[1][1] == 'v') if(argc != 3) return error_top("usage", "uxnasm [-v] input.tal output.rom");
return !fprintf(stdout, "Uxnasm - Uxntal Assembler, 25 Feb 2024.\n"); if(!assemble(argv[1])) return 1;
if(!(src = fopen(argv[1], "r"))) if(!resolve(argv[2])) return 1;
return !error("Invalid input", argv[1]); if(!build(argv[2])) return 1;
if(!assemble(src))
return !error("Assembly", "Failed to assemble rom.");
if(!(dst = fopen(argv[2], "wb")))
return !error("Invalid Output", argv[2]);
if(p.length <= TRIM)
return !error("Assembly", "Output rom is empty.");
fwrite(p.data + TRIM, p.length - TRIM, 1, dst);
if(!scmp(argv[2], "-", 2)) {
review(argv[2]);
writesym(argv[2]);
}
return 0; return 0;
} }

View File

@ -117,7 +117,7 @@ main(int argc, char **argv)
return system_error("usage", "uxncli [-v] file.rom [args..]"); return system_error("usage", "uxncli [-v] file.rom [args..]");
/* Read flags */ /* Read flags */
if(argv[i][0] == '-' && argv[i][1] == 'v') if(argv[i][0] == '-' && argv[i][1] == 'v')
return system_version("Uxncli - Console Varvara Emulator", "15 Feb 2024"); return system_version("Uxncli - Console Varvara Emulator", "18 Mar 2024");
if(!system_boot(&u, (Uint8 *)calloc(0x10000 * RAM_PAGES, sizeof(Uint8)), argv[i++])) if(!system_boot(&u, (Uint8 *)calloc(0x10000 * RAM_PAGES, sizeof(Uint8)), argv[i++]))
return system_error("Init", "Failed to initialize uxn."); return system_error("Init", "Failed to initialize uxn.");
/* Game Loop */ /* Game Loop */