Skip to content

Commit

Permalink
add screen device
Browse files Browse the repository at this point in the history
  • Loading branch information
Maya Karabula-Stysiak committed Apr 21, 2023
1 parent de9e9ff commit 6cc195e
Show file tree
Hide file tree
Showing 11 changed files with 543 additions and 129 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
node_modules
bundle.js
210 changes: 210 additions & 0 deletions devices/screen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import { PEEK2, POKE2, Uxn } from "../uxn";

export const WIDTH = 64 * 8
export const HEIGHT = 40 * 8

let FIXED_SIZE = 0;

export class Layer {
pixels: number[];
changed: number = 0;

constructor (width: number, height: number) {
this.pixels = new Array(width * height).fill(0)
}
};

export class UxnScreen {
palette: number[] = new Array(4).fill(0);
pixels: number[] = [];
width: number;
height: number;
fg: Layer;
bg: Layer;
mono: number = 0

constructor () {
this.width = WIDTH
this.height = HEIGHT
this.fg = new Layer(WIDTH, HEIGHT);
this.bg = new Layer(WIDTH, HEIGHT);
this.pixels = new Array(WIDTH * HEIGHT).fill(0)
}
};

export const uxn_screen = new UxnScreen();

const blending: number[][] = [
[0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0],
[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3],
[1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1],
[2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2],
];

const palette_mono: number[] = [
0x0f000000, 0x0fffffff,
];

function screen_write(p: UxnScreen, layer: Layer, x: number, y: number, color: number)
{
if(x < p.width && y < p.height) {
const i = x + y * p.width;

if(color != layer.pixels[i]) {
layer.pixels[i] = color;
layer.changed = 1;
}
}
}

function screen_fill(p: UxnScreen, layer: Layer, x1: number, y1: number, x2: number, y2: number, color: number)
{
let v: number, h: number;

for(v = y1; v < y2; v++)
for(h = x1; h < x2; h++)
screen_write(p, layer, h, v, color);

layer.changed = 1;
}

function screen_wipe(p: UxnScreen, layer: Layer, x: number, y: number)
{
screen_fill(p, layer, x, y, x + 8, y + 8, 0);
}

function screen_blit(p: UxnScreen, layer: Layer, x: number, y: number, sprite: number[], color: number, flipx: number, flipy: number, twobpp: number): void {
let v: number, h: number, opaque = (color % 5) || !color;

for(v = 0; v < 8; v++) {
let c = sprite[v] | (twobpp ? (sprite[v + 8] << 8) : 0);

for(h = 7; h >= 0; --h, c >>= 1) {
let ch = (c & 1) | ((c >> 7) & 2);

if(opaque || ch) {
screen_write(p, layer, x + (flipx ? 7 - h : h), y + (flipy ? 7 - v : v), blending[ch][color]);
}
}
}
}

export function screen_palette(p: UxnScreen, addr: number[]): void {
for (let i = 0, shift = 4; i < 4; ++i, shift ^= 4) {
const r = (addr[0 + Math.floor(i / 2)] >> shift) & 0x0f;
const g = (addr[2 + Math.floor(i / 2)] >> shift) & 0x0f;
const b = (addr[4 + Math.floor(i / 2)] >> shift) & 0x0f;

p.palette[i] = 0x000000 | r << 16 | g << 8 | b;
p.palette[i] |= p.palette[i] << 4;
}

p.fg.changed = p.bg.changed = 1;
}

function screen_resize(p: UxnScreen, width: number, height: number)
{
p.bg.pixels = new Array(width * height).fill(0)
p.fg.pixels = new Array(width * height).fill(0)
p.pixels = new Array(width * height).fill(0)

p.width = width;
p.height = height;
screen_fill(p, p.bg, 0, 0, p.width, p.height, 0);
screen_fill(p, p.fg, 0, 0, p.width, p.height, 0);
}

function screen_redraw(p: UxnScreen)
{
let i, size = p.width * p.height, palette = new Array(4).fill(0);

for(i = 0; i < 16; i++)
palette[i] = p.palette[(i >> 2) ? (i >> 2) : (i & 3)];

if(p.mono) {
for(i = 0; i < size; i++)
p.pixels[i] = palette_mono[(p.fg.pixels[i] ? p.fg.pixels[i] : p.bg.pixels[i]) & 0x1];
} else {
for(i = 0; i < size; i++)
p.pixels[i] = palette[p.fg.pixels[i] << 2 | p.bg.pixels[i]];
}

p.fg.changed = p.bg.changed = 0;
}

