Web-Ide mit aufgenommen
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
import { assertExists } from "@davidsouther/jiffies/lib/esm/assert.js";
|
||||
import { Memory } from "@nand2tetris/simulator/cpu/memory.js";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { useClockFrame, useClockReset } from "../clockface.js";
|
||||
|
||||
const WHITE = "white";
|
||||
const BLACK = "black";
|
||||
type COLOR = typeof WHITE | typeof BLACK;
|
||||
|
||||
export interface ScreenMemory {
|
||||
get(idx: number): number;
|
||||
}
|
||||
|
||||
export function reduceScreen(memory: Memory, offset = 0): ScreenMemory {
|
||||
return {
|
||||
get(idx: number): number {
|
||||
return memory.get(offset + idx);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function get(mem: ScreenMemory, x: number, y: number): COLOR {
|
||||
const byte = mem.get(32 * y + ((x / 16) | 0));
|
||||
const bit = byte & (1 << x % 16);
|
||||
return bit === 0 ? WHITE : BLACK;
|
||||
}
|
||||
|
||||
function set(data: Uint8ClampedArray, x: number, y: number, value: COLOR) {
|
||||
const pixel = (y * 512 + x) * 4;
|
||||
const color = value === WHITE ? 255 : 0;
|
||||
data[pixel] = color;
|
||||
data[pixel + 1] = color;
|
||||
data[pixel + 2] = color;
|
||||
data[pixel + 3] = 255;
|
||||
}
|
||||
|
||||
function drawImage(ctx: CanvasRenderingContext2D, memory: ScreenMemory) {
|
||||
const image = assertExists(
|
||||
ctx.getImageData(0, 0, 512, 256),
|
||||
"Failed to create Context2d",
|
||||
);
|
||||
for (let col = 0; col < 512; col++) {
|
||||
for (let row = 0; row < 256; row++) {
|
||||
const color = get(memory, col, row);
|
||||
set(image.data, col, row, color);
|
||||
}
|
||||
}
|
||||
ctx.putImageData(image, 0, 0);
|
||||
}
|
||||
|
||||
export type ScreenScales = 0 | 1 | 2;
|
||||
|
||||
export const Screen = ({
|
||||
memory,
|
||||
showScaleControls = false,
|
||||
scale = 1,
|
||||
onScale,
|
||||
}: {
|
||||
memory: ScreenMemory;
|
||||
showScaleControls?: boolean;
|
||||
scale?: ScreenScales;
|
||||
onScale?: (scale: ScreenScales) => void;
|
||||
}) => {
|
||||
const canvas = useRef<HTMLCanvasElement>();
|
||||
const [screenScale, setScreenScale] = useState<ScreenScales>(scale);
|
||||
|
||||
const onScaleCB = (scale: ScreenScales) => {
|
||||
onScale?.(scale);
|
||||
setScreenScale(scale);
|
||||
};
|
||||
|
||||
const draw = useCallback(() => {
|
||||
const ctx =
|
||||
canvas.current?.getContext("2d", { willReadFrequently: true }) ??
|
||||
undefined;
|
||||
|
||||
if (ctx) {
|
||||
drawImage(ctx, memory);
|
||||
}
|
||||
}, [memory]);
|
||||
|
||||
const ctxRef = useCallback(
|
||||
(ref: HTMLCanvasElement | null) => {
|
||||
canvas.current = ref ?? undefined;
|
||||
draw();
|
||||
},
|
||||
[canvas, draw],
|
||||
);
|
||||
|
||||
useClockFrame(draw);
|
||||
useClockReset(() => {
|
||||
canvas.current
|
||||
?.getContext("2d")
|
||||
?.clearRect(0, 0, canvas.current.width, canvas.current.height);
|
||||
});
|
||||
|
||||
return (
|
||||
<article className="panel">
|
||||
<header>
|
||||
<div>Screen</div>
|
||||
{showScaleControls && (
|
||||
<fieldset role="group">
|
||||
<button
|
||||
aria-current={screenScale === 0}
|
||||
onClick={() => onScaleCB(0)}
|
||||
>
|
||||
x0
|
||||
</button>
|
||||
<button
|
||||
aria-current={screenScale === 1}
|
||||
onClick={() => onScaleCB(1)}
|
||||
>
|
||||
x1
|
||||
</button>
|
||||
<button
|
||||
aria-current={screenScale === 2}
|
||||
onClick={() => onScaleCB(2)}
|
||||
>
|
||||
x2
|
||||
</button>
|
||||
</fieldset>
|
||||
)}
|
||||
</header>
|
||||
{screenScale > 0 && (
|
||||
<main style={{ backgroundColor: "var(--code-background-color)" }}>
|
||||
<figure
|
||||
style={{
|
||||
width: `${512 * screenScale}px`,
|
||||
height: `${256 * screenScale}px`,
|
||||
boxSizing: "content-box",
|
||||
marginInline: "auto",
|
||||
margin: "auto",
|
||||
borderTop: "2px solid gray",
|
||||
borderLeft: "2px solid gray",
|
||||
borderBottom: "2px solid lightgray",
|
||||
borderRight: "2px solid lightgray",
|
||||
}}
|
||||
>
|
||||
<canvas
|
||||
ref={ctxRef}
|
||||
width={512}
|
||||
height={256}
|
||||
style={{
|
||||
transform: `translate(-50%, -50%) scale(${screenScale}) translate(50%, 50%)`,
|
||||
imageRendering: "pixelated",
|
||||
}}
|
||||
></canvas>
|
||||
</figure>
|
||||
</main>
|
||||
)}
|
||||
</article>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user