import { h } from 'preact'; import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; import { toHex } from 'js/util'; import ApplesoftDecompiler from 'js/applesoft/decompiler'; import { ApplesoftHeap, ApplesoftVariable } from 'js/applesoft/heap'; import { Apple2 as Apple2Impl } from 'js/apple2'; import styles from './css/Applesoft.module.css'; import debuggerStyles from './css/Debugger.module.css'; export interface ApplesoftProps { apple2: Apple2Impl | undefined; } interface ApplesoftData { variables: ApplesoftVariable[]; internals: { txttab?: number; fac?: number; arg?: number; curline?: number; }; listing: string; } const TYPE_SYMBOL = ['', '$', '()', '%'] as const; const TYPE_NAME = ['Float', 'String', 'Function', 'Integer'] as const; const formatArray = (value: unknown): string => { if (Array.isArray(value)) { if (Array.isArray(value[0])) { return `[${value.map((x) => formatArray(x)).join(',\n ')}]`; } else { return `[${value.map((x) => formatArray(x)).join(', ')}]`; } } else { return `${JSON.stringify(value)}`; } }; const Variable = ({ variable }: { variable: ApplesoftVariable }) => { const { name, type, sizes, value } = variable; const isArray = !!sizes; const arrayStr = isArray ? `(${sizes.map((size) => size - 1).join(',')})` : ''; return ( {name}{TYPE_SYMBOL[type]}{arrayStr} {TYPE_NAME[type]}{isArray ? ' Array' : ''}
{isArray ? formatArray(value) : value}
); }; export const Applesoft = ({ apple2 }: ApplesoftProps) => { const animationRef = useRef(0); const [data, setData] = useState({ listing: '', variables: [], internals: {} }); const [heap, setHeap] = useState(); const cpu = apple2?.getCPU(); useEffect(() => { if (cpu) { // setDecompiler(); setHeap(new ApplesoftHeap(cpu)); } }, [cpu]); const animate = useCallback(() => { if (cpu && heap) { try { const decompiler = ApplesoftDecompiler.decompilerFromMemory(cpu); setData({ variables: heap.dumpVariables(), internals: heap.dumpInternals(), listing: decompiler.decompile() }); } catch (error) { if (error instanceof Error) { setData({ variables: [], internals: {}, listing: error.message }); } else { throw error; } } } animationRef.current = requestAnimationFrame(animate); }, [cpu, heap]); useEffect(() => { animationRef.current = requestAnimationFrame(animate); return () => cancelAnimationFrame(animationRef.current); }, [animate]); const { listing, internals, variables } = data; return (
Listing
{listing}
Variables
{variables.map((variable, idx) => )}
Name Type Value
Internals
TXTTAB {toHex(internals.txttab ?? 0)} FAC {internals.fac}
ARG {internals.arg} CURLINE {internals.curline}
); };