function clamp(val: number, min: number, max: number)
{
return (val >= min) ? (val <= max) ? val : max : min;
}

export function screen_mono(p: UxnScreen)
{
p.mono = p.mono ? 0 : 1;
screen_redraw(p);
}

export function screen_dei(u: Uxn, addr: number)
{
switch(addr) {
case 0x22: return uxn_screen.width >> 8;
case 0x23: return uxn_screen.width;
case 0x24: return uxn_screen.height >> 8;
case 0x25: return uxn_screen.height;
default: return u.dev[addr];
}
}

export function screen_deo(ram: number[], d: number[], port: number)
{
switch(port) {
case 0x3:
if(!FIXED_SIZE)
screen_resize(uxn_screen, clamp(PEEK2(d.slice(2)), 1, 1024), uxn_screen.height);
break;
case 0x5:
if(!FIXED_SIZE)
screen_resize(uxn_screen, uxn_screen.width, clamp(PEEK2(d.slice(4)), 1, 1024));
break;
case 0xe: {
let x = PEEK2(d.slice(0x8)), y = PEEK2(d.slice(0xa));
let layer: Layer = (d[0xf] & 0x40) ? uxn_screen.fg : uxn_screen.bg;

if(d[0xe] & 0x80)
screen_fill(
uxn_screen,
layer,
(d[0xe] & 0x10) ? 0 : x, (d[0xe] & 0x20) ? 0 : y, (d[0xe] & 0x10) ? x : uxn_screen.width, (d[0xe] & 0x20) ? y : uxn_screen.height, d[0xe] & 0x3)
;
else {
screen_write(uxn_screen, layer, x, y, d[0xe] & 0x3);
if(d[0x6] & 0x01) POKE2(d, (0x8), x + 1); /* auto x+1 */
if(d[0x6] & 0x02) POKE2(d, (0xa), y + 1); /* auto y+1 */
}
break;
}
case 0xf: {
let x = PEEK2(d.slice(0x8)), y = PEEK2(d.slice(0xa)), dx, dy, addr = PEEK2(d.slice(0xc));
let i, n, twobpp = !!(d[0xf] & 0x80) ? 1 : 0;
let layer: Layer = (d[0xf] & 0x40) ? uxn_screen.fg : uxn_screen.bg;

n = d[0x6] >> 4;
dx = (d[0x6] & 0x01) << 3;
dy = (d[0x6] & 0x02) << 2;

if(addr > 0x10000 - ((n + 1) << (3 + twobpp)))
return;
for(i = 0; i <= n; i++) {
if(!(d[0xf] & 0xf))
screen_wipe(uxn_screen, layer, x + dy * i, y + dx * i);
else {
screen_blit(uxn_screen, layer, x + dy * i, y + dx * i, ram.slice(addr), d[0xf] & 0xf, d[0xf] & 0x10, d[0xf] & 0x20, twobpp);
addr += (d[0x6] & 0x04) << (1 + twobpp);
}
}
POKE2(d, (0xc), addr); /* auto addr+length */
POKE2(d, (0x8), x + dx); /* auto x+8 */
POKE2(d, (0xa), y + dy); /* auto y+8 */
break;
}
}
}
101 changes: 85 additions & 16 deletions devices/system.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,54 @@
import { PAGE_PROGRAM , Uxn} from '../uxn'
import { RAM_PAGES } from '../uxncli'
// import { readFileSync } from 'fs';
import { out, outError } from '../out';
import { PAGE_PROGRAM, PEEK2, Stack, Uxn, uxn_eval} from '../uxn'
import { RAM_PAGES } from '../uxnemu'

