/*************************************************************** crc's _ _ ___ _ __ _ __ ___ _ _| | |_ / (_) | ___ / /_ __ | '_ ` _ \| | | | | __| / /| | |/ _ \ / /\ \/ / | | | | | | |_| | | |_ / / | | | (_) / / > < |_| |_| |_|\__,_|_|\__/_/ |_|_|\___/_/ /_/\_\ (c) charles childers 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 - 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 #define V void #define I int #define C char #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 a, b, f, s, d, l, ok; C i[1]; }; struct ilo systems[ILOS]; /* ilo session data */ I cur; /* track current session */ #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 */ I tx, ty; /* text cursor location */ /* variables for Arland's DEC subset */ I acc[32], acc_i = 0, ground = 0, on = 0; /* variables for X11 stuff */ Display *disp; Window win; GC gc; XEvent event; 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(); V iof(ilo *); 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) { 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() { 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); FAILED { ulf(v); return; } read(F, &font, 4096); close(F); } V pixel(I x, I y, I c) { I index = y * FW + x; if (c == 0) { frame[(index / 32)] &= ~(1 << (31 - index % 32)); XSetForeground(disp, gc, BG); } else { frame[(index / 32)] |= 1 << (31 - index % 32); XSetForeground(disp, gc, FG); } XDrawPoint(disp, win, gc, x, y); } I get_pixel(I x, I y) { I index = y * FW + x; I bit_pos = 31 - index % 32; I mask = 1 << bit_pos; return (frame[(index / 32)] & mask) >> bit_pos; } V redraw() { for (I x = 0; x < FW; x++) { for (I y = 0; y < FH; y++) { XSetForeground(disp, gc, get_pixel(x, y) ? FG : BG); XDrawPoint(disp, win, gc, x, y); } } } V scroll() { I memsize = (FH * FW) / 32; I last_row = (FW * FONT_H) / 32; for (I i = 0; i < memsize - last_row; i++) { frame[i] = frame[i + last_row]; } for (I i = memsize - last_row; i < memsize; i++) { frame[i] = 0; } redraw(); } 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; } 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) { switch (c) { case 8: case 127: special_backspace(); break; case 9: special_tab(); break; case 10: case 13: special_crlf(); break; } } V handle_regular_character(C *bitmap) { I x, y, set; for (x = 0; x < FONT_H; x++) { for (y = 0; y < FONT_W; y++) { set = reverse(bitmap[x]) & 1 << y; pixel(tx + y, ty + x, set ? 1 : 0); } } tx += FONT_W; } V advance_cursor_and_scroll() { if (tx >= FW) { tx = 0; ty += FONT_H; } 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 (NORMAL) { handle_regular_character(bitmap); } advance_cursor_and_scroll(); } I acc_pop() { I k = acc_i; if (acc_i > 0) { acc_i -= 1; if (acc_i == -1) { acc_i = 0; } } return acc[k]; } V acc_reset() { for (I x = 0; x < 32; x++) { acc[x] = 0; } ground = on = 0; } V gt_up(I n) { ty -= FONT_H; if (ty < 0) ty = 0; } 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 > 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 * ROWS) ty = FONT_H * ROWS; tx = a; if (tx < 0) tx = 0; if (tx > FONT_W * COLS) tx = FONT_W * COLS; acc_reset(); return; } /* home */ tx = 0; ty = 0; acc_reset(); } V gt_reset() { acc_reset(); } V gt_next() { acc_i += 1; acc[acc_i] = 0; } V gt_clear() { acc_reset(); for (I x = 0; x < (FW * FH) / 32; x++) frame[x] = 0; tx = ty = 0; redraw(); } V gt_set_attr() { acc_reset(); } V term_putc(I c) { if (c == 27) { on = -2; return; } switch (on) { case 0: dputc(c); return; case -2: if (c == '[') { on = -1; return; } on = 0; dputc(c); return; } if (c >= '0' && c <= '9') { acc[acc_i] *= 10; acc[acc_i] += c - 48; if (c == '3') { ground = 0; } if (c == '4') { ground = 1; } } else { switch (c) { case 'A': gt_up(acc_pop()); break; case 'B': gt_down(acc_pop()); break; case 'C': gt_left(acc_pop()); break; case 'D': gt_right(acc_pop()); break; case 'm': gt_set_attr(); break; case ';': gt_next(); break; case 'H': gt_move_cursor(); break; case 'J': gt_clear(); break; default: on = 0; dputc(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); FAILED { uli(v); return; } read(F, &M, 65536 * 4); close(F); IP = SP = RP = 0; } V save_image(ilo *v) { 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(F, 4096 * A, SEEK_SET); } V read_block(ilo *v) { F = open(v->blocks, O_RDONLY, 0666); FAILED { urb(v); return; } block_common(v); read(F, M + B, 4096); close(F); } V write_block(ilo *v) { F = open(v->blocks, O_WRONLY, 0666); FAILED { uwb(v); return; } block_common(v); 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 li(ilo *v) { IP += 1; push(v, M[IP]); } V du(ilo *v) { push(v, T); } V dr(ilo *v) { DS[SP] = 0; SP -= 1; } V sw(ilo *v) { A = T; T = N; N = A; } V pu(ilo *v) { RP += 1; R = pop(v); } V po(ilo *v) { push(v, R); RP -= 1; } V ju(ilo *v) { IP = pop(v) - 1; } V ca(ilo *v) { save_ip(v); IP = pop(v) - 1; } V cc(ilo *v) { A = pop(v); if (pop(v)) { save_ip(v); IP = A - 1; } } V cj(ilo *v) { A = pop(v); if (pop(v)) { IP = A - 1; } } V re(ilo *v) { IP = R; RP -= 1; } V eq(ilo *v) { N = (N == T) ? -1 : 0; SP -= 1; } V ne(ilo *v) { N = (N != T) ? -1 : 0; SP -= 1; } V lt(ilo *v) { N = (N < T) ? -1 : 0; SP -= 1; } V gt(ilo *v) { N = (N > T) ? -1 : 0; SP -= 1; } V fe(ilo *v) { T = M[T]; } V st(ilo *v) { M[T] = N; SP -= 2; } 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) { dbz(v); OK = 1; RESTART; return; } T = B / A; N = B % A; symmetric(v); } V an(ilo *v) { N = T & N; SP -= 1; } V or(ilo *v) { N = T | N; SP -= 1; } V xo(ilo *v) { N = T ^ N; SP -= 1; } V sl(ilo *v) { N = N << T; SP -= 1; } V sr(ilo *v) { N = N >> T; SP -= 1; } V cp(ilo *v) { L = pop(v); D = pop(v); S = T; T = -1; while (L) { if (M[D] != M[S]) { T = 0; } L -= 1; S += 1; D += 1; } } V cy(ilo *v) { L = pop(v); D = pop(v); S = pop(v); while (L) { M[D] = M[S]; L -= 1; S += 1; D += 1; } } 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] == 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) { RESTART; } V ioh(ilo *v) { push(v, SP); push(v, RP); } V io(ilo *v) { switch (pop(v)) { case 0: ioa(v); break; case 1: iob(v); break; 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; /* optional extensions after this point */ case 12: gra(v); break; case 13: ptr(v); break; default: break; } } V process(ilo *v, I o) { switch (o) { case 0: break; case 1: li(v); break; case 2: du(v); break; case 3: dr(v); break; case 4: sw(v); break; case 5: pu(v); break; case 6: po(v); break; case 7: ju(v); break; case 8: ca(v); break; case 9: cc(v); break; case 10: cj(v); break; case 11: re(v); break; case 12: eq(v); break; case 13: ne(v); break; case 14: lt(v); break; case 15: gt(v); break; case 16: fe(v); break; case 17: st(v); break; case 18: ad(v); break; case 19: su(v); break; case 20: mu(v); break; case 21: di(v); break; case 22: an(v); break; case 23: or(v); break; case 24: xo(v); break; case 25: sl(v); break; case 26: sr(v); break; case 27: cp(v); break; case 28: cy(v); break; case 29: io(v); break; default: break; } } /* 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]; 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); if (VALID(24)) process(v, (opcode >> 24) & 0xFF); IP += 1; v = &systems[cur]; } } /* main entry point ========================================= */ I main(I argc, C **argv) { ilo *v; I i; /* setup each session */ for (i = 0; i < ILOS; i++) { v = &systems[i]; v->blocks = (argc > 1) ? argv[1] : BLOCKS; v->rom = (argc > 2) ? argv[2] : ROM; load_image(v); } /* load the font data */ load_font(&systems[0]); initialize_display(); #ifdef CENTER_WINDOW XMoveWindow(disp, win, (DisplayWidth(disp, DefaultScreen(disp)) - FW) / 2, (DisplayHeight(disp, DefaultScreen(disp)) - FH - BUTTON_HEIGHT) / 2); #endif cur = 0; execute(); cleanup_display(); return 0; }