466 lines
12 KiB
C
466 lines
12 KiB
C
/***************************************************************
|
|
crc's _ _ __
|
|
(_) | ___ / /_ __
|
|
| | |/ _ \ / /\ \/ / a tiny virtual computer
|
|
| | | (_) / / > < 64kw RAM, 32-bit, Dual Stack, MISC
|
|
|_|_|\___/_/ /_/\_\ ilo-x.c (c) charles childers
|
|
**************************************************************/
|
|
|
|
/* cc -o ilo-x vm/ilo-x.c `pkg-config --cflags x11 --libs` */
|
|
|
|
/* Local Configuration */
|
|
|
|
#define BLOCKS "ilo.blocks"
|
|
#define ROM "ilo.rom"
|
|
#define FONT "ilo.fnt"
|
|
#define FG 0xFFFFFF
|
|
#define BG 0x000000
|
|
#define CURSOR 0x00FFFF
|
|
#define FW 640
|
|
#define FH 384
|
|
#define FONT_H 16
|
|
#define FONT_W 8
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
|
|
#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 V void
|
|
#define I int
|
|
#define C char
|
|
|
|
I ip, /* instruction pointer */
|
|
sp, /* data stack pointer */
|
|
rp, /* address stack pointer */
|
|
ds[33], /* data stack */
|
|
as[257], /* address stack */
|
|
m[65536], /* memory */
|
|
ok;
|
|
|
|
C *blocks, /* name of block file (ilo.blocks) */
|
|
*rom; /* name of image (ilo.rom) */
|
|
|
|
I a, b, f, s, d, l; /* these others are used */
|
|
C i[1]; /* for misc. purposes */
|
|
|
|
#define TERM_W (FW / FONT_W)
|
|
#define TERM_H (FH / FONT_H)
|
|
|
|
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;
|
|
C text[255];
|
|
|
|
V dputc(I);
|
|
V redraw();
|
|
V iof();
|
|
|
|
V p(char *s) { while(*s) dputc(*s++); }
|
|
V e(char *s) { p("E:"); p(s); p("\n"); ok = 1; iof(); }
|
|
V dso() { if ((sp + 1) > 32) { e("DSO"); } }
|
|
V dsu() { if ((sp - 1) < 0) { e("DSU"); } }
|
|
V rso() { if ((rp + 1) > 256) { e("RSO"); } }
|
|
V rsu() { if ((rp - 1) < 0) { e("RSU"); } }
|
|
V dbz() { if (T == 0) { e("DBZ"); } }
|
|
V mem() { if (T < 0 || T > 65535) { e("MEM"); } }
|
|
V uli() { if (f == -1) { e("ULI"); } }
|
|
V ulf() { if (f == -1) { e("ULF"); } }
|
|
V usi() { if (f == -1) { e("URI"); } }
|
|
V urb() { if (f == -1) { e("URB"); } }
|
|
V uwb() { if (f == -1) { e("UWB"); } }
|
|
V push(I v) { dso(); ds[sp + 1] = v; sp += 1; }
|
|
I pop() { dsu(); sp -= 1; return ds[sp + 1]; }
|
|
|
|
V cursor() {
|
|
XSetForeground(disp, gc, CURSOR);
|
|
XFillRectangle(disp, win, gc, tx, ty, FONT_W, FONT_H);
|
|
XFlush(disp);
|
|
}
|
|
|
|
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(); }
|
|
}
|
|
}
|
|
|
|
V load_font() {
|
|
f = open(FONT, O_RDONLY, 0666);
|
|
ulf();
|
|
if (!f) { 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);
|
|
}
|
|
}
|
|
cursor();
|
|
}
|
|
|
|
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 handle_special_character(I c) {
|
|
if (c == 8 || c == 127) { /* BACKSPACE/DELETE */
|
|
if (tx - FONT_W >= 0) {
|
|
tx -= FONT_W ;
|
|
dputc(' ');
|
|
tx -= FONT_W;
|
|
redraw();
|
|
}
|
|
} 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;
|
|
redraw();
|
|
}
|
|
}
|
|
|
|
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(); }
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
advance_cursor_and_scroll();
|
|
cursor();
|
|
}
|
|
|
|
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 > 24 * FONT_H) ty = 24 * 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_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;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
V load_image() {
|
|
f = open(rom, O_RDONLY, 0666);
|
|
uli();
|
|
if (!f) { return; };
|
|
read(f, &m, 65536 * 4);
|
|
close(f);
|
|
ip = sp = rp = 0;
|
|
}
|
|
|
|
V save_image() {
|
|
f = open(rom, O_WRONLY, 0666);
|
|
usi();
|
|
write(f, &m, 65536 * 4);
|
|
close(f);
|
|
}
|
|
|
|
V block_common() {
|
|
b = pop(); /* block buffer */
|
|
a = pop(); /* block number */
|
|
lseek(f, 4096 * a, SEEK_SET);
|
|
}
|
|
|
|
V read_block() {
|
|
f = open(blocks, O_RDONLY, 0666);
|
|
urb();
|
|
block_common();
|
|
read(f, m + b, 4096);
|
|
close(f);
|
|
}
|
|
|
|
V write_block() {
|
|
f = open(blocks, O_WRONLY, 0666);
|
|
uwb();
|
|
block_common();
|
|
write(f, m + b, 4096);
|
|
close(f);
|
|
}
|
|
|
|
V save_ip() { rso(); rp += 1; R = ip; }
|
|
V symmetric() { if (b >= 0 && N < 0) { T += 1; N -= b; } }
|
|
|
|
V li() { ip += 1; push(m[ip]); }
|
|
V du() { push(T); }
|
|
V dr() { pop(); }
|
|
V sw() { a = T; T = N; N = a; }
|
|
V pu() { rp += 1; R = pop(); }
|
|
V po() { push(R); rp -= 1; }
|
|
V ju() { ip = pop() - 1; }
|
|
V ca() { save_ip(); ip = pop() - 1; }
|
|
V cc() { a = pop(); if (pop()) { save_ip(); ip = a - 1; } }
|
|
V cj() { a = pop(); if (pop()) { ip = a - 1; } }
|
|
V re() { ip = R; rp -= 1; }
|
|
V eq() { N = (N == T) ? -1 : 0; sp -= 1; }
|
|
V ne() { N = (N != T) ? -1 : 0; sp -= 1; }
|
|
V lt() { N = (N < T) ? -1 : 0; sp -= 1; }
|
|
V gt() { N = (N > T) ? -1 : 0; sp -= 1; }
|
|
V fe() { mem(); T = m[T]; }
|
|
V st() { mem(); m[T] = N; sp -= 2; }
|
|
V ad() { N += T; sp -= 1; }
|
|
V su() { N -= T; sp -= 1; }
|
|
V mu() { N *= T; sp -= 1; }
|
|
V di() { dbz();
|
|
a = T; b = N; T = b / a; N = b % a; symmetric(); }
|
|
V an() { N = T & N; sp -= 1; }
|
|
V or() { N = T | N; sp -= 1; }
|
|
V xo() { N = T ^ N; sp -= 1; }
|
|
V sl() { N = N << T; sp -= 1; }
|
|
V sr() { N = N >> T; sp -= 1; }
|
|
V cp() { l = pop(); d = pop(); s = T; T = -1;
|
|
while (l) { if (m[d] != m[s]) { T = 0; }
|
|
l -= 1; s += 1; d += 1; } }
|
|
V cy() { l = pop(); d = pop(); s = pop();
|
|
while (l) { m[d] = m[s]; l -= 1; s += 1; d += 1; } }
|
|
V ioa() { term_putc(pop()); }
|
|
V iob() { push(wait_key()); term_putc(T); }
|
|
V ioc() { read_block(); }
|
|
V iod() { write_block(); }
|
|
V ioe() { save_image(); }
|
|
V iof() { load_image(); ip = -1; }
|
|
V iog() { ip = 65536; }
|
|
V ioh() { push(sp); push(rp); }
|
|
V iox() { I c = pop(); I y = pop(); I x = pop();
|
|
pixel(x, y, c); }
|
|
V ioy() { I y = pop(); I x = pop(); push(get_pixel(x, y)); }
|
|
V ioz() { I x, y, wx, wy, mask; Window w;
|
|
XQueryPointer(disp, win, &w, &w, &x, &y, &wx, &wy, &mask);
|
|
push(x); push(y); if (mask & Button1Mask) push(-1); else push(0); }
|
|
V io() {
|
|
switch (pop()) {
|
|
case 0: ioa(); break; case 1: iob(); break;
|
|
case 2: ioc(); break; case 3: iod(); break;
|
|
case 4: ioe(); break; case 5: iof(); break;
|
|
case 6: iog(); break; case 7: ioh(); break;
|
|
case 33: iox(); break; case 34: ioy(); break;
|
|
case 35: ioz(); break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
V process(I o) {
|
|
switch (o) {
|
|
case 0: break; case 1: li(); break;
|
|
case 2: du(); break; case 3: dr(); break;
|
|
case 4: sw(); break; case 5: pu(); break;
|
|
case 6: po(); break; case 7: ju(); break;
|
|
case 8: ca(); break; case 9: cc(); break;
|
|
case 10: cj(); break; case 11: re(); break;
|
|
case 12: eq(); break; case 13: ne(); break;
|
|
case 14: lt(); break; case 15: gt(); break;
|
|
case 16: fe(); break; case 17: st(); break;
|
|
case 18: ad(); break; case 19: su(); break;
|
|
case 20: mu(); break; case 21: di(); break;
|
|
case 22: an(); break; case 23: or(); break;
|
|
case 24: xo(); break; case 25: sl(); break;
|
|
case 26: sr(); break; case 27: cp(); break;
|
|
case 28: cy(); break; case 29: io(); break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
#define VALID(n) (ok == 0) && ((opcode >> n) & 0xFF)
|
|
|
|
V execute() {
|
|
I opcode;
|
|
while (ip < 65536) {
|
|
opcode = m[ip];
|
|
ok = 0;
|
|
if (VALID(0)) process(opcode & 0xFF);
|
|
if (VALID(8)) process((opcode >> 8) & 0xFF);
|
|
if (VALID(16)) process((opcode >> 16) & 0xFF);
|
|
if (VALID(24)) process((opcode >> 24) & 0xFF);
|
|
ip += 1;
|
|
}
|
|
}
|
|
|
|
I main(I argc, C **argv) {
|
|
blocks = (argc > 1) ? argv[1] : BLOCKS;
|
|
rom = (argc > 2) ? argv[2] : ROM;
|
|
ok = 0;
|
|
|
|
/*
|
|
#ifdef __OpenBSD__
|
|
pledge("stdio rpath wpath unix video", NULL);
|
|
#endif
|
|
*/
|
|
|
|
load_image();
|
|
|
|
load_font();
|
|
|
|
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);
|
|
XMoveWindow(disp, win, (DisplayWidth(disp, DefaultScreen(disp)) - FW) / 2,
|
|
(DisplayHeight(disp, DefaultScreen(disp)) - FH) / 2);
|
|
|
|
execute();
|
|
|
|
XUnmapWindow(disp, win);
|
|
XDestroyWindow(disp, win);
|
|
XCloseDisplay(disp);
|
|
|
|
return 0;
|
|
}
|