/***************************************************************
crc's _ _
(_) | ___
| | |/ _ \ a tiny virtual computer
| | | (_) | 64kw RAM, 32-bit, Dual Stack, MISC
|_|_|\___/ ilo-sdl-1bpp.c (c) charles childers
(c) arland childers
**************************************************************/
/* Configuration **********************************************/
#define FB_W 640
#define FB_H 320
#define FW 640
#define FH 320
#define SCALE 1
// #define FULLSCREEN
#define FONT_H 16
#define FONT_W 8
#define PADDING 0 /* set to 1 or 2 for better readability */
/* if using a display large enough */
#define KEY_QUEUE_SIZE 512
#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
/**************************************************************/
#if defined(_WIN32) || defined(_WIN64)
#define WINDOWS
#endif
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
#include <fcntl.h>
#include <limits.h>
#ifndef WINDOWS
#include <unistd.h>
#endif
#include "font.h"
/* Global Variables, Data Structures **************************/
V term_putc(I);
V dputc(I);
V cursor() {}
char *rom, *blocks;
/* SDL Globals */
SDL_Window *window;
SDL_Event event;
SDL_Renderer *renderer;
SDL_Texture *texture;
I mx, my, mb; /* mouse x, y, button */
I tx, ty; /* cursor x, y */
I current;
I frame[FB_H * FB_W]; /* display memory */
I cycles; /* inst. bundles before event poll */
I ip, /* instruction pointer */
sp, /* data stack pointer */
rp, /* address stack pointer */
ds[33], /* data stack */
as[257], /* address stack */
m[65536 + (((FB_H * FB_W)/32) + 64)]; /* memory */
/* the other variables are used by the various
functions for misc. purposes */
I a, b, f, s, d, l;
C i[1];
V push(I v) { ds[sp + 1] = v; sp += 1; }
I pop() { sp -= 1; return ds[sp + 1]; }
/* Keyboard input queue ***************************************/
I keyQueue[KEY_QUEUE_SIZE];
I keyQueueStart = 0;
I keyQueueEnd = 0;
I keyQueueCount = 0;
V enqueue_key(I key) {
if (keyQueueCount < KEY_QUEUE_SIZE) {
keyQueue[keyQueueEnd] = key;
keyQueueEnd = (keyQueueEnd + 1) % KEY_QUEUE_SIZE;
keyQueueCount++;
printf("Added %d to queue\n", key);
}
}
I dequeue_key() {
if (keyQueueCount > 0) {
I key = keyQueue[keyQueueStart];
keyQueueStart = (keyQueueStart + 1) % KEY_QUEUE_SIZE;
keyQueueCount--;
printf("Key from queue: %d, %d remaining\n", key, keyQueueCount);
return key;
}
return 0;
}
/* Graphica & SDL Functions ***********************************/
void handle_events() {
while (SDL_PollEvent(&event)) { process_sdl_event(event, 0); }
}
#define CURRENT_KEY event.key.keysym.sym
V pixel(I x, I y, I c) {
I index = y * FB_W + x;
if (c == 0) {
m[65536 + (index / 32)] &= ~(1 << (31 - index % 32));
} else {
m[65536 + (index / 32)] |= 1 << (31 - index % 32);
}
}
I get_pixel(I x, I y) {
I index = y * FB_W + x;
I bit_pos = 31 - index % 32;
I mask = 1 << bit_pos;
return (m[65536 + (index / 32)] & mask) >> bit_pos;
}
V g_draw_pixel() {
I color = pop(); I y = pop(); I x = pop(); pixel(x, y, color);
}
V g_mouse() { push(mx); push(my); push(mb); }
void g_get_pixel() {
I y = pop(); I x = pop(); I c = get_pixel(x, y);
push((c != 0) ? 1 : 0); }
V redraw() {
for (I i = 0; i < (FB_H * FB_W); i++) {
frame[i] = get_pixel(i, 0) ? -1 : 0;
}
SDL_UpdateTexture(texture, NULL, frame, FB_W * sizeof(int));
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
I process_sdl_event(SDL_Event event, I output) {
I mod, x, y, pressed;
C upper[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
'!', '"', '#', '$', '%', '&', '"', '(', ')', '*',
'+', '<', '_', '>', '?', ')', '!', '@', '#', '$',
'%', '^', '&', '*', '(', ':', ':', ':', '+', '>',
'?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|',
'}', '^', '_', '~', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'{', '|', '}', '~', };
switch(event.type) {
case SDL_QUIT:
exit(0);
break;
case SDL_MOUSEMOTION:
SDL_GetMouseState(&x, &y);
mx = x / SCALE;
my = y / SCALE;
break;
case SDL_MOUSEBUTTONDOWN:
SDL_GetMouseState(&x, &y);
mx = x / SCALE;
my = y / SCALE;
mb = 1;
break;
case SDL_MOUSEBUTTONUP:
SDL_GetMouseState(&x, &y);
mx = x / SCALE;
my = y / SCALE;
mb = 0;
break;
case SDL_KEYDOWN:
if (CURRENT_KEY != SDLK_LSHIFT && CURRENT_KEY != SDLK_RSHIFT) {
current = CURRENT_KEY;
mod = SDL_GetModState();
if (mod & KMOD_LSHIFT || mod & KMOD_RSHIFT ) {
current = upper[current];
}
}
break;
case SDL_KEYUP:
if (current == 13) current = 10;
if (current != 0) {
enqueue_key(current);
pressed = current;
current = 0;
return pressed;
}
break;
default:
break;
}
return 0;
}
I read_key() {
I key;
current = 0;
while (keyQueueCount == 0) { handle_events(); }
key = dequeue_key();
term_putc(key);
redraw();
return key;
}
/* a barebones terminal emulator by Arland ********************/
/* variables for Arland's DEC subset */
I acc[32], acc_i = 0, on = 0;
V clear() {
for (I i = 0; i < (FB_H * FB_W); i++) { pixel(i, 0, 0); }
tx = ty = 0;
redraw();
}
V scroll () {
I memsize = (FB_H*FB_W);
I last_row = FB_W * (FONT_H + PADDING);
for (I i = 0; i < memsize - last_row; i++) {
pixel(i, 0, get_pixel(i+last_row, 0));
}
for (I i = memsize-last_row; i < memsize; i++) {
pixel(i, 0, 0);
}
}
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; }
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;
} 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);
}
}
}
/* the ilo computer **************************************** */
V prepare_vm() {
for (ip = 0; ip < 32; ip++) ds[ip] = 0;
for (ip = 0; ip < 128; ip++) as[ip] = 0;
ip = sp = rp = 0;
}
V load_image() {
f = open(rom, O_RDONLY, 0666);
if (!f) { return; };
read(f, &m, 65536 * 4);
close(f);
ip = sp = rp = 0;
}
V save_image() {
f = open(rom, O_WRONLY, 0666);
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);
block_common();
read(f, m + b, 4096);
close(f);
}
V write_block() {
f = open(blocks, O_WRONLY, 0666);
block_common();
write(f, m + b, 4096);
close(f);
}
V save_ip() { 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() { ds[sp] = 0; sp -= 1; }
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() { T = m[T]; }
V st() { 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() { 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() { size_t l = pop() * 4;
void *d = m + pop();
void *s = m + pop();
push((memcmp(s, d, l) == 0) ? -1 : 0);
}
V cy() { size_t l = pop() * 4;
void *d = m + pop();
void *s = m + pop();
memmove(d, s, l);
}
V ioa() { i[0] = (char)pop(); write(1, &i, 1); }
V iob() { read(0, &i, 1); push(i[0]); }
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 io() {
switch (pop()) {
case 0: term_putc(pop()); redraw(); break;
case 1: push(read_key()); break;
case 2: read_block(); break;
case 3: write_block(); break;
case 4: save_image(); break;
case 5: load_image(); ip = -1; break;
case 6: exit(0); break;
case 7: push(sp); push(rp); break;
case 33: g_draw_pixel(); break;
case 34: g_get_pixel(); break;
case 35: g_mouse(); break;
default: break;
}
}
/* Using a switch here instead of a jump table to avoid */
/* some issues w/relocation stuff when building w/o libc */
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;
}
}
I execute(I address) {
I o, x;
ip = address;
while (ip < 65536) {
handle_events();
for (I i = 0; i < cycles; i++) {
o = m[ip];
x = o & 0xFF; if (x) process(x);
x = (o >> 8) & 0xFF; if (x) process(x);
x = (o >> 16) & 0xFF; if (x) process(x);
x = (o >> 24) & 0xFF; if (x) process(x);
ip++;
}
redraw();
}
}
/**************************************************************/
V initialize_display() {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
printf("SDL could not initialize! %s\n", SDL_GetError());
return;
}
window = NULL;
window = SDL_CreateWindow("ilo",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
FB_W * SCALE, FB_H * SCALE,
SDL_WINDOW_SHOWN);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
texture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STATIC,
FB_W, FB_H);
SDL_RenderSetScale(renderer, SCALE, SCALE);
SDL_RenderSetLogicalSize(renderer, FB_W, FB_H);
#ifdef FULLSCREEN
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
#endif
cycles = 25000;
clear();
}
V cleanup() {
SDL_DestroyWindow(window);
SDL_Quit();
}
I main (I argc, C* argv[]) {
blocks = "ilo.blocks";
rom = (argc > 1) ? argv[1] : "ilo.rom";
printf("rom:%s blocks:%s\n", rom, blocks);
load_image();
initialize_display();
execute(0);
cleanup();
return 0;
}