export function system_load(u: Uxn, filename: string): number {
//const f = readFileSync(filename);
const f = [128, 104, 128, 24, 23, 128, 101, 128, 24, 23, 128, 108, 128, 24, 23, 128, 108, 128, 24, 23, 128, 111, 128, 24, 23, 128, 10, 128, 24, 23];
const errors: string[] = [
"underflow",
"overflow",
"division by zero"
];

function system_print(s: Stack, name: string): void {
out(`<${name}>`);

for (let i = 0; i < s.ptr; i++) {
out(` ${s.dat[i].toString(16).padStart(2, '0')}`);
}
if (s.ptr === 0) {
out(' empty');
}
out('\n');
}

function system_cmd(ram: number[], addr: number)
{
if(ram[addr] == 0x01) {
let i, length = PEEK2(ram.slice(addr + 1));
const a_page = PEEK2(ram.slice(addr + 1 + 2));
const a_addr = PEEK2(ram.slice(addr + 1 + 4));
const b_page = PEEK2(ram.slice(addr + 1 + 6));
const b_addr = PEEK2(ram.slice(addr + 1 + 8));
const src = (a_page % RAM_PAGES) * 0x10000;
const dst = (b_page % RAM_PAGES) * 0x10000;

for(i = 0; i < length; i++)
ram[dst + (b_addr + i)] = ram[src + (a_addr + i)];
}
}

function system_inspect(u: Uxn)
{
system_print(u.wst, "wst");
system_print(u.rst, "rst");
}

// const HelloWorldRom = [128, 104, 128, 24, 23, 128, 101, 128, 24, 23, 128, 108, 128, 24, 23, 128, 108, 128, 24, 23, 128, 111, 128, 24, 23, 128, 10, 128, 24, 23];
const HelloWorldRom = [
160,1,61,128,32,55,160,240,127,128,8,55,160,240,224,128,10,55,160,240,192,128,12,55,160,1,0,128,34,55,160,0,176,128,36,55,128,34,54,128,1,63,128,0,49,128,36,54,128,1,63,128,2,49,96,1,119,96,0,87,0,160,0,0,161,128,250,51,128,1,63,160,0,31,60,160,0,64,57,38,128,40,55,128,42,55,160,3,35,96,0,252,128,0,48,160,0,112,57,128,40,55,128,2,48,160,0,72,57,128,42,55,160,3,195,128,200,50,160,0,255,60,128,2,31,128,7,28,128,64,31,56,128,44,55,128,143,128,47,23,96,1,33,96,0,1,0,128,0,48,160,0,96,57,128,40,55,128,2,48,160,0,72,57,128,42,55,160,3,67,128,44,55,128,246,128,38,23,128,5,128,47,23,128,0,48,160,0,112,57,128,40,55,128,2,48,160,0,56,57,128,42,55,160,3,67,128,44,55,128,245,128,38,23,128,5,128,47,23,128,0,128,38,23,160,3,51,128,44,55,128,0,128,0,7,128,15,28,128,48,31,128,0,48,160,0,96,57,56,128,40,55,128,0,7,128,240,28,128,1,31,128,2,48,160,0,56,57,56,128,42,55,6,128,47,23,1,6,32,255,207,2,108,24,15,160,3,51,128,44,55,128,42,55,128,40,55,128,1,128,38,23,79,128,47,151,128,2,128,38,23,4,128,16,24,4,151,128,40,182,160,0,8,57,5,55,128,1,128,38,23,4,128,16,24,4,151,4,128,16,24,4,23,108,128,44,55,160,1,38,23,128,40,54,128,61,51,128,34,54,128,3,63,3,128,8,24,128,18,19,128,36,54,128,3,63,3,128,8,24,128,0,6,128,2,31,15,128,0,128,0,6,128,2,31,207,24,128,1,28,128,47,23,1,138,32,255,239,128,42,182,160,0,8,56,5,55,160,0,0,128,40,55,34,66,1,138,32,255,208,34,108,128,1,128,38,23,128,0,48,160,0,48,56,128,40,55,128,2,48,160,0,72,57,128,42,55,128,34,54,96,0,14,160,3,43,128,44,55,128,1,128,47,23,128,36,54,128,0,128,27,19,160,39,16,96,0,51,160,3,232,96,0,45,160,0,100,96,0,39,160,0,10,96,0,33,3,6,128,0,8,32,0,23,128,255,128,246,19,128,0,7,128,48,31,160,3,67,56,128,44,55,128,1,128,47,23,2,108,187,6,128,219,14,58,57,108,170,85,170,85,170,85,170,85,0,0,0,24,24,0,0,0,15,56,103,95,223,191,191,191,0,7,24,32,35,68,72,72,0,124,130,130,130,130,130,124,0,48,16,16,16,16,16,16,0,124,130,2,124,128,128,254,0,124,130,2,28,2,130,124,0,12,20,36,68,132,254,4,0,254,128,128,124,2,130,124,0,124,130,128,252,130,130,124,0,124,130,2,30,2,2,2,0,124,130,130,124,130,130,124,0,124,130,130,126,2,130,124,0,124,130,2,126,130,130,126,0,252,130,130,252,130,130,252,0,124,130,128,128,128,130,124,0,252,130,130,130,130,130,252,0,124,130,128,240,128,130,124,0,124,130,128,240,128,128,128,0,0,0,24,24,0,0,0,195,129,0,0,0,0,129,195,0,0,24,60,60,24,0,0,0,0,0,0,0,0,0,0,0,24,60,126,126,60,24,0,0,0,0,0,0,0,0,0,60,126,255,231,231,255,126,60,0,0,0,24,24,0,0,0,255,255,231,195,195,231,255,255,0,0,24,60,60,24,0,0,255,231,195,129,129,195,231,255,0,24,60,126,126,60,24,0,195,129,0,0,0,0,129,195,60,126,255,231,231,255,126,60,0,0,0,0,0,0,0,0,255,255,231,195,195,231,255,255,0,0,0,0,0,0,0,0,255,231,195,129,129,195,231,255
]

export function system_load(u: Uxn): number {
const f = (window as any).rom || HelloWorldRom;

if (!f) {
return 0;
Expand All @@ -16,16 +60,41 @@ export function system_load(u: Uxn, filename: string): number {
u.ram[PAGE_PROGRAM + i] = (f.at(i) || 0)
}

// let data = new Uint8Array();
// let l = f.copy(data, PAGE_PROGRAM, 0, 0x10000 - PAGE_PROGRAM);
return 1;
}

// console.log(f)

// let i = 0;

// while (l && ++i < RAM_PAGES) {
// l = f.copy(data, 0x10000 * i, 0, 0x10000);
// }
/* IO */

return 1;
export function system_deo(u: Uxn, d: number[], port: number)
{
switch(port) {
case 0x3:
system_cmd(u.ram, PEEK2(d.slice(2)));
break;
case 0xe:
system_inspect(u);
break;
}
}

/* Error */

export function uxn_halt(u: Uxn, instr: number, err: number, addr: number)
{
const d = u.dev.slice(0x00);
const handler = PEEK2(d);

if(handler) {
u.wst.ptr = 4;
u.wst.dat[0] = addr >> 0x8;
u.wst.dat[1] = addr & 0xff;
u.wst.dat[2] = instr;
u.wst.dat[3] = err;

return uxn_eval(u, handler);
} else {
system_inspect(u);
outError(`${(instr & 0x40) ? "Return-stack" : "Working-stack"} ${errors[err - 1]}, by ${instr.toString(16).padStart(2, '0')} at 0x${addr.toString(16).padStart(4, '0')}.\n`);
}
return 0;
}
50 changes: 48 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,57 @@
requirejs.config({
baseUrl: '',
paths: {
'uxncli': 'bundle'
'uxnemu': 'bundle'
}
});

// Load the entry module
requirejs(['uxncli']);
requirejs(['uxnemu']);
</script>

<body>
<div class="out" id="out">
<pre class="stdlabel">UXN ts</pre>
<span class="stdout-label">stdout:</span>
<pre id="stdout"></pre>
<span class="stderr-label">stderr:</span>
<pre id="stderr"></pre>
</div>

<canvas width="512" height="320" id="canvas">
</canvas>

<style>
body {
display: flex;
flex-direction: column;
}

.out {
background-color: #282a36;
padding: 1em;
border-radius: 10px;
margin: 1em;
color: white;
font-family: monospace;
}

#canvas {
margin: 0 auto;
border: 2px solid #282a36;
}

.stdout-label {
color: #9aedf7;
}

.stderr-label {
color: #ff91d1;
}

#stdout, #stderr {
display: block;
}
</style>
</body>
</html>
Loading

0 comments on commit 6cc195e

Please sign in to comment.