import React, { useState, useEffect, useRef } from 'react'; import { Settings, RefreshCw, Hexagon, FileText, Binary, Hash } from 'lucide-react'; import { MEMORY_SIZE } from '../types'; interface MemoryViewerProps { memoryView: React.MutableRefObject; } type ViewMode = 'float' | 'int16' | 'int32' | 'byte' | 'hex' | 'ascii'; export const MemoryViewer: React.FC = ({ memoryView }) => { const [viewMode, setViewMode] = useState('float'); const [startAddr, setStartAddr] = useState(0); const [rows, setRows] = useState(20); const [, setTick] = useState(0); // For forcing re-render // Auto-refresh memory view useEffect(() => { const interval = setInterval(() => { setTick(t => t + 1); }, 100); // 10Hz refresh return () => clearInterval(interval); }, []); const getStride = () => { switch (viewMode) { case 'float': return 4; case 'int32': return 4; case 'int16': return 2; default: return 1; } }; const getCols = () => { switch (viewMode) { case 'float': case 'int32': return 4; // 4 items per row case 'int16': return 8; // 8 items per row default: return 16; // 16 bytes per row } }; const stride = getStride(); const cols = getCols(); const rowSize = stride * cols; const totalRows = Math.ceil(MEMORY_SIZE / rowSize); // Pagination / Scroll Helper const onScroll = (e: React.UIEvent) => { const top = e.currentTarget.scrollTop; const rowHeight = 24; // approx const startRow = Math.floor(top / rowHeight); // Optimization: Just render what's needed? // For now, let's keep it simple: render from startAddr relative to scroll if virtual, // or just use a fixed window if we don't assume full scroll height. // Let's implement full scroll height logic: // Actually simplicity first: User can jump addresses. }; const renderCell = (addr: number) => { if (addr >= MEMORY_SIZE) return -; try { const view = memoryView.current; let val: string | number = ''; let color = 'text-gray-400'; switch (viewMode) { case 'float': val = view.getFloat32(addr, true).toFixed(4); if (parseFloat(val) !== 0) color = 'text-blue-400 font-bold'; break; case 'int32': val = view.getInt32(addr, true); if (val !== 0) color = 'text-green-400 font-bold'; break; case 'int16': val = view.getInt16(addr, true); if (val !== 0) color = 'text-green-400 font-bold'; break; case 'byte': val = view.getUint8(addr); if (val !== 0) color = 'text-yellow-400 font-bold'; break; case 'hex': val = view.getUint8(addr).toString(16).toUpperCase().padStart(2, '0'); if (val !== '00') color = 'text-purple-400 font-bold'; break; case 'ascii': const charCode = view.getUint8(addr); // Printable ASCII 32-126 val = (charCode >= 32 && charCode <= 126) ? String.fromCharCode(charCode) : '.'; if (val !== '.') color = 'text-orange-400 font-bold'; else color = 'text-gray-700'; break; } return {val}; } catch (e) { return ERR; } }; // Generate rows const visibleRows = []; // For scrolling, let's just make a really tall div and stick the viewport? // Or just a simple Pager for now to ensure performance? // "Layout | Logic | Memory" - full screen. // Let's do a virtualized-like list but fixed to logical startAddr for manual navigation? // Or just render all 10000 bytes? // 10000 / 16 = 625 rows. Not too bad for React. // Let's render all rows (clamped to sensible limit if needed, but 600 rows is fine if simple) // Actually, let's render *only* visible based on scrollTop if we can, or just render all for simplicity if performant. // 600 rows of 16 cols = 9600 elements. Might be heavy on 10Hz re-render. // Let's render a "Window" window around `startAddr`. const displayRows = 50; // Show 50 rows at a time (enough for screen) return (
{/* Toolbar */}
Data View:
Go To Address: setStartAddr(Math.max(0, parseInt(e.target.value) || 0))} />
{/* Grid Content */}
{/* Headers +0, +1 ... */} {Array.from({ length: cols }).map((_, i) => ( ))} {Array.from({ length: 100 }).map((_, rIndex) => { // Render 100 rows starting from startAddr const rowAddr = startAddr + (rIndex * rowSize); if (rowAddr >= MEMORY_SIZE) return null; const isReserved = rowAddr >= 8000; return ( {Array.from({ length: cols }).map((_, cIndex) => { const cellAddr = rowAddr + (cIndex * stride); return ( ); })} {/* Optional ASCII side-view for the whole row */} ); })} {/* Empty row to indicate reserved space start if not visible? No, pure highlighting is enough */}
Address +{(i * stride).toString(16).toUpperCase().padStart(2, '0')} ASCII / Interpretation
M{rowAddr.toString().padStart(4, '0')} {renderCell(cellAddr)} {Array.from({ length: rowSize }).map((_, b) => { const bAddr = rowAddr + b; if (bAddr >= MEMORY_SIZE) return ''; const c = memoryView.current.getUint8(bAddr); return (c >= 32 && c <= 126) ? String.fromCharCode(c) : '.'; }).join('')}
); };