/*************************************************************** 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; }