import { CPU } from '../cpu/cpu' ; import { assemble } from './assembler' ; import { avrInstruction } from '../cpu/instruction' ; const BREAK_OPCODE = 0x9598 ; export function asmProgram ( source ) { const { bytes , errors , lines , labels } = assemble ( source ) ; if ( errors . length ) { throw new Error ( 'Assembly failed: ' + errors ) ; } return { program : new Uint16Array ( bytes . buffer ) , lines , instructionCount : lines . length , labels } ; } const defaultOnBreak = ( ) => { throw new Error ( 'BREAK instruction encountered' ) ; } ; export class TestProgramRunner { constructor ( private readonly cpu , private readonly onBreak = defaultOnBreak ) { } runInstructions ( count ) { const { cpu , onBreak } = this ; for ( let i = 0 ; i < count ; i ++ ) { if ( cpu . progMem [ cpu . pc ] === BREAK_OPCODE ) { onBreak ?. ( cpu ) ; } avrInstruction ( cpu ) ; cpu . tick ( ) ; } } runUntil ( predicate , maxIterations = 5000 ) { const { cpu , onBreak } = this ; for ( let i = 0 ; i < maxIterations ; i ++ ) { if ( cpu . progMem [ cpu . pc ] === BREAK_OPCODE ) { onBreak ?. ( cpu ) ; } if ( predicate ( cpu ) ) { return ; } avrInstruction ( cpu ) ; cpu . tick ( ) ; } throw new Error ( 'Test program ran for too long, check your predicate' ) ; } runToBreak ( ) { this . runUntil ( ( cpu ) => cpu . progMem [ cpu . pc ] === BREAK_OPCODE ) ; } runToAddress ( byteAddr ) { this . runUntil ( ( cpu ) => cpu . pc * 2 === byteAddr ) ; } }