diff --git a/source/x11-mult-ilo.c b/source/x11-mult-ilo.c index e6c42ce..d494209 100644 --- a/source/x11-mult-ilo.c +++ b/source/x11-mult-ilo.c @@ -6,63 +6,134 @@ |_| |_| |_|\__,_|_|\__/_/ |_|_|\___/_/ /_/\_\ (c) charles childers - This is a special implementation of my ilo computer, with many - copies of ilo running under a single process. Under this you - can quickly switch between ilo instances. + This is a special implementation of my ilo computer, with + multiple copies of ilo running under a single host process. + Using this you can quickly switch between ilo instances. + + User Interface + + This implementation runs on X11 and provides a visual display + of the terminal with a set of buttons below the display. These + can be used to switch between ilo instances. Closing the window + will terminate the system. + + Keyboard Shortcuts + + +--------+-----------------------------+ + | CTRL+n | Switch to the next ilo | + | CTRL+p | Switch to the previous ilo | + | CTRL+c | Restart the current ilo | + | CTRL+a | Shut down the system | + +--------+-----------------------------+ + + Additional Functionality + + This implementation supports the basic Graphica (Level 0) and + pointer extensions. These are assigned to I/O devices 12 & 13. + + Notes - `bye` will act like `restart` on the current computer - - ctrl+c restarts the current computer - - ctrl+n switches to the next ilo - - ctrl+p switches to the previous ilo - - ctrl+a shuts down mult/ilo - switching can only be done when the system is waiting for input + Building + + I use this to build it: + + cc -O2 -s `pkg-config --cflags --libs x11` mix.c -o mix + + It'll probably also work with -O3 and might be slightly faster + **************************************************************/ +/* Configuration ============================================ */ + +#define BLOCKS "ilo.blocks" /* default data storage */ +#define ROM "ilo.rom" /* default ilo rom */ +#define FONT "ilo.fnt" /* default font */ + +#define FONT_H 16 /* font height */ +#define FONT_W 8 /* font width */ + + /* colors are 0xRRGGBB */ +#define FG 0xFFC000 /* foreground color, amber */ +#define BG 0x000000 /* background color, black */ +#define CURSOR 0x663999 /* cursor color, purple */ + +#define FW 640 /* terminal width */ +#define FH 384 /* terminal height */ + +#define BUTTON_HEIGHT 30 /* height for button row */ + +#define CENTER_WINDOW /* center window on start */ + +#define ILOS 3 /* number of instances */ +char *ButtonLabels[ILOS] = {"Instance 0", "Instance 1", + "Instance 2"}; + +/* ========================================================== */ + +#include +#include #include #include -#include +#include #include -#include #include #include -#define BLOCKS "ilo.blocks" -#define ROM "ilo.rom" -#define FONT "ilo.fnt" -#define FG 0xFFFFFF -#define BG 0x000000 -/* Orange = 0xFFAA00, Red = 0xFF0000, Cyan = 0x00FFFF */ -#define CURSOR 0xFFAA00 -#define FW 640 -#define FH 384 -#define FONT_H 16 -#define FONT_W 8 - #define V void #define I int #define C char -#define ILOS 3 +#define DS v->ds /* data stack */ +#define AS v->as /* address stack pointer */ +#define SP v->sp /* data stack pointer */ +#define RP v->rp /* address ("return") stack pointer */ +#define IP v->ip /* instruction pointer */ +#define M v->m /* memory */ +#define T DS[SP] /* top of data stack */ +#define N DS[SP-1] /* next on data stack */ +#define R AS[RP] /* top of address stack */ +#define A v->a /* internal use by instructions */ +#define B v->b /* internal use by instructions */ +#define S v->s /* internal use by instructions */ +#define D v->d /* internal use by instructions */ +#define L v->l /* internal use by instructions */ +#define F v->f /* used to track block file pointer */ +#define RESTART iof(v) /* restart ilo instance on error */ +#define OK v->ok /* ok to process further instructions? */ + +#define FAILED if (F == -1) + +#define CTRL_A 1 /* CTRL+a, shut down mult/ilo/x */ +#define CTRL_C 3 /* CTRL+c, restart instance */ +#define CTRL_N 14 /* CTRL+n, switch to next instance */ +#define CTRL_P 16 /* CTRL+p, switch to prior instance */ + +#define ROWS ((FH/FONT_H) - 1) +#define COLS ((FW/FONT_W) - 1) typedef struct ilo ilo; - struct ilo { - I ip, /* instruction pointer */ - sp, /* data stack pointer */ - rp, /* address stack pointer */ - ds[33], /* data stack */ - as[257], /* address stack */ - m[65536]; /* memory */ - C *blocks, /* name of block file (ilo.blocks) */ - *rom; /* name of image (ilo.rom) */ + I ip, /* instruction pointer */ + sp, /* data stack pointer */ + rp, /* address stack pointer */ + ds[33], /* data stack */ + as[257], /* address stack */ + m[65536]; /* memory */ + C *blocks, /* name of block file (ilo.blocks) */ + *rom; /* name of image (ilo.rom) */ I a, b, f, s, d, l, ok; C i[1]; }; +struct ilo systems[ILOS]; /* ilo session data */ +I cur; /* track current session */ -#define TERM_W (FW / FONT_W) -#define TERM_H (FH / FONT_H) +#define BUTTON_WIDTH (FW / ILOS) +#define WINDOW_HEIGHT (FH + BUTTON_HEIGHT) +#define WINDOW_WIDTH (FW) I frame[(FW * FH)/32]; /* frame buffer for display */ I font[4096]; /* font bitmap */ @@ -76,73 +147,120 @@ Display *disp; Window win; GC gc; XEvent event; -C text[255]; +Window Buttons[ILOS]; + +V update_labels() { + unsigned long white; + white = WhitePixel(disp, DefaultScreen(disp)); + XSetForeground(disp, gc, white); + for (I i = 0; i < ILOS; i++) { + XClearWindow(disp, Buttons[i]); + XDrawString(disp, Buttons[i], gc, 20, 20, ButtonLabels[i], + strlen(ButtonLabels[i])); + if (cur == i) { + XDrawString(disp, Buttons[i], gc, 10, 20, "*", 1); + } +// XSetForeground(disp, gc, white); + XDrawLine(disp, Buttons[i], gc, 0, 0, FW, 0); + } +} V dputc(I); +V term_putc(I c); V redraw(); -struct ilo systems[ILOS]; - -I cur; - -#define DS v->ds -#define AS v->as -#define SP v->sp -#define RP v->rp -#define IP v->ip -#define M v->m -#define A v->a -#define B v->b -#define S v->s -#define D v->d -#define L v->l -#define T DS[SP] /* Top of Data Stack */ -#define N DS[SP-1] /* Next on Data Stack */ -#define R AS[RP] /* Top of Address Stack */ -#define F v->f - V iof(ilo *); -V p(char *s) { while(*s) dputc(*s++); } -V e(ilo *v, char *s) { p("E:"); p(s); p("\n"); v->ok = 1; iof(v); } +V p(C *s) { while(*s) dputc(*s++); } +V e(ilo *v, C *s) { p("E:"); p(s); p("\n"); OK = 1; RESTART; } V dso(ilo *v) { if ((SP + 1) > 32) { e(v, "DSO"); } } V dsu(ilo *v) { if ((SP - 1) < 0) { e(v, "DSU"); } } V rso(ilo *v) { if ((RP + 1) > 256) { e(v, "RSO"); } } V rsu(ilo *v) { if ((RP - 1) < 0) { e(v, "RSU"); } } V dbz(ilo *v) { if (T == 0) { e(v, "DBZ"); } } V mem(ilo *v) { if (T < 0 || T > 65535) { e(v, "MEM"); } } -V uli(ilo *v) { if (F == -1) { e(v, "ULI"); } } -V ulf(ilo *v) { if (F == -1) { e(v, "ULF"); } } -V usi(ilo *v) { if (F == -1) { e(v, "URI"); } } -V urb(ilo *v) { if (F == -1) { e(v, "URB"); } } -V uwb(ilo *v) { if (F == -1) { e(v, "UWB"); } } +V uli(ilo *v) { e(v, "ULI"); } +V ulf(ilo *v) { e(v, "ULF"); } +V usi(ilo *v) { e(v, "URI"); } +V urb(ilo *v) { e(v, "URB"); } +V uwb(ilo *v) { e(v, "UWB"); } V push(ilo *v, I n) { dso(v); DS[SP + 1] = n; SP += 1; } I pop(ilo *v) { dsu(v); SP -= 1; return DS[SP + 1]; } +/* display & terminal emulation ============================= */ + +V add_buttons() { + for (I i = 0; i < ILOS; i++) { + Buttons[i] = XCreateSimpleWindow(disp, win, i*BUTTON_WIDTH, + FH, BUTTON_WIDTH, + BUTTON_HEIGHT, 1, 0, 0); + XSelectInput(disp, Buttons[i], + ButtonPressMask | ExposureMask); + XMapWindow(disp, Buttons[i]); + } +} + +V initialize_display() { + disp = XOpenDisplay(NULL); + win = XCreateSimpleWindow(disp, RootWindow(disp, 0), + 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, + 0, 0x0, 0x0); + XSelectInput(disp, win, ExposureMask | KeyPressMask | + ButtonPressMask | ButtonReleaseMask); + XStoreName(disp, win, "mult/ilo"); + gc = DefaultGC(disp, DefaultScreen(disp)); + add_buttons(); + XMapWindow(disp, win); + XFlush(disp); +} + +V cleanup_display() { + XUnmapWindow(disp, win); + XDestroyWindow(disp, win); + XCloseDisplay(disp); +} + V cursor() { XSetForeground(disp, gc, CURSOR); XFillRectangle(disp, win, gc, tx, ty, FONT_W, FONT_H); XFlush(disp); } +V sel(I n) { cur = n; } + +I handle_keypress() { + C text[255]; + KeySym key = XLookupKeysym(&event.xkey, 0); + XLookupString(&event.xkey, text, 255, &key, 0); + if (text[0] == 13) text[0] = 10; + if (text[0] != 0) return (I) text[0]; + return 0; +} + +V handle_expose() { redraw(); update_labels(); cursor(); } + +I handle_button() { + for (I i = 0; i < ILOS; i++) { + if (event.xbutton.window == Buttons[i]) { sel(i); } + } + update_labels(); + return 32; +} + I wait_key() { - while (1) { - XNextEvent(disp, &event); - if (event.type == KeyPress) { - KeySym key = XLookupKeysym(&event.xkey, 0); - XLookupString(&event.xkey, text, 255, &key, 0); - if (text[0] == 13) text[0] = 10; - if (text[0] != 0) return (I) text[0]; - } - if (event.type == Expose) { redraw(); cursor(); } - } + I k = 0; + while (k == 0) { + XNextEvent(disp, &event); + if (event.type == KeyPress) { k = handle_keypress(); } + if (event.type == Expose) { handle_expose(); } + if (event.type == ButtonPress) { k = handle_button(); } + } + return k; } V load_font(ilo *v) { - F = open(FONT, O_RDONLY, 0666); - ulf(v); - if (!F) { return; }; + F = open(FONT, O_RDONLY, 0666); FAILED { ulf(v); return; } read(F, &font, 4096); close(F); } @@ -188,25 +306,32 @@ V scroll() { } unsigned C reverse(unsigned C b) { - b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; - b = (b & 0xCC) >> 2 | (b & 0x33) << 2; - b = (b & 0xAA) >> 1 | (b & 0x55) << 1; - return b; + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; } +V special_backspace() { + if (tx - FONT_W >= 0) { + tx -= FONT_W ; dputc(' '); tx -= FONT_W; + } +} + +V special_tab() { + I ts = tx / FONT_W; + for (I i = ts; i < ((ts + 7) / 8) * 8; i++) { dputc(' '); } +} + +V special_crlf() { ty += FONT_H; tx = 0; } + V handle_special_character(I c) { - if (c == 8 || c == 127) { /* BACKSPACE/DELETE */ - if (tx - FONT_W >= 0) { - tx -= FONT_W ; - dputc(' '); - tx -= FONT_W; - } - } else if (c == 9) { /* TAB */ - I ts = tx / FONT_W; - for (I i = ts; i < ((ts + 7) / 8) * 8; i++) { dputc(' '); } - } else if (c == 10 || c == 13) { /* CR or LF */ - ty += FONT_H; - tx = 0; + switch (c) { + case 8: + case 127: special_backspace(); break; + case 9: special_tab(); break; + case 10: + case 13: special_crlf(); break; } } @@ -226,15 +351,12 @@ V advance_cursor_and_scroll() { if (ty > (FH - FONT_H)) { ty -= FONT_H; scroll(); } } +#define NORMAL !(c < 32 || c == 127) + V dputc(I c) { C *bitmap = (C *)font + (c * ((FONT_H * FONT_W) / 8)); - handle_special_character(c); - - if (!(c == 8 || c == 127 || c == 9 || c == 10 || c == 13)) { - handle_regular_character(bitmap); - } - + if (NORMAL) { handle_regular_character(bitmap); } advance_cursor_and_scroll(); } @@ -255,15 +377,15 @@ V acc_reset() { } V gt_up(I n) { ty -= FONT_H; if (ty < 0) ty = 0; } -V gt_down(I n) { ty += FONT_H; if (ty > 24 * FONT_H) ty = 24 * FONT_H; } +V gt_down(I n) { ty += FONT_H; if (ty > ROWS * FONT_H) ty = ROWS * FONT_H; } V gt_left(I n) { tx -= FONT_W; if (tx < 0) tx = 0; } -V gt_right(I n) { tx += FONT_W; if (tx > 79 * FONT_W) ty = 79 * FONT_W; } +V gt_right(I n) { tx += FONT_W; if (tx > COLS * FONT_W) ty = COLS * FONT_W; } V gt_move_cursor() { if (acc_i >= 1) { I a = (acc_pop() - 1) * FONT_W; I b = (acc_pop() - 1) * FONT_H; - ty = b; if (ty < 0) ty = 0; if (ty > FONT_H * 24) ty = FONT_H * 24; - tx = a; if (tx < 0) tx = 0; if (tx > FONT_W * 79) tx = FONT_W * 79; + ty = b; if (ty < 0) ty = 0; if (ty > FONT_H * ROWS) ty = FONT_H * ROWS; + tx = a; if (tx < 0) tx = 0; if (tx > FONT_W * COLS) tx = FONT_W * COLS; acc_reset(); return; } @@ -283,10 +405,7 @@ V gt_clear() { V gt_set_attr() { acc_reset(); } V term_putc(I c) { - if (c == 27) { - on = -2; - return; - } + if (c == 27) { on = -2; return; } switch (on) { case 0: dputc(c); @@ -325,43 +444,80 @@ V term_putc(I c) { } } +/* graphica, level 0 ======================================== */ + +I p0, p0x, p0y, pcolor = FG; +I px(I p) { return p >> 16; } +I py(I p) { return p & 65535; } + +V gra0(ilo *v) { push(v, 0); } +V gra1(ilo *v) { push(v, 2); } +V gra2(ilo *v) { push(v, FONT_H); push(v, FONT_W); } +V gra3(ilo *v) { push(v, FH); push(v, FW); } +V gra4(ilo *v) { pcolor = (pop(v) != 0) ? FG : BG; } +V gra5(ilo *v) { p0 = pop(v); p0x = px(p0); p0y = py(p0); + pixel(p0x, p0y, pcolor); } +V gra6(ilo *v) { p0 = pop(v); p0x = px(p0); p0y = py(p0); + push(v, get_pixel(p0x, p0y)); } +V gra(ilo *v) { + switch (pop(v)) { + case 0: gra0(v); break; case 1: gra1(v); break; + case 2: gra2(v); break; case 3: gra3(v); break; + case 4: gra4(v); break; case 5: gra5(v); break; + case 6: gra6(v); break; + } +} + +/* pointer device =========================================== */ +/* returns x, y, button state */ + +V ptr(ilo *v) { + I x, y, wx, wy; + unsigned I mask; + Window w; + XQueryPointer(disp, win, &w, &w, &x, &y, &wx, &wy, &mask); + push(v, x); + push(v, y); + if (mask & Button1Mask) push(v, -1); else push(v, 0); +} + +/* the ilo cpu ============================================== */ + V load_image(ilo *v) { - F = open(v->rom, O_RDONLY, 0666); - if (!F) { return; }; + F = open(v->rom, O_RDONLY, 0666); FAILED { uli(v); return; } read(F, &M, 65536 * 4); close(F); IP = SP = RP = 0; } V save_image(ilo *v) { - v->f = open(v->rom, O_WRONLY, 0666); - write(v->f, &M, 65536 * 4); - close(v->f); + F = open(v->rom, O_WRONLY, 0666); FAILED { usi(v); return; } + write(F, &M, 65536 * 4); + close(F); } V block_common(ilo *v) { B = pop(v); /* block buffer */ A = pop(v); /* block number */ - lseek(v->f, 4096 * A, SEEK_SET); + lseek(F, 4096 * A, SEEK_SET); } V read_block(ilo *v) { - v->f = open(v->blocks, O_RDONLY, 0666); + F = open(v->blocks, O_RDONLY, 0666); FAILED { urb(v); return; } block_common(v); - read(v->f, M + B, 4096); - close(v->f); + read(F, M + B, 4096); + close(F); } V write_block(ilo *v) { - v->f = open(v->blocks, O_WRONLY, 0666); + F = open(v->blocks, O_WRONLY, 0666); FAILED { uwb(v); return; } block_common(v); - write(v->f, M + B, 4096); - close(v->f); + write(F, M + B, 4096); + close(F); } V save_ip(ilo *v) { RP += 1; R = IP; } -V symmetric(ilo *v) { - if (B >= 0 && N < 0) { T += 1; N -= B; } } +V symmetric(ilo *v) { if (B >= 0 && N < 0) { T += 1; N -= B; } } V li(ilo *v) { IP += 1; push(v, M[IP]); } V du(ilo *v) { push(v, T); } @@ -387,7 +543,7 @@ V ad(ilo *v) { N += T; SP -= 1; } V su(ilo *v) { N -= T; SP -= 1; } V mu(ilo *v) { N *= T; SP -= 1; } V di(ilo *v) { A = T; B = N; - if (A == 0) { v->ok = 1; iof(v); return; } + if (A == 0) { dbz(v); OK = 1; RESTART; return; } T = B / A; N = B % A; symmetric(v); } V an(ilo *v) { N = T & N; SP -= 1; } @@ -404,36 +560,23 @@ V cy(ilo *v) { L = pop(v); D = pop(v); S = pop(v); M[D] = M[S]; L -= 1; S += 1; D += 1; } } -V pre(V) { cur -= 1; if (cur < 0) cur = ILOS - 1; } -V nxt(V) { cur += 1; if (cur > (ILOS - 1)) cur = 0; } - -V msgs(V) { printf("\n*** SWITCH TO %d ***\n", cur); } -V msgx(V) { printf("\n*** SHUTDOWN REQUESTED ***\n"); - exit(1); } -V msgr(V) { printf("\n*** RESTART %d ***\n", cur); } - -V term_putc(I c); +V pre(V) { cur -= 1; if (cur < 0) cur = ILOS - 1; update_labels(); } +V nxt(V) { cur += 1; if (cur > (ILOS - 1)) cur = 0; update_labels(); } V ioa(ilo *v) { term_putc(pop(v)); } V iob(ilo *v) { redraw(); cursor(); v->i[0] = wait_key(); push(v, v->i[0]); term_putc(T); - if (v->i[0] == 14) { T = 32; nxt(); msgs(); } - if (v->i[0] == 16) { T = 32; pre(); msgs(); } - if (v->i[0] == 3) { T = 32; msgr(); iof(v); - v->ok = 1; } - if (v->i[0] == 1) { T = 32; msgx(); } } + if (v->i[0] == CTRL_N) { T = 32; nxt(); } + if (v->i[0] == CTRL_P) { T = 32; pre(); } + if (v->i[0] == CTRL_C) { T = 32; RESTART; + OK = 1; } + if (v->i[0] == CTRL_A) { T = 32; exit(0); } } V ioc(ilo *v) { read_block(v); } V iod(ilo *v) { write_block(v); } V ioe(ilo *v) { save_image(v); } V iof(ilo *v) { load_image(v); IP = -1; } -V iog(ilo *v) { iof(v); } +V iog(ilo *v) { RESTART; } V ioh(ilo *v) { push(v, SP); push(v, RP); } -V iox(ilo *v) { I c = pop(v); I y = pop(v); I x = pop(v); - pixel(x, y, c); } -V ioy(ilo *v) { I y = pop(v); I x = pop(v); push(v, get_pixel(x, y)); } -V ioz(ilo *v) { I x, y, wx, wy, mask; Window w; - XQueryPointer(disp, win, &w, &w, &x, &y, &wx, &wy, &mask); - push(v, x); push(v, y); if (mask & Button1Mask) push(v, -1); else push(v, 0); } V io(ilo *v) { switch (pop(v)) { @@ -441,8 +584,8 @@ V io(ilo *v) { case 2: ioc(v); break; case 3: iod(v); break; case 4: ioe(v); break; case 5: iof(v); break; case 6: iog(v); break; case 7: ioh(v); break; - case 33: iox(v); break; case 34: ioy(v); break; - case 35: ioz(v); break; + /* optional extensions after this point */ + case 12: gra(v); break; case 13: ptr(v); break; default: break; } } @@ -468,14 +611,17 @@ V process(ilo *v, I o) { } } -#define VALID(n) (v->ok == 0) && ((opcode >> n) & 0xFF) +/* This is used to help skip processing NOP operations and */ +/* abort on errors */ + +#define VALID(n) (OK == 0) && ((opcode >> n) & 0xFF) V execute() { I opcode; ilo *v = &systems[cur]; while (IP < 65536) { opcode = M[IP]; - v->ok = 0; + OK = 0; if (VALID(0)) process(v, opcode & 0xFF); if (VALID(8)) process(v, (opcode >> 8) & 0xFF); if (VALID(16)) process(v, (opcode >> 16) & 0xFF); @@ -485,13 +631,13 @@ V execute() { } } -V load_font(ilo *); +/* main entry point ========================================= */ I main(I argc, C **argv) { ilo *v; I i; - cur = 0; + /* setup each session */ for (i = 0; i < ILOS; i++) { v = &systems[i]; v->blocks = (argc > 1) ? argv[1] : BLOCKS; @@ -499,24 +645,18 @@ I main(I argc, C **argv) { load_image(v); } + /* load the font data */ load_font(&systems[0]); + initialize_display(); - disp = XOpenDisplay(NULL); - win = XCreateSimpleWindow(disp, RootWindow(disp, 0), - 0, 0, FW, FH, 0, 0x0, 0x0); - XSelectInput(disp, win, ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask); - XStoreName(disp, win, "x/ilo"); - gc = DefaultGC(disp, DefaultScreen(disp)); - XMapWindow(disp, win); - XFlush(disp); +#ifdef CENTER_WINDOW XMoveWindow(disp, win, (DisplayWidth(disp, DefaultScreen(disp)) - FW) / 2, - (DisplayHeight(disp, DefaultScreen(disp)) - FH) / 2); + (DisplayHeight(disp, DefaultScreen(disp)) - FH - BUTTON_HEIGHT) / 2); +#endif + cur = 0; execute(); - XUnmapWindow(disp, win); - XDestroyWindow(disp, win); - XCloseDisplay(disp); - + cleanup_display(); return 0; }